CI / Build & Quality Checks (pull_request) Successful in 1m53s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 7s
CI / Playwright smoke (e2e) (pull_request) Successful in 12m51s
#266: remembered widget approvals (lotus.widgetConsent.*) survived logout, so they applied silently to the next account on the device and listed the previous account's rooms; clearPlaintextCaches now sweeps them. Space structure, call membership and Lotus room-policy state join PROTECTED_STATE_TYPES, so no widget can be granted them. #267: any website could auto-submit a form to /share-target and land the user in the share picker with attacker-chosen text and files. The worker now refuses a share whose Origin is another site. (Sec-Fetch-Site, as the issue suggested, never reaches a service worker: checked in Chromium.) Origin null/absent, as the OS share sheet sends, stays accepted, and /share now says the content came from another app or website. Both logout paths await clearSharedPayload(); the server-forced one also clears the soundboard clip cache, as #57 intended. Fixes #266 Fixes #267 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
39 lines
1.6 KiB
TypeScript
39 lines
1.6 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { isTrustedShareRequest, shareDraftText } from './swShare';
|
|
|
|
const base = { files: [], receivedAt: 0 };
|
|
|
|
test('shareDraftText joins title, text and url', () => {
|
|
assert.equal(
|
|
shareDraftText({ ...base, title: 'A page', text: 'look at this', url: 'https://x.test/p' }),
|
|
'A page\nlook at this\nhttps://x.test/p',
|
|
);
|
|
});
|
|
|
|
test('shareDraftText drops a title/url the text already contains', () => {
|
|
assert.equal(
|
|
shareDraftText({
|
|
...base,
|
|
title: 'A page',
|
|
text: 'A page https://x.test/p',
|
|
url: 'https://x.test/p',
|
|
}),
|
|
'A page https://x.test/p',
|
|
);
|
|
assert.equal(shareDraftText({ ...base, text: 'from the share sheet' }), 'from the share sheet');
|
|
assert.equal(shareDraftText({ ...base, url: 'https://x.test' }), 'https://x.test');
|
|
assert.equal(shareDraftText(base), '');
|
|
});
|
|
|
|
test('[#267] a share another website submitted is refused', () => {
|
|
const app = 'https://chat.lotusguild.org';
|
|
const h = (origin?: string) => new Headers(origin ? { Origin: origin } : {});
|
|
assert.equal(isTrustedShareRequest(h(), app), true, 'no Origin (OS share sheet)');
|
|
assert.equal(isTrustedShareRequest(h('null'), app), true, 'opaque origin (OS share sheet)');
|
|
assert.equal(isTrustedShareRequest(h(app), app), true, 'the app itself');
|
|
assert.equal(isTrustedShareRequest(h('https://evil.example'), app), false);
|
|
assert.equal(isTrustedShareRequest(h('https://chat.lotusguild.org.evil.example'), app), false);
|
|
assert.equal(isTrustedShareRequest(h('http://chat.lotusguild.org'), app), false, 'other scheme');
|
|
});
|