@clear-capabilities/agentic-security-scanner 0.184.0 → 0.186.1

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 (228) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/bin/agentic-security.js +111 -1
  3. package/dist/1128.index.js +912 -0
  4. package/dist/1665.index.js +828 -0
  5. package/dist/2584.index.js +1 -1
  6. package/dist/4171.index.js +2 -2
  7. package/dist/5752.index.js +466 -0
  8. package/dist/7405.index.js +1 -1
  9. package/dist/8158.index.js +1713 -0
  10. package/dist/8798.index.js +1 -1
  11. package/dist/9382.index.js +2 -2
  12. package/dist/9579.index.js +1 -1
  13. package/dist/agentic-security.mjs +29 -29
  14. package/dist/agentic-security.mjs.sha256 +1 -1
  15. package/dist/{dad58d1113a12c23a142.json → ca62ee93079be4c24eee.json} +29 -12
  16. package/dist/frontend/src/shell.js +12 -2
  17. package/package.json +29 -12
  18. package/src/dataflow/CLAUDE.md +46 -1
  19. package/src/dataflow/access-paths.js +12 -2
  20. package/src/dataflow/backward.js +42 -7
  21. package/src/dataflow/cross-service-taint.js +83 -1
  22. package/src/dataflow/demand-query.js +1168 -0
  23. package/src/dataflow/dep-fingerprint.js +718 -0
  24. package/src/dataflow/engine.js +48 -6
  25. package/src/dataflow/exploit-prover.js +6 -2
  26. package/src/dataflow/ifds-precise.js +22 -0
  27. package/src/dataflow/incremental-replay.js +118 -0
  28. package/src/dataflow/incremental-session.js +257 -0
  29. package/src/dataflow/incremental.js +237 -35
  30. package/src/dataflow/index.js +25 -94
  31. package/src/dataflow/ir-cache.js +268 -0
  32. package/src/dataflow/privacy-taint.js +7 -0
  33. package/src/dataflow/summaries.js +72 -6
  34. package/src/dataflow/tabulation.js +80 -24
  35. package/src/engine.js +113 -8
  36. package/src/graph/CLAUDE.md +31 -0
  37. package/src/graph/adapters/artifact.js +81 -0
  38. package/src/graph/adapters/crossrepo.js +75 -0
  39. package/src/graph/adapters/dataflow.js +76 -0
  40. package/src/graph/adapters/provenance.js +92 -0
  41. package/src/graph/adapters/semantic.js +89 -0
  42. package/src/graph/crosswalk-builders.js +152 -0
  43. package/src/graph/crosswalk.js +146 -0
  44. package/src/graph/index.js +15 -0
  45. package/src/graph/observation.js +57 -0
  46. package/src/graph/refs.js +105 -0
  47. package/src/graph/registry.js +129 -0
  48. package/src/graph/relations.js +81 -0
  49. package/src/graph/scc.js +241 -0
  50. package/src/graph/view.js +254 -0
  51. package/src/ir/CLAUDE.md +4 -0
  52. package/src/ir/callgraph.js +36 -8
  53. package/src/ir/index.js +18 -4
  54. package/src/ir/parser-js.js +10 -0
  55. package/src/language/CLAUDE.md +13 -0
  56. package/src/language/nix-option-types.js +170 -0
  57. package/src/language/nix-script-taint.js +46 -3
  58. package/src/language/nix-secrets.js +20 -2
  59. package/src/lineage/CLAUDE.md +72 -8
  60. package/src/lineage/DESIGN_GRAPH_BUILDER.md +1 -1
  61. package/src/lineage/DESIGN_PATH_PROVENANCE.md +6 -0
  62. package/src/lineage/boundary-contract.js +273 -0
  63. package/src/lineage/completion.js +205 -0
  64. package/src/lineage/continuation.js +140 -0
  65. package/src/lineage/control-credit.js +313 -0
  66. package/src/lineage/correlation.js +199 -0
  67. package/src/lineage/coverage.js +8 -1
  68. package/src/lineage/cross-repo-link.js +22 -0
  69. package/src/lineage/cut-planner.js +548 -0
  70. package/src/lineage/driver.js +219 -66
  71. package/src/lineage/engine.js +166 -21
  72. package/src/lineage/export-json.js +5 -0
  73. package/src/lineage/graph-builder.js +84 -7
  74. package/src/lineage/haskell-view.js +59 -0
  75. package/src/lineage/index.js +3 -0
  76. package/src/lineage/nix-view.js +35 -0
  77. package/src/lineage/owned-build.js +77 -0
  78. package/src/lineage/owned-worker-entry.js +22 -0
  79. package/src/lineage/owned-worker.js +95 -0
  80. package/src/lineage/path-evidence.js +123 -0
  81. package/src/lineage/path-predicates.js +529 -0
  82. package/src/lineage/path-query.js +189 -20
  83. package/src/lineage/path-store.js +191 -17
  84. package/src/lineage/scc-solver.js +186 -0
  85. package/src/lineage/smt-orchestrator.js +174 -0
  86. package/src/lineage/smt-z3-backend.js +294 -0
  87. package/src/lineage/source-seeding.js +36 -2
  88. package/src/lineage/summaries.js +66 -4
  89. package/src/lineage/value-versions.js +130 -0
  90. package/src/lineage/witness-selection.js +95 -0
  91. package/src/pipeline/scan-health.js +18 -0
  92. package/src/posture/CLAUDE.md +17 -0
  93. package/src/posture/business-logic.js +16 -7
  94. package/src/posture/dag-commands.js +442 -0
  95. package/src/posture/evidence-bundle.js +1 -1
  96. package/src/posture/finding-defaults.js +19 -0
  97. package/src/posture/git-history.js +91 -1
  98. package/src/posture/witness-bundle.js +573 -0
  99. package/src/posture/witness-runtime.js +276 -0
  100. package/src/report/index.js +2 -0
  101. package/src/sast/CLAUDE.md +2 -0
  102. package/src/sast/_call-args.js +70 -0
  103. package/src/sast/init-scope.js +87 -0
  104. package/src/sast/listen-bind-address.js +49 -0
  105. package/src/sast/redirect-target.js +106 -0
  106. package/src/sast/reflected-input.js +76 -0
  107. package/src/sast/send-file-root.js +27 -0
  108. package/src/sast/sensitive-data-logging.js +38 -1
  109. package/dist/119.index.js +0 -197
  110. package/dist/128.index.js +0 -129
  111. package/dist/1293.index.js +0 -5689
  112. package/dist/1336.index.js +0 -16182
  113. package/dist/1351.index.js +0 -133
  114. package/dist/1387.index.js +0 -271
  115. package/dist/1437.index.js +0 -23
  116. package/dist/1445.index.js +0 -110
  117. package/dist/1505.index.js +0 -151
  118. package/dist/1557.index.js +0 -293
  119. package/dist/1621.index.js +0 -311
  120. package/dist/1751.index.js +0 -160
  121. package/dist/1765.index.js +0 -727
  122. package/dist/1861.index.js +0 -690
  123. package/dist/1906.index.js +0 -433
  124. package/dist/208.index.js +0 -1631
  125. package/dist/2223.index.js +0 -159
  126. package/dist/2238.index.js +0 -295
  127. package/dist/2530.index.js +0 -152
  128. package/dist/2565.index.js +0 -116
  129. package/dist/2574.index.js +0 -127
  130. package/dist/2620.index.js +0 -345
  131. package/dist/2773.index.js +0 -98
  132. package/dist/2775.index.js +0 -32
  133. package/dist/2966.index.js +0 -171
  134. package/dist/2972.index.js +0 -87
  135. package/dist/2991.index.js +0 -131
  136. package/dist/3063.index.js +0 -833
  137. package/dist/3075.index.js +0 -227
  138. package/dist/3121.index.js +0 -297
  139. package/dist/3314.index.js +0 -263
  140. package/dist/3480.index.js +0 -1822
  141. package/dist/3481.index.js +0 -24
  142. package/dist/3506.index.js +0 -268
  143. package/dist/3533.index.js +0 -591
  144. package/dist/3653.index.js +0 -450
  145. package/dist/3740.index.js +0 -722
  146. package/dist/3822.index.js +0 -131
  147. package/dist/3868.index.js +0 -292
  148. package/dist/3918.index.js +0 -192
  149. package/dist/4090.index.js +0 -288
  150. package/dist/4113.index.js +0 -1286
  151. package/dist/4121.index.js +0 -1190
  152. package/dist/4142.index.js +0 -690
  153. package/dist/4168.index.js +0 -509
  154. package/dist/4281.index.js +0 -993
  155. package/dist/4336.index.js +0 -436
  156. package/dist/4387.index.js +0 -532
  157. package/dist/4413.index.js +0 -302
  158. package/dist/4476.index.js +0 -4510
  159. package/dist/4491.index.js +0 -133
  160. package/dist/4538.index.js +0 -185
  161. package/dist/4684.index.js +0 -319
  162. package/dist/4740.index.js +0 -165
  163. package/dist/4780.index.js +0 -209
  164. package/dist/4802.index.js +0 -225
  165. package/dist/4812.index.js +0 -379
  166. package/dist/4835.index.js +0 -126
  167. package/dist/494.index.js +0 -226
  168. package/dist/4991.index.js +0 -549
  169. package/dist/5199.index.js +0 -305
  170. package/dist/5216.index.js +0 -109
  171. package/dist/5439.index.js +0 -301
  172. package/dist/5443.index.js +0 -97
  173. package/dist/5496.index.js +0 -377
  174. package/dist/5738.index.js +0 -240
  175. package/dist/5801.index.js +0 -588
  176. package/dist/5817.index.js +0 -714
  177. package/dist/5858.index.js +0 -223
  178. package/dist/5933.index.js +0 -143
  179. package/dist/601.index.js +0 -23
  180. package/dist/6173.index.js +0 -273
  181. package/dist/6291.index.js +0 -1871
  182. package/dist/6312.index.js +0 -666
  183. package/dist/634.index.js +0 -16182
  184. package/dist/644.index.js +0 -87
  185. package/dist/6508.index.js +0 -283
  186. package/dist/6560.index.js +0 -645
  187. package/dist/6576.index.js +0 -422
  188. package/dist/6579.index.js +0 -271
  189. package/dist/6655.index.js +0 -3221
  190. package/dist/6745.index.js +0 -305
  191. package/dist/6818.index.js +0 -21642
  192. package/dist/6932.index.js +0 -1190
  193. package/dist/6972.index.js +0 -167
  194. package/dist/6995.index.js +0 -163
  195. package/dist/7249.index.js +0 -191
  196. package/dist/7372.index.js +0 -1197
  197. package/dist/7546.index.js +0 -541
  198. package/dist/76.index.js +0 -448
  199. package/dist/7879.index.js +0 -163
  200. package/dist/8025.index.js +0 -130
  201. package/dist/8054.index.js +0 -248
  202. package/dist/808.index.js +0 -1330
  203. package/dist/8097.index.js +0 -201
  204. package/dist/8126.index.js +0 -191
  205. package/dist/8178.index.js +0 -78
  206. package/dist/8382.index.js +0 -93
  207. package/dist/8408.index.js +0 -261
  208. package/dist/8540.index.js +0 -174
  209. package/dist/8635.index.js +0 -802
  210. package/dist/8688.index.js +0 -410
  211. package/dist/8699.index.js +0 -779
  212. package/dist/8891.index.js +0 -457
  213. package/dist/8916.index.js +0 -885
  214. package/dist/9146.index.js +0 -170
  215. package/dist/9168.index.js +0 -841
  216. package/dist/9292.index.js +0 -369
  217. package/dist/9422.index.js +0 -866
  218. package/dist/9440.index.js +0 -453
  219. package/dist/9479.index.js +0 -152
  220. package/dist/9481.index.js +0 -220
  221. package/dist/9539.index.js +0 -100
  222. package/dist/9547.index.js +0 -103
  223. package/dist/9692.index.js +0 -90
  224. package/dist/9800.index.js +0 -462
  225. package/dist/9899.index.js +0 -138
  226. package/dist/9981.index.js +0 -206
  227. package/dist/999.index.js +0 -117
  228. package/dist/9999.index.js +0 -1698
