@reticlehq/browser 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +5 -0
- package/dist/actions/actions.d.ts +1 -1
- package/dist/actions/actions.d.ts.map +1 -1
- package/dist/actions/actions.js +37 -19
- package/dist/actions/actions.js.map +1 -1
- package/dist/commands/commands.d.ts +2 -0
- package/dist/commands/commands.d.ts.map +1 -1
- package/dist/commands/commands.js +20 -6
- package/dist/commands/commands.js.map +1 -1
- package/dist/dom/a11y.d.ts +17 -6
- package/dist/dom/a11y.d.ts.map +1 -1
- package/dist/dom/a11y.js +68 -32
- package/dist/dom/a11y.js.map +1 -1
- package/dist/dom/auto-anchor.d.ts +10 -10
- package/dist/dom/auto-anchor.js +10 -10
- package/dist/dom/blind-spots.d.ts +14 -0
- package/dist/dom/blind-spots.d.ts.map +1 -0
- package/dist/dom/blind-spots.js +30 -0
- package/dist/dom/blind-spots.js.map +1 -0
- package/dist/dom/chart.d.ts +93 -0
- package/dist/dom/chart.d.ts.map +1 -0
- package/dist/dom/chart.js +119 -0
- package/dist/dom/chart.js.map +1 -0
- package/dist/dom/dom-ignore.d.ts.map +1 -1
- package/dist/dom/dom-ignore.js +7 -2
- package/dist/dom/dom-ignore.js.map +1 -1
- package/dist/dom/query.d.ts +17 -4
- package/dist/dom/query.d.ts.map +1 -1
- package/dist/dom/query.js +185 -30
- package/dist/dom/query.js.map +1 -1
- package/dist/dom/refs.d.ts +25 -2
- package/dist/dom/refs.d.ts.map +1 -1
- package/dist/dom/refs.js +78 -3
- package/dist/dom/refs.js.map +1 -1
- package/dist/dom/snapshot.d.ts +6 -0
- package/dist/dom/snapshot.d.ts.map +1 -1
- package/dist/dom/snapshot.js +6 -1
- package/dist/dom/snapshot.js.map +1 -1
- package/dist/dom/source.d.ts +40 -0
- package/dist/dom/source.d.ts.map +1 -0
- package/dist/dom/source.js +59 -0
- package/dist/dom/source.js.map +1 -0
- package/dist/dom/theme.js +2 -2
- package/dist/dom/theme.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/observers/blind-spots.d.ts +19 -0
- package/dist/observers/blind-spots.d.ts.map +1 -0
- package/dist/observers/blind-spots.js +89 -0
- package/dist/observers/blind-spots.js.map +1 -0
- package/dist/observers/console.d.ts.map +1 -1
- package/dist/observers/console.js +39 -6
- package/dist/observers/console.js.map +1 -1
- package/dist/observers/dom.d.ts.map +1 -1
- package/dist/observers/dom.js +68 -9
- package/dist/observers/dom.js.map +1 -1
- package/dist/observers/focus.d.ts +10 -0
- package/dist/observers/focus.d.ts.map +1 -0
- package/dist/observers/focus.js +49 -0
- package/dist/observers/focus.js.map +1 -0
- package/dist/observers/network-body.d.ts +19 -0
- package/dist/observers/network-body.d.ts.map +1 -0
- package/dist/observers/network-body.js +143 -0
- package/dist/observers/network-body.js.map +1 -0
- package/dist/observers/network-redact.d.ts +8 -0
- package/dist/observers/network-redact.d.ts.map +1 -0
- package/dist/observers/network-redact.js +66 -0
- package/dist/observers/network-redact.js.map +1 -0
- package/dist/observers/network.d.ts +13 -6
- package/dist/observers/network.d.ts.map +1 -1
- package/dist/observers/network.js +288 -162
- package/dist/observers/network.js.map +1 -1
- package/dist/observers/route.d.ts.map +1 -1
- package/dist/observers/route.js +15 -9
- package/dist/observers/route.js.map +1 -1
- package/dist/observers/scroll.d.ts.map +1 -1
- package/dist/observers/scroll.js +35 -8
- package/dist/observers/scroll.js.map +1 -1
- package/dist/observers/state.d.ts +20 -0
- package/dist/observers/state.d.ts.map +1 -0
- package/dist/observers/state.js +87 -0
- package/dist/observers/state.js.map +1 -0
- package/dist/observers/storage.d.ts +9 -0
- package/dist/observers/storage.d.ts.map +1 -1
- package/dist/observers/storage.js +105 -3
- package/dist/observers/storage.js.map +1 -1
- package/dist/observers/types.d.ts +15 -0
- package/dist/observers/types.d.ts.map +1 -1
- package/dist/observers/types.js +29 -1
- package/dist/observers/types.js.map +1 -1
- package/dist/patching/capture-method.d.ts +27 -0
- package/dist/patching/capture-method.d.ts.map +1 -0
- package/dist/patching/capture-method.js +37 -0
- package/dist/patching/capture-method.js.map +1 -0
- package/dist/presenter/presenter-config.d.ts +3 -3
- package/dist/presenter/presenter-config.d.ts.map +1 -1
- package/dist/presenter/presenter-config.js +3 -3
- package/dist/presenter/presenter-config.js.map +1 -1
- package/dist/presenter/presenter-controls.d.ts +1 -1
- package/dist/presenter/presenter-controls.d.ts.map +1 -1
- package/dist/presenter/presenter-controls.js +3 -3
- package/dist/presenter/presenter-controls.js.map +1 -1
- package/dist/presenter/presenter-glow.d.ts +1 -1
- package/dist/presenter/presenter-glow.js +1 -1
- package/dist/presenter/presenter-styles.d.ts +1 -1
- package/dist/presenter/presenter-styles.d.ts.map +1 -1
- package/dist/presenter/presenter-styles.js +12 -3
- package/dist/presenter/presenter-styles.js.map +1 -1
- package/dist/presenter/presenter.d.ts +4 -4
- package/dist/presenter/presenter.d.ts.map +1 -1
- package/dist/presenter/presenter.js +5 -5
- package/dist/presenter/presenter.js.map +1 -1
- package/dist/recorder/recorder.d.ts +8 -8
- package/dist/recorder/recorder.d.ts.map +1 -1
- package/dist/recorder/recorder.js +9 -9
- package/dist/recorder/recorder.js.map +1 -1
- package/dist/registry/capabilities.d.ts +2 -2
- package/dist/registry/capabilities.d.ts.map +1 -1
- package/dist/registry/capabilities.js +1 -1
- package/dist/registry/capabilities.js.map +1 -1
- package/dist/registry/domains.d.ts +1 -1
- package/dist/registry/domains.js +1 -1
- package/dist/registry/emitter.d.ts +2 -2
- package/dist/registry/emitter.js +2 -2
- package/dist/registry/store-adapters.d.ts +128 -0
- package/dist/registry/store-adapters.d.ts.map +1 -0
- package/dist/registry/store-adapters.js +135 -0
- package/dist/registry/store-adapters.js.map +1 -0
- package/dist/registry/stores.d.ts +38 -2
- package/dist/registry/stores.d.ts.map +1 -1
- package/dist/registry/stores.js +109 -8
- package/dist/registry/stores.js.map +1 -1
- package/dist/reticle.d.ts +22 -3
- package/dist/reticle.d.ts.map +1 -1
- package/dist/reticle.js +79 -13
- package/dist/reticle.js.map +1 -1
- package/dist/review/annotator.js +1 -1
- package/dist/review/annotator.js.map +1 -1
- package/dist/review/mark-anchor.d.ts.map +1 -1
- package/dist/review/mark-anchor.js +1 -21
- package/dist/review/mark-anchor.js.map +1 -1
- package/dist/security/serialization.d.ts +29 -3
- package/dist/security/serialization.d.ts.map +1 -1
- package/dist/security/serialization.js +169 -28
- package/dist/security/serialization.js.map +1 -1
- package/dist/timers/clock.d.ts +13 -0
- package/dist/timers/clock.d.ts.map +1 -1
- package/dist/timers/clock.js +76 -6
- package/dist/timers/clock.js.map +1 -1
- package/dist/transport/transport.d.ts +1 -1
- package/dist/transport/transport.d.ts.map +1 -1
- package/dist/transport/transport.js +42 -4
- package/dist/transport/transport.js.map +1 -1
- package/package.json +7 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { EventType } from '@reticlehq/core';
|
|
2
|
+
import { getAccessibleName, getRole } from '../dom/a11y.js';
|
|
3
|
+
/** A human-legible label for a focused element, or undefined for body/none. Pure; exported for testing. */
|
|
4
|
+
export function focusLabel(el) {
|
|
5
|
+
if (!(el instanceof Element) || el === document.body)
|
|
6
|
+
return undefined;
|
|
7
|
+
const role = getRole(el);
|
|
8
|
+
const name = getAccessibleName(el);
|
|
9
|
+
return name.length > 0 ? `${role} "${name}"` : role;
|
|
10
|
+
}
|
|
11
|
+
function toBody(el) {
|
|
12
|
+
return el === null || el === document.body;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Track element focus movement. Focus dropping to <body> after an act — a modal that closed and stranded
|
|
16
|
+
* focus, a button that vanished — is a real accessibility/UX regression no screenshot names. Emits
|
|
17
|
+
* FOCUS_CHANGE {to, from, toBody}. Reversible.
|
|
18
|
+
*/
|
|
19
|
+
export function installFocus(emit) {
|
|
20
|
+
let last = null;
|
|
21
|
+
const onFocusIn = (event) => {
|
|
22
|
+
const to = event.target;
|
|
23
|
+
const from = last;
|
|
24
|
+
last = to;
|
|
25
|
+
emit(EventType.FOCUS_CHANGE, {
|
|
26
|
+
...(focusLabel(to) === undefined ? {} : { to: focusLabel(to) }),
|
|
27
|
+
...(focusLabel(from) === undefined ? {} : { from: focusLabel(from) }),
|
|
28
|
+
toBody: toBody(to),
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
const onFocusOut = (event) => {
|
|
32
|
+
// Focus left with nothing gaining it → it dropped to the body (the regression case).
|
|
33
|
+
if (event.relatedTarget !== null)
|
|
34
|
+
return;
|
|
35
|
+
const from = last;
|
|
36
|
+
last = null;
|
|
37
|
+
emit(EventType.FOCUS_CHANGE, {
|
|
38
|
+
...(focusLabel(from) === undefined ? {} : { from: focusLabel(from) }),
|
|
39
|
+
toBody: true,
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
document.addEventListener('focusin', onFocusIn);
|
|
43
|
+
document.addEventListener('focusout', onFocusOut);
|
|
44
|
+
return () => {
|
|
45
|
+
document.removeEventListener('focusin', onFocusIn);
|
|
46
|
+
document.removeEventListener('focusout', onFocusOut);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=focus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"focus.js","sourceRoot":"","sources":["../../src/observers/focus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAG5D,2GAA2G;AAC3G,MAAM,UAAU,UAAU,CAAC,EAAsB;IAC/C,IAAI,CAAC,CAAC,EAAE,YAAY,OAAO,CAAC,IAAI,EAAE,KAAK,QAAQ,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IACvE,MAAM,IAAI,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;IACzB,MAAM,IAAI,GAAG,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AACtD,CAAC;AAED,SAAS,MAAM,CAAC,EAAsB;IACpC,OAAO,EAAE,KAAK,IAAI,IAAI,EAAE,KAAK,QAAQ,CAAC,IAAI,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,IAAU;IACrC,IAAI,IAAI,GAAuB,IAAI,CAAC;IAEpC,MAAM,SAAS,GAAG,CAAC,KAAiB,EAAQ,EAAE;QAC5C,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,GAAG,EAAE,CAAC;QACV,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE;YAC3B,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,UAAU,CAAC,EAAE,CAAC,EAAE,CAAC;YAC/D,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,UAAU,GAAG,CAAC,KAAiB,EAAQ,EAAE;QAC7C,qFAAqF;QACrF,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI;YAAE,OAAO;QACzC,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE;YAC3B,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACrE,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,QAAQ,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAChD,QAAQ,CAAC,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAClD,OAAO,GAAG,EAAE;QACV,QAAQ,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACnD,QAAQ,CAAC,mBAAmB,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve with the body text, or `undefined` if it takes too long.
|
|
3
|
+
*
|
|
4
|
+
* The app is already awaiting our patched fetch, so any unbounded read here is a hang in the host app.
|
|
5
|
+
* Losing an observation is always preferable to freezing the page being observed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function withBodyDeadline(read: Promise<string>): Promise<string | undefined>;
|
|
8
|
+
export declare function isCapturableType(contentType: string | null): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Redact + cap a body for the agent transcript. JSON is parsed and run through the same
|
|
11
|
+
* sanitizeForTransport redaction used for state (sensitive keys -> [REDACTED]); every other text body
|
|
12
|
+
* (and JSON that didn't parse) goes through redactText so form/plain-text credentials can't leak.
|
|
13
|
+
* Returns the (possibly truncated) body plus whether it was cut.
|
|
14
|
+
*/
|
|
15
|
+
export declare function projectBody(rawText: string, contentType: string | null): {
|
|
16
|
+
body: string;
|
|
17
|
+
truncated: boolean;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=network-body.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-body.d.ts","sourceRoot":"","sources":["../../src/observers/network-body.ts"],"names":[],"mappings":"AAuCA;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAgBzF;AAED,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAIpE;AAkDD;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAAG,IAAI,GACzB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CA8BtC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response/request BODY projection for the network observer.
|
|
3
|
+
*
|
|
4
|
+
* Split out of network.ts, which had grown to hold URL redaction, body projection, fetch, XHR, SSE,
|
|
5
|
+
* WebSocket and sendBeacon at once. This module owns exactly one question: given a body, what is safe
|
|
6
|
+
* and affordable to report?
|
|
7
|
+
*/
|
|
8
|
+
import { REDACTED_VALUE } from '@reticlehq/core';
|
|
9
|
+
import { isSensitiveKey, safeStringify, scrubKnownSecrets } from '../security/serialization.js';
|
|
10
|
+
import { nativeClearTimeout, nativeSetTimeout } from '../timers/native-timers.js';
|
|
11
|
+
/** Only text-like bodies are worth capturing; binary (images/fonts/octet-stream) is skipped. */
|
|
12
|
+
const CAPTURABLE_CONTENT = /application\/json|text\/|application\/xml|x-www-form-urlencoded|graphql/i;
|
|
13
|
+
/**
|
|
14
|
+
* Content types that are STREAMS, never complete bodies.
|
|
15
|
+
*
|
|
16
|
+
* `text/event-stream` matches `text/` in CAPTURABLE_CONTENT, and body capture awaited
|
|
17
|
+
* `res.clone.text` BEFORE returning the response to the app. A clone of a stream only settles when
|
|
18
|
+
* the stream ends — and an SSE stream does not end — so the app's `await fetch...)` never resolved.
|
|
19
|
+
* Enabling body capture silently hung every streaming endpoint (SSE, and the chunked `application/json`
|
|
20
|
+
* every token-streaming model API uses), leaving a permanently loading UI with no reason to suspect the
|
|
21
|
+
* observability SDK. Never read these; the frame observer covers SSE properly.
|
|
22
|
+
*/
|
|
23
|
+
const STREAMING_CONTENT = /event-stream|x-ndjson|application\/stream/i;
|
|
24
|
+
/**
|
|
25
|
+
* Longest the SDK will wait on a response-body clone before emitting without it.
|
|
26
|
+
*
|
|
27
|
+
* Two layers protect the app. `text/event-stream` and friends are skipped outright, which covers the
|
|
28
|
+
* common streaming case at zero cost. This deadline is the backstop for a body that streams WITHOUT
|
|
29
|
+
* announcing it in its content type — chunked `application/json` from a token-streaming API. Gating on
|
|
30
|
+
* `content-length` instead was tried and rejected: plenty of complete responses omit it (gzip, HTTP/2),
|
|
31
|
+
* so that would silently stop capturing bodies for ordinary apps. A bounded half-second on a rare path
|
|
32
|
+
* beats an unbounded hang, and beats losing capture everywhere.
|
|
33
|
+
*/
|
|
34
|
+
const BODY_READ_TIMEOUT_MS = 500;
|
|
35
|
+
/**
|
|
36
|
+
* Resolve with the body text, or `undefined` if it takes too long.
|
|
37
|
+
*
|
|
38
|
+
* The app is already awaiting our patched fetch, so any unbounded read here is a hang in the host app.
|
|
39
|
+
* Losing an observation is always preferable to freezing the page being observed.
|
|
40
|
+
*/
|
|
41
|
+
export async function withBodyDeadline(read) {
|
|
42
|
+
// The timer is CLEARED on the winning path. Left uncleared, every captured body leaves a live timer
|
|
43
|
+
// and a retained closure behind — harmless individually, but `boundedFrame` in this same package
|
|
44
|
+
// already races-with-cleanup correctly, so not reusing that shape was an oversight rather than a
|
|
45
|
+
// choice. It also keeps test workers from being held open by pending handles.
|
|
46
|
+
let timer;
|
|
47
|
+
try {
|
|
48
|
+
return await Promise.race([
|
|
49
|
+
read,
|
|
50
|
+
new Promise((resolve) => {
|
|
51
|
+
timer = nativeSetTimeout(() => resolve(undefined), BODY_READ_TIMEOUT_MS);
|
|
52
|
+
}),
|
|
53
|
+
]);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
if (timer !== undefined)
|
|
57
|
+
nativeClearTimeout(timer);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function isCapturableType(contentType) {
|
|
61
|
+
if (contentType === null)
|
|
62
|
+
return false;
|
|
63
|
+
if (STREAMING_CONTENT.test(contentType))
|
|
64
|
+
return false;
|
|
65
|
+
return CAPTURABLE_CONTENT.test(contentType);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Per-body character cap. Bodies ride the same ring buffer as the DOM/route/console timeline, so an
|
|
69
|
+
* uncapped body could evict the whole behavioral history; the small cap keeps a few large responses
|
|
70
|
+
* from starving the timeline (the separate-budget concern in the scalability audit).
|
|
71
|
+
*/
|
|
72
|
+
const MAX_BODY_CHARS = 8192;
|
|
73
|
+
/**
|
|
74
|
+
* Hard bound on how much of a body is ever PARSED or SCANNED, independent of how much is reported.
|
|
75
|
+
*
|
|
76
|
+
* Only twice the output cap, and that ceiling is measured rather than guessed. The redaction pass uses
|
|
77
|
+
* `[A-Za-z0-9_.-]+` followed by a delimiter, which backtracks quadratically on a long run without one.
|
|
78
|
+
* Timed on this machine: 8 KB → 36 ms, 16 KB → 136 ms, 64 KB → 2067 ms. A first attempt at 8× the
|
|
79
|
+
* output cap therefore still froze the main thread for seconds, so the bound is set where the cost
|
|
80
|
+
* stays bounded AND redaction keeps enough context to catch a secret straddling the reported edge.
|
|
81
|
+
* Scanning far past the output cap is wasted work in any case — only 8 KB is ever reported.
|
|
82
|
+
*/
|
|
83
|
+
const MAX_BODY_SCAN_CHARS = MAX_BODY_CHARS * 2;
|
|
84
|
+
/**
|
|
85
|
+
* An `Authorization: Bearer …` / `Basic …` credential. The token side requires credential shape — 16+
|
|
86
|
+
* chars AND at least one digit or symbol — so ordinary prose ("Basic subscription includes…", "Bearer
|
|
87
|
+
* capacity exceeded") is NOT mangled, only actual opaque tokens are.
|
|
88
|
+
*/
|
|
89
|
+
const AUTH_SCHEME_TOKEN = /\b(Bearer|Basic)\s+(?=[A-Za-z0-9._~+/=-]{16,})(?=[A-Za-z0-9._~+/=-]*[\d._~+/=-])[A-Za-z0-9._~+/=-]+/gi;
|
|
90
|
+
/**
|
|
91
|
+
* Redact credentials in ANY text body (form-urlencoded, text/plain, xml, or JSON that failed to parse).
|
|
92
|
+
* sanitizeForTransport only redacts KEYS inside a parsed object, so a `password=secret` form post or a
|
|
93
|
+
* `Bearer <token>` string would otherwise ship verbatim. Redacts the value of any `key=value` / `key: value`
|
|
94
|
+
* pair whose key is sensitive, plus credential-shaped auth-scheme tokens.
|
|
95
|
+
*/
|
|
96
|
+
function redactText(text) {
|
|
97
|
+
return (text
|
|
98
|
+
// Auth-scheme tokens FIRST: `Authorization: Bearer <token>` — the key/value rule below would
|
|
99
|
+
// otherwise consume just "Bearer" as Authorization's value (it stops at whitespace) and leave the
|
|
100
|
+
// token behind, so the scheme rule must run before it.
|
|
101
|
+
.replace(AUTH_SCHEME_TOKEN, (_m, scheme) => `${scheme} ${REDACTED_VALUE}`)
|
|
102
|
+
.replace(/([A-Za-z0-9_.-]+)(\s*[=:]\s*"?)([^&\s,;"}]+)/g, (match, key, sep) => isSensitiveKey(key) ? `${key}${sep}${REDACTED_VALUE}` : match));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Redact + cap a body for the agent transcript. JSON is parsed and run through the same
|
|
106
|
+
* sanitizeForTransport redaction used for state (sensitive keys -> [REDACTED]); every other text body
|
|
107
|
+
* (and JSON that didn't parse) goes through redactText so form/plain-text credentials can't leak.
|
|
108
|
+
* Returns the (possibly truncated) body plus whether it was cut.
|
|
109
|
+
*/
|
|
110
|
+
export function projectBody(rawText, contentType) {
|
|
111
|
+
// Bound the INPUT before any parsing or scanning. The cap used to be applied only at the end, so a
|
|
112
|
+
// large body was JSON.parsed in full, deep-walked, re-stringified, and swept by two global regexes
|
|
113
|
+
// before all but 8 KB of the result was thrown away — seconds of synchronous main-thread work for a
|
|
114
|
+
// fixed-size output. A 30 MB CSV export or a big HTML error page froze the app, which is the same
|
|
115
|
+
// class of host-app damage as the streaming hang, with the SDK still the cause.
|
|
116
|
+
//
|
|
117
|
+
// The slice is generous (a multiple of the output cap) so redaction still sees enough context to
|
|
118
|
+
// recognise a secret that straddles the boundary, while the work stays bounded regardless of body size.
|
|
119
|
+
const oversized = rawText.length > MAX_BODY_SCAN_CHARS;
|
|
120
|
+
const text = oversized ? rawText.slice(0, MAX_BODY_SCAN_CHARS) : rawText;
|
|
121
|
+
let out;
|
|
122
|
+
if (contentType !== null && /json|graphql/i.test(contentType)) {
|
|
123
|
+
try {
|
|
124
|
+
// safeStringify already sanitizes internally — the extra sanitizeForTransport here re-walked
|
|
125
|
+
// every captured JSON body a second time (~21% of the per-response cost) for an identical result.
|
|
126
|
+
out = safeStringify(JSON.parse(text));
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
out = redactText(text); // looked like JSON but wasn't — still redact key/value + auth tokens
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
out = redactText(text);
|
|
134
|
+
}
|
|
135
|
+
// Key-based redaction can't see a secret sitting in a VALUE under a benign key — scan the projected
|
|
136
|
+
// text for high-confidence secret shapes (JWTs, provider keys) as a backstop, JSON or not.
|
|
137
|
+
out = scrubKnownSecrets(out);
|
|
138
|
+
// Truncated if the projection overflows the cap OR the input itself was clipped above — a body the
|
|
139
|
+
// SDK never fully read must not be reported as complete.
|
|
140
|
+
const truncated = oversized || out.length > MAX_BODY_CHARS;
|
|
141
|
+
return { body: out.length > MAX_BODY_CHARS ? out.slice(0, MAX_BODY_CHARS) : out, truncated };
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=network-body.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-body.js","sourceRoot":"","sources":["../../src/observers/network-body.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redact credential-bearing values so they don't leak into the agent transcript / flow / run
|
|
3
|
+
* artifacts: query params (`?access_token=…`, signed-URL keys) via the shared `isSensitiveKey` regex,
|
|
4
|
+
* AND path-embedded tokens (`/reset/<token>`, `/invite/<token>`) that live in the path, not the query.
|
|
5
|
+
* The hash is preserved and the URL is returned byte-for-byte when nothing matched.
|
|
6
|
+
*/
|
|
7
|
+
export declare function redactUrl(raw: string): string;
|
|
8
|
+
//# sourceMappingURL=network-redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-redact.d.ts","sourceRoot":"","sources":["../../src/observers/network-redact.ts"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA4D7C"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { REDACTED_VALUE } from '@reticlehq/core';
|
|
2
|
+
import { isSensitiveKey } from '../security/serialization.js';
|
|
3
|
+
/** A path segment name that is typically followed by a single-use secret token in the NEXT segment. */
|
|
4
|
+
const SENSITIVE_PATH_SEGMENT = /^(reset|verify|verification|confirm|activate|invite|magic|magiclink|token|key|oauth|unsubscribe|password)$/i;
|
|
5
|
+
/** Only mask a following segment that looks token-like — short ids/words (`reset/form`) are left alone. */
|
|
6
|
+
const PATH_TOKEN_MIN_LENGTH = 12;
|
|
7
|
+
/**
|
|
8
|
+
* Redact credential-bearing values so they don't leak into the agent transcript / flow / run
|
|
9
|
+
* artifacts: query params (`?access_token=…`, signed-URL keys) via the shared `isSensitiveKey` regex,
|
|
10
|
+
* AND path-embedded tokens (`/reset/<token>`, `/invite/<token>`) that live in the path, not the query.
|
|
11
|
+
* The hash is preserved and the URL is returned byte-for-byte when nothing matched.
|
|
12
|
+
*/
|
|
13
|
+
export function redactUrl(raw) {
|
|
14
|
+
const hashStart = raw.indexOf('#');
|
|
15
|
+
const hash = hashStart === -1 ? '' : raw.slice(hashStart);
|
|
16
|
+
const beforeHash = hashStart === -1 ? raw : raw.slice(0, hashStart);
|
|
17
|
+
const queryStart = beforeHash.indexOf('?');
|
|
18
|
+
const pathPart = queryStart === -1 ? beforeHash : beforeHash.slice(0, queryStart);
|
|
19
|
+
const query = queryStart === -1 ? '' : beforeHash.slice(queryStart + 1);
|
|
20
|
+
let changed = false;
|
|
21
|
+
// Credentials in the authority (`scheme://user:pass@host`) never belong in a transcript.
|
|
22
|
+
// Match to the LAST `@` before the path (`[^/]*@`, greedy), not the first — a password containing
|
|
23
|
+
// `@` (`user:p@ss@host`) otherwise left its tail (`ss@host`) in the clear.
|
|
24
|
+
let authority = pathPart;
|
|
25
|
+
const userinfo = /^([a-z][a-z0-9+.-]*:\/\/)[^/]*@/i.exec(pathPart);
|
|
26
|
+
if (userinfo !== null) {
|
|
27
|
+
authority = `${userinfo[1] ?? ''}${REDACTED_VALUE}@${pathPart.slice(userinfo[0].length)}`;
|
|
28
|
+
changed = true;
|
|
29
|
+
}
|
|
30
|
+
let newQuery = query;
|
|
31
|
+
if (query !== '') {
|
|
32
|
+
const params = new URLSearchParams(query);
|
|
33
|
+
for (const key of [...params.keys()]) {
|
|
34
|
+
if (isSensitiveKey(key)) {
|
|
35
|
+
params.set(key, REDACTED_VALUE);
|
|
36
|
+
changed = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
newQuery = params.toString();
|
|
40
|
+
}
|
|
41
|
+
const segments = authority.split('/');
|
|
42
|
+
for (let i = 0; i + 1 < segments.length; i++) {
|
|
43
|
+
const name = segments[i];
|
|
44
|
+
const next = segments[i + 1];
|
|
45
|
+
if (name !== undefined &&
|
|
46
|
+
next !== undefined &&
|
|
47
|
+
next.length >= PATH_TOKEN_MIN_LENGTH &&
|
|
48
|
+
SENSITIVE_PATH_SEGMENT.test(name)) {
|
|
49
|
+
segments[i + 1] = REDACTED_VALUE;
|
|
50
|
+
changed = true;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// OAuth implicit flow puts the access_token in the FRAGMENT (`#access_token=…`), and hash-routers carry
|
|
54
|
+
// `?token=…` in the hash — redact sensitive params there too, leaving plain anchors (`#section`) alone.
|
|
55
|
+
let newHash = hash;
|
|
56
|
+
if (hash.length > 1) {
|
|
57
|
+
newHash = hash.replace(/([A-Za-z0-9_.-]+)=([^&\s]+)/g, (m, key) => isSensitiveKey(key) ? `${key}=${REDACTED_VALUE}` : m);
|
|
58
|
+
if (newHash !== hash)
|
|
59
|
+
changed = true;
|
|
60
|
+
}
|
|
61
|
+
if (!changed)
|
|
62
|
+
return raw;
|
|
63
|
+
const queryOut = queryStart === -1 ? '' : `?${newQuery}`;
|
|
64
|
+
return `${segments.join('/')}${queryOut}${newHash}`;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=network-redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-redact.js","sourceRoot":"","sources":["../../src/observers/network-redact.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,uGAAuG;AACvG,MAAM,sBAAsB,GAC1B,6GAA6G,CAAC;AAChH,2GAA2G;AAC3G,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,SAAS,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,IAAI,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACpE,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;IAClF,MAAM,KAAK,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC;IAExE,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,yFAAyF;IACzF,kGAAkG;IAClG,2EAA2E;IAC3E,IAAI,SAAS,GAAG,QAAQ,CAAC;IACzB,MAAM,QAAQ,GAAG,kCAAkC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACnE,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;QACtB,SAAS,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,cAAc,IAAI,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1F,OAAO,GAAG,IAAI,CAAC;IACjB,CAAC;IAED,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC;QAC1C,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YACrC,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;gBAChC,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QACD,QAAQ,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAC;IAC/B,CAAC;IAED,MAAM,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QACzB,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7B,IACE,IAAI,KAAK,SAAS;YAClB,IAAI,KAAK,SAAS;YAClB,IAAI,CAAC,MAAM,IAAI,qBAAqB;YACpC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EACjC,CAAC;YACD,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,cAAc,CAAC;YACjC,OAAO,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;IAED,wGAAwG;IACxG,wGAAwG;IACxG,IAAI,OAAO,GAAG,IAAI,CAAC;IACnB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,8BAA8B,EAAE,CAAC,CAAS,EAAE,GAAW,EAAE,EAAE,CAChF,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CACrD,CAAC;QACF,IAAI,OAAO,KAAK,IAAI;YAAE,OAAO,GAAG,IAAI,CAAC;IACvC,CAAC;IAED,IAAI,CAAC,OAAO;QAAE,OAAO,GAAG,CAAC;IACzB,MAAM,QAAQ,GAAG,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC;IACzD,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,QAAQ,GAAG,OAAO,EAAE,CAAC;AACtD,CAAC"}
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
import type { Emit, Teardown } from './types.js';
|
|
2
|
+
import { redactUrl } from './network-redact.js';
|
|
3
|
+
export { redactUrl };
|
|
2
4
|
/** Config for the network observer. Body capture is OFF by default and dev-only opt-in. */
|
|
3
5
|
export interface NetworkOptions {
|
|
4
6
|
/** Capture request/response bodies (text-like content only, redacted, per-body capped). */
|
|
5
7
|
captureBodies?: boolean;
|
|
6
8
|
}
|
|
9
|
+
/** Pure: the first real app-code frame in a stack string, capped. Exported for unit testing. */
|
|
10
|
+
export declare function firstAppFrame(stack: string | undefined): string | undefined;
|
|
11
|
+
/** The timing fields we lift from a resource entry — TTFB is the perf signal a duration alone hides. */
|
|
12
|
+
export interface NetTiming {
|
|
13
|
+
ttfbMs?: number;
|
|
14
|
+
transferSize?: number;
|
|
15
|
+
}
|
|
7
16
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* The hash is preserved and the URL is returned byte-for-byte when nothing matched.
|
|
17
|
+
* Pure: derive TTFB + transfer size from a PerformanceResourceTiming entry. TTFB = responseStart -
|
|
18
|
+
* requestStart; cross-origin responses zero those (Timing-Allow-Origin), so we omit rather than report a
|
|
19
|
+
* bogus 0. Exported for unit testing (jsdom doesn't post real resource entries).
|
|
12
20
|
*/
|
|
13
|
-
export declare function
|
|
14
|
-
/** Patch fetch + XMLHttpRequest to emit net.request events. Fully reversible. */
|
|
21
|
+
export declare function extractTiming(entry: PerformanceResourceTiming | undefined): NetTiming;
|
|
15
22
|
export declare function installNetwork(emit: Emit, opts?: NetworkOptions): Teardown;
|
|
16
23
|
//# sourceMappingURL=network.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/observers/network.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../src/observers/network.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAIhD,OAAO,EAAE,SAAS,EAAE,CAAC;AAErB,2FAA2F;AAC3F,MAAM,WAAW,cAAc;IAC7B,2FAA2F;IAC3F,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA2GD,gGAAgG;AAChG,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAS3E;AAMD,wGAAwG;AACxG,MAAM,WAAW,SAAS;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,yBAAyB,GAAG,SAAS,GAAG,SAAS,CAQrF;AAwDD,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAE,cAAmB,GAAG,QAAQ,CAgV9E"}
|