@clear-capabilities/agentic-security-scanner 0.145.0 → 0.147.5

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 (195) hide show
  1. package/CHANGELOG.md +266 -0
  2. package/bin/agentic-security.js +3508 -69
  3. package/dist/1122.index.js +779 -0
  4. package/dist/{301.index.js → 1301.index.js} +2 -2
  5. package/dist/1379.index.js +591 -0
  6. package/dist/{444.index.js → 1444.index.js} +2 -2
  7. package/dist/{660.index.js → 1660.index.js} +2 -2
  8. package/dist/{700.index.js → 1700.index.js} +2 -2
  9. package/dist/{905.index.js → 1905.index.js} +3 -3
  10. package/dist/{920.index.js → 1920.index.js} +3 -3
  11. package/dist/{238.index.js → 2238.index.js} +3 -3
  12. package/dist/{271.index.js → 2271.index.js} +2 -2
  13. package/dist/{985.index.js → 2376.index.js} +1260 -340
  14. package/dist/2432.index.js +793 -0
  15. package/dist/2659.index.js +93 -0
  16. package/dist/{826.index.js → 2826.index.js} +2 -2
  17. package/dist/{830.index.js → 2830.index.js} +2 -2
  18. package/dist/2923.index.js +298 -0
  19. package/dist/{1.index.js → 3001.index.js} +5 -5
  20. package/dist/{117.index.js → 3117.index.js} +3 -3
  21. package/dist/3180.index.js +379 -0
  22. package/dist/3276.index.js +117 -0
  23. package/dist/{415.index.js → 3415.index.js} +2 -2
  24. package/dist/{499.index.js → 3499.index.js} +2 -2
  25. package/dist/3518.index.js +450 -0
  26. package/dist/{526.index.js → 3526.index.js} +5 -5
  27. package/dist/{736.index.js → 3736.index.js} +4 -4
  28. package/dist/{839.index.js → 3839.index.js} +4 -4
  29. package/dist/{113.index.js → 4113.index.js} +5 -5
  30. package/dist/{265.index.js → 4265.index.js} +2 -2
  31. package/dist/{384.index.js → 4384.index.js} +3 -3
  32. package/dist/4547.index.js +268 -0
  33. package/dist/4863.index.js +422 -0
  34. package/dist/{970.index.js → 4970.index.js} +2 -2
  35. package/dist/5051.index.js +841 -0
  36. package/dist/{144.index.js → 5144.index.js} +5 -5
  37. package/dist/{333.index.js → 5333.index.js} +3 -3
  38. package/dist/5343.index.js +185 -0
  39. package/dist/5350.index.js +866 -0
  40. package/dist/5561.index.js +436 -0
  41. package/dist/{637.index.js → 5637.index.js} +3 -3
  42. package/dist/{449.index.js → 5830.index.js} +2 -2
  43. package/dist/6626.index.js +532 -0
  44. package/dist/6662.index.js +297 -0
  45. package/dist/{675.index.js → 6675.index.js} +5 -5
  46. package/dist/{730.index.js → 6730.index.js} +6 -6
  47. package/dist/6829.index.js +225 -0
  48. package/dist/6944.index.js +130 -0
  49. package/dist/{178.index.js → 7178.index.js} +3 -3
  50. package/dist/{227.index.js → 7227.index.js} +2 -2
  51. package/dist/7310.index.js +520 -0
  52. package/dist/{552.index.js → 7552.index.js} +4 -4
  53. package/dist/7709.index.js +78 -0
  54. package/dist/8218.index.js +160 -0
  55. package/dist/{476.index.js → 8476.index.js} +4 -4
  56. package/dist/{513.index.js → 8513.index.js} +5 -5
  57. package/dist/{520.index.js → 8520.index.js} +2 -2
  58. package/dist/{718.index.js → 8718.index.js} +2 -2
  59. package/dist/{752.index.js → 8752.index.js} +2 -2
  60. package/dist/8846.index.js +100 -0
  61. package/dist/{435.index.js → 9091.index.js} +560 -189
  62. package/dist/{207.index.js → 9207.index.js} +2 -2
  63. package/dist/{220.index.js → 9220.index.js} +2 -2
  64. package/dist/9390.index.js +163 -0
  65. package/dist/{503.index.js → 9503.index.js} +2 -2
  66. package/dist/{801.index.js → 9801.index.js} +2 -2
  67. package/dist/{824.index.js → 9824.index.js} +2 -2
  68. package/dist/agentic-security.mjs +16 -16
  69. package/dist/agentic-security.mjs.sha256 +1 -1
  70. package/dist/compliance-frameworks/hipaa-security-rule.json +3 -2
  71. package/dist/frontend/index.html +21 -0
  72. package/dist/frontend/src/app.js +176 -0
  73. package/dist/frontend/src/components/evidence-inspector.js +141 -0
  74. package/dist/frontend/src/components/filter-rail.js +119 -0
  75. package/dist/frontend/src/components/query-bar.js +126 -0
  76. package/dist/frontend/src/data/flagship-graph.js +1460 -0
  77. package/dist/frontend/src/export-entry.js +36 -0
  78. package/dist/frontend/src/lib/api-client.js +92 -0
  79. package/dist/frontend/src/lib/contrast.js +34 -0
  80. package/dist/frontend/src/lib/dom.js +24 -0
  81. package/dist/frontend/src/lib/escape-html.js +16 -0
  82. package/dist/frontend/src/lib/flow-path.js +40 -0
  83. package/dist/frontend/src/lib/focus-controls.js +149 -0
  84. package/dist/frontend/src/lib/protection-visual.js +46 -0
  85. package/dist/frontend/src/lib/query-language.js +240 -0
  86. package/dist/frontend/src/lib/row-filters.js +43 -0
  87. package/dist/frontend/src/lib/state.js +84 -0
  88. package/dist/frontend/src/main.js +83 -0
  89. package/dist/frontend/src/shell.js +184 -0
  90. package/dist/frontend/src/views/architecture-view.js +798 -0
  91. package/dist/frontend/src/views/inventory-view.js +292 -0
  92. package/dist/frontend/src/views/privacy-view.js +172 -0
  93. package/dist/frontend/src/views/trace-view.js +206 -0
  94. package/dist/frontend/styles/architecture-view.css +93 -0
  95. package/dist/frontend/styles/filter-rail.css +34 -0
  96. package/dist/frontend/styles/inspector.css +69 -0
  97. package/dist/frontend/styles/inventory-view.css +74 -0
  98. package/dist/frontend/styles/privacy-view.css +86 -0
  99. package/dist/frontend/styles/query-bar.css +107 -0
  100. package/dist/frontend/styles/shell.css +155 -0
  101. package/dist/frontend/styles/tokens.css +128 -0
  102. package/dist/frontend/styles/trace-view.css +95 -0
  103. package/package.json +13 -6
  104. package/src/dataflow/CLAUDE.md +1 -1
  105. package/src/dataflow/catalog.js +42 -0
  106. package/src/dataflow/orm-write-catalog.js +175 -0
  107. package/src/engine.js +92 -1
  108. package/src/ir/CLAUDE.md +1 -0
  109. package/src/ir/chrome-probe.mjs +150 -0
  110. package/src/ir/parser-js.js +94 -7
  111. package/src/lineage/CLAUDE.md +1203 -0
  112. package/src/lineage/DESIGN_DESTINATION_RESOLVER.md +156 -0
  113. package/src/lineage/DESIGN_GRAPH_BUILDER.md +938 -0
  114. package/src/lineage/DESIGN_HANDLING_ANALYZER.md +355 -0
  115. package/src/lineage/DESIGN_INTRAPROCEDURAL.md +628 -0
  116. package/src/lineage/DESIGN_PATH_PROVENANCE.md +3451 -0
  117. package/src/lineage/DESIGN_QUEUE_DETAIL.md +120 -0
  118. package/src/lineage/DESIGN_REGISTRIES.md +880 -0
  119. package/src/lineage/DESIGN_STORE_DETAIL.md +143 -0
  120. package/src/lineage/DESIGN_TRANSIT_PROTECTION.md +245 -0
  121. package/src/lineage/classification.js +56 -0
  122. package/src/lineage/coverage.js +658 -0
  123. package/src/lineage/cross-repo-link.js +107 -0
  124. package/src/lineage/dataflow-graph.schema.json +184 -0
  125. package/src/lineage/decision-story.js +206 -0
  126. package/src/lineage/drift-policy.js +279 -0
  127. package/src/lineage/driver.js +135 -0
  128. package/src/lineage/engine.js +992 -0
  129. package/src/lineage/export-briefing.js +628 -0
  130. package/src/lineage/export-csv.js +62 -0
  131. package/src/lineage/export-json.js +238 -0
  132. package/src/lineage/export-privacy.js +258 -0
  133. package/src/lineage/federation-loader.js +111 -0
  134. package/src/lineage/field-identity.js +78 -0
  135. package/src/lineage/fixtures/build-flagship-fixture.mjs +272 -0
  136. package/src/lineage/fixtures/flagship-graph.json +1453 -0
  137. package/src/lineage/flow-grade.js +221 -0
  138. package/src/lineage/governance-edit.js +169 -0
  139. package/src/lineage/graph-builder.js +1114 -0
  140. package/src/lineage/graph-diff.js +431 -0
  141. package/src/lineage/graph-snapshot.js +180 -0
  142. package/src/lineage/handling-analyzer.js +168 -0
  143. package/src/lineage/ids.js +349 -0
  144. package/src/lineage/impact-assessment.js +76 -0
  145. package/src/lineage/impact-engine.js +268 -0
  146. package/src/lineage/index.js +281 -0
  147. package/src/lineage/language-coverage-tiers.js +58 -0
  148. package/src/lineage/obligation-mapping.js +126 -0
  149. package/src/lineage/obligation-predicates.js +235 -0
  150. package/src/lineage/observation-adapters.js +282 -0
  151. package/src/lineage/observation-correlation.js +622 -0
  152. package/src/lineage/observation-store.js +497 -0
  153. package/src/lineage/path-query.js +410 -0
  154. package/src/lineage/path-store.js +400 -0
  155. package/src/lineage/protection.js +53 -0
  156. package/src/lineage/recipient-profile.js +192 -0
  157. package/src/lineage/recipient-registry.js +394 -0
  158. package/src/lineage/redact-graph.js +224 -0
  159. package/src/lineage/remediation.js +417 -0
  160. package/src/lineage/resolve-destination.js +91 -0
  161. package/src/lineage/runtime-observation.js +464 -0
  162. package/src/lineage/scenario-diff.js +84 -0
  163. package/src/lineage/scenario-engine.js +251 -0
  164. package/src/lineage/scenario.js +101 -0
  165. package/src/lineage/schema.js +167 -0
  166. package/src/lineage/sink-registry.js +427 -0
  167. package/src/lineage/source-registry.js +357 -0
  168. package/src/lineage/source-seeding.js +212 -0
  169. package/src/lineage/summaries.js +590 -0
  170. package/src/lineage/transform-catalog.js +397 -0
  171. package/src/lineage/transit-protection.js +150 -0
  172. package/src/lineage/validate.js +285 -0
  173. package/src/mcp/CLAUDE.md +7 -1
  174. package/src/mcp/dataflow-tools.js +160 -0
  175. package/src/mcp/server.js +1 -1
  176. package/src/mcp/tools.js +2 -1
  177. package/src/pipeline/scan-health.js +19 -1
  178. package/src/posture/CLAUDE.md +19 -0
  179. package/src/posture/artifact-registry.js +52 -0
  180. package/src/posture/auditor-walkthrough.js +76 -0
  181. package/src/posture/compliance-frameworks/hipaa-security-rule.json +3 -2
  182. package/src/posture/obligation-evidence-pack.js +202 -0
  183. package/src/posture/remediation-ledger.js +337 -0
  184. package/src/server/CLAUDE.md +47 -0
  185. package/src/server/graph-loader.js +141 -0
  186. package/src/server/http-server.js +325 -0
  187. package/src/server/routes.js +129 -0
  188. package/src/server/security.js +111 -0
  189. package/src/server/static-assets.js +144 -0
  190. package/src/shared/frontend-root.js +52 -0
  191. package/dist/11.index.js +0 -353
  192. package/dist/259.index.js +0 -975
  193. package/dist/317.index.js +0 -300
  194. package/dist/609.index.js +0 -741
  195. package/dist/838.index.js +0 -152