@@ -1,302 +0,0 @@
1
- export const id = 4413;
2
- export const ids = [4413];
3
- export const modules = {
4
-
5
- /***/ 14413:
6
- /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
7
-
8
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
9
- /* harmony export */ buildPolicyBundle: () => (/* binding */ buildPolicyBundle),
10
- /* harmony export */ ensurePolicyKeyPair: () => (/* binding */ ensurePolicyKeyPair),
11
- /* harmony export */ loadPolicyBundles: () => (/* binding */ loadPolicyBundles),
12
- /* harmony export */ loadPolicyPublicKey: () => (/* binding */ loadPolicyPublicKey),
13
- /* harmony export */ rE: () => (/* binding */ computePolicyDrift),
14
- /* harmony export */ resolveEffectivePolicy: () => (/* binding */ resolveEffectivePolicy),
15
- /* harmony export */ signPolicyBundle: () => (/* binding */ signPolicyBundle)
16
- /* harmony export */ });
17
- /* unused harmony exports POLICY_BUNDLE_SCHEMA, SCOPES, canonicalPolicyBytes, verifyPolicyBundle */
18
- /* harmony import */ var node_fs__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(73024);
19
- /* harmony import */ var node_os__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(48161);
20
- /* harmony import */ var node_path__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(76760);
21
- /* harmony import */ var node_crypto__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(77598);
22
- /* harmony import */ var _evidence_bundle_js__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(32876);
23
- /* harmony import */ var _state_dir_js__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(69259);
24
- // Signed, portable policy bundles (assurance-hardening PRD FR-1001).
25
- //
26
- // "Support signed portable policy bundles with organization, repository, and
27
- // environment inheritance" | "Tampered or expired policy is rejected;
28
- // effective policy is explainable."
29
- //
30
- // WHY REUSE evidence-bundle.js's Ed25519 SCHEME, NOT integrity.js's HMAC
31
- // -------------------------------------------------------------------------
32
- // A policy bundle is authored by one party (typically a central security
33
- // team, "the organization") and distributed to many repositories that never
34
- // had the signing key. integrity.js's per-install symmetric HMAC is
35
- // tamper-evidence for the SAME install that wrote it — a different install
36
- // verifying a bundle it didn't sign is exactly the case that scheme cannot
37
- // serve. evidence-bundle.js already solved this for findings: Ed25519,
38
- // verify with only the public key. This module reuses that primitive
39
- // directly (canonicalisation shape, allowlist-signed-fields discipline,
40
- // exclusive-create key generation) rather than inventing a third signing
41
- // mechanism, per this session's own established "survey before building a
42
- // parallel primitive" practice (see D-0023/D-0025 for the same discipline
43
- // applied to identity and exception mechanisms respectively).
44
- //
45
- // EA-03 LESSON, APPLIED HERE FROM THE START
46
- // -------------------------------------------------------------------------
47
- // evidence-bundle.js's own history (see its EA-03 comment) is the reason
48
- // `canonicalPolicyBytes` signs an explicit ALLOWLIST and `verifyPolicyBundle`
49
- // REJECTS any top-level key outside it: a bundle with a field stapled on
50
- // after signing must fail verification, not silently pass with the addition
51
- // unverified. Getting this right on the first pass — rather than discovering
52
- // the gap the way evidence-bundle.js did — is the entire point of writing it
53
- // down here.
54
- //
55
- // SCOPE — WHAT THIS MODULE DOES NOT DO
56
- // -------------------------------------------------------------------------
57
- // It does not invent a policy SCHEMA — `policy` is an opaque object; whatever
58
- // keys a bundle carries (egress rules, severity floors, approved providers,
59
- // anything else this repo already treats as policy) are merged the same way.
60
- // It does not implement deep/recursive merging — inheritance is shallow,
61
- // top-level-key override (organization -> repository -> environment, most
62
- // specific wins per key), matching how every other flat policy config file
63
- // in this repo (egress-policy.yml, compliance-severity-policy.json, …) is
64
- // already shaped. A future consumer needing nested-key inheritance is new,
65
- // separate scope, not assumed here.
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
- const POLICY_BUNDLE_SCHEMA = 'agentic-security/policy-bundle@1';
75
-
76
- const SCOPES = ['organization', 'repository', 'environment'];
77
- // Inheritance order — later entries override earlier ones, per top-level key.
78
- const INHERITANCE_ORDER = ['organization', 'repository', 'environment'];
79
-
80
- function keyDir() {
81
- const xdg = process.env.XDG_CONFIG_HOME || node_path__WEBPACK_IMPORTED_MODULE_2__.join(node_os__WEBPACK_IMPORTED_MODULE_1__.homedir(), '.config');
82
- return node_path__WEBPACK_IMPORTED_MODULE_2__.join(xdg, 'agentic-security', 'policy-bundles');
83
- }
84
-
85
- /** A SEPARATE Ed25519 keypair from evidence-bundle.js's finding-attestation
86
- * key — signing "this policy is authentic" and "this finding really came
87
- * from a scan run by me" are different trust domains and must not share a
88
- * key. Reuses evidence-bundle.js's hardened, race-safe generation logic
89
- * directly (its exclusive-create collision handling is the kind of code
90
- * worth NOT re-implementing) rather than duplicating it — the separation
91
- * that matters is the DIRECTORY (`policy-bundles/`, isolated from
92
- * evidence-bundle.js's own key dir), not the leaf filenames, which are
93
- * `attest-key.pem`/`attest-key.pub.pem` regardless of caller (a
94
- * evidence-bundle.js internal, not overridable here) — a readability wart,
95
- * not a security one, since the directory is what prevents key reuse. */
96
- function ensurePolicyKeyPair(dir = keyDir()) {
97
- return (0,_evidence_bundle_js__WEBPACK_IMPORTED_MODULE_4__.ensureKeyPair)(dir);
98
- }
99
-
100
- /**
101
- * Deterministic JSON, keys sorted at every level — identical algorithm to
102
- * evidence-bundle.js's canonicalJson (duplicated rather than imported: it is
103
- * a pure, three-line function, and importing it would couple this module's
104
- * signature format to evidence-bundle.js's internals for no real benefit).
105
- */
106
- function canonicalJson(value) {
107
- if (value === null || typeof value !== 'object') return JSON.stringify(value ?? null);
108
- if (Array.isArray(value)) return `[${value.map(canonicalJson).join(',')}]`;
109
- const keys = Object.keys(value).sort();
110
- return `{${keys.map(k => `${JSON.stringify(k)}:${canonicalJson(value[k])}`).join(',')}}`;
111
- }
112
-
113
- // The complete set of fields a legitimately-built bundle carries, BEFORE
114
- // signing. This is the signed allowlist AND (in verifyPolicyBundle) the
115
- // complete set of keys a signed bundle is permitted to have alongside
116
- // `signature` — see the EA-03 note above for why both matter.
117
- const BUNDLE_FIELDS = ['schema', 'scope', 'policy', 'issuedAt', 'expiresAt'];
118
- const BUNDLE_TOP_LEVEL_KEYS = new Set([...BUNDLE_FIELDS, 'signature']);
119
-
120
- function canonicalPolicyBytes(bundle) {
121
- const signed = {};
122
- for (const k of BUNDLE_FIELDS) signed[k] = bundle[k] ?? null;
123
- return Buffer.from(canonicalJson(signed), 'utf8');
124
- }
125
-
126
- /**
127
- * Build an unsigned bundle. `policy` is caller-supplied and opaque — this
128
- * module does not validate its shape, only its provenance and freshness.
129
- */
130
- function buildPolicyBundle(scope, policy, { issuedAt, expiresAt } = {}) {
131
- if (!SCOPES.includes(scope)) return null;
132
- if (!policy || typeof policy !== 'object' || Array.isArray(policy)) return null;
133
- return {
134
- schema: POLICY_BUNDLE_SCHEMA,
135
- scope,
136
- policy,
137
- issuedAt: issuedAt ?? new Date().toISOString(),
138
- expiresAt: expiresAt ?? null,
139
- };
140
- }
141
-
142
- /** Sign a bundle. Returns a new object; the input is not mutated. */
143
- function signPolicyBundle(bundle, privateKeyPem) {
144
- const sig = node_crypto__WEBPACK_IMPORTED_MODULE_3__.sign(null, canonicalPolicyBytes(bundle), privateKeyPem);
145
- return {
146
- ...bundle,
147
- signature: { algorithm: 'ed25519', canonicalisation: POLICY_BUNDLE_SCHEMA, value: sig.toString('base64') },
148
- };
149
- }
150
-
151
- /**
152
- * Verify a bundle: signature authenticity AND freshness. Never throws — a
153
- * malformed or tampered bundle from an untrusted source (a repo checking out
154
- * whatever an org distributed) is an expected input, not exceptional.
155
- *
156
- * @returns {{ok: boolean, reason: string|null}}
157
- */
158
- function verifyPolicyBundle(bundle, publicKeyPem, { now = Date.now() } = {}) {
159
- if (!bundle || typeof bundle !== 'object') return { ok: false, reason: 'bundle is not an object' };
160
- if (bundle.schema !== POLICY_BUNDLE_SCHEMA) return { ok: false, reason: `unrecognised schema: ${bundle.schema}` };
161
- if (!SCOPES.includes(bundle.scope)) return { ok: false, reason: `unrecognised scope: ${bundle.scope}` };
162
- const unknownKeys = Object.keys(bundle).filter(k => !BUNDLE_TOP_LEVEL_KEYS.has(k));
163
- if (unknownKeys.length) {
164
- return { ok: false, reason: `unrecognised top-level key(s) not covered by the signature: ${unknownKeys.join(', ')}` };
165
- }
166
- const sig = bundle.signature;
167
- if (!sig?.value) return { ok: false, reason: 'bundle is unsigned' };
168
- if (sig.algorithm !== 'ed25519') return { ok: false, reason: `unsupported algorithm: ${sig.algorithm}` };
169
- if (!publicKeyPem) return { ok: false, reason: 'no public key supplied' };
170
- let sigOk = false;
171
- try {
172
- sigOk = node_crypto__WEBPACK_IMPORTED_MODULE_3__.verify(null, canonicalPolicyBytes(bundle), publicKeyPem, Buffer.from(sig.value, 'base64'));
173
- } catch (e) {
174
- return { ok: false, reason: `verification error: ${e.message}` };
175
- }
176
- if (!sigOk) return { ok: false, reason: 'signature does not match the bundle contents — it was modified after signing' };
177
- if (bundle.expiresAt) {
178
- const exp = Date.parse(bundle.expiresAt);
179
- if (!Number.isFinite(exp)) return { ok: false, reason: `expiresAt is not a valid date: ${bundle.expiresAt}` };
180
- if (exp < now) return { ok: false, reason: `policy expired on ${bundle.expiresAt}` };
181
- }
182
- return { ok: true, reason: null };
183
- }
184
-
185
- /**
186
- * Merge verified bundles into one effective policy, in inheritance order
187
- * (organization -> repository -> environment; most specific wins per key).
188
- * A tampered or expired bundle is EXCLUDED from the merge (rejected, not
189
- * silently dropped — its scope and reason are reported so a rejection is
190
- * visible, not just absent). `provenance[key]` names which scope's bundle
191
- * last set that key — the "effective policy is explainable" half of the
192
- * acceptance criterion.
193
- *
194
- * @param {Array<{scope: string, bundle: object}>} entries
195
- * @param {string} publicKeyPem
196
- * @returns {{effective: object, provenance: Record<string,string>,
197
- * accepted: string[], rejected: Array<{scope:string, reason:string}>}}
198
- */
199
- function resolveEffectivePolicy(entries, publicKeyPem, { now = Date.now() } = {}) {
200
- const byScope = new Map();
201
- for (const e of entries || []) {
202
- if (e && SCOPES.includes(e.scope)) byScope.set(e.scope, e.bundle);
203
- }
204
- const effective = {};
205
- const provenance = {};
206
- const accepted = [];
207
- const rejected = [];
208
- for (const scope of INHERITANCE_ORDER) {
209
- const bundle = byScope.get(scope);
210
- if (!bundle) continue;
211
- const v = verifyPolicyBundle(bundle, publicKeyPem, { now });
212
- if (!v.ok) { rejected.push({ scope, reason: v.reason }); continue; }
213
- accepted.push(scope);
214
- for (const [k, val] of Object.entries(bundle.policy)) {
215
- effective[k] = val;
216
- provenance[k] = scope;
217
- }
218
- }
219
- return { effective, provenance, accepted, rejected };
220
- }
221
-
222
- /**
223
- * Load whichever policy bundle files exist under
224
- * `.agentic-security/policy-bundles/{organization,repository,environment}.json`.
225
- * Degrades gracefully — a missing directory or missing/malformed individual
226
- * file is simply absent from the result, never thrown. Read-first-in-try/
227
- * catch throughout (no existsSync-then-readFileSync — D-0012/D-0022).
228
- */
229
- function loadPolicyBundles(scanRoot) {
230
- if (!scanRoot) return [];
231
- const entries = [];
232
- for (const scope of SCOPES) {
233
- let fp;
234
- try { fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_5__.statePath)(scanRoot, 'policy-bundles', `${scope}.json`); } catch { continue; }
235
- let raw;
236
- try { raw = node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8'); } catch { continue; }
237
- try {
238
- const bundle = JSON.parse(raw);
239
- if (bundle && typeof bundle === 'object') entries.push({ scope, bundle });
240
- } catch { /* malformed — skip, do not throw */ }
241
- }
242
- return entries;
243
- }
244
-
245
- /**
246
- * Read the operator-supplied public key an org distributes alongside its
247
- * bundles, from `.agentic-security/policy-bundle-public-key.pem`. Returns
248
- * null (not a throw) if absent or unreadable — resolution then rejects
249
- * every bundle for lack of a key, same as any other "no public key
250
- * supplied" case in verifyPolicyBundle.
251
- */
252
- function loadPolicyPublicKey(scanRoot) {
253
- if (!scanRoot) return null;
254
- let fp;
255
- try { fp = (0,_state_dir_js__WEBPACK_IMPORTED_MODULE_5__.statePath)(scanRoot, 'policy-bundle-public-key.pem'); } catch { return null; }
256
- try { return node_fs__WEBPACK_IMPORTED_MODULE_0__.readFileSync(fp, 'utf8'); } catch { return null; }
257
- }
258
-
259
- /**
260
- * FR-1006 ("policy drift"): does this repository's LOCALLY resolved effective
261
- * policy diverge from what the organization-scope bundle alone specifies —
262
- * and is anything the repo is relying on actually being rejected (tampered
263
- * or expired) rather than enforced? Both are GOVERNANCE gaps, distinct from
264
- * risk findings, which is the exact distinction FR-1006's acceptance
265
- * criterion asks fleet output to make.
266
- *
267
- * No bundles configured at all is a no-op (`null`), matching this session's
268
- * established convention: drift can only be reported relative to a
269
- * baseline, and a repo with no organization bundle has no baseline to drift
270
- * from.
271
- *
272
- * @returns {{accepted: string[], rejected: Array<{scope,reason}>,
273
- * overrides: Array<{key, organizationValue, effectiveValue, overriddenBy}>}
274
- * | null}
275
- */
276
- function computePolicyDrift(scanRoot) {
277
- const entries = loadPolicyBundles(scanRoot);
278
- if (!entries.length) return null;
279
- const publicKeyPem = loadPolicyPublicKey(scanRoot);
280
- const { effective, provenance, accepted, rejected } = resolveEffectivePolicy(entries, publicKeyPem);
281
-
282
- const overrides = [];
283
- const orgEntry = entries.find(e => e.scope === 'organization');
284
- if (orgEntry && accepted.includes('organization')) {
285
- for (const [key, organizationValue] of Object.entries(orgEntry.bundle.policy || {})) {
286
- const overriddenBy = provenance[key];
287
- if (overriddenBy && overriddenBy !== 'organization' && !_deepEqual(effective[key], organizationValue)) {
288
- overrides.push({ key, organizationValue, effectiveValue: effective[key], overriddenBy });
289
- }
290
- }
291
- }
292
- return { accepted, rejected, overrides };
293
- }
294
-
295
- function _deepEqual(a, b) {
296
- return JSON.stringify(a) === JSON.stringify(b);
297
- }
298
-
299
-
300
- /***/ })
301
-
302
- };