@keithadler/frostjs 0.1.0 → 0.3.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.
Files changed (66) hide show
  1. package/README.md +98 -8
  2. package/dist/audit.d.ts +71 -0
  3. package/dist/audit.d.ts.map +1 -0
  4. package/dist/audit.js +202 -0
  5. package/dist/audit.js.map +1 -0
  6. package/dist/capabilities.d.ts +22 -0
  7. package/dist/capabilities.d.ts.map +1 -0
  8. package/dist/capabilities.js +140 -0
  9. package/dist/capabilities.js.map +1 -0
  10. package/dist/cli/args.d.ts +4 -3
  11. package/dist/cli/args.d.ts.map +1 -1
  12. package/dist/cli/args.js +23 -2
  13. package/dist/cli/args.js.map +1 -1
  14. package/dist/cli/run.d.ts.map +1 -1
  15. package/dist/cli/run.js +99 -5
  16. package/dist/cli/run.js.map +1 -1
  17. package/dist/extract/html.d.ts +11 -0
  18. package/dist/extract/html.d.ts.map +1 -1
  19. package/dist/extract/html.js +58 -0
  20. package/dist/extract/html.js.map +1 -1
  21. package/dist/extract/index.d.ts +2 -0
  22. package/dist/extract/index.d.ts.map +1 -1
  23. package/dist/extract/index.js +19 -0
  24. package/dist/extract/index.js.map +1 -1
  25. package/dist/extract/recognizers/device.d.ts +10 -0
  26. package/dist/extract/recognizers/device.d.ts.map +1 -0
  27. package/dist/extract/recognizers/device.js +46 -0
  28. package/dist/extract/recognizers/device.js.map +1 -0
  29. package/dist/extract/recognizers/dom-escape.d.ts.map +1 -1
  30. package/dist/extract/recognizers/dom-escape.js +11 -0
  31. package/dist/extract/recognizers/dom-escape.js.map +1 -1
  32. package/dist/extract/recognizers/message.d.ts +16 -0
  33. package/dist/extract/recognizers/message.d.ts.map +1 -0
  34. package/dist/extract/recognizers/message.js +92 -0
  35. package/dist/extract/recognizers/message.js.map +1 -0
  36. package/dist/extract/recognizers/network.d.ts +10 -2
  37. package/dist/extract/recognizers/network.d.ts.map +1 -1
  38. package/dist/extract/recognizers/network.js +49 -6
  39. package/dist/extract/recognizers/network.js.map +1 -1
  40. package/dist/extract/taint.d.ts +36 -0
  41. package/dist/extract/taint.d.ts.map +1 -0
  42. package/dist/extract/taint.js +503 -0
  43. package/dist/extract/taint.js.map +1 -0
  44. package/dist/extract/target.d.ts.map +1 -1
  45. package/dist/extract/target.js +23 -1
  46. package/dist/extract/target.js.map +1 -1
  47. package/dist/policy/compile.d.ts +3 -1
  48. package/dist/policy/compile.d.ts.map +1 -1
  49. package/dist/policy/compile.js +1 -0
  50. package/dist/policy/compile.js.map +1 -1
  51. package/dist/policy/index.js +1 -1
  52. package/dist/policy/index.js.map +1 -1
  53. package/dist/policy/parse.d.ts +2 -0
  54. package/dist/policy/parse.d.ts.map +1 -1
  55. package/dist/policy/parse.js +11 -1
  56. package/dist/policy/parse.js.map +1 -1
  57. package/dist/policy/vocabulary.d.ts.map +1 -1
  58. package/dist/policy/vocabulary.js +20 -0
  59. package/dist/policy/vocabulary.js.map +1 -1
  60. package/dist/report/summary.d.ts.map +1 -1
  61. package/dist/report/summary.js +2 -0
  62. package/dist/report/summary.js.map +1 -1
  63. package/dist/report/text.d.ts.map +1 -1
  64. package/dist/report/text.js +5 -0
  65. package/dist/report/text.js.map +1 -1
  66. package/package.json +4 -3