@@ -0,0 +1,128 @@
1
+ /*
2
+ * Data Flow Explorer design tokens (PRD §8.1). Dark is the reference theme.
3
+ * Values are copied verbatim from the PRD — do not adjust without a
4
+ * deliberate, separately-reviewed change.
5
+ */
6
+
7
+ :root {
8
+ /* Surfaces */
9
+ --surface-canvas: #061625;
10
+ --surface-panel: #0B1E2F;
11
+ --surface-elevated: #102A40;
12
+ --border-default: #28445A;
13
+
14
+ /* Text */
15
+ --text-primary: #F3F7FA;
16
+ --text-secondary: #9FB3C5;
17
+
18
+ /* Selection / focus */
19
+ --accent-selection: #3DA9FF;
20
+
21
+ /* Protection verdict status */
22
+ --status-protected: #59D17D;
23
+ --status-unprotected: #FF625C;
24
+ --status-unknown: #F5B83D;
25
+
26
+ /* Text color for content painted directly on a status token background
27
+ (e.g. the coverage banner). NOT the same as --text-primary, which is
28
+ sized against --surface-canvas/--surface-panel, not against a status
29
+ color. Verified with lib/contrast.js's contrastRatio(): #061625 on
30
+ dark --status-unknown (#F5B83D) is 10.27:1. See the light-theme block
31
+ below for the light-theme value and its own verified ratio. */
32
+ --status-banner-text: #061625;
33
+
34
+ /* AI processing context */
35
+ --context-ai: #B47AFF;
36
+
37
+ /* Data class overlays */
38
+ --class-pii: #28D1C5;
39
+ --class-phi: #A97BFF;
40
+ --class-pci: #4CA7FF;
41
+
42
+ /* Spacing (8px grid, PRD §7.7) */
43
+ --space-1: 8px;
44
+ --space-2: 16px;
45
+ --space-3: 24px;
46
+ --space-4: 32px;
47
+
48
+ /* Radius / border (PRD §8.2) */
49
+ --radius-default: 8px;
50
+ --border-width: 1px;
51
+
52
+ /* Typography (PRD §8.2) — bundled system stack; never a remote/CDN font */
53
+ --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
54
+ --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
55
+ --font-size-view-title: 23px;
56
+ --font-size-panel-title: 15px;
57
+ --font-size-node-title: 14px;
58
+ --font-size-body: 13px;
59
+ --font-size-code: 12.5px;
60
+
61
+ /* Motion (PRD §8.2 — focus/topology change only, no ambient animation) */
62
+ --motion-duration: 200ms;
63
+ --motion-duration-reduced: 0ms;
64
+
65
+ /* Shell region sizing (PRD §7.7) */
66
+ --header-height: 56px;
67
+ --view-tabs-height: 44px;
68
+ --left-rail-width: 248px;
69
+ --left-rail-collapsed-width: 56px;
70
+ --right-inspector-width: 360px;
71
+ --context-rail-height: 72px;
72
+ }
73
+
74
+ /* Light theme — same semantic tokens, WCAG-AA-equivalent contrast (PRD §8.1/§23) */
75
+ :root[data-theme="light"] {
76
+ --surface-canvas: #FFFFFF;
77
+ --surface-panel: #F4F7FA;
78
+ --surface-elevated: #E7EEF3;
79
+ --border-default: #C7D3DC;
80
+
81
+ --text-primary: #0B1E2F;
82
+ --text-secondary: #3E5568;
83
+
84
+ --accent-selection: #1B6FC2;
85
+
86
+ /* status-protected and status-unknown darkened slightly from their
87
+ original values (#1E8A4C, #9A6B00) — Milestone 3, sub-project A11y,
88
+ found via test/tokens-contrast.test.js that both originals failed
89
+ WCAG AA (4.5:1) against --surface-panel (#F4F7FA): 4.07:1 and 4.36:1
90
+ respectively. Verified with lib/contrast.js's contrastRatio() against
91
+ BOTH backgrounds a status color plausibly renders on: --status-
92
+ protected (#1C8047) is 4.97:1 on --surface-canvas / 4.62:1 on
93
+ --surface-panel; --status-unknown (#956700) is 4.98:1 / 4.63:1. Both
94
+ now clear AA with margin (>=4.6) rather than sitting at the exact
95
+ 4.50 boundary, which is fragile against hex-rounding. --status-
96
+ unprotected was already compliant (5.26:1 / 4.89:1), unchanged. */
97
+ --status-protected: #1C8047;
98
+ --status-unprotected: #C7362D;
99
+ --status-unknown: #956700;
100
+
101
+ /* Verified with lib/contrast.js's contrastRatio(): #FFFFFF on light
102
+ --status-unknown (#956700) is 4.98:1 (>= 4.5:1 AA). The dark-block
103
+ value (#061625, --surface-canvas's own dark value) is 3.90:1 here --
104
+ below AA -- which is the bug this token pair fixes: shell.css must
105
+ never hardcode dark theme's value and expect it to also work in
106
+ light theme. */
107
+ --status-banner-text: #FFFFFF;
108
+
109
+ --context-ai: #6E38B8;
110
+
111
+ --class-pii: #0E7C73;
112
+ --class-phi: #6E44B8;
113
+ --class-pci: #1C63A8;
114
+ }
115
+
116
+ @media (prefers-reduced-motion: reduce) {
117
+ :root {
118
+ --motion-duration: var(--motion-duration-reduced);
119
+ }
120
+ }
121
+
122
+ body {
123
+ background: var(--surface-canvas);
124
+ color: var(--text-primary);
125
+ font-family: var(--font-family);
126
+ font-size: var(--font-size-body);
127
+ margin: 0;
128
+ }
@@ -0,0 +1,95 @@
1
+ .trace-view {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: var(--space-2);
5
+ max-width: 640px;
6
+ }
7
+
8
+ .trace-step {
9
+ display: flex;
10
+ gap: var(--space-2);
11
+ padding: var(--space-2);
12
+ background: var(--surface-panel);
13
+ border: var(--border-width) solid var(--border-default);
14
+ border-radius: var(--radius-default);
15
+ }
16
+
17
+ .trace-step-number {
18
+ flex: 0 0 auto;
19
+ width: 24px;
20
+ height: 24px;
21
+ border-radius: 50%;
22
+ background: var(--surface-elevated);
23
+ color: var(--text-primary);
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ font-size: var(--font-size-code);
28
+ font-weight: 600;
29
+ }
30
+
31
+ .trace-step-body {
32
+ flex: 1 1 auto;
33
+ }
34
+
35
+ .trace-step-kind {
36
+ color: var(--text-secondary);
37
+ font-size: var(--font-size-code);
38
+ text-transform: uppercase;
39
+ }
40
+
41
+ .trace-step-mapping {
42
+ font-family: var(--font-mono);
43
+ font-size: var(--font-size-code);
44
+ color: var(--text-primary);
45
+ margin: 4px 0;
46
+ }
47
+
48
+ .trace-step-node {
49
+ color: var(--text-secondary);
50
+ font-size: var(--font-size-body);
51
+ }
52
+
53
+ .trace-step-boundary {
54
+ display: inline-block;
55
+ border: 1px solid var(--status-unprotected);
56
+ color: var(--status-unprotected);
57
+ border-radius: var(--radius-default);
58
+ padding: 1px 6px;
59
+ font-size: var(--font-size-code);
60
+ margin-top: 4px;
61
+ }
62
+
63
+ .trace-branch-group {
64
+ display: flex;
65
+ flex-direction: column;
66
+ gap: var(--space-2);
67
+ padding-left: var(--space-2);
68
+ border-left: 2px solid var(--accent-selection);
69
+ }
70
+
71
+ .trace-branch-label {
72
+ color: var(--text-secondary);
73
+ font-size: var(--font-size-code);
74
+ text-transform: uppercase;
75
+ }
76
+
77
+ .trace-alternates {
78
+ margin-top: var(--space-2);
79
+ }
80
+
81
+ .trace-alternate-item {
82
+ padding: var(--space-1) 0;
83
+ cursor: pointer;
84
+ color: var(--text-secondary);
85
+ }
86
+
87
+ .trace-alternate-item:hover {
88
+ color: var(--text-primary);
89
+ }
90
+
91
+ .trace-empty {
92
+ color: var(--text-secondary);
93
+ font-style: italic;
94
+ padding: var(--space-2);
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clear-capabilities/agentic-security-scanner",
3
- "version": "0.145.0",
3
+ "version": "0.147.5",
4
4
  "description": "Scanner engine for the agentic-security Claude Code plugin — SAST, SCA (function-level reachability + CISA KEV), secrets, IaC, prompt-injection, MCP/agent-tool audit, auth/authZ deep analysis, attack chains, PoC generation, business logic, toxic-combinations scoring, SBOM, pipeline integrity, compliance attestation, and more.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -56,19 +56,21 @@
56
56
  "lodash-es": "^4.18.1"
57
57
  },
