mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
fix(e2e): preserve share query context in diagnostic evidence
This commit is contained in:
@@ -43,6 +43,14 @@ The local recorder unit tests are not browser/privacy/SSO acceptance. Actual ano
|
||||
6. **Before republishing**, create the step7 local comment via Owner UI; capture its actual POST ID. Edit actual rendered content. Enter a real unsent visitor draft. Owner publishes, copies UI link again; verify stable URL and fresh page loads changed Owner output. Original page/draft must remain. If real toast is visible, its actual refresh button is exercised with native confirm: cancel preserves draft/document, accept reloads the same URL into new content and clears draft. No window.confirm replacement, page.reload shortcut, clock acceleration, or injected version event.
|
||||
7. Only the captured pre-update local comment ID may satisfy public-read and visitor-list checks. Failure branch is not fault-injected. Missing delivery is UNKNOWN until a real terminal/backfill status distinguishes it from latency; it is never marked successful by note text alone.
|
||||
|
||||
## Rechecking an observed endpoint
|
||||
|
||||
Evidence URLs are **not replay inputs**. Every HTTP record labels its URL `urlPurpose: diagnostic-only`; userinfo/fragments and unrelated query values may have been removed or redacted. The recorder preserves `projectId`, `filePath` and `shareAlias` to explain routing, not to promise transport equivalence.
|
||||
|
||||
A permitted read-only recheck must reuse the actual live product request's full URL, including its complete query string, unchanged in memory. Never strip query parameters, build a URL from the pathname, or send a redacted report URL. Do not export credential-bearing URLs or copy auth headers/cookies into evidence. If the live request is no longer available, observe a fresh authorized product request instead of guessing missing values.
|
||||
|
||||
For stable-alias discovery, the actual metadata request carries `projectId` and `shareAlias=1`; the observed comment read carries `projectId` and `filePath`. Missing pairing context can correctly return not-found to prevent probing. A404 from an incomplete reconstructed request does not establish that the original artifact/comments were deleted. Preserve such a failed measurement with its correction; do not rewrite it into successful evidence.
|
||||
|
||||
## Human login handoff
|
||||
|
||||
Do not open a headed window until the human is ready and the browser seat has been checked using exact executable names. Use an isolated ephemeral context and follow the real share-page comment entry to login; never construct a replacement authentication URL. The human alone operates authentication fields, including email. Do not inspect field values, record the credential-entry process, take login screenshots, export cookies/storageState, or use capture/registration intermediate sessions as an authentication shortcut. No traces, HAR or video during authentication.
|
||||
|
||||
@@ -12,19 +12,20 @@ export interface HttpResponseSource {
|
||||
function parseEvidenceUrl(value: string): URL | null {
|
||||
try { return new URL(value); } catch { return null; }
|
||||
}
|
||||
/** Never persist URL userinfo, fragments, or arbitrary query parameters. */
|
||||
/** Diagnostic output only, never a transport URL. Rechecks must use the live
|
||||
* request URL unchanged, not a path reconstructed from this redacted record. */
|
||||
export function evidenceUrl(value: string): string {
|
||||
const url = parseEvidenceUrl(value);
|
||||
if (!url) return 'invalid:<REDACTED>';
|
||||
if (!['http:', 'https:'].includes(url.protocol)) return `${url.protocol}<REDACTED>`;
|
||||
url.username = ''; url.password = ''; url.hash = '';
|
||||
for (const key of [...url.searchParams.keys()]) {
|
||||
if (!['projectId', 'filePath'].includes(key)) url.searchParams.set(key, '<REDACTED>');
|
||||
if (!['projectId', 'filePath', 'shareAlias'].includes(key)) url.searchParams.set(key, '<REDACTED>');
|
||||
}
|
||||
return url.href;
|
||||
}
|
||||
export function captureHttpRequest(request: HttpRequestSource, page: string) {
|
||||
return { page, at: Date.now(), method: request.method(), type: request.resourceType(), url: evidenceUrl(request.url()) };
|
||||
return { page, at: Date.now(), method: request.method(), type: request.resourceType(), url: evidenceUrl(request.url()), urlPurpose: 'diagnostic-only' as const };
|
||||
}
|
||||
/** Only business bodies needed by the probe are read. Identity/directory/auth bodies are not evidence inputs. */
|
||||
export function mayReadEvidenceBody(pathname: string): boolean {
|
||||
|
||||
@@ -32,6 +32,22 @@ describe('share probe evidence (local helper tests, not live acceptance)', () =>
|
||||
expect(await captureHttpResponse(response('not a URL secret', json), 'visitor')).toMatchObject({ url: 'invalid:<REDACTED>', bodyRead: 'not-requested' });
|
||||
expect(json).not.toHaveBeenCalled();
|
||||
});
|
||||
it.each([
|
||||
'https://console.invalid/api/v1/public/snapshots/alias?projectId=p&shareAlias=1',
|
||||
'https://console.invalid/api/v1/collab/share/alias/comments?projectId=p&filePath=slides%2Findex.html',
|
||||
])('preserves routing context but labels evidence as non-transport: %s', original => {
|
||||
const recorded = captureHttpRequest(request(original), 'visitor');
|
||||
expect(recorded.url).toBe(original);
|
||||
expect(recorded).toHaveProperty('urlPurpose', 'diagnostic-only');
|
||||
});
|
||||
it('retains pairing parameters while redacting unrelated credentials', () => {
|
||||
const recorded = captureHttpRequest(request('https://console.invalid/api/v1/public/snapshots/alias?projectId=p&shareAlias=1&token=secret'), 'visitor');
|
||||
const url = new URL(recorded.url);
|
||||
expect(url.searchParams.get('projectId')).toBe('p');
|
||||
expect(url.searchParams.get('shareAlias')).toBe('1');
|
||||
expect(url.searchParams.get('token')).toBe('<REDACTED>');
|
||||
expect(recorded.url).not.toContain('secret');
|
||||
});
|
||||
it('redacts credentials and arbitrary query values', () => {
|
||||
const url = new URL(evidenceUrl('https://u:secret@console.invalid/api/x?token=secret&projectId=p#secret'));
|
||||
expect(evidenceUrl('data:text/plain,secret')).toBe('data:<REDACTED>');
|
||||
|
||||
Reference in New Issue
Block a user