@@ -0,0 +1,10 @@
1
+ import { type Recognizer } from "./types.js";
2
+ /**
3
+ * Reaching the machine, not just the page: WebUSB, Web Bluetooth, Web
4
+ * Serial, WebHID, Web MIDI and the wake lock through `navigator`; the File
5
+ * System Access pickers, which hand back a real file or directory handle;
6
+ * and `Notification`. These are strong capabilities a page rarely needs and
7
+ * a policy will almost always want to deny by default.
8
+ */
9
+ export declare const device: Recognizer;
10
+ //# sourceMappingURL=device.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.d.ts","sourceRoot":"","sources":["../../../src/extract/recognizers/device.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAgB3D;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,EAAE,UAsBpB,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { match } from "./types.js";
2
+ import { asGlobalIn, asNamedGlobal, isIdentifier, memberName } from "./resolve.js";
3
+ /** navigator members that reach hardware or the user's device beyond fingerprinting. */
4
+ const NAVIGATOR = new Map([
5
+ ["usb", "device.usb"],
6
+ ["bluetooth", "device.bluetooth"],
7
+ ["serial", "device.serial"],
8
+ ["hid", "device.hid"],
9
+ ["wakeLock", "device.wakelock"],
10
+ ["requestMIDIAccess", "device.midi"],
11
+ ]);
12
+ /** Bare/window globals that open the File System Access picker: read or write the user's real files. */
13
+ const FILE_PICKERS = new Set(["showOpenFilePicker", "showSaveFilePicker", "showDirectoryPicker"]);
14
+ /**
15
+ * Reaching the machine, not just the page: WebUSB, Web Bluetooth, Web
16
+ * Serial, WebHID, Web MIDI and the wake lock through `navigator`; the File
17
+ * System Access pickers, which hand back a real file or directory handle;
18
+ * and `Notification`. These are strong capabilities a page rarely needs and
19
+ * a policy will almost always want to deny by default.
20
+ */
21
+ export const device = ({ node, binding }) => {
22
+ if (binding)
23
+ return null;
24
+ const n = node;
25
+ // showOpenFilePicker(...), window.showSaveFilePicker(...)
26
+ const picker = asGlobalIn(n, FILE_PICKERS);
27
+ if (picker)
28
+ return match("device.filesystem", picker.r, node);
29
+ // Notification: new Notification(...), Notification.requestPermission(), Notification.permission
30
+ if (isIdentifier(n, "Notification"))
31
+ return { capability: "device.notification", target: null, confidence: "certain", via: n, node };
32
+ if (n.type === "MemberExpression" && memberName(n) === "Notification") {
33
+ const r = asNamedGlobal(n["object"], "window");
34
+ if (r)
35
+ return match("device.notification", r, node);
36
+ }
37
+ // navigator.usb, navigator.bluetooth, navigator.requestMIDIAccess, ...
38
+ if (n.type !== "MemberExpression")
39
+ return null;
40
+ const cap = NAVIGATOR.get(memberName(n) ?? "");
41
+ if (!cap)
42
+ return null;
43
+ const r = asNamedGlobal(n["object"], "navigator");
44
+ return r ? match(cap, r, node) : null;
45
+ };
46
+ //# sourceMappingURL=device.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"device.js","sourceRoot":"","sources":["../../../src/extract/recognizers/device.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAA0B,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEnF,wFAAwF;AACxF,MAAM,SAAS,GAAgC,IAAI,GAAG,CAAC;IACrD,CAAC,KAAK,EAAE,YAAY,CAAC;IACrB,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACjC,CAAC,QAAQ,EAAE,eAAe,CAAC;IAC3B,CAAC,KAAK,EAAE,YAAY,CAAC;IACrB,CAAC,UAAU,EAAE,iBAAiB,CAAC;IAC/B,CAAC,mBAAmB,EAAE,aAAa,CAAC;CACrC,CAAC,CAAC;AAEH,wGAAwG;AACxG,MAAM,YAAY,GAAwB,IAAI,GAAG,CAAC,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,qBAAqB,CAAC,CAAC,CAAC;AAEvH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,MAAM,GAAe,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;IACtD,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,CAAC,GAAG,IAAe,CAAC;IAE1B,0DAA0D;IAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC;IAC3C,IAAI,MAAM;QAAE,OAAO,KAAK,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAE9D,iGAAiG;IACjG,IAAI,YAAY,CAAC,CAAC,EAAE,cAAc,CAAC;QACjC,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC;IAClG,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,cAAc,EAAE,CAAC;QACtE,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAY,EAAE,QAAQ,CAAC,CAAC;QAC1D,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC,qBAAqB,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;IACtD,CAAC;IAED,uEAAuE;IACvE,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAY,EAAE,WAAW,CAAC,CAAC;IAC7D,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACxC,CAAC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"dom-escape.d.ts","sourceRoot":"","sources":["../../../src/extract/recognizers/dom-escape.ts"],"names":[],"mappings":"AACA,OAAO,EAAuC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAelF;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,UAmCvB,CAAC"}
1
+ {"version":3,"file":"dom-escape.d.ts","sourceRoot":"","sources":["../../../src/extract/recognizers/dom-escape.ts"],"names":[],"mappings":"AACA,OAAO,EAAuC,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAkBlF;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,EAAE,UA0CvB,CAAC"}
@@ -4,6 +4,8 @@ import { asNamedGlobal, memberName } from "./resolve.js";
4
4
  const HTML_SINKS = new Set(["innerHTML", "outerHTML", "srcdoc"]);