58
58
  "scripts": {
59
- "build": "ncc build bin/agentic-security.js -o dist --minify -e web-tree-sitter -e tree-sitter-wasms && rm -rf dist/compliance-frameworks && mkdir -p dist/compliance-frameworks && cp src/posture/compliance-frameworks/*.json dist/compliance-frameworks/ && mv dist/index.js dist/agentic-security.mjs && rm -f dist/package.json && chmod +x dist/agentic-security.mjs && node -e \"const fs=require('fs');const p='dist/agentic-security.mjs';const c=fs.readFileSync(p,'utf8');if(!c.startsWith('#!'))fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c);\" && node -e \"const fs=require('fs');const c=require('crypto');const h=c.createHash('sha256').update(fs.readFileSync('dist/agentic-security.mjs')).digest('hex');fs.writeFileSync('dist/agentic-security.mjs.sha256',h+' agentic-security.mjs\\n');\"",
59
+ "build": "ncc build bin/agentic-security.js -o dist --minify -e web-tree-sitter -e tree-sitter-wasms && rm -rf dist/compliance-frameworks && mkdir -p dist/compliance-frameworks && cp src/posture/compliance-frameworks/*.json dist/compliance-frameworks/ && node scripts/copy-frontend.mjs && mv dist/index.js dist/agentic-security.mjs && rm -f dist/package.json && chmod +x dist/agentic-security.mjs && node -e \"const fs=require('fs');const p='dist/agentic-security.mjs';const c=fs.readFileSync(p,'utf8');if(!c.startsWith('#!'))fs.writeFileSync(p,'#!/usr/bin/env node\\n'+c);\" && node -e \"const fs=require('fs');const c=require('crypto');const h=c.createHash('sha256').update(fs.readFileSync('dist/agentic-security.mjs')).digest('hex');fs.writeFileSync('dist/agentic-security.mjs.sha256',h+' agentic-security.mjs\\n');\"",
60
60
  "prepare": "node ../scripts/pre-push-gate.mjs --install-hook",
61
61
  "prepublishOnly": "npm run build && node ../scripts/sync-scanner-changelog.mjs && node ../scripts/release-check.mjs",
62
- "test": "node ../scripts/run-unit-tests.mjs && AGENTIC_SECURITY_CPP_DATAFLOW=1 node --test test/cpp-dataflow.test.js && node --experimental-test-module-mocks --test test/fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection-k8s-admission.test.js && npm run test:python",
62
+ "test": "node ../scripts/run-unit-tests.mjs && AGENTIC_SECURITY_CPP_DATAFLOW=1 node --test test/cpp-dataflow.test.js && node --experimental-test-module-mocks --test test/fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection.test.js && node --experimental-test-module-mocks --test test/detector-fault-injection-k8s-admission.test.js && node --experimental-test-module-mocks --test test/lineage-fault-injection.test.js && npm run test:python",
63
63
  "test:python": "node ../scripts/run-python-tests.mjs",
64
64
  "test:smoke": "node --test test/smoke.test.js test/demo-app.test.js",
65
65
  "test:glob": "node --test test/glob-compat.test.js",
66
66
  "test:sast": "node --test test/cpp-hardcoded-secret.test.js test/sibling-guard.test.js test/comment-blindness.test.js test/crypto-specialist.test.js test/llm.test.js test/llm-cost-advisor.test.js test/llm-owasp.test.js test/logic.test.js test/authz.test.js test/model-load.test.js test/prompt-template.test.js test/business-logic.test.js test/python-sinks.test.js test/phase1-detectors.test.js test/phase2-detectors.test.js test/phase3-v3.test.js test/phase7-extensions.test.js test/phase8-extensions.test.js test/new-cwe-detectors.test.js test/file-upload.test.js test/llmsecops-detectors.test.js test/db-taint.test.js test/dart-swift.test.js test/zip-slip-containment-guard.test.js test/redos-anchoring.test.js test/path-guard-validator-call.test.js test/redos-nfa.test.js test/weak-randomness.test.js test/csharp-pipeline.test.js test/post-quantum-crypto.test.js test/web3-advanced.test.js test/cloud-iam-k8s.test.js test/crypto-protocol.test.js test/ml-supply-chain.test.js test/mcp-audit-scan.test.js test/wrong-context-sanitizer.test.js test/sanitizer-context.test.js test/frontend-hygiene.test.js test/csv-injection.test.js test/stored-taint.test.js test/tree-sitter-sinks.test.js test/kotlin-structural.test.js test/ruby-php-structural.test.js test/java-csharp-structural.test.js test/guard-recognition.test.js test/guard-window-identifier-correlation.test.js test/js-python-framework-structural.test.js test/go-structural.test.js test/secret-concat.test.js test/secret-redaction.test.js test/xss-reflected-multilang.test.js test/code-injection-multilang.test.js test/xxe-multilang.test.js test/xpath-injection-multilang.test.js test/gapfill-batch13.test.js test/agent-untrusted-flow.test.js test/api-authz.test.js test/event-entrypoint.test.js test/iac-terraform.test.js test/cross-service.test.js test/rbac-consistency.test.js test/adversarial-robustness.test.js test/secret-entropy.test.js test/comment-strip.test.js test/juliet-path-filter-gate.test.js test/java-bench-shape-opt-in.test.js test/reachability-missing-callgraph-data.test.js test/rate-limit.test.js test/auth-signals.test.js test/convention-deviation.test.js test/resource-exhaustion.test.js test/resource-exhaustion-realworld.test.js test/redirect-toctou.test.js test/redirect-toctou-realworld.test.js test/codegen-sink.test.js test/ownership-authz.test.js test/ownership-authz-realworld.test.js test/t21-precision-audit.test.js test/secrets-coverage.test.js test/prompt-injection-payloads.test.js test/iac-cloud-templates.test.js test/ruby-path-join.test.js test/sast/manifest-line-tracking.test.js",
67
- "test:posture": "node --test test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/release-workflow.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/framework-provenance-controlrefs.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.test.js test/independent-population-gate.test.js test/catalog-freshness.test.js test/incremental-parity.test.js test/surface-smoke.test.js test/fix-coverage.test.js test/raw-source-carveout.test.js test/mlbom-conformance.test.js test/deploy-gate-replay.test.js test/sbom-conformance.test.js test/unscored-claims.test.js test/mcp-rug-pull.test.js test/agent-boundary-taint.test.js test/confinement-adversarial.test.js test/comment-strip-cost.test.js test/reachability-claim.test.js test/entrypoint-breadth.test.js test/dep-file-admission.test.js test/custom-rules-freshness.test.js test/posture/provenance-schema.test.js test/posture/provenance-cache.test.js test/helpers/build-git-fixture.test.js test/posture/provenance-git-evidence.test.js test/posture/provenance-dag-walk.test.js test/posture/provenance-confidence.test.js test/posture/provenance-predicate-replay.test.js test/posture/provenance-origin-resolver.test.js test/posture/provenance-ai-authorship.test.js test/posture/provenance-branch-entry.test.js test/posture/provenance-evidence-attribution.test.js test/posture/provenance-lifecycle.test.js test/posture/provenance-missing-control-resolver.test.js test/posture/provenance-missing-control-wiring.test.js test/posture/provenance-coordinator.test.js test/posture/provenance-sca-origin.test.js test/posture/provenance-transitive-sca.test.js test/posture/provenance-coordinator-sca.test.js test/posture/provenance-providers.test.js test/posture/provenance-evidence-bundle.test.js test/posture/provenance-repo-lineage.test.js test/posture/provenance-secrets-logic.test.js test/cli/provenance-flags.test.js test/cli/attest-provenance.test.js test/security/provenance-safety.test.js test/security/provenance-injection.test.js",
68
- "test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/dataflow/provenance-pipeline-integration.test.js",
69
- "test:mcp": "node --test test/mcp.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js",
67
+ "test:posture": "node --test test/git-hardening.test.js test/privacy-framework.test.js test/material-change.test.js test/drift.test.js test/scorecard.test.js test/accuracy-scorecard.test.js test/scorecard-gate.test.js test/predeploy-gate.test.js test/release-check.test.js test/pre-push-gate.test.js test/dependency-currency.test.js test/package-contents-check.test.js test/gate-verdict-cache.test.js test/run-unit-tests.test.js test/release-workflow.test.js test/independent-population.test.js test/why-missed.test.js test/independent-scoring.test.js test/evidence-bundle.test.js test/obligation-evidence-pack.test.js test/policy-bundle.test.js test/discovery-memory.test.js test/mttr.test.js test/license-policy.test.js test/aibom.test.js test/sbom.test.js test/api-inventory.test.js test/iam-policy.test.js test/container.test.js test/container-runtime.test.js test/image-packages.test.js test/kev.test.js test/dep-confusion.test.js test/sca-deprecated.test.js test/sca-batch.test.js test/composite-risk.test.js test/epss-enrich.test.js test/sca-coverage.test.js test/gradle-deps.test.js test/sca-route-reachable.test.js test/sca-policy.test.js test/sca-verdict.test.js test/install-script.test.js test/sca-linked-findings.test.js test/packs.test.js test/flow-narration.test.js test/regression-test-gen.test.js test/deterministic-fix.test.js test/falsification.test.js test/verification-separation.test.js test/logic-claims.test.js test/attestation.test.js test/verify-attestation-cli.test.js test/comparison.test.js test/determinism-cross-machine.test.js test/fix-honesty-gate.test.js test/model-routing.test.js test/root-cause-sweep.test.js test/entrypoint-inventory.test.js test/relevance.test.js test/untrusted.test.js test/agent-hardening.test.js test/rule-synthesis.test.js test/policy-gate.test.js test/agents-memory.test.js test/cve-lookup.test.js test/cve-alert-daemon.test.js test/fix-verify-loop.test.js test/fix-verify-tests.test.js test/fix-acceptance.test.js test/exploitability-probability.test.js test/history-scan.test.js test/viral-features.test.js test/viral-v074.test.js test/state-dir.test.js test/ignore-pragma.test.js test/license-graph.test.js test/secret-live-check.test.js test/attack-taxonomy.test.js test/triage-memory.test.js test/pr-augment.test.js test/chat-batch2.test.js test/chat-batch3.test.js test/chat-batch4.test.js test/chat-batch5.test.js test/chat-batch6.test.js test/risk-scenario-disclosure.test.js test/egress-policy.test.js test/egress-policy-completeness.test.js test/egress-policy-integration.test.js test/router.test.js test/legacy-alias-redirect.test.js test/cache-economics.test.js test/coverage-report.test.js test/corpus-status.test.js test/provenance.test.js test/secret-history.test.js test/execution-proof.test.js test/fix-metrics.test.js test/corpus-enroll.test.js test/prove-findings.test.js test/corpus-match.test.js test/corpus-provenance.test.js test/learning-quorum.test.js test/llm-cache-integrity.test.js test/proof-artifact.test.js test/providers.test.js test/fleet.test.js test/autopilot.test.js test/autopilot-cli.test.js test/verifier-independence.test.js test/integrity-legacy-key.test.js test/suppression-visibility.test.js test/cost-ceiling.test.js test/local-endpoint.test.js test/model-trust.test.js test/vuln-archaeology.test.js test/scan-checkpoint.test.js test/llm-redact.test.js test/world-class-batch2.test.js test/world-class-modules.test.js test/privacy-ir-adapter.test.js test/privacy-taxonomy.test.js test/privacy-sink-policy.test.js test/privacy-governance.test.js test/privacy-inventory.test.js test/integration-end-to-end.test.js test/streak-cli-wiring.test.js test/stack-playbook.test.js test/machine-output-flag.test.js test/stdout-flush.test.js test/format-determinism.test.js test/only-flag-channels.test.js test/confidence.test.js test/security-trend.test.js test/calibration-drift.test.js test/validator-metrics.test.js test/clustering-ir-taint-line.test.js test/concurrency-cwe.test.js test/compliance-mapping-liveness.test.js test/compliance-severity-threshold.test.js test/compliance-severity-policy.test.js test/compliance-evidence-binding.test.js test/compliance-evidence-signing.test.js test/compliance-file-contains.test.js test/label-isolation.test.js test/evidence-grade-wording.test.js test/llm-model-status.test.js test/egress-policy-constraints.test.js test/egress-redact.test.js test/egress-audit.test.js test/egress-approved-providers.test.js test/family-registry.test.js test/coverage-strength.test.js test/framework-provenance.test.js test/framework-provenance-controlrefs.test.js test/third-party-verification.test.js test/proof-coverage.test.js test/calibration-holdout.test.js test/independent-population-gate.test.js test/catalog-freshness.test.js test/incremental-parity.test.js test/surface-smoke.test.js test/fix-coverage.test.js test/raw-source-carveout.test.js test/mlbom-conformance.test.js test/deploy-gate-replay.test.js test/sbom-conformance.test.js test/unscored-claims.test.js test/mcp-rug-pull.test.js test/agent-boundary-taint.test.js test/confinement-adversarial.test.js test/comment-strip-cost.test.js test/reachability-claim.test.js test/entrypoint-breadth.test.js test/dep-file-admission.test.js test/custom-rules-freshness.test.js test/posture/provenance-schema.test.js test/posture/provenance-cache.test.js test/helpers/build-git-fixture.test.js test/posture/provenance-git-evidence.test.js test/posture/provenance-dag-walk.test.js test/posture/provenance-confidence.test.js test/posture/provenance-predicate-replay.test.js test/posture/provenance-origin-resolver.test.js test/posture/provenance-ai-authorship.test.js test/posture/provenance-branch-entry.test.js test/posture/provenance-evidence-attribution.test.js test/posture/provenance-lifecycle.test.js test/posture/provenance-missing-control-resolver.test.js test/posture/provenance-missing-control-wiring.test.js test/posture/provenance-coordinator.test.js test/posture/provenance-sca-origin.test.js test/posture/provenance-transitive-sca.test.js test/posture/provenance-coordinator-sca.test.js test/posture/provenance-providers.test.js test/posture/provenance-evidence-bundle.test.js test/posture/provenance-repo-lineage.test.js test/posture/provenance-secrets-logic.test.js test/cli/provenance-flags.test.js test/cli/attest-provenance.test.js test/cli/attest-obligations.test.js test/cli/compliance-walkthrough-lineage.test.js test/cli/dataflow-export-privacy.test.js test/cli/dataflow-export-briefing.test.js test/cli/lineage-snapshot-persist.test.js test/cli/dataflow-diff.test.js test/cli/dataflow-watch.test.js test/cli/dataflow-recipients.test.js test/cli/dataflow-coverage.test.js test/security/provenance-safety.test.js test/security/provenance-injection.test.js test/posture/remediation-ledger.test.js",
68
+ "test:dataflow": "node --test test/privacy-catalog.test.js test/privacy-deep-walker.test.js test/privacy-deep-e2e.test.js test/container-taint.test.js test/stored-taint-families.test.js test/py-annotation-sources.test.js test/fn-reach.test.js test/deep-taint.test.js test/calibration.test.js test/holdout-eval.test.js test/cross-lang-meta.test.js test/cross-lang-queues.test.js test/phase5-xlang.test.js test/phase5-coverage.test.js test/phase6-taint.test.js test/llm-validator-consistency.test.js test/llm-validator-default-on.test.js test/llm-validator-preset.test.js test/parser-py-cst.test.js test/chrome-probe.test.js test/parser-cs-kt.test.js test/parser-cs-annotations.test.js test/parser-cs-control-flow.test.js test/parser-kt-control-flow.test.js test/parser-js-annotations.test.js test/parser-java-annotations.test.js test/parser-java-control-flow.test.js test/parser-java-calls.test.js test/parser-go.test.js test/parser-php-rb.test.js test/parser-php-control-flow.test.js test/interproc-k2.test.js test/proven-clean.test.js test/backward-default.test.js test/incremental-cache.test.js test/string-regex-lattice.test.js test/closure-capture.test.js test/points-to.test.js test/type-stubs.test.js test/soft-taint.test.js test/ifds.test.js test/symbolic-exec-proof.test.js test/ifds-summary-edges.test.js test/stub-aware-filter.test.js test/cross-repo.test.js test/proof-gate.test.js test/layer-recall.test.js test/java-taint-flow.test.js test/kt-taint-flow.test.js test/sanitizer-typed-flow.test.js test/sanitizer-gate-unit.test.js test/proof-safe.test.js test/collection-taint.test.js test/kcfa-context.test.js test/kcfa-callstring.test.js test/flow-parity.test.js test/callgraph-resolve.test.js test/import-reachability.test.js test/ir-stats.test.js test/parser-cpp.test.js test/parser-js-decorators.test.js test/parser-js-if-else-cfg.test.js test/cpp-integration.test.js test/engine-reconnect.test.js test/phase2-scoping.test.js test/engine-recall.test.js test/catalog-annotation-source.test.js test/catalog-cs-p1.test.js test/catalog-expanded.test.js test/builtin-summaries.test.js test/interproc-mutation-crash.test.js test/mutated-param-taint.test.js test/return-sink-check.test.js test/higher-order-inline-callback.test.js test/interproc-findings-discard.test.js test/runscan-deep-option.test.js test/summary-cache-convergence.test.js test/parser-rb-calls.test.js test/parser-rb-module-level.test.js test/balanced-call.test.js test/destructuring-taint.test.js test/computed-member-taint.test.js test/string-interpolation-taint.test.js test/dataflow-deadcode-severity.test.js test/catalog-dotted-callee-lookup.test.js test/builtin-summary-family-blind-kill.test.js test/deep-mode-annotator-pipeline.test.js test/receiver-type-and-nested-calls.test.js test/member-write-and-loop-taint.test.js test/parser-py-module-level.test.js test/parser-php-module-level.test.js test/r14b-module-level-e2e.test.js test/annotation-taint-engine.test.js test/interproc-nested-call-taint.test.js test/catalog-xpath-injection.test.js test/catalog-ldap-injection.test.js test/catalog-xxe.test.js test/catalog-response-splitting.test.js test/catalog-code-injection.test.js test/parser-rb-control-flow.test.js test/catalog-ruby-p1.test.js test/callee-receiver-taint.test.js test/catalog-command-injection-p4.test.js test/catalog-xss-p4.test.js test/catalog-path-ssrf-p3.test.js test/catalog-ai-model-provider-precision.test.js test/catalog-orm-write.test.js test/dataflow/provenance-pipeline-integration.test.js",
69
+ "test:mcp": "node --test test/mcp.test.js test/mcp-protocol-smoke.test.js test/mcp-audit.test.js test/audit-cli.test.js test/mcp-scratchpad.test.js test/mcp-offload.test.js test/sca-upgrade.test.js test/lsp-server.test.js test/mcp-dataflow-tools.test.js test/cli/governance-propose-edit.test.js test/cli/remediation-open-update.test.js test/cli/remediation-verify-reopen.test.js test/cli/dataflow-observations.test.js test/cli/federate-declare-list.test.js",
70
70
  "test:report": "node --test test/junit.test.js test/ci.test.js test/poc-generator.test.js test/verifier.test.js test/verifier-target.test.js test/annotator-errors.test.js test/scan-health.test.js test/finding-schema.test.js test/pipeline/finding-schema-provenance.test.js test/evidence-provenance.test.js test/report/provenance-output.test.js test/report/provenance-format-parity.test.js test/legacy-compat.test.js test/annotator-runner.test.js test/detector-runner.test.js test/detector-errors.test.js test/analyzer-supervisor.test.js test/cascade-worker-pool.test.js test/cascade-pool-wiring.test.js test/coverage-ledger.test.js test/coverage-ledger-completeness.test.js test/assurance-mode.test.js test/artifact-registry.test.js test/artifact-registry-completeness.test.js test/retention-policy.test.js test/state-lifecycle-report.test.js test/legal-hold.test.js test/production-feedback.test.js test/calibration-feedback.test.js test/encryption-provider.test.js test/grader-calibration.test.js test/pr-delta-gate.test.js test/vex.test.js test/report-render.test.js test/oscal-conformance.test.js test/cli-fix-apply.test.js test/apply-fix-service.test.js test/high-impact-approval-gate.test.js test/approver-registry.test.js test/suppression-exceptions.test.js test/fix-history.test.js test/producer-registry.test.js test/producer-collector.test.js test/enrichment-completion.test.js",
71
+ "test:lineage": "node --test test/lineage/schema.test.js test/lineage/ids.test.js test/lineage/protection.test.js test/lineage/classification.test.js test/lineage/validate.test.js test/lineage/json-schema-parity.test.js test/lineage/flagship-fixture.test.js test/lineage/flagship-fixture-semantics.test.js test/lineage/field-identity.test.js test/lineage/engine-expr-resolver.test.js test/lineage/engine-walker.test.js test/lineage/engine-integration.test.js test/lineage/engine-receiver-identity.test.js test/lineage/summaries.test.js test/lineage/driver.test.js test/lineage/engine-provenance.test.js test/lineage/engine-provenance-interprocedural.test.js test/lineage/path-store.test.js test/lineage/path-query.test.js test/lineage/flow-grade.test.js test/lineage/source-registry.test.js test/lineage/sink-registry.test.js test/lineage/transform-catalog.test.js test/lineage/handling-analyzer.test.js test/lineage/registry-real-code.test.js test/lineage/source-seeding.test.js test/lineage/graph-builder.test.js test/lineage/coverage.test.js test/lineage/resolve-destination.test.js test/lineage/queue-detail.test.js test/lineage/index.test.js test/lineage/obligation-mapping.test.js test/lineage/obligation-predicates.test.js test/lineage/obligation-predicates-walkthrough.test.js test/lineage/transit-protection.test.js test/lineage/at-rest-protection.test.js test/lineage/protection-summary.test.js test/lineage/ac01-multi-sink.test.js test/lineage/policy-verdict.test.js test/lineage/governance-refs.test.js test/lineage/export-privacy.test.js test/lineage/export-json.test.js test/lineage/export-csv.test.js test/lineage/decision-story.test.js test/lineage/export-briefing.test.js test/lineage/graph-snapshot.test.js test/lineage/graph-diff.test.js test/lineage/drift-policy.test.js test/lineage/recipient-profile.test.js test/lineage/recipient-registry.test.js test/lineage/recipient-wiring.test.js test/lineage/redact-graph.test.js test/lineage-scan-wiring.test.js test/lineage-artifact-write.test.js test/bench-data-lineage-runner.test.js test/bundle-frontend.test.js test/bundle-frontend-golden.test.js test/generate-html-report.test.js test/export-image.test.js test/lineage/scenario.test.js test/lineage/scenario-engine.test.js test/lineage/scenario-diff.test.js test/lineage/scenario-no-obligation-wiring.test.js test/lineage/impact-assessment.test.js test/lineage/impact-engine.test.js test/lineage/impact-no-obligation-wiring.test.js test/lineage/governance-edit.test.js test/lineage/remediation.test.js test/lineage/runtime-observation.test.js test/lineage/observation-correlation.test.js test/lineage/observation-adapters.test.js test/lineage/observation-store.test.js test/lineage/runtime-corroboration-wiring.test.js test/lineage/cross-repo-link.test.js test/lineage/federation-loader.test.js test/lineage/cross-repo-link-wiring.test.js",
71
72
  "test:bench-modules": "node --test test/phase4-harness.test.js test/pipeline.test.js test/proof-corpus-lib.test.js test/proof-corpus-runner.test.js",
73
+ "test:server": "node --test test/server/graph-loader.test.js test/server/security.test.js test/server/routes.test.js test/server/http-server.test.js test/server/cmd-explore.test.js test/server/cmd-dataflow-export.test.js test/server/static-assets.test.js test/server/dataflow-scenario-cli.test.js test/server/dataflow-impact-cli.test.js",
72
74
  "test:lifecycle": "node --test test/lsp-protocol-smoke.test.js test/ide-surfaces.test.js test/detector-liveness.test.js test/bench-watchdog.test.js test/ci-parity.test.js test/dead-code.test.js test/no-dead-modules.test.js test/no-orphan-scripts.test.js test/no-stray-state.test.js test/tree-integrity.test.js test/stop-hook.test.js test/plugin-self-check.test.js test/skills-registry.test.js test/bodyguard.test.js test/cache-invalidator-guard.test.js test/dispatch-pre-tool.test.js test/dispatch-user-prompt.test.js test/session-start-model-capture.test.js test/sandbox.test.js test/sandbox-escape.test.js test/ci-templates.test.js test/agent-mcp-tools.test.js test/triage-command.test.js test/no-orphan-tests.test.js test/supply-command.test.js test/posture-command.test.js test/labs-command.test.js test/compliance-command.test.js test/check-doc-drift.test.js test/dist-chunks-tracked.test.js && node ../scripts/lint-command-descriptions.mjs",
73
75
  "test:eval": "node --test test/independent-eval.test.js test/realworld-recall.test.js",
74
76
  "test:discovery": "node --test test/discovery-partition.test.js test/discovery-lenses.test.js test/discovery-hunter.test.js test/discovery-confirm.test.js test/discovery-disprove.test.js test/discovery-judge.test.js test/discovery-run.test.js test/discovery-wiring.test.js test/discovery-llm-invoke.test.js test/local-only-mode.test.js",
@@ -81,6 +83,8 @@
81
83
  "prebench:realworld": "npm run build",
82
84
  "bench:realworld": "node test/benchmark/realworld/bench-realworld.js",
83
85
  "bench:llm-goats": "node test/benchmark/llm-goats/bench-llm-goats.js",
86
+ "bench:data-lineage": "node ../bench/data-lineage/runner.mjs",
87
+ "bench:data-lineage:check": "node ../bench/data-lineage/runner.mjs --check",
84
88
  "bench:cve-replay": "node ../bench/cve-replay/runner.mjs",
85
89
  "bench:cve-replay:ci": "node ../bench/cve-replay/runner.mjs --ci",
86
90
  "bench:cve-replay:check": "node ../bench/cve-replay/runner.mjs --check-baseline",
@@ -109,6 +113,7 @@
109
113
  "bench:self-scan": "node ../bench/self-scan/measure.mjs",
110
114
  "bench:self-scan:check": "node ../bench/self-scan/check.mjs",
111
115
  "bench:mutation:check": "node ../bench/mutation/runner.mjs",
116
+ "bench:protection-verdict:check": "node ../bench/protection-verdict/runner.mjs",
112
117
  "bench:fix-correctness": "node ../bench/fix-correctness/runner.mjs",
113
118
  "bench:iac-coverage": "node ../bench/iac-coverage/runner.mjs",
114
119
  "bench:prompt-injection": "node ../bench/prompt-injection/runner.mjs",
@@ -125,12 +130,14 @@
125
130
  "bench:provenance-accuracy:check": "node ../bench/provenance-accuracy/runner.mjs --check",
126
131
  "bench:provenance-accuracy:update-baseline": "node ../bench/provenance-accuracy/runner.mjs --update-baseline",
127
132
  "bench:privacy-recall": "node ../bench/privacy-recall/measure.mjs",
133
+ "bench:privacy-recall:compare-lineage": "node ../bench/privacy-recall/compare-lineage.mjs",
128
134
  "bench:privacy-recall:check": "node ../bench/privacy-recall/check.mjs",
129
135
  "bench:privacy-recall:update-baseline": "node ../bench/privacy-recall/check.mjs --update-baseline",
130
136
  "bench:self-scan:update-baseline": "node ../bench/self-scan/check.mjs --update-baseline",
131
137
  "bench:engine-recall": "node ../bench/engine-recall/measure.mjs",
132
138
  "scorecard": "node ../scripts/scorecard.mjs",
133
139
  "check-doc-drift": "node ../scripts/check-doc-drift.mjs",
140
+ "docs:verify-examples": "node ../scripts/verify-doc-examples.mjs",
134
141
  "harness-score": "node ../scripts/harness-score.cjs",
135
142
  "corpus:enroll": "node ../scripts/enroll-proven-finding.mjs",
136
143
  "corpus:provenance": "node ../scripts/corpus-provenance-check.mjs",
@@ -6,7 +6,7 @@ Layer-2 taint engine. Walks the Layer-1 IR (`../ir/`) with field-sensitive forwa
6
6
 
7
7
  - **Intra-procedural field-sensitive taint** with access-path lattice (`access-paths.js`). `user.email` is distinguishable from `user.password`.
8
8
  - **Value-context-sensitive interprocedural taint (FR-SEM-2).** `SummaryCache` (`summaries.js`) holds a distinct summary per distinct entry-taint-state. A pre-pass computes the empty-entry base for every function; call sites then lazily compute the summary under their actual tainted-arg context (at both assign-from-call and plain-call sites) so a helper that is clean with clean args but tainted with user input is detected per call site. Bounded by a per-function context cap (`AGENTIC_SECURITY_KCFA_MAX_CONTEXTS`, default 16; 0 = monovariant). Over the cap → reuse the empty-entry summary.
9
- - **Catalog-driven source/sink matching.** Add entries in `catalog.js`. Each entry: `kind` ∈ {source, sink, sanitizer}, plus language + framework + match shape. `match.type` ∈ {`call`, `member`, `global`, `annotation`} — 655 entries (149 source, 124 sink, 382 sanitizer — re-derive via `CATALOG.length` / a per-`kind` count rather than trusting a hardcoded number here again; this has already drifted once) spanning Express/Flask/FastAPI/Django/Rails/PHP/Go-net-http/Gin/Echo.
9
+ - **Catalog-driven source/sink matching.** Add entries in `catalog.js`. Each entry: `kind` ∈ {source, sink, sanitizer}, plus language + framework + match shape. `match.type` ∈ {`call`, `member`, `global`, `annotation`} — 655 entries (149 source, 124 sink, 382 sanitizer — re-derive via `CATALOG.length` / a per-`kind` count rather than trusting a hardcoded number here again; this has already drifted once) **— and it has now drifted a SECOND time: as of 2026-08-31 the real figures are 756 (180 source, 194 sink, 382 sanitizer). See `../lineage/DESIGN_REGISTRIES.md` §7.3 item 1 — originally measured and pinned by the design-phase PoC, since deleted; the 180-source and 194-sink figures are now pinned as shipped, permanent equality tests in the lineage package's own source-registry.test.js and sink-registry.test.js (see `../lineage/CLAUDE.md`'s module table for their exact paths). The stale numbers are left in place above rather than hand-patched a third time, because nothing in this file can keep them true; trust `CATALOG.length`, not this sentence — and it HAS drifted a third time already, predictably: Sub-project H's AC-07 closure (2026-08-31) added 4 sink entries, so the real sink-entry count is now 198, not 194** — spanning Express/Flask/FastAPI/Django/Rails/PHP/Go-net-http/Gin/Echo.
10
10
  - **`match.type: 'call'`/`'member'`/`'global'`** are matched against an *expression* the taint-walker encounters while visiting a CFG node (`matchSource` / `matchSinkOrSanitizer`) — `call` against a call expression's callee, `member` against a property-access chain (`match.object` + `match.prop`), `global` against a bare identifier read.
