Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2b67c41
WIP: desktop Part B (1/n) — type foundation for the libsession-parsed…
jagerman Jul 19, 2026
8af3078
WIP: desktop Part B (2/n) — relay raw response bytes to libsession, n…
jagerman Jul 19, 2026
1e3ebc7
pro: finish request-side raw-wire relay + single-source url/pubkeys
jagerman Jul 20, 2026
0f324e3
pro: adapt response consumers to flat libsession-parsed structs
jagerman Jul 20, 2026
f3a2459
pro: re-key desktop response handling to the Delta #12 status field
jagerman Jul 20, 2026
8ef5d4a
pro: desktop-B — provider metadata via i18n + on-demand URLs
jagerman Jul 20, 2026
aad2867
Pro: dynamic provider display, {pro_stores} store list, and error_cod…
jagerman Jul 21, 2026
f8c23d8
Pro: rename genIndexHash -> revocationTag (match libsession revocatio…
jagerman Jul 22, 2026
94e0f0d
Pro: send proof expiry as whole seconds on the wire (was milliseconds)
jagerman Jul 22, 2026
0cb446a
Pro: migrate proGenIndexHashB64 column via a new v57, not by editing v50
jagerman Jul 22, 2026
3c8516c
Pro: remove the unused request-state `t` field
jagerman Jul 22, 2026
844c6d5
Pro: don't clear the (synced) proof on an unknown backend status
jagerman Jul 22, 2026
65cf2b3
Pro: guard openProviderUrl against an empty provider URL
jagerman Jul 22, 2026
5e205e7
Pro: version the persisted get-details cache
jagerman Jul 22, 2026
5357c13
Pro: leave a transition reminder for the persisted get-details cache …
jagerman Jul 22, 2026
f6e1da3
Pro: remove hardcoded dev-backend URL/key copies; fail fast instead
jagerman Jul 22, 2026
4d8ffe7
Pro: clarify the isSimpleTokenNoArgs existence check + trim doc comment
jagerman Jul 22, 2026
a3ad394
Pro: rename ProStatus.NeverBeenPro -> Never
jagerman Jul 22, 2026
c7a77a3
Pro: desktop app consumes get_pro_status (was get_pro_details)
jagerman Jul 23, 2026
0eb631e
Pro: purge get_pro_status endpoint naming on desktop (-> ProStatus)
jagerman Jul 23, 2026
e62d7ea
Render plan period generically from {count, unit}
jagerman Jul 23, 2026
b654fa4
Drop stale Delta-N spec citations; reference current § sections
jagerman Jul 23, 2026
70f1658
Localizer: allow trusted HTML args via `htmlArgs` opt-out
jagerman Jul 23, 2026
82031e2
Purge remaining "pro details" naming for the get_pro_status result
jagerman Jul 23, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions protos/SignalService.proto
Original file line number Diff line number Diff line change
Expand Up @@ -370,9 +370,9 @@ message GroupUpdateDeleteMemberContentMessage {

message ProProof {
optional uint32 version = 1;
optional bytes genIndexHash = 2;
optional bytes revocationTag = 2;
optional bytes rotatingPublicKey = 3;
optional uint64 expireAtMs = 4; // Epoch timestamp in milliseconds
optional uint64 expiryUnixTs = 4; // Epoch timestamp in whole seconds (what the Session Pro backend signs over)
optional bytes sig = 5;
}

Expand Down
30 changes: 28 additions & 2 deletions ts/components/basic/Localizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ const StyledHtmlRenderer = styled.span`

export type WithAsTag = { asTag?: LocalizerHtmlTag };
export type WithClassName = { className?: string };
/**
* Names of args whose value is trusted, pre-formatted HTML that should skip the arg-level escaping
* applied when the template contains formatting tags. Safe only for app-controlled content: the final
* string is still run through DOMPurify (the {@link supportedFormattingTags} whitelist) by
* SessionHtmlRenderer, so only formatting survives — never scripts or attributes. NEVER list a
* user-controlled arg here.
*/
export type WithHtmlArgs = { htmlArgs?: Array<string> };

/**
* Retrieve a localized message string, substituting dynamic parts where necessary and formatting it as HTML if necessary.
Expand All @@ -48,14 +56,32 @@ export type WithClassName = { className?: string };
* @returns The localized message string with substitutions and formatting applied.
*/
export const Localizer = <T extends MergedLocalizerTokens>(
props: GetMessageArgs<T> & WithAsTag & WithClassName
props: GetMessageArgs<T> & WithAsTag & WithClassName & WithHtmlArgs
) => {
const args = messageArgsToArgsOnly(props);

let rawString: string = getRawMessage<T>(getCrowdinLocale(), props);

const containsFormattingTags = createSupportedFormattingTagsRegex().test(rawString);
const cleanArgs = args && containsFormattingTags ? sanitizeArgs(args) : args;
// When the template has formatting tags we HTML-escape the args so dynamic values can't inject
// markup. Args named in `htmlArgs` opt out of that escaping so they can carry trusted, pre-formatted
// HTML (e.g. a client-built <br/>• list) — still XSS-safe because SessionHtmlRenderer runs DOMPurify
// (supportedFormattingTags whitelist) on the final string.
const cleanArgs = ((): typeof args => {
if (!args || !containsFormattingTags) {
return args;
}
const htmlArgKeys = props.htmlArgs;
if (!htmlArgKeys?.length) {
return sanitizeArgs(args) as typeof args;
}
const toSanitize: Record<string, number | string> = {};
const trusted: Record<string, number | string> = {};
for (const [key, value] of Object.entries(args as Record<string, number | string>)) {
(htmlArgKeys.includes(key) ? trusted : toSanitize)[key] = value;
}
return { ...sanitizeArgs(toSanitize), ...trusted } as typeof args;
})();

const containsIcons = !!(cleanArgs && Object.keys(cleanArgs).includes('icon'));
if (containsIcons && (cleanArgs as any).icon) {
Expand Down
4 changes: 2 additions & 2 deletions ts/components/dialog/ProCTADescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { formatNumber } from '../../util/i18n/formatting/generics';
import { assertUnreachable } from '../../types/sqlSharedTypes';
import { ProIconButton } from '../buttons/ProButton';
import { CTAVariant, type ProCTAVariant } from './cta/types';
import { useProBackendProDetails } from '../../state/selectors/proBackendData';
import { useProBackendProStatus } from '../../state/selectors/proBackendData';

const variantsWithoutFeatureList = [
CTAVariant.PRO_GROUP_NON_ADMIN,
Expand Down Expand Up @@ -96,7 +96,7 @@ function FeatureList({ variant }: { variant: CTAVariant }) {
}

function ProExpiringSoonDescription() {
const { data } = useProBackendProDetails();
const { data } = useProBackendProStatus();
return <Localizer token="proExpiringSoonDescription" time={data.expiryTimeRelativeString} />;
}

Expand Down
41 changes: 19 additions & 22 deletions ts/components/dialog/debug/FeatureFlags.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { isBoolean, isNil } from 'lodash';
import { Dispatch, useCallback, useEffect, useMemo, useState } from 'react';
import { clipboard } from 'electron';
import useAsync from 'react-use/lib/useAsync';
import { ProConfig, type ProProof } from 'libsession_util_nodejs';
import { ProConfig } from 'libsession_util_nodejs';
import { getAppDispatch } from '../../../state/dispatch';
import {
getDataFeatureFlag,
Expand Down Expand Up @@ -41,7 +41,7 @@ import {
import { UserConfigWrapperActions } from '../../../webworker/workers/browser/libsession/libsession_worker_userconfig_interface';
import { isDebugMode } from '../../../shared/env_vars';
import {
useProBackendProDetails,
useProBackendProStatus,
useProBackendRefetch,
} from '../../../state/selectors/proBackendData';
import ProBackendAPI from '../../../session/apis/pro_backend_api/ProBackendAPI';
Expand Down Expand Up @@ -740,7 +740,7 @@ function ProConfigForm({
);
const [sigInput, setSigInput] = useState<string>(proConfig?.proProof.signatureHex ?? '');
const [genHashInput, setGenHashInput] = useState<string>(
proConfig?.proProof.genIndexHashB64 ?? ''
proConfig?.proProof.revocationTagB64 ?? ''
);
const [versionInput, setVersionInput] = useState<string>(
proConfig?.proProof.version.toString() ?? ''
Expand Down Expand Up @@ -780,7 +780,7 @@ function ProConfigForm({
}

function ProConfigManager({ forceUpdate }: { forceUpdate: () => void }) {
const { isFetching } = useProBackendProDetails();
const { isFetching } = useProBackendProStatus();
const refetch = useProBackendRefetch();
const [proConfig, setProConfig] = useState<ProConfig | null>(null);
const getProConfig = useCallback(async () => {
Expand Down Expand Up @@ -829,7 +829,7 @@ export const ProDebugSection = ({

const resetPro = useCallback(async () => {
await UserConfigWrapperActions.removeProConfig();
await Storage.remove(SettingsKey.proDetails);
await Storage.remove(SettingsKey.proStatus);
await Storage.remove(SettingsKey.proExpiringSoonCTA);
await Storage.remove(SettingsKey.proExpiredCTA);
dispatch(proBackendDataActions.reset({ key: 'details' }));
Expand Down Expand Up @@ -907,10 +907,10 @@ export const ProDebugSection = ({
if (!proAvailable) {
return;
}
dispatch(proBackendDataActions.refreshGetProDetailsFromProBackend({}) as any);
dispatch(proBackendDataActions.refreshGetProStatusFromProBackend({}) as any);
}}
>
Refresh Pro Details
Refresh Pro Status
</DebugButton>
<DebugButton
onClick={async () => {
Expand All @@ -924,15 +924,12 @@ export const ProDebugSection = ({
if (getFeatureFlag('debugServerRequests')) {
window?.log?.debug('getProProof response: ', response);
}
if (response?.status_code === 200) {
const proProof: ProProof = {
expiryMs: response.result.expiry_unix_ts_ms,
genIndexHashB64: response.result.gen_index_hash_b64,
rotatingPubkeyHex: response.result.rotating_pkey_hex,
version: response.result.version,
signatureHex: response.result.sig_hex,
};
await UserConfigWrapperActions.setProConfig({ proProof, rotatingSeedHex });
if (response && response.status === 'ok') {
// libsession returns a ready-made ProProof; relay it verbatim.
await UserConfigWrapperActions.setProConfig({
proProof: response.proof,
rotatingSeedHex,
});
}
}}
>
Expand All @@ -941,13 +938,13 @@ export const ProDebugSection = ({
<DebugButton
onClick={async () => {
const masterPrivKeyHex = await getProMasterKeyHex();
const response = await ProBackendAPI.getProDetails({ masterPrivKeyHex });
const response = await ProBackendAPI.getProStatus({ masterPrivKeyHex });
if (getFeatureFlag('debugServerRequests')) {
window?.log?.debug('Pro Details: ', response);
window?.log?.debug('Pro Status: ', response);
}
}}
>
Get Pro Details
Get Pro Status
</DebugButton>
<DebugButton
hide={!proAvailable}
Expand Down Expand Up @@ -983,7 +980,7 @@ export const ProDebugSection = ({
label="Current Status"
flag="mockProCurrentStatus"
options={[
{ label: 'Never Had Pro', value: ProStatus.NeverBeenPro },
{ label: 'Never Had Pro', value: ProStatus.Never },
{ label: 'Active', value: ProStatus.Active },
{ label: 'Expired', value: ProStatus.Expired },
]}
Expand All @@ -1007,7 +1004,7 @@ export const ProDebugSection = ({
visibleWithBooleanFlag="proAvailable"
visibleWithEnumFlag={{
flag: 'mockProCurrentStatus',
isVisible: v => v !== ProStatus.NeverBeenPro,
isVisible: v => v !== ProStatus.Never,
}}
/>
<FlagEnumDropdownInput
Expand All @@ -1023,7 +1020,7 @@ export const ProDebugSection = ({
visibleWithBooleanFlag="proAvailable"
visibleWithEnumFlag={{
flag: 'mockProCurrentStatus',
isVisible: v => v !== ProStatus.NeverBeenPro,
isVisible: v => v !== ProStatus.Never,
}}
/>
<FlagEnumDropdownInput
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ import { APP_URL, DURATION_SECONDS } from '../../../../session/constants';
import { getFeatureFlag } from '../../../../state/ducks/types/releasedFeaturesReduxTypes';
import { useUserSettingsCloseAction } from './userSettingsHooks';
import {
useProBackendProDetails,
useProBackendProStatus,
useProBackendRefetch,
} from '../../../../state/selectors/proBackendData';
import { focusVisibleBoxShadowOutsetStr } from '../../../../styles/focusVisible';
Expand Down Expand Up @@ -330,7 +330,7 @@ const SessionInfo = () => {
export const DefaultSettingPage = (modalState: UserSettingsModalState) => {
const dispatch = getAppDispatch();
const closeAction = useUserSettingsCloseAction(modalState);
const { t } = useProBackendProDetails();
const { t } = useProBackendProStatus();
const refetch = useProBackendRefetch();

const profileName = useOurConversationUsername() || '';
Expand Down
Loading
Loading