@tracelane/report 0.1.0-alpha.16 → 0.1.0-alpha.18
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/dist/_security/detectors/insecure-cookies.d.ts +4 -0
- package/dist/_security/detectors/insecure-cookies.d.ts.map +1 -0
- package/dist/_security/detectors/insecure-cookies.js +32 -0
- package/dist/_security/detectors/insecure-cookies.js.map +1 -0
- package/dist/_security/detectors/missing-headers.d.ts +4 -0
- package/dist/_security/detectors/missing-headers.d.ts.map +1 -0
- package/dist/_security/detectors/missing-headers.js +36 -0
- package/dist/_security/detectors/missing-headers.js.map +1 -0
- package/dist/_security/detectors/mixed-content.d.ts +5 -0
- package/dist/_security/detectors/mixed-content.d.ts.map +1 -0
- package/dist/_security/detectors/mixed-content.js +74 -0
- package/dist/_security/detectors/mixed-content.js.map +1 -0
- package/dist/_security/detectors/reverse-tabnabbing.d.ts +10 -0
- package/dist/_security/detectors/reverse-tabnabbing.d.ts.map +1 -0
- package/dist/_security/detectors/reverse-tabnabbing.js +44 -0
- package/dist/_security/detectors/reverse-tabnabbing.js.map +1 -0
- package/dist/_security/index.d.ts +29 -0
- package/dist/_security/index.d.ts.map +1 -0
- package/dist/_security/index.js +36 -0
- package/dist/_security/index.js.map +1 -0
- package/dist/_security/response-meta.d.ts +28 -0
- package/dist/_security/response-meta.d.ts.map +1 -0
- package/dist/_security/response-meta.js +50 -0
- package/dist/_security/response-meta.js.map +1 -0
- package/dist/_security/serialized-dom.d.ts +20 -0
- package/dist/_security/serialized-dom.d.ts.map +1 -0
- package/dist/_security/serialized-dom.js +32 -0
- package/dist/_security/serialized-dom.js.map +1 -0
- package/dist/_security/suppress.d.ts +7 -0
- package/dist/_security/suppress.d.ts.map +1 -0
- package/dist/_security/suppress.js +8 -0
- package/dist/_security/suppress.js.map +1 -0
- package/dist/build-report.d.ts +13 -0
- package/dist/build-report.d.ts.map +1 -1
- package/dist/build-report.js +9 -2
- package/dist/build-report.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/markdown.d.ts +2 -1
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +11 -1
- package/dist/markdown.js.map +1 -1
- package/dist/panels.d.ts +12 -1
- package/dist/panels.d.ts.map +1 -1
- package/dist/panels.js +13 -2
- package/dist/panels.js.map +1 -1
- package/dist/report-writer.d.ts +5 -0
- package/dist/report-writer.d.ts.map +1 -1
- package/dist/report-writer.js +9 -2
- package/dist/report-writer.js.map +1 -1
- package/dist/template.d.ts +7 -0
- package/dist/template.d.ts.map +1 -1
- package/dist/template.js +73 -1
- package/dist/template.js.map +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insecure-cookies.d.ts","sourceRoot":"","sources":["../../../src/_security/detectors/insecure-cookies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAY,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQxD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,eAAe,EAAE,CAuBvF"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const FLAGS = [
|
|
2
|
+
{ key: 'secure', label: 'Secure', severity: 'medium' },
|
|
3
|
+
{ key: 'httpOnly', label: 'HttpOnly', severity: 'low' },
|
|
4
|
+
{ key: 'sameSite', label: 'SameSite', severity: 'low' },
|
|
5
|
+
];
|
|
6
|
+
export function detectInsecureCookies(metas) {
|
|
7
|
+
const out = [];
|
|
8
|
+
const seen = new Set();
|
|
9
|
+
for (const m of metas) {
|
|
10
|
+
for (const c of m.setCookies) {
|
|
11
|
+
for (const f of FLAGS) {
|
|
12
|
+
if (c[f.key])
|
|
13
|
+
continue;
|
|
14
|
+
const id = `insecure-cookie:${c.name}:${f.label}`;
|
|
15
|
+
if (seen.has(id))
|
|
16
|
+
continue;
|
|
17
|
+
seen.add(id);
|
|
18
|
+
out.push({
|
|
19
|
+
id,
|
|
20
|
+
signal: 'insecure-cookie',
|
|
21
|
+
severity: f.severity,
|
|
22
|
+
title: `Cookie '${c.name}' missing ${f.label}`,
|
|
23
|
+
detail: `Set-Cookie '${c.name}' did not set the ${f.label} attribute.`,
|
|
24
|
+
evidence: `${c.name}:${f.label}`,
|
|
25
|
+
advisory: true,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=insecure-cookies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insecure-cookies.js","sourceRoot":"","sources":["../../../src/_security/detectors/insecure-cookies.ts"],"names":[],"mappings":"AAGA,MAAM,KAAK,GAAqF;IAC9F,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE;IACtD,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE;IACvD,EAAE,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE;CACxD,CAAC;AAEF,MAAM,UAAU,qBAAqB,CAAC,KAA8B;IAClE,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;YAC7B,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;gBACtB,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACvB,MAAM,EAAE,GAAG,mBAAmB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBAClD,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,SAAS;gBAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACb,GAAG,CAAC,IAAI,CAAC;oBACP,EAAE;oBACF,MAAM,EAAE,iBAAiB;oBACzB,QAAQ,EAAE,CAAC,CAAC,QAAQ;oBACpB,KAAK,EAAE,WAAW,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,KAAK,EAAE;oBAC9C,MAAM,EAAE,eAAe,CAAC,CAAC,IAAI,qBAAqB,CAAC,CAAC,KAAK,aAAa;oBACtE,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,KAAK,EAAE;oBAChC,QAAQ,EAAE,IAAI;iBACf,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-headers.d.ts","sourceRoot":"","sources":["../../../src/_security/detectors/missing-headers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAY,MAAM,aAAa,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAcxD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,eAAe,EAAE,CAoBtF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const HEADERS = [
|
|
2
|
+
{ name: 'content-security-policy', severity: 'high', label: 'Content-Security-Policy' },
|
|
3
|
+
{
|
|
4
|
+
name: 'strict-transport-security',
|
|
5
|
+
severity: 'medium',
|
|
6
|
+
label: 'Strict-Transport-Security (HSTS)',
|
|
7
|
+
},
|
|
8
|
+
{ name: 'x-frame-options', severity: 'medium', label: 'X-Frame-Options' },
|
|
9
|
+
{ name: 'x-content-type-options', severity: 'medium', label: 'X-Content-Type-Options' },
|
|
10
|
+
{ name: 'referrer-policy', severity: 'low', label: 'Referrer-Policy' },
|
|
11
|
+
];
|
|
12
|
+
export function detectMissingHeaders(metas) {
|
|
13
|
+
const main = metas.find((m) => m.isMainDocument);
|
|
14
|
+
if (!main)
|
|
15
|
+
return [];
|
|
16
|
+
// HTTPS gate: header/HSTS checks are moot + noisy on non-HTTPS (localhost/non-prod).
|
|
17
|
+
if (!main.url.startsWith('https://'))
|
|
18
|
+
return [];
|
|
19
|
+
const present = new Set(main.presentSecurityHeaders.map((h) => h.toLowerCase()));
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const h of HEADERS) {
|
|
22
|
+
if (present.has(h.name))
|
|
23
|
+
continue;
|
|
24
|
+
out.push({
|
|
25
|
+
id: `missing-security-header:${h.name}`,
|
|
26
|
+
signal: 'missing-security-header',
|
|
27
|
+
severity: h.severity,
|
|
28
|
+
title: `Missing ${h.label} header`,
|
|
29
|
+
detail: `The main document response did not set the ${h.label} header.`,
|
|
30
|
+
evidence: h.name,
|
|
31
|
+
advisory: true,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return out;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=missing-headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"missing-headers.js","sourceRoot":"","sources":["../../../src/_security/detectors/missing-headers.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,GAA0D;IACrE,EAAE,IAAI,EAAE,yBAAyB,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,yBAAyB,EAAE;IACvF;QACE,IAAI,EAAE,2BAA2B;QACjC,QAAQ,EAAE,QAAQ;QAClB,KAAK,EAAE,kCAAkC;KAC1C;IACD,EAAE,IAAI,EAAE,iBAAiB,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE;IACzE,EAAE,IAAI,EAAE,wBAAwB,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,wBAAwB,EAAE;IACvF,EAAE,IAAI,EAAE,iBAAiB,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE;CACvE,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,KAA8B;IACjE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;IACjD,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,qFAAqF;IACrF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,CAAC;IAChD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACjF,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YAAE,SAAS;QAClC,GAAG,CAAC,IAAI,CAAC;YACP,EAAE,EAAE,2BAA2B,CAAC,CAAC,IAAI,EAAE;YACvC,MAAM,EAAE,yBAAyB;YACjC,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,KAAK,EAAE,WAAW,CAAC,CAAC,KAAK,SAAS;YAClC,MAAM,EAAE,8CAA8C,CAAC,CAAC,KAAK,UAAU;YACvE,QAAQ,EAAE,CAAC,CAAC,IAAI;YAChB,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
import type { SecurityFinding } from '../index.js';
|
|
3
|
+
import type { ResponseMeta } from '../response-meta.js';
|
|
4
|
+
export declare function detectMixedContent(events: readonly eventWithTime[], metas: readonly ResponseMeta[]): SecurityFinding[];
|
|
5
|
+
//# sourceMappingURL=mixed-content.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mixed-content.d.ts","sourceRoot":"","sources":["../../../src/_security/detectors/mixed-content.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AA0CxD,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,KAAK,EAAE,SAAS,YAAY,EAAE,GAC7B,eAAe,EAAE,CA8BnB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { collectRoots, walk } from '../serialized-dom.js';
|
|
2
|
+
/**
|
|
3
|
+
* Walks rrweb serialized DOM snapshots for subresource-loading attributes that
|
|
4
|
+
* point at `http://` on an HTTPS page — the mixed-content risk. Re-sourced from
|
|
5
|
+
* the DOM (like reverse-tabnabbing) because the capture layer only emits a
|
|
6
|
+
* `[tracelane.sec]` meta for the MAIN DOCUMENT, so subresource URLs never reach
|
|
7
|
+
* the meta stream. The HTTPS gate comes from the main-document meta.
|
|
8
|
+
*
|
|
9
|
+
* Scope (kept tight to limit false positives for the MVP):
|
|
10
|
+
* - the `src` attribute on ANY element (img, script, iframe, video, audio,
|
|
11
|
+
* source, …),
|
|
12
|
+
* - the `href` attribute ONLY on a `<link>` whose `rel` actually fetches a
|
|
13
|
+
* subresource (stylesheet/preload/icon/…). A `<link rel="canonical">` or
|
|
14
|
+
* `<a href>` is a navigation/metadata hint, not a subresource, so it is NOT
|
|
15
|
+
* flagged.
|
|
16
|
+
* - `srcset` is not parsed.
|
|
17
|
+
* Dedupes by url; advisory, never an audit result.
|
|
18
|
+
*/
|
|
19
|
+
// `<link rel>` values that actually fetch a resource over the wire (so an
|
|
20
|
+
// `http://` href is genuine mixed content). Excludes metadata/hint rels like
|
|
21
|
+
// canonical, alternate, author, license, and the connection-only hints
|
|
22
|
+
// dns-prefetch/preconnect (which establish a connection but load no content).
|
|
23
|
+
const SUBRESOURCE_LINK_RELS = new Set([
|
|
24
|
+
'stylesheet',
|
|
25
|
+
'preload',
|
|
26
|
+
'modulepreload',
|
|
27
|
+
'prefetch',
|
|
28
|
+
'prerender',
|
|
29
|
+
'icon',
|
|
30
|
+
'manifest',
|
|
31
|
+
]);
|
|
32
|
+
function linkLoadsSubresource(rel) {
|
|
33
|
+
if (typeof rel !== 'string')
|
|
34
|
+
return false;
|
|
35
|
+
return rel
|
|
36
|
+
.toLowerCase()
|
|
37
|
+
.split(/\s+/)
|
|
38
|
+
.some((token) => SUBRESOURCE_LINK_RELS.has(token));
|
|
39
|
+
}
|
|
40
|
+
export function detectMixedContent(events, metas) {
|
|
41
|
+
const main = metas.find((mt) => mt.isMainDocument);
|
|
42
|
+
// Mixed content is only meaningful on an HTTPS page.
|
|
43
|
+
if (!main || !main.url.startsWith('https://'))
|
|
44
|
+
return [];
|
|
45
|
+
const out = [];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
for (const root of collectRoots(events)) {
|
|
48
|
+
for (const n of walk(root)) {
|
|
49
|
+
if (n.type !== 2)
|
|
50
|
+
continue;
|
|
51
|
+
const attrs = n.attributes ?? {};
|
|
52
|
+
const tag = n.tagName?.toLowerCase();
|
|
53
|
+
const urls = [attrs.src];
|
|
54
|
+
if (tag === 'link' && linkLoadsSubresource(attrs.rel))
|
|
55
|
+
urls.push(attrs.href);
|
|
56
|
+
for (const u of urls) {
|
|
57
|
+
if (typeof u !== 'string' || !u.startsWith('http://') || seen.has(u))
|
|
58
|
+
continue;
|
|
59
|
+
seen.add(u);
|
|
60
|
+
out.push({
|
|
61
|
+
id: `mixed-content:${u}`,
|
|
62
|
+
signal: 'mixed-content',
|
|
63
|
+
severity: 'high',
|
|
64
|
+
title: 'Mixed content (HTTP resource on an HTTPS page)',
|
|
65
|
+
detail: `An HTTP resource (${u}) was loaded by the HTTPS page ${main.url}.`,
|
|
66
|
+
evidence: u,
|
|
67
|
+
advisory: true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=mixed-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mixed-content.js","sourceRoot":"","sources":["../../../src/_security/detectors/mixed-content.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;;;;;;;;;;;;;;GAgBG;AAEH,0EAA0E;AAC1E,6EAA6E;AAC7E,uEAAuE;AACvE,8EAA8E;AAC9E,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,YAAY;IACZ,SAAS;IACT,eAAe;IACf,UAAU;IACV,WAAW;IACX,MAAM;IACN,UAAU;CACX,CAAC,CAAC;AAEH,SAAS,oBAAoB,CAAC,GAAY;IACxC,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,GAAG;SACP,WAAW,EAAE;SACb,KAAK,CAAC,KAAK,CAAC;SACZ,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AACvD,CAAC;AACD,MAAM,UAAU,kBAAkB,CAChC,MAAgC,EAChC,KAA8B;IAE9B,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC;IACnD,qDAAqD;IACrD,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,EAAE,CAAC;IAEzD,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;gBAAE,SAAS;YAC3B,MAAM,KAAK,GAAG,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;YACrC,MAAM,IAAI,GAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,GAAG,KAAK,MAAM,IAAI,oBAAoB,CAAC,KAAK,CAAC,GAAG,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7E,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;oBAAE,SAAS;gBAC/E,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBACZ,GAAG,CAAC,IAAI,CAAC;oBACP,EAAE,EAAE,iBAAiB,CAAC,EAAE;oBACxB,MAAM,EAAE,eAAe;oBACvB,QAAQ,EAAE,MAAM;oBAChB,KAAK,EAAE,gDAAgD;oBACvD,MAAM,EAAE,qBAAqB,CAAC,kCAAkC,IAAI,CAAC,GAAG,GAAG;oBAC3E,QAAQ,EAAE,CAAC;oBACX,QAAQ,EAAE,IAAI;iBACf,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
import type { SecurityFinding } from '../index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Walks rrweb serialized DOM snapshots (FullSnapshot trees + IncrementalSnapshot
|
|
5
|
+
* `adds`) for `<a target="_blank">` links lacking a `rel` of `noopener`/
|
|
6
|
+
* `noreferrer` — the classic reverse-tabnabbing risk. Pure over plain serialized
|
|
7
|
+
* node objects; no DOM API. Dedupes by href; advisory, never an audit result.
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectReverseTabnabbing(events: readonly eventWithTime[]): SecurityFinding[];
|
|
10
|
+
//# sourceMappingURL=reverse-tabnabbing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reverse-tabnabbing.d.ts","sourceRoot":"","sources":["../../../src/_security/detectors/reverse-tabnabbing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AASnD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,eAAe,EAAE,CA0B3F"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { collectRoots, walk } from '../serialized-dom.js';
|
|
2
|
+
function relIsSafe(rel) {
|
|
3
|
+
if (typeof rel !== 'string')
|
|
4
|
+
return false;
|
|
5
|
+
const tokens = rel.toLowerCase().split(/\s+/);
|
|
6
|
+
return tokens.includes('noopener') || tokens.includes('noreferrer');
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Walks rrweb serialized DOM snapshots (FullSnapshot trees + IncrementalSnapshot
|
|
10
|
+
* `adds`) for `<a target="_blank">` links lacking a `rel` of `noopener`/
|
|
11
|
+
* `noreferrer` — the classic reverse-tabnabbing risk. Pure over plain serialized
|
|
12
|
+
* node objects; no DOM API. Dedupes by href; advisory, never an audit result.
|
|
13
|
+
*/
|
|
14
|
+
export function detectReverseTabnabbing(events) {
|
|
15
|
+
const roots = collectRoots(events);
|
|
16
|
+
const out = [];
|
|
17
|
+
const seen = new Set();
|
|
18
|
+
for (const root of roots) {
|
|
19
|
+
for (const n of walk(root)) {
|
|
20
|
+
if (n.type !== 2 || n.tagName?.toLowerCase() !== 'a')
|
|
21
|
+
continue;
|
|
22
|
+
const attrs = n.attributes ?? {};
|
|
23
|
+
const target = typeof attrs.target === 'string' ? attrs.target.toLowerCase() : '';
|
|
24
|
+
if (target !== '_blank' || relIsSafe(attrs.rel))
|
|
25
|
+
continue;
|
|
26
|
+
const href = typeof attrs.href === 'string' ? attrs.href : '(no href)';
|
|
27
|
+
const id = `reverse-tabnabbing:${href}`;
|
|
28
|
+
if (seen.has(id))
|
|
29
|
+
continue;
|
|
30
|
+
seen.add(id);
|
|
31
|
+
out.push({
|
|
32
|
+
id,
|
|
33
|
+
signal: 'reverse-tabnabbing',
|
|
34
|
+
severity: 'medium',
|
|
35
|
+
title: 'Reverse tabnabbing risk (target="_blank" without rel="noopener")',
|
|
36
|
+
detail: `An <a target="_blank"> link (${href}) is missing rel="noopener"/"noreferrer".`,
|
|
37
|
+
evidence: href,
|
|
38
|
+
advisory: true,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=reverse-tabnabbing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reverse-tabnabbing.js","sourceRoot":"","sources":["../../../src/_security/detectors/reverse-tabnabbing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAE1D,SAAS,SAAS,CAAC,GAAY;IAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,MAAM,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AACtE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,MAAgC;IACtE,MAAM,KAAK,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,GAAG;gBAAE,SAAS;YAC/D,MAAM,KAAK,GAAG,CAAC,CAAC,UAAU,IAAI,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClF,IAAI,MAAM,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC1D,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC;YACvE,MAAM,EAAE,GAAG,sBAAsB,IAAI,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,SAAS;YAC3B,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACb,GAAG,CAAC,IAAI,CAAC;gBACP,EAAE;gBACF,MAAM,EAAE,oBAAoB;gBAC5B,QAAQ,EAAE,QAAQ;gBAClB,KAAK,EAAE,kEAAkE;gBACzE,MAAM,EAAE,gCAAgC,IAAI,2CAA2C;gBACvF,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,IAAI;aACf,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
import { type Suppression } from './suppress.js';
|
|
3
|
+
export type { Suppression };
|
|
4
|
+
/** console.error prefix the capture layer uses for privacy-safe response metadata. */
|
|
5
|
+
export declare const SEC_CONSOLE_PREFIX = "[tracelane.sec]";
|
|
6
|
+
/** rrweb Custom event tag the capture layer uses for privacy-safe response metadata. */
|
|
7
|
+
export declare const SEC_EVENT_TAG = "tracelane.sec";
|
|
8
|
+
export type SecuritySignal = 'missing-security-header' | 'mixed-content' | 'insecure-cookie' | 'reverse-tabnabbing';
|
|
9
|
+
export type Severity = 'low' | 'medium' | 'high';
|
|
10
|
+
export interface SecurityFinding {
|
|
11
|
+
/** stable id, e.g. `${signal}:${evidence}` */
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly signal: SecuritySignal;
|
|
14
|
+
readonly severity: Severity;
|
|
15
|
+
readonly title: string;
|
|
16
|
+
readonly detail: string;
|
|
17
|
+
readonly evidence: string;
|
|
18
|
+
/** framing invariant — always true; these are advisory, not audit results */
|
|
19
|
+
readonly advisory: true;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Derive advisory security-hygiene findings from a captured event stream.
|
|
23
|
+
* Pure + total: never throws (a failing detector contributes nothing). Findings
|
|
24
|
+
* are advisory only — NOT a security audit/scan/guarantee.
|
|
25
|
+
*/
|
|
26
|
+
export declare function analyze(events: readonly eventWithTime[], opts?: {
|
|
27
|
+
suppress?: readonly Suppression[];
|
|
28
|
+
}): SecurityFinding[];
|
|
29
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/_security/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAO1D,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,eAAe,CAAC;AAEpE,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AAEpD,wFAAwF;AACxF,eAAO,MAAM,aAAa,kBAAkB,CAAC;AAE7C,MAAM,MAAM,cAAc,GACtB,yBAAyB,GACzB,eAAe,GACf,iBAAiB,GACjB,oBAAoB,CAAC;AAEzB,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjD,MAAM,WAAW,eAAe;IAC9B,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;CACzB;AAYD;;;;GAIG;AACH,wBAAgB,OAAO,CACrB,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,SAAS,WAAW,EAAE,CAAA;CAAO,GAC/C,eAAe,EAAE,CAanB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { detectInsecureCookies } from './detectors/insecure-cookies.js';
|
|
2
|
+
import { detectMissingHeaders } from './detectors/missing-headers.js';
|
|
3
|
+
import { detectMixedContent } from './detectors/mixed-content.js';
|
|
4
|
+
import { detectReverseTabnabbing } from './detectors/reverse-tabnabbing.js';
|
|
5
|
+
import { scrapeResponseMeta } from './response-meta.js';
|
|
6
|
+
import { applySuppressions } from './suppress.js';
|
|
7
|
+
/** console.error prefix the capture layer uses for privacy-safe response metadata. */
|
|
8
|
+
export const SEC_CONSOLE_PREFIX = '[tracelane.sec]';
|
|
9
|
+
/** rrweb Custom event tag the capture layer uses for privacy-safe response metadata. */
|
|
10
|
+
export const SEC_EVENT_TAG = 'tracelane.sec';
|
|
11
|
+
const SEVERITY_RANK = { high: 0, medium: 1, low: 2 };
|
|
12
|
+
function safe(fn, fallback) {
|
|
13
|
+
try {
|
|
14
|
+
return fn();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return fallback;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Derive advisory security-hygiene findings from a captured event stream.
|
|
22
|
+
* Pure + total: never throws (a failing detector contributes nothing). Findings
|
|
23
|
+
* are advisory only — NOT a security audit/scan/guarantee.
|
|
24
|
+
*/
|
|
25
|
+
export function analyze(events, opts = {}) {
|
|
26
|
+
const metas = safe(() => scrapeResponseMeta(events), []);
|
|
27
|
+
const findings = [
|
|
28
|
+
...safe(() => detectMissingHeaders(metas), []),
|
|
29
|
+
...safe(() => detectMixedContent(events, metas), []),
|
|
30
|
+
...safe(() => detectInsecureCookies(metas), []),
|
|
31
|
+
...safe(() => detectReverseTabnabbing(events), []),
|
|
32
|
+
];
|
|
33
|
+
const kept = applySuppressions(findings, opts.suppress ?? []);
|
|
34
|
+
return [...kept].sort((a, b) => SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity] || a.signal.localeCompare(b.signal));
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/_security/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAoB,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAIpE,sFAAsF;AACtF,MAAM,CAAC,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEpD,wFAAwF;AACxF,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAC;AAsB7C,MAAM,aAAa,GAA6B,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAE/E,SAAS,IAAI,CAAI,EAAW,EAAE,QAAW;IACvC,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO,CACrB,MAAgC,EAChC,OAA8C,EAAE;IAEhD,MAAM,KAAK,GAAG,IAAI,CAAiB,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IACzE,MAAM,QAAQ,GAAsB;QAClC,GAAG,IAAI,CAAoB,GAAG,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QACjE,GAAG,IAAI,CAAoB,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC;QACvE,GAAG,IAAI,CAAoB,GAAG,EAAE,CAAC,qBAAqB,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC;QAClE,GAAG,IAAI,CAAoB,GAAG,EAAE,CAAC,uBAAuB,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;KACtE,CAAC;IACF,MAAM,IAAI,GAAG,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CACnB,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACP,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAC5F,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
/**
|
|
3
|
+
* Privacy-safe response metadata recovered from a `tracelane.sec` Custom event.
|
|
4
|
+
* Carries only header NAMES (never values), per-cookie flag presence
|
|
5
|
+
* booleans, and the cookie name — never header values or cookie values.
|
|
6
|
+
*/
|
|
7
|
+
export interface ResponseMeta {
|
|
8
|
+
url: string;
|
|
9
|
+
status: number;
|
|
10
|
+
isMainDocument: boolean;
|
|
11
|
+
/** lowercased header NAMES that were present (never values) */
|
|
12
|
+
presentSecurityHeaders: string[];
|
|
13
|
+
/** per-cookie flag presence (never names/values beyond the cookie name) */
|
|
14
|
+
setCookies: {
|
|
15
|
+
name: string;
|
|
16
|
+
secure: boolean;
|
|
17
|
+
httpOnly: boolean;
|
|
18
|
+
sameSite: boolean;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Read privacy-safe response metadata out of the rrweb Custom events the
|
|
23
|
+
* capture layer injects (tag `tracelane.sec`, payload = the meta object).
|
|
24
|
+
* Replaces the old console-scrape channel, which raced navigation. Malformed
|
|
25
|
+
* payloads are skipped.
|
|
26
|
+
*/
|
|
27
|
+
export declare function scrapeResponseMeta(events: readonly eventWithTime[]): ResponseMeta[];
|
|
28
|
+
//# sourceMappingURL=response-meta.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-meta.d.ts","sourceRoot":"","sources":["../../src/_security/response-meta.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,+DAA+D;IAC/D,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,2EAA2E;IAC3E,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CACvF;AAkCD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,YAAY,EAAE,CASnF"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { EventType } from '@cubenest/rrweb-core';
|
|
2
|
+
import { SEC_EVENT_TAG } from './index.js';
|
|
3
|
+
function isCookieFlags(c) {
|
|
4
|
+
if (!c || typeof c !== 'object')
|
|
5
|
+
return false;
|
|
6
|
+
const k = c;
|
|
7
|
+
return (typeof k.name === 'string' &&
|
|
8
|
+
typeof k.secure === 'boolean' &&
|
|
9
|
+
typeof k.httpOnly === 'boolean' &&
|
|
10
|
+
typeof k.sameSite === 'boolean');
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Full structural validation of a `tracelane.sec` Custom-event payload, including
|
|
14
|
+
* array ELEMENT types. A page can't forge a Node-side Custom event, but a
|
|
15
|
+
* shape-malformed object (e.g. `presentSecurityHeaders: [123]` or
|
|
16
|
+
* `setCookies: [{}]`) must still be rejected to honor the "malformed payloads
|
|
17
|
+
* are skipped" contract and protect downstream consumers from runtime errors.
|
|
18
|
+
*/
|
|
19
|
+
function isResponseMeta(parsed) {
|
|
20
|
+
if (!parsed || typeof parsed !== 'object')
|
|
21
|
+
return false;
|
|
22
|
+
const m = parsed;
|
|
23
|
+
return (typeof m.url === 'string' &&
|
|
24
|
+
typeof m.status === 'number' &&
|
|
25
|
+
typeof m.isMainDocument === 'boolean' &&
|
|
26
|
+
Array.isArray(m.presentSecurityHeaders) &&
|
|
27
|
+
m.presentSecurityHeaders.every((h) => typeof h === 'string') &&
|
|
28
|
+
Array.isArray(m.setCookies) &&
|
|
29
|
+
m.setCookies.every(isCookieFlags));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Read privacy-safe response metadata out of the rrweb Custom events the
|
|
33
|
+
* capture layer injects (tag `tracelane.sec`, payload = the meta object).
|
|
34
|
+
* Replaces the old console-scrape channel, which raced navigation. Malformed
|
|
35
|
+
* payloads are skipped.
|
|
36
|
+
*/
|
|
37
|
+
export function scrapeResponseMeta(events) {
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const e of events) {
|
|
40
|
+
if (e.type !== EventType.Custom)
|
|
41
|
+
continue;
|
|
42
|
+
const data = e.data;
|
|
43
|
+
if (data.tag !== SEC_EVENT_TAG)
|
|
44
|
+
continue;
|
|
45
|
+
if (isResponseMeta(data.payload))
|
|
46
|
+
out.push(data.payload);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=response-meta.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-meta.js","sourceRoot":"","sources":["../../src/_security/response-meta.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAiB3C,SAAS,aAAa,CAAC,CAAU;IAC/B,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9C,MAAM,CAAC,GAAG,CAA4B,CAAC;IACvC,OAAO,CACL,OAAO,CAAC,CAAC,IAAI,KAAK,QAAQ;QAC1B,OAAO,CAAC,CAAC,MAAM,KAAK,SAAS;QAC7B,OAAO,CAAC,CAAC,QAAQ,KAAK,SAAS;QAC/B,OAAO,CAAC,CAAC,QAAQ,KAAK,SAAS,CAChC,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,MAAe;IACrC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxD,MAAM,CAAC,GAAG,MAAiC,CAAC;IAC5C,OAAO,CACL,OAAO,CAAC,CAAC,GAAG,KAAK,QAAQ;QACzB,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAC5B,OAAO,CAAC,CAAC,cAAc,KAAK,SAAS;QACrC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,sBAAsB,CAAC;QACvC,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC;QAC5D,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC;QAC3B,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,aAAa,CAAC,CAClC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAgC;IACjE,MAAM,GAAG,GAAmB,EAAE,CAAC;IAC/B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,MAAM;YAAE,SAAS;QAC1C,MAAM,IAAI,GAAG,CAAC,CAAC,IAA4C,CAAC;QAC5D,IAAI,IAAI,CAAC,GAAG,KAAK,aAAa;YAAE,SAAS;QACzC,IAAI,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;YAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal shape of an rrweb serialized DOM node. `type === 2` is an Element.
|
|
4
|
+
* Pure plain-object view — no DOM API, no rrweb internals beyond this shape.
|
|
5
|
+
*/
|
|
6
|
+
export interface SNode {
|
|
7
|
+
type?: number;
|
|
8
|
+
tagName?: string;
|
|
9
|
+
attributes?: Record<string, unknown>;
|
|
10
|
+
childNodes?: SNode[];
|
|
11
|
+
}
|
|
12
|
+
/** Depth-first walk: yields `node`, then recurses into `childNodes`. */
|
|
13
|
+
export declare function walk(node: SNode | undefined): Generator<SNode>;
|
|
14
|
+
/**
|
|
15
|
+
* Collect serialized-DOM roots from a captured event stream: the `data.node`
|
|
16
|
+
* tree of each FullSnapshot plus each `data.adds[].node` from an
|
|
17
|
+
* IncrementalSnapshot mutation. Shared by the DOM-walking detectors.
|
|
18
|
+
*/
|
|
19
|
+
export declare function collectRoots(events: readonly eventWithTime[]): SNode[];
|
|
20
|
+
//# sourceMappingURL=serialized-dom.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialized-dom.d.ts","sourceRoot":"","sources":["../../src/_security/serialized-dom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D;;;GAGG;AACH,MAAM,WAAW,KAAK;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,CAAC,EAAE,KAAK,EAAE,CAAC;CACtB;AAED,wEAAwE;AACxE,wBAAiB,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAI/D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,KAAK,EAAE,CAatE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EventType } from '@cubenest/rrweb-core';
|
|
2
|
+
/** Depth-first walk: yields `node`, then recurses into `childNodes`. */
|
|
3
|
+
export function* walk(node) {
|
|
4
|
+
if (!node)
|
|
5
|
+
return;
|
|
6
|
+
yield node;
|
|
7
|
+
for (const child of node.childNodes ?? [])
|
|
8
|
+
yield* walk(child);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Collect serialized-DOM roots from a captured event stream: the `data.node`
|
|
12
|
+
* tree of each FullSnapshot plus each `data.adds[].node` from an
|
|
13
|
+
* IncrementalSnapshot mutation. Shared by the DOM-walking detectors.
|
|
14
|
+
*/
|
|
15
|
+
export function collectRoots(events) {
|
|
16
|
+
const roots = [];
|
|
17
|
+
for (const e of events) {
|
|
18
|
+
if (e.type === EventType.FullSnapshot) {
|
|
19
|
+
roots.push(e.data.node);
|
|
20
|
+
}
|
|
21
|
+
else if (e.type === EventType.IncrementalSnapshot) {
|
|
22
|
+
const adds = e.data.adds;
|
|
23
|
+
if (Array.isArray(adds)) {
|
|
24
|
+
for (const a of adds)
|
|
25
|
+
if (a.node)
|
|
26
|
+
roots.push(a.node);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return roots;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=serialized-dom.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialized-dom.js","sourceRoot":"","sources":["../../src/_security/serialized-dom.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAcjD,wEAAwE;AACxE,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,IAAuB;IAC3C,IAAI,CAAC,IAAI;QAAE,OAAO;IAClB,MAAM,IAAI,CAAC;IACX,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,UAAU,IAAI,EAAE;QAAE,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,MAAgC;IAC3D,MAAM,KAAK,GAAY,EAAE,CAAC;IAC1B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,YAAY,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC,IAAwB,CAAC,IAAI,CAAC,CAAC;QAC/C,CAAC;aAAM,IAAI,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,mBAAmB,EAAE,CAAC;YACpD,MAAM,IAAI,GAAI,CAAC,CAAC,IAAsC,CAAC,IAAI,CAAC;YAC5D,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,KAAK,MAAM,CAAC,IAAI,IAAI;oBAAE,IAAI,CAAC,CAAC,IAAI;wBAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SecurityFinding, SecuritySignal } from './index.js';
|
|
2
|
+
export interface Suppression {
|
|
3
|
+
signal?: SecuritySignal;
|
|
4
|
+
evidence?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function applySuppressions(findings: readonly SecurityFinding[], rules: readonly Suppression[]): SecurityFinding[];
|
|
7
|
+
//# sourceMappingURL=suppress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppress.d.ts","sourceRoot":"","sources":["../../src/_security/suppress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAElE,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,SAAS,eAAe,EAAE,EACpC,KAAK,EAAE,SAAS,WAAW,EAAE,GAC5B,eAAe,EAAE,CAWnB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function applySuppressions(findings, rules) {
|
|
2
|
+
if (rules.length === 0)
|
|
3
|
+
return [...findings];
|
|
4
|
+
return findings.filter((f) => !rules.some((r) => (r.signal !== undefined || r.evidence !== undefined) &&
|
|
5
|
+
(r.signal === undefined || r.signal === f.signal) &&
|
|
6
|
+
(r.evidence === undefined || r.evidence === f.evidence)));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=suppress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppress.js","sourceRoot":"","sources":["../../src/_security/suppress.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,iBAAiB,CAC/B,QAAoC,EACpC,KAA6B;IAE7B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;IAC7C,OAAO,QAAQ,CAAC,MAAM,CACpB,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,KAAK,CAAC,IAAI,CACT,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC;QACpD,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAC;QACjD,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,CAAC,CAC1D,CACJ,CAAC;AACJ,CAAC"}
|
package/dist/build-report.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
import type { Suppression } from './_security/index.js';
|
|
2
3
|
import type { ReportMeta } from './types.js';
|
|
3
4
|
/** Options for {@link buildReport}. */
|
|
4
5
|
export interface BuildReportOptions {
|
|
@@ -13,6 +14,18 @@ export interface BuildReportOptions {
|
|
|
13
14
|
* audit A-8).
|
|
14
15
|
*/
|
|
15
16
|
footer?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Run the advisory `@tracelane/security` analyzer over the captured stream
|
|
19
|
+
* and surface its findings in the report (Markdown section + collapsed
|
|
20
|
+
* panel). Default true. Pass `false` to skip analysis entirely — `analyze`
|
|
21
|
+
* is not called and nothing security-related is rendered.
|
|
22
|
+
*/
|
|
23
|
+
security?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Suppressions forwarded to the analyzer (e.g. silence a known-acceptable
|
|
26
|
+
* signal/url). Ignored when `security` is `false`.
|
|
27
|
+
*/
|
|
28
|
+
securitySuppress?: Suppression[];
|
|
16
29
|
}
|
|
17
30
|
/**
|
|
18
31
|
* Build a self-contained HTML report for one test run.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-report.d.ts","sourceRoot":"","sources":["../src/build-report.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"build-report.d.ts","sourceRoot":"","sources":["../src/build-report.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAMzE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,uCAAuC;AACvC,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,WAAW,EAAE,CAAC;CAClC;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,aAAa,EAAE,EACvB,IAAI,EAAE,UAAU,EAChB,OAAO,GAAE,kBAAuB,GAC/B,MAAM,CA6CR"}
|
package/dist/build-report.js
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// exceeds the 25 MB budget (ADR-0005); the report renders a banner when a prune
|
|
15
15
|
// fired so the truncation is visible to the user.
|
|
16
16
|
import { pruneToSizeBudget } from '@tracelane/core';
|
|
17
|
+
import { analyze } from './_security/index.js';
|
|
17
18
|
import { encodeEventsBlob } from './embed.js';
|
|
18
19
|
import { buildMarkdown, extractActionLog } from './markdown.js';
|
|
19
20
|
import { resolveCiMetadata } from './metadata.js';
|
|
@@ -27,7 +28,7 @@ import { renderReportHtml } from './template.js';
|
|
|
27
28
|
* @returns a complete `.html` document string — write it to disk as-is
|
|
28
29
|
*/
|
|
29
30
|
export function buildReport(events, meta, options = {}) {
|
|
30
|
-
const { enforceSizeBudget = true, footer = true } = options;
|
|
31
|
+
const { enforceSizeBudget = true, footer = true, security = true, securitySuppress = [], } = options;
|
|
31
32
|
// Keep the report within budget; surface a banner if anything was dropped.
|
|
32
33
|
const { events: sized, pruned } = enforceSizeBudget
|
|
33
34
|
? pruneToSizeBudget(events)
|
|
@@ -36,7 +37,12 @@ export function buildReport(events, meta, options = {}) {
|
|
|
36
37
|
const consoleRows = extractConsole(sized);
|
|
37
38
|
const networkRows = extractNetwork(sized);
|
|
38
39
|
const actions = extractActionLog(sized);
|
|
39
|
-
|
|
40
|
+
// Advisory security-hygiene analysis (default on). Runs over the SAME
|
|
41
|
+
// size-pruned events the other extractors consume; skipped when disabled.
|
|
42
|
+
const securityFindings = security
|
|
43
|
+
? analyze(sized, { suppress: securitySuppress })
|
|
44
|
+
: [];
|
|
45
|
+
const markdown = buildMarkdown(resolvedMeta, consoleRows, networkRows, actions, securityFindings);
|
|
40
46
|
// First/last event timestamps drive the hero meta strip's "Events" item,
|
|
41
47
|
// the replay-header session-range label, and the failure marker on the
|
|
42
48
|
// custom timeline strip. `?? 0` fallback covers the crashed-before-
|
|
@@ -49,6 +55,7 @@ export function buildReport(events, meta, options = {}) {
|
|
|
49
55
|
eventsGzB64: encodeEventsBlob(sized),
|
|
50
56
|
console: consoleRows,
|
|
51
57
|
network: networkRows,
|
|
58
|
+
security: securityFindings,
|
|
52
59
|
markdown,
|
|
53
60
|
pruned,
|
|
54
61
|
eventCount: sized.length,
|
package/dist/build-report.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-report.js","sourceRoot":"","sources":["../src/build-report.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,yEAAyE;AACzE,8EAA8E;AAC9E,wCAAwC;AACxC,yEAAyE;AACzE,gEAAgE;AAChE,iEAAiE;AACjE,iEAAiE;AACjE,iEAAiE;AACjE,iDAAiD;AACjD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,kDAAkD;AAGlD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"build-report.js","sourceRoot":"","sources":["../src/build-report.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,yEAAyE;AACzE,8EAA8E;AAC9E,wCAAwC;AACxC,yEAAyE;AACzE,gEAAgE;AAChE,iEAAiE;AACjE,iEAAiE;AACjE,iEAAiE;AACjE,iDAAiD;AACjD,EAAE;AACF,gFAAgF;AAChF,gFAAgF;AAChF,kDAAkD;AAGlD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AA8BjD;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CACzB,MAAuB,EACvB,IAAgB,EAChB,UAA8B,EAAE;IAEhC,MAAM,EACJ,iBAAiB,GAAG,IAAI,EACxB,MAAM,GAAG,IAAI,EACb,QAAQ,GAAG,IAAI,EACf,gBAAgB,GAAG,EAAE,GACtB,GAAG,OAAO,CAAC;IAEZ,2EAA2E;IAC3E,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,iBAAiB;QACjD,CAAC,CAAC,iBAAiB,CAAC,MAAM,CAAC;QAC3B,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAE9B,MAAM,YAAY,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,WAAW,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACxC,sEAAsE;IACtE,0EAA0E;IAC1E,MAAM,gBAAgB,GAAsB,QAAQ;QAClD,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,gBAAgB,EAAE,CAAC;QAChD,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,QAAQ,GAAG,aAAa,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,gBAAgB,CAAC,CAAC;IAElG,yEAAyE;IACzE,uEAAuE;IACvE,oEAAoE;IACpE,qEAAqE;IACrE,gEAAgE;IAChE,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,SAAS,IAAI,CAAC,CAAC;IAEvD,OAAO,gBAAgB,CAAC;QACtB,IAAI,EAAE,YAAY;QAClB,WAAW,EAAE,gBAAgB,CAAC,KAAK,CAAC;QACpC,OAAO,EAAE,WAAW;QACpB,OAAO,EAAE,WAAW;QACpB,QAAQ,EAAE,gBAAgB;QAC1B,QAAQ;QACR,MAAM;QACN,UAAU,EAAE,KAAK,CAAC,MAAM;QACxB,OAAO;QACP,MAAM;QACN,MAAM;KACP,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -9,4 +9,5 @@ export type { ConsoleEntry, NetworkEntry } from './panels.js';
|
|
|
9
9
|
export { resolveCiMetadata } from './metadata.js';
|
|
10
10
|
export { buildMarkdown, extractActionLog, MAX_CONSOLE_MESSAGES, MAX_ACTIONS } from './markdown.js';
|
|
11
11
|
export type { ActionEntry } from './markdown.js';
|
|
12
|
+
export type { SecurityFinding, Severity, SecuritySignal, Suppression } from './_security/index.js';
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIrE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGhE,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACnG,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIrE,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACpF,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGhE,OAAO,EACL,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACnG,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAKjD,YAAY,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/markdown.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
import type { SecurityFinding } from './_security/index.js';
|
|
2
3
|
import type { ConsoleEntry, NetworkEntry } from './panels.js';
|
|
3
4
|
import type { ReportMeta } from './types.js';
|
|
4
5
|
/** Max console messages included in the prompt (P1 PRD §F.3: "last 30"). */
|
|
@@ -18,5 +19,5 @@ export interface ActionEntry {
|
|
|
18
19
|
*/
|
|
19
20
|
export declare function extractActionLog(events: readonly eventWithTime[]): ActionEntry[];
|
|
20
21
|
/** Build the Markdown prompt (P1 PRD §F.3). */
|
|
21
|
-
export declare function buildMarkdown(meta: ReportMeta, consoleRows: readonly ConsoleEntry[], networkRows: readonly NetworkEntry[], actions: readonly ActionEntry[]): string;
|
|
22
|
+
export declare function buildMarkdown(meta: ReportMeta, consoleRows: readonly ConsoleEntry[], networkRows: readonly NetworkEntry[], actions: readonly ActionEntry[], securityFindings?: readonly SecurityFinding[]): string;
|
|
22
23
|
//# sourceMappingURL=markdown.d.ts.map
|
package/dist/markdown.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,uEAAuE;AACvE,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAqBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,WAAW,EAAE,CAsBhF;AAMD,+CAA+C;AAC/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,SAAS,YAAY,EAAE,EACpC,WAAW,EAAE,SAAS,YAAY,EAAE,EACpC,OAAO,EAAE,SAAS,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,4EAA4E;AAC5E,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,uEAAuE;AACvE,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB;AAqBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,WAAW,EAAE,CAsBhF;AAMD,+CAA+C;AAC/C,wBAAgB,aAAa,CAC3B,IAAI,EAAE,UAAU,EAChB,WAAW,EAAE,SAAS,YAAY,EAAE,EACpC,WAAW,EAAE,SAAS,YAAY,EAAE,EACpC,OAAO,EAAE,SAAS,WAAW,EAAE,EAC/B,gBAAgB,GAAE,SAAS,eAAe,EAAO,GAChD,MAAM,CAuDR"}
|
package/dist/markdown.js
CHANGED
|
@@ -54,7 +54,7 @@ function bullet(line) {
|
|
|
54
54
|
return `- ${line}`;
|
|
55
55
|
}
|
|
56
56
|
/** Build the Markdown prompt (P1 PRD §F.3). */
|
|
57
|
-
export function buildMarkdown(meta, consoleRows, networkRows, actions) {
|
|
57
|
+
export function buildMarkdown(meta, consoleRows, networkRows, actions, securityFindings = []) {
|
|
58
58
|
const out = [];
|
|
59
59
|
out.push('## Failing test');
|
|
60
60
|
out.push(bullet(`Spec: ${meta.spec ?? '(unknown)'}`));
|
|
@@ -96,6 +96,16 @@ export function buildMarkdown(meta, consoleRows, networkRows, actions) {
|
|
|
96
96
|
else {
|
|
97
97
|
lastActions.forEach((a, i) => out.push(`${i + 1}. ${a.description}`));
|
|
98
98
|
}
|
|
99
|
+
// Advisory security-hygiene section — additive, omitted entirely when there
|
|
100
|
+
// are no findings (Task 10). These are observations, NOT a security audit.
|
|
101
|
+
if (securityFindings.length > 0) {
|
|
102
|
+
out.push('');
|
|
103
|
+
out.push('## Security hygiene (advisory)');
|
|
104
|
+
out.push('_Observed during the test run — advisory hygiene signals, not a security audit._');
|
|
105
|
+
for (const s of securityFindings) {
|
|
106
|
+
out.push(bullet(`[${s.severity}] ${s.title} — ${s.evidence}`));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
99
109
|
return `${out.join('\n')}\n`;
|
|
100
110
|
}
|
|
101
111
|
//# sourceMappingURL=markdown.js.map
|
package/dist/markdown.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,EAAE;AACF,+EAA+E;AAC/E,2EAA2E;AAC3E,0EAA0E;AAC1E,4EAA4E;AAE5E,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"markdown.js","sourceRoot":"","sources":["../src/markdown.ts"],"names":[],"mappings":"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"}
|
package/dist/panels.d.ts
CHANGED
|
@@ -24,12 +24,23 @@ export declare const NETWORK_PLUGIN: "rrweb/network@1";
|
|
|
24
24
|
export declare const NETWORK_EVENT_TAG = "tracelane.test.network-error";
|
|
25
25
|
/** console.error prefix for the v1 network fallback (P1 PRD §E.2). */
|
|
26
26
|
export declare const NETWORK_CONSOLE_PREFIX = "[tracelane.net]";
|
|
27
|
+
/**
|
|
28
|
+
* console.error prefix the capture layer uses for privacy-safe response
|
|
29
|
+
* metadata consumed by `@tracelane/security`. Like the network-scrape lines,
|
|
30
|
+
* these are a non-console signal and are filtered OUT of the console panel —
|
|
31
|
+
* the security analyzer surfaces them as advisory findings instead. Defined
|
|
32
|
+
* locally (mirroring {@link NETWORK_CONSOLE_PREFIX}) rather than imported from
|
|
33
|
+
* `@tracelane/security` just for a string constant.
|
|
34
|
+
*/
|
|
35
|
+
export declare const SEC_CONSOLE_PREFIX = "[tracelane.sec]";
|
|
27
36
|
/**
|
|
28
37
|
* Extract console panel rows. Drops the CDP network-scrape lines
|
|
29
38
|
* ('[tracelane.net] …'): they are a *network* signal that `extractNetwork`
|
|
30
39
|
* already surfaces as structured rows in the Network panel. Letting them
|
|
31
40
|
* through here double-renders the same failure as a raw console string,
|
|
32
|
-
* inconsistently with the structured row (audit A-4).
|
|
41
|
+
* inconsistently with the structured row (audit A-4). Also drops the
|
|
42
|
+
* '[tracelane.sec] …' response-metadata lines: those are a *security* signal
|
|
43
|
+
* the analyzer turns into advisory findings, not console output (Task 9).
|
|
33
44
|
*/
|
|
34
45
|
export declare function extractConsole(events: readonly eventWithTime[]): ConsoleEntry[];
|
|
35
46
|
/**
|
package/dist/panels.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAuC,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE/F,2BAA2B;AAC3B,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,8CAA8C;AAC9C,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD;;;;GAIG;AACH,eAAO,MAAM,cAAc,mBAAsB,CAAC;AAClD,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,iCAAiC,CAAC;AAChE,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AA+
|
|
1
|
+
{"version":3,"file":"panels.d.ts","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAuC,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE/F,2BAA2B;AAC3B,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,8CAA8C;AAC9C,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD;;;;GAIG;AACH,eAAO,MAAM,cAAc,mBAAsB,CAAC;AAClD,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,iCAAiC,CAAC;AAChE,sEAAsE;AACtE,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AACxD;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,oBAAoB,CAAC;AA+EpD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,YAAY,EAAE,CAK/E;AAkGD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,aAAa,EAAE,GAAG,YAAY,EAAE,CAmD/E"}
|
package/dist/panels.js
CHANGED
|
@@ -29,6 +29,15 @@ export const NETWORK_PLUGIN = NETWORK_PLUGIN_NAME;
|
|
|
29
29
|
export const NETWORK_EVENT_TAG = 'tracelane.test.network-error';
|
|
30
30
|
/** console.error prefix for the v1 network fallback (P1 PRD §E.2). */
|
|
31
31
|
export const NETWORK_CONSOLE_PREFIX = '[tracelane.net]';
|
|
32
|
+
/**
|
|
33
|
+
* console.error prefix the capture layer uses for privacy-safe response
|
|
34
|
+
* metadata consumed by `@tracelane/security`. Like the network-scrape lines,
|
|
35
|
+
* these are a non-console signal and are filtered OUT of the console panel —
|
|
36
|
+
* the security analyzer surfaces them as advisory findings instead. Defined
|
|
37
|
+
* locally (mirroring {@link NETWORK_CONSOLE_PREFIX}) rather than imported from
|
|
38
|
+
* `@tracelane/security` just for a string constant.
|
|
39
|
+
*/
|
|
40
|
+
export const SEC_CONSOLE_PREFIX = '[tracelane.sec]';
|
|
32
41
|
function isPlugin(e) {
|
|
33
42
|
return e.type === EventType.Plugin;
|
|
34
43
|
}
|
|
@@ -94,10 +103,12 @@ function extractConsoleRows(events) {
|
|
|
94
103
|
* ('[tracelane.net] …'): they are a *network* signal that `extractNetwork`
|
|
95
104
|
* already surfaces as structured rows in the Network panel. Letting them
|
|
96
105
|
* through here double-renders the same failure as a raw console string,
|
|
97
|
-
* inconsistently with the structured row (audit A-4).
|
|
106
|
+
* inconsistently with the structured row (audit A-4). Also drops the
|
|
107
|
+
* '[tracelane.sec] …' response-metadata lines: those are a *security* signal
|
|
108
|
+
* the analyzer turns into advisory findings, not console output (Task 9).
|
|
98
109
|
*/
|
|
99
110
|
export function extractConsole(events) {
|
|
100
|
-
return extractConsoleRows(events).filter((row) => !row.message.includes(NETWORK_CONSOLE_PREFIX));
|
|
111
|
+
return extractConsoleRows(events).filter((row) => !row.message.includes(NETWORK_CONSOLE_PREFIX) && !row.message.includes(SEC_CONSOLE_PREFIX));
|
|
101
112
|
}
|
|
102
113
|
/** Parse a '[tracelane.net] <METHOD> <STATUS> <URL>' console line, if it is one. */
|
|
103
114
|
function parseNetConsoleLine(message, timestamp) {
|
package/dist/panels.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"panels.js","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,8EAA8E;AAC9E,0EAA0E;AAC1E,2EAA2E;AAC3E,6EAA6E;AAC7E,EAAE;AACF,qEAAqE;AACrE,6EAA6E;AAC7E,6EAA6E;AAC7E,8EAA8E;AAC9E,+DAA+D;AAC/D,yEAAyE;AACzE,4DAA4D;AAC5D,6EAA6E;AAC7E,0EAA0E;AAC1E,0EAA0E;AAC1E,mCAAmC;AAEnC,OAAO,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAkBtE,8CAA8C;AAC9C,MAAM,CAAC,MAAM,cAAc,GAAG,iBAAiB,CAAC;AAChD;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,mBAAmB,CAAC;AAClD,qEAAqE;AACrE,MAAM,CAAC,MAAM,iBAAiB,GAAG,8BAA8B,CAAC;AAChE,sEAAsE;AACtE,MAAM,CAAC,MAAM,sBAAsB,GAAG,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"panels.js","sourceRoot":"","sources":["../src/panels.ts"],"names":[],"mappings":"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"}
|
package/dist/report-writer.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
+
import type { Suppression } from './_security/index.js';
|
|
2
3
|
import type { ReportMeta } from './types.js';
|
|
3
4
|
/** Inputs for {@link writeReport}. */
|
|
4
5
|
export interface WriteReportInput {
|
|
@@ -12,6 +13,10 @@ export interface WriteReportInput {
|
|
|
12
13
|
meta: ReportMeta;
|
|
13
14
|
/** Render the report's "Generated by tracelane" footer. Default true. */
|
|
14
15
|
footer?: boolean | undefined;
|
|
16
|
+
/** Run the advisory security analyzer + render its findings. Default true. */
|
|
17
|
+
security?: boolean | undefined;
|
|
18
|
+
/** Suppressions forwarded to the analyzer. Ignored when `security` is false. */
|
|
19
|
+
securitySuppress?: Suppression[] | undefined;
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
17
22
|
* Turn an arbitrary spec path / test title into a filesystem-safe slug:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report-writer.d.ts","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"report-writer.d.ts","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,qHAAqH;IACrH,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,mCAAmC;IACnC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,kEAAkE;IAClE,IAAI,EAAE,UAAU,CAAC;IACjB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC/B,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,WAAW,EAAE,GAAG,SAAS,CAAC;CAC9C;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAa3D"}
|
package/dist/report-writer.js
CHANGED
|
@@ -46,8 +46,15 @@ export function specSlug(spec) {
|
|
|
46
46
|
* Returns the absolute-or-relative path written (as `join` produced it).
|
|
47
47
|
*/
|
|
48
48
|
export function writeReport(input) {
|
|
49
|
-
const { outDir, cid, events, meta, footer } = input;
|
|
50
|
-
const
|
|
49
|
+
const { outDir, cid, events, meta, footer, security, securitySuppress } = input;
|
|
50
|
+
const buildOptions = {};
|
|
51
|
+
if (footer === false)
|
|
52
|
+
buildOptions.footer = false;
|
|
53
|
+
if (security !== undefined)
|
|
54
|
+
buildOptions.security = security;
|
|
55
|
+
if (securitySuppress !== undefined)
|
|
56
|
+
buildOptions.securitySuppress = securitySuppress;
|
|
57
|
+
const html = buildReport(events, meta, buildOptions);
|
|
51
58
|
if (!existsSync(outDir)) {
|
|
52
59
|
mkdirSync(outDir, { recursive: true });
|
|
53
60
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"report-writer.js","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,2EAA2E;AAC3E,yBAAyB;AAEzB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"report-writer.js","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,8EAA8E;AAC9E,8EAA8E;AAC9E,2EAA2E;AAC3E,yBAAyB;AAEzB,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAA2B,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAqBzE;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,IAAI,GAAG,KAAK;SACf,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,6EAA6E;IAC7E,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAgB,EAAE,GAAY;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,OAAO,GAAG,QAAQ,KAAK,SAAS,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC;AACnE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,wCAAwC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;IAChF,MAAM,YAAY,GAAuB,EAAE,CAAC;IAC5C,IAAI,MAAM,KAAK,KAAK;QAAE,YAAY,CAAC,MAAM,GAAG,KAAK,CAAC;IAClD,IAAI,QAAQ,KAAK,SAAS;QAAE,YAAY,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7D,IAAI,gBAAgB,KAAK,SAAS;QAAE,YAAY,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;IACrF,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACzD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
package/dist/template.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SecurityFinding } from './_security/index.js';
|
|
1
2
|
import type { ConsoleEntry, NetworkEntry } from './panels.js';
|
|
2
3
|
import type { ReportMeta } from './types.js';
|
|
3
4
|
/** Everything `renderReportHtml` needs; build-report.ts prepares it. */
|
|
@@ -9,6 +10,12 @@ export interface ReportTemplateData {
|
|
|
9
10
|
console: ConsoleEntry[];
|
|
10
11
|
/** Extracted network panel rows (Task 2.10). */
|
|
11
12
|
network: NetworkEntry[];
|
|
13
|
+
/**
|
|
14
|
+
* Advisory security-hygiene findings (Task 12). Rendered as a collapsed
|
|
15
|
+
* panel; the panel + its tab are OMITTED entirely when this is empty (no
|
|
16
|
+
* zero-state). Advisory only — NOT a security audit.
|
|
17
|
+
*/
|
|
18
|
+
security: SecurityFinding[];
|
|
12
19
|
/** Pre-rendered "Copy as Markdown for AI paste" payload (Task 2.12). */
|
|
13
20
|
markdown: string;
|
|
14
21
|
/** Whether the events were pruned to fit the size budget (ADR-0005 banner). */
|
package/dist/template.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAW5D,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,UAAU,CAAC;IACjB,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,gDAAgD;IAChD,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB;;;;OAIG;IACH,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,wEAAwE;IACxE,QAAQ,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,MAAM,EAAE,OAAO,CAAC;IAChB,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB;yEACqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AA86BD,qDAAqD;AACrD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,kBAAkB,GAAG,MAAM,CAqJjE"}
|
package/dist/template.js
CHANGED
|
@@ -434,6 +434,27 @@ main.investigation {
|
|
|
434
434
|
.row.row-net .lvl.st-5 { color: var(--amber); }
|
|
435
435
|
.row.row-net .lvl.st-0 { color: var(--amber); } /* net error */
|
|
436
436
|
|
|
437
|
+
/* Advisory security findings: not time-synced (no seek), so not clickable. */
|
|
438
|
+
.row.row-sec { cursor: default; grid-template-columns: max-content 1fr; }
|
|
439
|
+
.row.row-sec.sev-high .lvl { color: var(--amber); }
|
|
440
|
+
.row.row-sec.sev-medium .lvl { color: var(--warn); }
|
|
441
|
+
.row.row-sec.sev-low .lvl { color: var(--muted-strong); }
|
|
442
|
+
.row.row-sec .sec-detail {
|
|
443
|
+
color: var(--muted);
|
|
444
|
+
font-size: 10.5px;
|
|
445
|
+
margin-top: 3px;
|
|
446
|
+
white-space: pre-wrap;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/* Security panel subtitle (advisory framing) shown in its toolbar. */
|
|
450
|
+
.panel-toolbar-sec { flex-direction: column; align-items: stretch; gap: 8px; }
|
|
451
|
+
.panel-subtitle {
|
|
452
|
+
color: var(--muted);
|
|
453
|
+
font-size: 11px;
|
|
454
|
+
font-style: italic;
|
|
455
|
+
line-height: 1.5;
|
|
456
|
+
}
|
|
457
|
+
|
|
437
458
|
/* "Coming soon" pane content (Actions / Timeline tabs reserved for follow-ups) */
|
|
438
459
|
.coming-soon {
|
|
439
460
|
padding: 32px 16px;
|
|
@@ -681,8 +702,37 @@ const BOOTSTRAP = `
|
|
|
681
702
|
}
|
|
682
703
|
}
|
|
683
704
|
|
|
705
|
+
// Advisory security-hygiene findings (Task 12). Unlike console/network rows
|
|
706
|
+
// these are NOT time-synced (a finding has no single moment in the replay) —
|
|
707
|
+
// each renders as a static, always-visible row: "[severity] title — evidence"
|
|
708
|
+
// with the detail as muted secondary text. The container is only present in
|
|
709
|
+
// the DOM when there are findings (the tab + pane are omitted when empty).
|
|
710
|
+
function renderSecurity(container, findings) {
|
|
711
|
+
if (!container) return;
|
|
712
|
+
if (!findings.length) {
|
|
713
|
+
container.appendChild(el('div', 'panel-empty', 'No advisory security findings.'));
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
for (var i = 0; i < findings.length; i++) {
|
|
717
|
+
var f = findings[i];
|
|
718
|
+
var sev = (f.severity || 'low').toLowerCase();
|
|
719
|
+
var row = el('div', 'row row-sec sev-' + sev);
|
|
720
|
+
row.appendChild(el('span', 'lvl', sev));
|
|
721
|
+
var msg = el('span', 'msg');
|
|
722
|
+
msg.appendChild(document.createTextNode(f.title + ' — ' + f.evidence));
|
|
723
|
+
if (f.detail) {
|
|
724
|
+
msg.appendChild(el('div', 'sec-detail', f.detail));
|
|
725
|
+
}
|
|
726
|
+
row.appendChild(msg);
|
|
727
|
+
var text = (sev + ' ' + f.title + ' ' + f.evidence + ' ' + (f.detail || '')).toLowerCase();
|
|
728
|
+
row.setAttribute('data-text', text);
|
|
729
|
+
container.appendChild(row);
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
684
733
|
renderConsole(document.getElementById('console-rows'), CONSOLE, FIRST_TS);
|
|
685
734
|
renderNetwork(document.getElementById('network-rows'), NETWORK, FIRST_TS);
|
|
735
|
+
renderSecurity(document.getElementById('security-rows'), SECURITY);
|
|
686
736
|
|
|
687
737
|
// ---- Time-sync: reveal rows as playback advances ----------------------
|
|
688
738
|
// TODO(perf): re-queries DOM and re-parses data-time on every tick (~30 Hz).
|
|
@@ -894,7 +944,7 @@ function formatRelativeMs(ms) {
|
|
|
894
944
|
}
|
|
895
945
|
/** Compose the full self-contained HTML document. */
|
|
896
946
|
export function renderReportHtml(data) {
|
|
897
|
-
const { meta, eventsGzB64, console: consoleRows, network, markdown, pruned, eventCount, firstTs, lastTs, footer = true, } = data;
|
|
947
|
+
const { meta, eventsGzB64, console: consoleRows, network, security, markdown, pruned, eventCount, firstTs, lastTs, footer = true, } = data;
|
|
898
948
|
const title = `tracelane — ${meta.spec ?? '(no spec)'} :: ${meta.title} (${meta.status})`;
|
|
899
949
|
// Banner only when the events were pruned to fit the size cap (ADR-0005).
|
|
900
950
|
const banner = pruned
|
|
@@ -902,11 +952,30 @@ export function renderReportHtml(data) {
|
|
|
902
952
|
: '';
|
|
903
953
|
const consoleCount = consoleRows.length;
|
|
904
954
|
const networkCount = network.length;
|
|
955
|
+
const securityCount = security.length;
|
|
956
|
+
// Advisory security tab + pane — rendered ONLY when there are findings
|
|
957
|
+
// (no zero-state; the analyzer is advisory). Mirrors the Console/Network
|
|
958
|
+
// panel markup but without time-sync (findings have no replay moment).
|
|
959
|
+
const securityTab = securityCount > 0
|
|
960
|
+
? `<button class="tab" type="button" role="tab" id="tab-security" aria-selected="false" aria-controls="pane-security" data-pane="pane-security">
|
|
961
|
+
Security <span class="count-total">${securityCount}</span>
|
|
962
|
+
</button>`
|
|
963
|
+
: '';
|
|
964
|
+
const securityPane = securityCount > 0
|
|
965
|
+
? `<div class="panel-pane" id="pane-security" role="tabpanel" aria-labelledby="tab-security">
|
|
966
|
+
<div class="panel-toolbar panel-toolbar-sec">
|
|
967
|
+
<span class="panel-subtitle">Observed during the test run — advisory hygiene signals, not a security audit.</span>
|
|
968
|
+
<input type="text" class="panel-filter" placeholder="Filter findings…" aria-label="Filter security findings" />
|
|
969
|
+
</div>
|
|
970
|
+
<div id="security-rows" class="panel-content"></div>
|
|
971
|
+
</div>`
|
|
972
|
+
: '';
|
|
905
973
|
// Data payloads embedded as JS consts, all escaped for inline-script safety.
|
|
906
974
|
const dataScript = `const META = ${serializeForScript(meta)};\n` +
|
|
907
975
|
`const EVENTS_GZ_B64 = "${eventsGzB64}";\n` +
|
|
908
976
|
`const CONSOLE = ${serializeForScript(consoleRows)};\n` +
|
|
909
977
|
`const NETWORK = ${serializeForScript(network)};\n` +
|
|
978
|
+
`const SECURITY = ${serializeForScript(security)};\n` +
|
|
910
979
|
`const MARKDOWN = ${serializeForScript(markdown)};\n` +
|
|
911
980
|
`const FIRST_TS = ${firstTs};\n` +
|
|
912
981
|
`const LAST_TS = ${lastTs};`;
|
|
@@ -941,6 +1010,7 @@ ${banner}
|
|
|
941
1010
|
<button class="tab" type="button" role="tab" id="tab-network" aria-selected="false" aria-controls="pane-network" data-pane="pane-network">
|
|
942
1011
|
Network <span class="count">0</span><span class="count-total">/ ${networkCount}</span>
|
|
943
1012
|
</button>
|
|
1013
|
+
${securityTab}
|
|
944
1014
|
<button class="tab" type="button" role="tab" id="tab-actions" aria-selected="false" aria-controls="pane-actions" data-pane="pane-actions">
|
|
945
1015
|
Actions <span class="soon-pill">soon</span>
|
|
946
1016
|
</button>
|
|
@@ -967,6 +1037,8 @@ ${banner}
|
|
|
967
1037
|
<div class="panel-pending" id="network-pending" role="status" aria-live="polite">Network errors will appear during playback.</div>
|
|
968
1038
|
</div>
|
|
969
1039
|
|
|
1040
|
+
${securityPane}
|
|
1041
|
+
|
|
970
1042
|
<div class="panel-pane" id="pane-actions" role="tabpanel" aria-labelledby="tab-actions">
|
|
971
1043
|
<div class="coming-soon">
|
|
972
1044
|
<strong>Actions panel — coming soon</strong>
|
package/dist/template.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,gCAAgC;AAChC,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,4EAA4E;AAC5E,0EAA0E;AAC1E,4EAA4E;AAC5E,8CAA8C;
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tracelane/report",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.18",
|
|
4
4
|
"description": "Self-contained, offline HTML report builder for tracelane. Embeds the rrweb player and a gzipped event blob into a single .html file.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tracelane",
|
|
@@ -35,10 +35,11 @@
|
|
|
35
35
|
"fflate": "^0.8.2",
|
|
36
36
|
"rrweb-player": "1.0.0-alpha.4",
|
|
37
37
|
"@cubenest/rrweb-core": "0.1.0-alpha.6",
|
|
38
|
-
"@tracelane/core": "0.1.0-alpha.
|
|
38
|
+
"@tracelane/core": "0.1.0-alpha.15"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"jsdom": "^25.0.1"
|
|
41
|
+
"jsdom": "^25.0.1",
|
|
42
|
+
"@tracelane/security": "0.1.0-alpha.1"
|
|
42
43
|
},
|
|
43
44
|
"publishConfig": {
|
|
44
45
|
"access": "public",
|
|
@@ -57,8 +58,8 @@
|
|
|
57
58
|
"node": ">=22"
|
|
58
59
|
},
|
|
59
60
|
"scripts": {
|
|
60
|
-
"build": "tsc -p tsconfig.json",
|
|
61
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
62
|
-
"test": "vitest run --passWithNoTests"
|
|
61
|
+
"build": "node scripts/vendor-security.mjs && tsc -p tsconfig.json",
|
|
62
|
+
"typecheck": "node scripts/vendor-security.mjs && tsc -p tsconfig.json --noEmit",
|
|
63
|
+
"test": "node scripts/vendor-security.mjs && vitest run --passWithNoTests"
|
|
63
64
|
}
|
|
64
65
|
}
|