11
11
  - **`match.type: 'annotation'`** (R14(a): Spring `@RequestParam`/`@PathVariable`/`@RequestBody`/`@RequestHeader`, ASP.NET Core `[FromQuery]`/`[FromBody]`/`[FromForm]`/`[FromRoute]`/`[FromHeader]`, NestJS `@Query()`/`@Body()`/`@Param()`/`@Headers()`) works differently from the other three, and this is the one fact worth internalizing before touching it: **it is not consulted during the CFG walk.** There is no CFG node for "this function's own declared parameter list." Instead `matchAnnotationParams` (`catalog.js`) is called once per function — by `engine.js`'s `_unionAnnotationTaint`, before `analyzeFunction` runs — against the IR's `fn.paramAnnotations` side-channel (`{index, name, decorator}[]`, populated by `parser-cs.js`/`parser-js.js`/`parser-java.js`; see `../ir/CLAUDE.md`), and the resulting tainted param names are unioned into the entry taint-state for that function/call-site. A reader who assumes `annotation` entries get matched against expressions mid-walk, the way `call`/`member`/`global` do, will be wrong.
12
12
  - **Sanitizer entries are RECORDED, never trusted to kill taint.** `_sanitizersForExpr()` collects the sanitizer callees applied to the value reaching each sink argument — inline in the argument, or inherited from the variable it reads (`_sanitizersByVar`, updated on every assignment and cleared when the RHS is clean). They are stamped on the finding as `_sanitizersOnPath` and consumed by `sanitizer-gate.js`, which sets `sanitized` + `sanitizerProof` **only when the sanitizer's `appliesTo` family covers the finding's threat class** (`*` covers everything). The proof gate then demotes. Taint itself still dies only on clean re-assignment (`removePathAndDescendants`).