5
5
  /** Methods whose call parses HTML. */
6
6
  const HTML_METHODS = new Set(["insertAdjacentHTML", "createContextualFragment"]);
7
+ /** Attribute names whose value is HTML or code: setAttribute("srcdoc"/"onclick", ...) is a sink. */
8
+ const isCodeAttribute = (name) => name === "srcdoc" || /^on[a-z]+$/.test(name);
7
9
  /** Intrinsic JSX elements, and createElement tag names, that run code. */
8
10
  const CODE_ELEMENTS = new Map([
9
11
  ["script", "dom-escape.script"],
@@ -48,6 +50,15 @@ export const domEscape = ({ node, ancestors, binding }) => {
48
50
  return null;
49
51
  if (HTML_METHODS.has(prop))
50
52
  return plain("dom-escape.html", node);
53
+ // setAttribute("srcdoc", html) parses HTML; setAttribute("onclick", code) installs a handler from a string.
54
+ if (prop === "setAttribute") {
55
+ const attr = stringValue(args[0])?.toLowerCase();
56
+ if (attr === "srcdoc")
57
+ return plain("dom-escape.html", node);
58
+ if (attr !== undefined && isCodeAttribute(attr))
59
+ return plain("dom-escape.handler", node);
60
+ return null;
61
+ }
51
62
  if (prop === "createElement") {
52
63
  const r = asNamedGlobal(n["object"], "document");
53
64
  const cap = r ? CODE_ELEMENTS.get(stringValue(args[0])?.toLowerCase() ?? "") : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"dom-escape.js","sourceRoot":"","sources":["../../../src/extract/recognizers/dom-escape.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dom-escape.js","sourceRoot":"","sources":["../../../src/extract/recognizers/dom-escape.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { type Recognizer } from "./types.js";
2
+ /**
3
+ * A main-thread `message` listener that reads the message but never checks
4
+ * its origin. `window.addEventListener("message", handler)` receives
5
+ * messages from any frame or window; a handler that uses `event.data`
6
+ * without comparing `event.origin` is the classic postMessage-XSS shape.
7
+ * This is a heuristic: it fires when the handler references the event's
8
+ * `data` and nowhere references its `origin`. `window.onmessage = handler`
9
+ * is covered too.
10
+ *
11
+ * Only `window` counts, not `self`/`globalThis`: `self.onmessage` is the
12
+ * worker idiom, and a worker's messages come from its own creator, so an
13
+ * origin check does not apply there.
14
+ */
15
+ export declare const message: Recognizer;
16
+ //# sourceMappingURL=message.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../../src/extract/recognizers/message.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAM9D;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,OAAO,EAAE,UA0BrB,CAAC"}
@@ -0,0 +1,92 @@
1
+ import { callArgs, plain } from "./types.js";
2
+ import { isIdentifier, memberName } from "./resolve.js";
3
+ import { FREE } from "../annotations.js";
4
+ /**
5
+ * A main-thread `message` listener that reads the message but never checks
6
+ * its origin. `window.addEventListener("message", handler)` receives
7
+ * messages from any frame or window; a handler that uses `event.data`
8
+ * without comparing `event.origin` is the classic postMessage-XSS shape.
9
+ * This is a heuristic: it fires when the handler references the event's
10
+ * `data` and nowhere references its `origin`. `window.onmessage = handler`
11
+ * is covered too.
12
+ *
13
+ * Only `window` counts, not `self`/`globalThis`: `self.onmessage` is the
14
+ * worker idiom, and a worker's messages come from its own creator, so an
15
+ * origin check does not apply there.
16
+ */
17
+ export const message = ({ node, ancestors, binding }) => {
18
+ if (binding)
19
+ return null;
20
+ const n = node;
21
+ const parent = ancestors[0];
22
+ if (n.type !== "MemberExpression" || !isWindow(n["object"]))
23
+ return null;
24
+ let handler;
25
+ // window.addEventListener("message", handler)
26
+ if (memberName(n) === "addEventListener") {
27
+ const args = callArgs(node, parent);
28
+ if (args && stringLit(args[0]) === "message")
29
+ handler = args[1];
30
+ }
31
+ // window.onmessage = handler
32
+ if (memberName(n) === "onmessage" && parent?.type === "AssignmentExpression" && parent["left"] === node) {
33
+ handler = parent["right"];
34
+ }
35
+ if (handler === undefined)
36
+ return null;
37
+ const param = firstParamName(handler);
38
+ if (param === null)
39
+ return null; // no named event parameter to reason about
40
+ const body = handler["body"];
41
+ if (!body)
42
+ return null;
43
+ const readsData = referencesMember(body, param, "data");
44
+ const checksOrigin = referencesMember(body, param, "origin");
45
+ if (readsData && !checksOrigin)
46
+ return plain("navigation.message-receive", n);
47
+ return null;
48
+ };
49
+ /** True for a free reference to the `window` global. */
50
+ function isWindow(nd) {
51
+ return isIdentifier(nd, "window") && nd[FREE] === true;
52
+ }
53
+ function stringLit(nd) {
54
+ return nd?.type === "Literal" && typeof nd["value"] === "string" ? nd["value"] : null;
55
+ }
56
+ /** The first parameter's identifier name of a function/arrow, or null if it is not a plain name. */
57
+ function firstParamName(fn) {
58
+ const params = fn["params"];
59
+ const p = params?.[0];
60
+ return p?.type === "Identifier" ? p["name"] : null;
61
+ }
62
+ /** Does the subtree contain `<param>.<prop>` (or `<param>["<prop>"]`)? A shallow, allocation-free walk. */
63
+ function referencesMember(root, param, prop) {
64
+ let found = false;
65
+ const visit = (nd) => {
66
+ if (found)
67
+ return;
68
+ if (nd.type === "MemberExpression") {
69
+ const obj = nd["object"];
70
+ if (isIdentifier(obj, param) && memberName(nd) === prop) {
71
+ found = true;
72
+ return;
73
+ }
74
+ }
75
+ for (const key of Object.keys(nd)) {
76
+ if (key === "type" || key === "start" || key === "end")
77
+ continue;
78
+ const v = nd[key];
79
+ if (Array.isArray(v)) {
80
+ for (const item of v)
81
+ if (item && typeof item === "object" && typeof item.type === "string")
82
+ visit(item);
83
+ }
84
+ else if (v && typeof v === "object" && typeof v.type === "string") {
85
+ visit(v);
86
+ }
87
+ }
88
+ };
89
+ visit(root);
90
+ return found;
91
+ }
92
+ //# sourceMappingURL=message.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message.js","sourceRoot":"","sources":["../../../src/extract/recognizers/message.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,19 @@
1
1
  import { type Recognizer } from "./types.js";
2
2
  /**
3
3
  * Ways code reaches another host: fetch, XMLHttpRequest, WebSocket,
4
- * EventSource, navigator.sendBeacon, and dynamic import() of an absolute
5
- * URL or an expression whose destination cannot be fixed. import() of a
4
+ * EventSource, navigator.sendBeacon, importScripts() (which loads and runs
5
+ * a script in a worker), and dynamic import() of an absolute URL or an
6
+ * expression whose destination cannot be fixed. import() of a
6
7
  * relative path (code splitting) or a bare specifier ("lodash", "node:fs")
7
8
  * resolves through the bundler or import map, not the network, and is not
8
9
  * reported.
10
+ *
11
+ * Also `network.resource`: an element's `src` set to another host
12
+ * (`script.src = "https://cdn..."`, `setAttribute("src", ...)`), when the
13
+ * URL is a literal or a folded const. The element is not checked; what
14
+ * matters is the host. A value that cannot be read is not reported: `.src`
15
+ * is set on tokens and props as often as on elements, and a list of
16
+ * "unknown" lines nobody can act on is noise.
9
17
  */
10
18
  export declare const network: Recognizer;
11
19
  //# sourceMappingURL=network.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../src/extract/recognizers/network.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAarE;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,UAoBrB,CAAC"}
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../src/extract/recognizers/network.ts"],"names":[],"mappings":"AACA,OAAO,EAA0B,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAcrE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,OAAO,EAAE,UAkDrB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { callArgs, match, plain } from "./types.js";
2
- import { asGlobalIn, asNamedGlobal, memberName } from "./resolve.js";
3
- import { resolveTargetOf } from "../target.js";
2
+ import { asGlobalIn, asGlobalObject, asNamedGlobal, isIdentifier, memberName } from "./resolve.js";
3
+ import { resolveTargetOf, SAME_ORIGIN } from "../target.js";
4
4
  /** Globals that are network entry points. The target comes from the first call argument. */
5
5
  const NETWORK_GLOBALS = new Map([
6
6
  ["fetch", "network.fetch"],
@@ -9,13 +9,22 @@ const NETWORK_GLOBALS = new Map([
9
9
  ["EventSource", "network.eventsource"],
10
10
  ]);
11
11
  const NETWORK_NAMES = new Set(NETWORK_GLOBALS.keys());
12
+ const IMPORT_SCRIPTS = "importScripts";
12
13
  /**
13
14
  * Ways code reaches another host: fetch, XMLHttpRequest, WebSocket,
14
- * EventSource, navigator.sendBeacon, and dynamic import() of an absolute
15
- * URL or an expression whose destination cannot be fixed. import() of a
15
+ * EventSource, navigator.sendBeacon, importScripts() (which loads and runs
16
+ * a script in a worker), and dynamic import() of an absolute URL or an
17
+ * expression whose destination cannot be fixed. import() of a
16
18
  * relative path (code splitting) or a bare specifier ("lodash", "node:fs")
17
19
  * resolves through the bundler or import map, not the network, and is not
18
20
  * reported.
21
+ *
22
+ * Also `network.resource`: an element's `src` set to another host
23
+ * (`script.src = "https://cdn..."`, `setAttribute("src", ...)`), when the
24
+ * URL is a literal or a folded const. The element is not checked; what
25
+ * matters is the host. A value that cannot be read is not reported: `.src`
26
+ * is set on tokens and props as often as on elements, and a list of
27
+ * "unknown" lines nobody can act on is noise.
19
28
  */
20
29
  export const network = ({ node, ancestors, binding }) => {
21
30
  if (binding)
@@ -32,10 +41,44 @@ export const network = ({ node, ancestors, binding }) => {
32
41
  const g = asGlobalIn(n, NETWORK_NAMES);
33
42
  if (g)
34
43
  return match(NETWORK_GLOBALS.get(g.name), g.r, node, firstArg());
35
- if (n.type === "MemberExpression" && memberName(n) === "sendBeacon") {
44
+ // importScripts("https://...") / self.importScripts(...): loads and runs a script in a worker.
45
+ if (callArgs(node, parent)) {
46
+ if (isIdentifier(n, IMPORT_SCRIPTS)) {
47
+ return { capability: "network.importscripts", target: firstArg(), confidence: "certain", via: n, node };
48
+ }
49
+ if (n.type === "MemberExpression" && memberName(n) === IMPORT_SCRIPTS) {
50
+ const r = asGlobalObject(n["object"]);
51
+ if (r)
52
+ return match("network.importscripts", r, node, firstArg());
53
+ }
54
+ }
55
+ if (n.type !== "MemberExpression")
56
+ return null;
57
+ const prop = memberName(n);
58
+ if (prop === "sendBeacon") {
36
59
  const r = asNamedGlobal(n["object"], "navigator");
37
60
  return r ? match("network.beacon", r, node, firstArg()) : null;
38
61
  }
39
- return null;
62
+ // el.src = url; el.setAttribute("src", url)
63
+ let value;
64
+ let anchor = n;
65
+ if (prop === "src" && parent?.type === "AssignmentExpression" && parent["left"] === node) {
66
+ value = parent["right"];
67
+ anchor = parent;
68
+ }
69
+ else if (prop === "setAttribute") {
70
+ const args = callArgs(node, parent);
71
+ if (args && args[0]?.type === "Literal" && args[0]["value"] === "src")
72
+ value = args[1];
73
+ }
74
+ if (value === undefined)
75
+ return null;
76
+ // URL.createObjectURL(...) is a blob by construction.
77
+ if (value.type === "CallExpression" && memberName(value["callee"]) === "createObjectURL")
78
+ return null;
79
+ const target = resolveTargetOf(value);
80
+ if (target === null || target === SAME_ORIGIN || target === "bare" || target === "data:" || target === "blob:")
81
+ return null;
82
+ return { capability: "network.resource", target, confidence: "certain", via: null, node: anchor };
40
83
  };
41
84
  //# sourceMappingURL=network.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"network.js","sourceRoot":"","sources":["../../../src/extract/recognizers/network.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAmB,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/C,4FAA4F;AAC5F,MAAM,eAAe,GAAgC,IAAI,GAAG,CAAC;IAC3D,CAAC,OAAO,EAAE,eAAe,CAAC;IAC1B,CAAC,gBAAgB,EAAE,aAAa,CAAC;IACjC,CAAC,WAAW,EAAE,mBAAmB,CAAC;IAClC,CAAC,aAAa,EAAE,qBAAqB,CAAC;CACvC,CAAC,CAAC;AACH,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC,CAAC;AAE3E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,OAAO,GAAe,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE;IAClE,IAAI,OAAO;QAAE,OAAO,IAAI,CAAC;IACzB,MAAM,CAAC,GAAG,IAAe,CAAC;IAC1B,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAwB,CAAC;IACnD,MAAM,QAAQ,GAAG,GAAkB,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEzF,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,eAAe,CAAC,CAAC,CAAC,QAAQ,CAAY,EAAE,WAAW,CAAC,CAAC;QACpE,IAAI,MAAM,KAAK,aAAa,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC;QAC/D,OAAO,KAAK,CAAC,gBAAgB,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;IAC/C,CAAC;IAED,MAAM,CAAC,GAAG,UAAU,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC;IACvC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEzE,IAAI,CAAC,CAAC,IAAI,KAAK,kBAAkB,IAAI,UAAU,CAAC,CAAC,CAAC,KAAK,YAAY,EAAE,CAAC;QACpE,MAAM,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAY,EAAE,WAAW,CAAC,CAAC;QAC7D,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,gBAAgB,EAAE,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
1
+ {"version":3,"file":"network.js","sourceRoot":"","sources":["../../../src/extract/recognizers/network.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Bounded taint analysis: does untrusted input reach a dangerous sink?
3
+ *
4
+ * frostjs's recognizers say "this file can eval" or "this file reaches
5
+ * host X". Taint answers the harder question: does a value that came from
6
+ * an attacker-influenced source (a URL parameter, document.cookie, a
7
+ * postMessage payload) actually flow into eval, innerHTML, importScripts,
8
+ * a redirect, and so on. That is the difference between a capability and a
9
+ * vulnerability, and it is what the three.js/ECSY finding was underneath:
10
+ * `location.search` gated a path that eval'd relay data.
11
+ *
12
+ * The one rule that keeps this from crying wolf: **taint survives only
13
+ * through operations that provably preserve it** - string methods, URL
14
+ * decoding, JSON.parse, template concatenation, member access. Any other
15
+ * function call breaks the chain, so `el.innerHTML = DOMPurify.sanitize(x)`
16
+ * is not flagged while `el.innerHTML = x` is. Flow is followed within a
17
+ * function (with closure inheritance and message-handler seeding) and one
18
+ * hop across a call: a tainted argument to a local function whose parameter
19
+ * reaches a sink. A whole call graph is not walked, and that is stated as a
20
+ * limit rather than hidden.
21
+ */
22
+ import { type ParsedFile } from "./ast.js";
23
+ export interface TaintFinding {
24
+ /** Where the value came from, e.g. "location.hash", "document.cookie", "postMessage data". */
25
+ source: string;
26
+ /** What it reached, e.g. "eval", "innerHTML", "importScripts". */
27
+ sink: string;
28
+ file: string;
29
+ line: number;
30
+ column: number;
31
+ /** Source text of the sink expression. */
32
+ expression: string;
33
+ }
34
+ /** Find every untrusted-source-to-dangerous-sink flow in the file. */
35
+ export declare function taint(parsed: ParsedFile): TaintFinding[];
36
+ //# sourceMappingURL=taint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"taint.d.ts","sourceRoot":"","sources":["../../src/extract/taint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAuC,KAAK,UAAU,EAAE,MAAM,UAAU,CAAC;AAKhF,MAAM,WAAW,YAAY;IAC3B,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,UAAU,EAAE,MAAM,CAAC;CACpB;AA4bD,sEAAsE;AACtE,wBAAgB,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,EAAE,CAkBxD"}