@@ -606,6 +606,48 @@ export const CATALOG = [
606
606
  vuln: { name: 'SSRF (axios/http.get)', severity: 'high', cwe: 'CWE-918',
607
607
  remediation: 'Validate the target URL against an allow-list before fetching; reject RFC1918 + metadata endpoints.' } },
608
608
 
609
+ // ─── SINKS (AI model provider) ────────────────────────────────────────────
610
+ // AC-07 closure (Data Flow Explorer, Sub-project H). Regulated data
611
+ // (PCI/PHI/PII) reaching a hosted third-party AI model provider is a real
612
+ // data-exposure concern the same way any other third-party API call is —
613
+ // CWE-201 (Insertion of Sensitive Information Into Sent Data), NOT CWE-359
614
+ // (reserved exclusively for privacy-catalog.js's own "Privacy Leak" family
615
+ // — sink-registry.js's completeness/1c test enforces this).
616
+ //
617
+ // Precision comes from `receiver`/`receiverBase`: CALLEE_INDEX is keyed on
618
+ // the bare name `create`, so every `X.create(...)` in the tree is a
619
+ // candidate. Every receiver-bearing entry below constrains BOTH the
620
+ // property chain AND the receiver's own name/alias — `receiver` alone
621
+ // (checked via `_receiverAllowed`'s `.some()` over the whole member
622
+ // chain) is not enough: a first cut of `js-anthropic-messages-create`/
623
+ // `js-openai-responses-create` shipped with only `receiver` set, and a
624
+ // task review found it matched ordinary `db.messages.create()`/
625
+ // `prisma.messages.create()` (Sequelize/Prisma's idiomatic row-insert
626
+ // shape) — a real false-attribution SAST finding, not a hypothetical.
627
+ // `receiverBase: '^chat$'` on the OpenAI chat-completions entry is what
628
+ // separates it from the legacy `anthropic.completions.create` shape
629
+ // (deliberately not cataloged here — AC-07 names only OpenAI/Anthropic/
630
+ // Bedrock).
631
+ { kind: 'sink', id: 'js-openai-chat-completions-create', language: 'js', framework: 'openai',
632
+ match: { type: 'call', callee: 'create', receiver: '^completions$', receiverBase: '^chat$' }, argIndex: 0,
633
+ vuln: { name: 'Regulated Data to AI Model Provider (OpenAI chat.completions.create)', severity: 'medium', cwe: 'CWE-201',
634
+ remediation: 'Confirm the request payload carries no PCI/PHI/PII before sending to a third-party model provider, or route through an approved DPA / redaction layer.' } },
635
+ { kind: 'sink', id: 'js-openai-responses-create', language: 'js', framework: 'openai',
636
+ match: { type: 'call', callee: 'create', receiver: '^responses$', receiverBase: '^(?:openai|client|oai)$' }, argIndex: 0,
637
+ vuln: { name: 'Regulated Data to AI Model Provider (OpenAI responses.create)', severity: 'medium', cwe: 'CWE-201',
638
+ remediation: 'Confirm the request payload carries no PCI/PHI/PII before sending to a third-party model provider, or route through an approved DPA / redaction layer.' } },
639
+ { kind: 'sink', id: 'js-anthropic-messages-create', language: 'js', framework: 'anthropic',
640
+ match: { type: 'call', callee: 'create', receiver: '^messages$', receiverBase: '^(?:anthropic|client|claude)$' }, argIndex: 0,
641
+ vuln: { name: 'Regulated Data to AI Model Provider (Anthropic messages.create)', severity: 'medium', cwe: 'CWE-201',
642
+ remediation: 'Confirm the request payload carries no PCI/PHI/PII before sending to a third-party model provider, or route through an approved DPA / redaction layer.' } },
643
+ // Bare, constructor-shaped call with no fixed receiver — safe to match on the
644
+ // bare name alone, the same precedent `js-fetch` already uses, because
645
+ // `InvokeModelCommand` is a distinctive, unambiguous identifier.
646
+ { kind: 'sink', id: 'js-bedrock-invoke-model-command', language: 'js', framework: 'bedrock',
647
+ match: { type: 'call', callee: 'InvokeModelCommand' }, argIndex: 0,
648
+ vuln: { name: 'Regulated Data to AI Model Provider (AWS Bedrock InvokeModelCommand)', severity: 'medium', cwe: 'CWE-201',
649
+ remediation: 'Confirm the request payload carries no PCI/PHI/PII before sending to a third-party model provider, or route through an approved DPA / redaction layer.' } },
650
+
609
651
  // ─── SINKS (command exec) ─────────────────────────────────────────────────
610
652
  { kind: 'sink', id: 'py-subprocess-run', language: 'py', framework: 'subprocess', match: { type: 'call', callee: 'run', requireKeyword: { name: 'shell', pattern: '^(?:true|True|1)$' } }, argIndex: 0,
611
653
  vuln: { name: 'Command Injection (subprocess.run shell=True)', severity: 'critical', cwe: 'CWE-78',
@@ -0,0 +1,175 @@
1
+ // Milestone 2, Sub-project E, increment 1 (ORM-write sink recognition).
2
+ // Per docs/superpowers/plans/2026-08-31-data-flow-explorer-m2-subproject-e1-plan.md
3
+ // and its own referenced scoping doc's Finding 3 (the precision-mechanism
4
+ // gap): a catalog of ORM row-write call shapes (`Model.create({...})`,
5
+ // `Model.save({...})`, `Model.update({...})`, `Model.upsert({...})`) for
6
+ // `src/lineage/`'s Data Flow Explorer only.
7
+ //
8
+ // DELIBERATELY NOT ADDED TO `dataflow/catalog.js`'s own `CATALOG`, and NOT
9
+ // wired into `matchSinkOrSanitizer`/`runTaintEngine` — the exact same
10
+ // isolation reasoning `privacy-catalog.js`'s own header comment gives for
11
+ // itself (see that file, lines 12-32), reapplied here for a stronger
12
+ // reason: `CATALOG` is a shared, always-active singleton consulted for
13
+ // EVERY scan, with no per-family filtering, so merging these entries in
14
+ // would make every already-tainted general source (`req.body`, etc.)
15
+ // immediately produce a new, unreviewed SAST finding class the moment it
16
+ // reached an ORM `.create()`/`.save()`/`.update()`/`.upsert()` call — a
17
+ // real, unintended expansion of the general SAST pipeline's output as a
18
+ // side effect of a Data Flow Explorer feature, not something this
19
+ // increment is chartered to decide. Unlike `privacy-catalog.js` (which
20
+ // shares its entry shape with something that COULD wire into `CATALOG`
21
+ // later), this catalog's own precision gate — "the call's first argument
22
+ // must be an object-literal expression" — cannot be expressed inside
23
+ // `matchSinkOrSanitizer`'s existing `(calleeExpr, file, receiverType)`
24
+ // signature at all, since that signature never receives the call's
25
+ // arguments. Making that check work requires a caller with `site.args`
26
+ // (`src/lineage/graph-builder.js`'s own site enumeration), so the
27
+ // isolation here is closer to structural than a mere policy choice.
28
+ //
29
+ // Keeping this catalog in its own file, imported ONLY by `src/lineage/`'s
30
+ // own registry/graph-builder modules, is what makes it genuinely inert to
31
+ // every other scan surface.
32
+ //
33
+ // TWO signals gate a real match, and only one lives in this file:
34
+ // 1. Callee shape (HERE, in `ORM_WRITE_CATALOG` + `matchOrmWrite`):
35
+ // method name is one of create/save/update/upsert, receiver is a
36
+ // BARE, CAPITALIZED identifier (`^[A-Z]\w*$`) — the SAME shape
37
+ // `sast/mass-assignment.js` (line 27) and `posture/cross-lang-orm.js`
38
+ // (`findOrmWrites`) already use in production as real SAST
39
+ // detectors; this is not a new or untested heuristic, it is the
40
+ // established one, reused here for a lower-stakes (candidate-tier,
41
+ // disclosed) purpose than either of those two modules' own
42
+ // full-confidence findings.
43
+ // 2. Argument shape (NOT here — lineage-side, in `graph-builder.js`'s
44
+ // site enumeration): the matched call's first argument must be an
45
+ // object-literal expression (`kind: 'object'`). A call whose first
46
+ // argument is not an object literal (a bare variable, a spread, a
47
+ // positional string — e.g. `User.create(req.body)`) never becomes an
48
+ // ORM-write sink candidate at all; this file cannot express that gate
49
+ // itself, since `matchOrmWrite`'s signature (mirroring
50
+ // `matchPrivacySink`'s) never receives the call's arguments.
51
+ //
52
+ // `coverageStatus` for every site this catalog recognizes is the literal
53
+ // string 'candidate', unconditionally, computed nowhere — see
54
+ // `sink-registry.js`'s `reclassifyOrmWrite`. Even with both signals
55
+ // present, this is real, disclosed uncertainty: an arbitrary
56
+ // capitalized-identifier receiver could still be a non-ORM builder
57
+ // pattern that happens to take an object-literal argument.
58
+ //
59
+ // Scope boundary, disclosed rather than silently assumed: JS/TS only.
60
+ // Python/Java/Go/Ruby/PHP ORM shapes (Django `.objects.create()`, GORM,
61
+ // ActiveRecord, Eloquent) are real, common shapes this catalog does not
62
+ // recognize — out of scope for this increment, per the plan's own
63
+ // "Explicitly deferred" list. Table/column/operation extraction, and
64
+ // Prisma's distinctive `data: {...}` wrapper form's own precise handling,
65
+ // are likewise deferred (the object-literal-argument check as specified
66
+ // sees Prisma's OUTER `{ data: {...} }` literal and passes, which is
67
+ // consistent for the callee-match gate, but this file makes no claim
68
+ // about extracting the inner Prisma shape — that's E2's job).
69
+ //
70
+ // `category: 'database'` reuses the existing `SINK_CATEGORIES` value
71
+ // (schema.js) rather than minting a new `orm-write`-style category — a
72
+ // genuine, lower-confidence detection of the same kind of destination a
73
+ // raw-SQL sink already models. Whether a dedicated category is warranted
74
+ // is an open question left to a future increment, not decided here.
75
+
76
+ import { _languageFamilyExtensions } from './catalog.js';
77
+
78
+ // The method-name vocabulary is verified against `cross-lang-orm.js`'s own
79
+ // `findOrmWrites` regex (`create|save|update|build|insert|upsert`) and
80
+ // `mass-assignment.js`'s own JS pattern (`create|update|build|save`) —
81
+ // this catalog uses the plan's suggested starting set (create/save/update/
82
+ // upsert), a subset of both, not the full union: `build`/`insert` are
83
+ // real ORM-adjacent method names too, but the plan's own "starting point,
84
+ // not a mandate" framing leaves widening this list to a future increment.
85
+ export const ORM_WRITE_CATALOG = [
86
+ { kind: 'sink', id: 'orm-write-js-create', language: 'js', framework: 'orm', category: 'database',
87
+ match: { type: 'call', callee: 'create' }, argIndex: 0,
88
+ vuln: { name: 'ORM Write (create)', severity: 'info', cwe: null,
89
+ remediation: 'Heuristic ORM row-write recognition for the Data Flow Explorer only — not a SAST finding. No remediation is implied.' } },
90
+ { kind: 'sink', id: 'orm-write-js-save', language: 'js', framework: 'orm', category: 'database',
91
+ match: { type: 'call', callee: 'save' }, argIndex: 0,
92
+ vuln: { name: 'ORM Write (save)', severity: 'info', cwe: null,
93
+ remediation: 'Heuristic ORM row-write recognition for the Data Flow Explorer only — not a SAST finding. No remediation is implied.' } },
94
+ { kind: 'sink', id: 'orm-write-js-update', language: 'js', framework: 'orm', category: 'database',
95
+ match: { type: 'call', callee: 'update' }, argIndex: 0,
96
+ vuln: { name: 'ORM Write (update)', severity: 'info', cwe: null,
97
+ remediation: 'Heuristic ORM row-write recognition for the Data Flow Explorer only — not a SAST finding. No remediation is implied.' } },
98
+ { kind: 'sink', id: 'orm-write-js-upsert', language: 'js', framework: 'orm', category: 'database',
99
+ match: { type: 'call', callee: 'upsert' }, argIndex: 0,
100
+ vuln: { name: 'ORM Write (upsert)', severity: 'info', cwe: null,
101
+ remediation: 'Heuristic ORM row-write recognition for the Data Flow Explorer only — not a SAST finding. No remediation is implied.' } },
102
+ ];
103
+
104
+ function _ormLanguageAllowed(entry, file) {
105
+ if (!file) return true;
106
+ const res = _languageFamilyExtensions(entry.language);
107
+ if (!res.length) return true; // unmapped language stays permissive
108
+ return res.some((re) => re.test(file));
109
+ }
110
+
111
+ // Small, local copy of the callee-name extraction shape `privacy-catalog.js`'s
112
+ // own `_privacyCalleeNames` establishes — not imported from that file (it's
113
+ // private/unexported there), matching this package's established precedent
114
+ // of a small local duplicate over an awkward cross-module dependency on
115
+ // another module's private helper (see `src/lineage/handling-analyzer.js`'s
116
+ // own disclosed `calleeDescriptorOf` duplicate).
117
+ function _ormCalleeNames(calleeExpr) {
118
+ let last = null;
119
+ let full = null;
120
+ if (typeof calleeExpr === 'string') {
121
+ full = calleeExpr;
122
+ last = calleeExpr.includes('.') ? calleeExpr.slice(calleeExpr.lastIndexOf('.') + 1) : calleeExpr;
123
+ } else if (calleeExpr && calleeExpr.kind === 'member' && calleeExpr.prop) {
124
+ last = calleeExpr.prop;
125
+ if (calleeExpr.object && calleeExpr.object.kind === 'ident') full = `${calleeExpr.object.name}.${calleeExpr.prop}`;
126
+ } else if (calleeExpr && calleeExpr.kind === 'ident') {
127
+ last = calleeExpr.name || null;
128
+ }
129
+ return { last, full };
130
+ }
131
+
132
+ // Bare, capitalized receiver identifier — a SHAPE regex on the receiver's
133
+ // own name, not a `receiverTypeIn` exact-alias lookup (unrelated to the
134
+ // IR's CHA-resolved `receiverType`). Only a `kind: 'member'` calleeExpr
135
+ // whose `object` is itself a plain `kind: 'ident'` can satisfy this — a
136
+ // computed or chained receiver (`db.models.User.create(...)`) does not,
137
+ // by design (the same conservative shape `mass-assignment.js`/
138
+ // `cross-lang-orm.js` already use in production).
139
+ const CAPITALIZED_IDENT_RE = /^[A-Z]\w*$/;
140
+
141
+ function _ormReceiverIsCapitalizedIdent(calleeExpr) {
142
+ return Boolean(
143
+ calleeExpr && typeof calleeExpr === 'object' &&
144
+ calleeExpr.kind === 'member' &&
145
+ calleeExpr.object && calleeExpr.object.kind === 'ident' &&
146
+ typeof calleeExpr.object.name === 'string' &&
147
+ CAPITALIZED_IDENT_RE.test(calleeExpr.object.name),
148
+ );
149
+ }
150
+
151
+ /**
152
+ * Signal 1 only (callee shape) — the argument-shape signal (signal 2) is
153
+ * NOT checked here; see this file's own header for why it structurally
154
+ * cannot be. Mirrors `matchPrivacySink`'s `(calleeExpr, file, receiverType)`
155
+ * contract, minus the unused third parameter (this catalog's receiver
156
+ * constraint is a name-shape regex, not a `receiverTypeIn` lookup, so no
157
+ * `receiverType` input is ever consulted). Returns an array of hits, or
158
+ * `null` — same "array of hits, or null" contract `matchSinkOrSanitizer`/
159
+ * `matchPrivacySink` already establish.
160
+ */
161
+ export function matchOrmWrite(calleeExpr, file) {
162
+ if (!calleeExpr) return null;
163
+ if (!_ormReceiverIsCapitalizedIdent(calleeExpr)) return null;
164
+ const { last, full } = _ormCalleeNames(calleeExpr);
165
+ if (!last && !full) return null;
166
+ const hits = ORM_WRITE_CATALOG.filter((e) => {
167
+ if (!e || e.kind !== 'sink') return false;
168
+ const cName = e.match && e.match.callee;
169
+ if (!cName) return false;
170
+ if (cName !== last && cName !== full) return false;
171
+ if (!_ormLanguageAllowed(e, file)) return false;
172
+ return true;
173
+ });
174
+ return hits.length ? hits : null;
175
+ }