@intentius/chant 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (433) hide show
  1. package/dist/attrref.d.ts +35 -0
  2. package/dist/attrref.d.ts.map +1 -0
  3. package/dist/audit/catalog.d.ts +65 -0
  4. package/dist/audit/catalog.d.ts.map +1 -0
  5. package/dist/audit/core.d.ts +76 -0
  6. package/dist/audit/core.d.ts.map +1 -0
  7. package/dist/audit/discover.d.ts +76 -0
  8. package/dist/audit/discover.d.ts.map +1 -0
  9. package/dist/audit/fetch.d.ts +90 -0
  10. package/dist/audit/fetch.d.ts.map +1 -0
  11. package/dist/audit/proof.d.ts +53 -0
  12. package/dist/audit/proof.d.ts.map +1 -0
  13. package/dist/audit/report-html.d.ts +32 -0
  14. package/dist/audit/report-html.d.ts.map +1 -0
  15. package/dist/audit/report-model.d.ts +119 -0
  16. package/dist/audit/report-model.d.ts.map +1 -0
  17. package/dist/audit/report.d.ts +31 -0
  18. package/dist/audit/report.d.ts.map +1 -0
  19. package/dist/audit/rules-doc.d.ts +11 -0
  20. package/dist/audit/rules-doc.d.ts.map +1 -0
  21. package/dist/build.d.ts +120 -0
  22. package/dist/build.d.ts.map +1 -0
  23. package/dist/builder.d.ts +56 -0
  24. package/dist/builder.d.ts.map +1 -0
  25. package/dist/child-project.d.ts +34 -0
  26. package/dist/child-project.d.ts.map +1 -0
  27. package/dist/cli/commands/audit.d.ts +60 -0
  28. package/dist/cli/commands/audit.d.ts.map +1 -0
  29. package/dist/cli/commands/build.d.ts +70 -0
  30. package/dist/cli/commands/build.d.ts.map +1 -0
  31. package/dist/cli/commands/check-lexicon.d.ts +19 -0
  32. package/dist/cli/commands/check-lexicon.d.ts.map +1 -0
  33. package/dist/cli/commands/describe.d.ts +39 -0
  34. package/dist/cli/commands/describe.d.ts.map +1 -0
  35. package/dist/cli/commands/diff.d.ts +32 -0
  36. package/dist/cli/commands/diff.d.ts.map +1 -0
  37. package/dist/cli/commands/doctor.d.ts +11 -0
  38. package/dist/cli/commands/doctor.d.ts.map +1 -0
  39. package/dist/cli/commands/import.d.ts +68 -0
  40. package/dist/cli/commands/import.d.ts.map +1 -0
  41. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +9 -0
  42. package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -0
  43. package/dist/cli/commands/init-lexicon/templates/docs.d.ts +9 -0
  44. package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -0
  45. package/dist/cli/commands/init-lexicon/templates/examples.d.ts +8 -0
  46. package/dist/cli/commands/init-lexicon/templates/examples.d.ts.map +1 -0
  47. package/dist/cli/commands/init-lexicon/templates/lint.d.ts +8 -0
  48. package/dist/cli/commands/init-lexicon/templates/lint.d.ts.map +1 -0
  49. package/dist/cli/commands/init-lexicon/templates/lsp.d.ts +6 -0
  50. package/dist/cli/commands/init-lexicon/templates/lsp.d.ts.map +1 -0
  51. package/dist/cli/commands/init-lexicon/templates/plugin.d.ts +12 -0
  52. package/dist/cli/commands/init-lexicon/templates/plugin.d.ts.map +1 -0
  53. package/dist/cli/commands/init-lexicon/templates/project.d.ts +19 -0
  54. package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -0
  55. package/dist/cli/commands/init-lexicon/templates/spec.d.ts +6 -0
  56. package/dist/cli/commands/init-lexicon/templates/spec.d.ts.map +1 -0
  57. package/dist/cli/commands/init-lexicon/templates/tests.d.ts +12 -0
  58. package/dist/cli/commands/init-lexicon/templates/tests.d.ts.map +1 -0
  59. package/dist/cli/commands/init-lexicon.d.ts +37 -0
  60. package/dist/cli/commands/init-lexicon.d.ts.map +1 -0
  61. package/dist/cli/commands/init.d.ts +48 -0
  62. package/dist/cli/commands/init.d.ts.map +1 -0
  63. package/dist/cli/commands/licenses.d.ts +23 -0
  64. package/dist/cli/commands/licenses.d.ts.map +1 -0
  65. package/dist/cli/commands/lint.d.ts +52 -0
  66. package/dist/cli/commands/lint.d.ts.map +1 -0
  67. package/dist/cli/commands/list.d.ts +38 -0
  68. package/dist/cli/commands/list.d.ts.map +1 -0
  69. package/dist/cli/commands/migrate.d.ts +51 -0
  70. package/dist/cli/commands/migrate.d.ts.map +1 -0
  71. package/dist/cli/commands/onboard.d.ts +19 -0
  72. package/dist/cli/commands/onboard.d.ts.map +1 -0
  73. package/dist/cli/commands/update.d.ts +29 -0
  74. package/dist/cli/commands/update.d.ts.map +1 -0
  75. package/dist/cli/commands/vendor.d.ts +84 -0
  76. package/dist/cli/commands/vendor.d.ts.map +1 -0
  77. package/dist/cli/conflict-check.d.ts +18 -0
  78. package/dist/cli/conflict-check.d.ts.map +1 -0
  79. package/dist/cli/debug.d.ts +5 -0
  80. package/dist/cli/debug.d.ts.map +1 -0
  81. package/dist/cli/format.d.ts +36 -0
  82. package/dist/cli/format.d.ts.map +1 -0
  83. package/dist/cli/handlers/build.d.ts +3 -0
  84. package/dist/cli/handlers/build.d.ts.map +1 -0
  85. package/dist/cli/handlers/dev.d.ts +7 -0
  86. package/dist/cli/handlers/dev.d.ts.map +1 -0
  87. package/dist/cli/handlers/graph.d.ts +8 -0
  88. package/dist/cli/handlers/graph.d.ts.map +1 -0
  89. package/dist/cli/handlers/init.d.ts +4 -0
  90. package/dist/cli/handlers/init.d.ts.map +1 -0
  91. package/dist/cli/handlers/lifecycle.d.ts +39 -0
  92. package/dist/cli/handlers/lifecycle.d.ts.map +1 -0
  93. package/dist/cli/handlers/lint.d.ts +3 -0
  94. package/dist/cli/handlers/lint.d.ts.map +1 -0
  95. package/dist/cli/handlers/migrate.d.ts +3 -0
  96. package/dist/cli/handlers/migrate.d.ts.map +1 -0
  97. package/dist/cli/handlers/misc.d.ts +8 -0
  98. package/dist/cli/handlers/misc.d.ts.map +1 -0
  99. package/dist/cli/handlers/run-client.d.ts +107 -0
  100. package/dist/cli/handlers/run-client.d.ts.map +1 -0
  101. package/dist/cli/handlers/run-report.d.ts +5 -0
  102. package/dist/cli/handlers/run-report.d.ts.map +1 -0
  103. package/dist/cli/handlers/run.d.ts +27 -0
  104. package/dist/cli/handlers/run.d.ts.map +1 -0
  105. package/dist/cli/handlers/serve.d.ts +5 -0
  106. package/dist/cli/handlers/serve.d.ts.map +1 -0
  107. package/dist/cli/handlers/vendor.d.ts +9 -0
  108. package/dist/cli/handlers/vendor.d.ts.map +1 -0
  109. package/dist/cli/index.d.ts +4 -0
  110. package/dist/cli/index.d.ts.map +1 -0
  111. package/dist/cli/lsp/capabilities.d.ts +21 -0
  112. package/dist/cli/lsp/capabilities.d.ts.map +1 -0
  113. package/dist/cli/lsp/diagnostics.d.ts +30 -0
  114. package/dist/cli/lsp/diagnostics.d.ts.map +1 -0
  115. package/dist/cli/lsp/server.d.ts +106 -0
  116. package/dist/cli/lsp/server.d.ts.map +1 -0
  117. package/dist/cli/main.d.ts +7 -0
  118. package/dist/cli/main.d.ts.map +1 -0
  119. package/dist/cli/mcp/lifecycle-tools.d.ts +15 -0
  120. package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -0
  121. package/dist/cli/mcp/op-tools.d.ts +7 -0
  122. package/dist/cli/mcp/op-tools.d.ts.map +1 -0
  123. package/dist/cli/mcp/resource-handlers.d.ts +28 -0
  124. package/dist/cli/mcp/resource-handlers.d.ts.map +1 -0
  125. package/dist/cli/mcp/resources/context.d.ts +6 -0
  126. package/dist/cli/mcp/resources/context.d.ts.map +1 -0
  127. package/dist/cli/mcp/server.d.ts +44 -0
  128. package/dist/cli/mcp/server.d.ts.map +1 -0
  129. package/dist/cli/mcp/tools/build.d.ts +31 -0
  130. package/dist/cli/mcp/tools/build.d.ts.map +1 -0
  131. package/dist/cli/mcp/tools/explain.d.ts +27 -0
  132. package/dist/cli/mcp/tools/explain.d.ts.map +1 -0
  133. package/dist/cli/mcp/tools/import.d.ts +26 -0
  134. package/dist/cli/mcp/tools/import.d.ts.map +1 -0
  135. package/dist/cli/mcp/tools/lint.d.ts +26 -0
  136. package/dist/cli/mcp/tools/lint.d.ts.map +1 -0
  137. package/dist/cli/mcp/tools/scaffold.d.ts +31 -0
  138. package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -0
  139. package/dist/cli/mcp/tools/search.d.ts +31 -0
  140. package/dist/cli/mcp/tools/search.d.ts.map +1 -0
  141. package/dist/cli/mcp/types.d.ts +42 -0
  142. package/dist/cli/mcp/types.d.ts.map +1 -0
  143. package/dist/cli/plugins.d.ts +21 -0
  144. package/dist/cli/plugins.d.ts.map +1 -0
  145. package/dist/cli/registry.d.ts +100 -0
  146. package/dist/cli/registry.d.ts.map +1 -0
  147. package/dist/cli/reporters/stylish.d.ts +26 -0
  148. package/dist/cli/reporters/stylish.d.ts.map +1 -0
  149. package/dist/cli/watch.d.ts +29 -0
  150. package/dist/cli/watch.d.ts.map +1 -0
  151. package/dist/codegen/bound-property-types.d.ts +38 -0
  152. package/dist/codegen/bound-property-types.d.ts.map +1 -0
  153. package/dist/codegen/coverage.d.ts +57 -0
  154. package/dist/codegen/coverage.d.ts.map +1 -0
  155. package/dist/codegen/docs-file-markers.d.ts +16 -0
  156. package/dist/codegen/docs-file-markers.d.ts.map +1 -0
  157. package/dist/codegen/docs-rule-scanning.d.ts +14 -0
  158. package/dist/codegen/docs-rule-scanning.d.ts.map +1 -0
  159. package/dist/codegen/docs-rules.d.ts +17 -0
  160. package/dist/codegen/docs-rules.d.ts.map +1 -0
  161. package/dist/codegen/docs-sections.d.ts +11 -0
  162. package/dist/codegen/docs-sections.d.ts.map +1 -0
  163. package/dist/codegen/docs-sidebar.d.ts +6 -0
  164. package/dist/codegen/docs-sidebar.d.ts.map +1 -0
  165. package/dist/codegen/docs-types.d.ts +84 -0
  166. package/dist/codegen/docs-types.d.ts.map +1 -0
  167. package/dist/codegen/docs.d.ts +28 -0
  168. package/dist/codegen/docs.d.ts.map +1 -0
  169. package/dist/codegen/fetch.d.ts +76 -0
  170. package/dist/codegen/fetch.d.ts.map +1 -0
  171. package/dist/codegen/generate-post-synth-barrel.d.ts +32 -0
  172. package/dist/codegen/generate-post-synth-barrel.d.ts.map +1 -0
  173. package/dist/codegen/generate-registry.d.ts +40 -0
  174. package/dist/codegen/generate-registry.d.ts.map +1 -0
  175. package/dist/codegen/generate-runtime-index.d.ts +40 -0
  176. package/dist/codegen/generate-runtime-index.d.ts.map +1 -0
  177. package/dist/codegen/generate-typescript.d.ts +48 -0
  178. package/dist/codegen/generate-typescript.d.ts.map +1 -0
  179. package/dist/codegen/generate.d.ts +92 -0
  180. package/dist/codegen/generate.d.ts.map +1 -0
  181. package/dist/codegen/json-patch.d.ts +12 -0
  182. package/dist/codegen/json-patch.d.ts.map +1 -0
  183. package/dist/codegen/json-schema.d.ts +82 -0
  184. package/dist/codegen/json-schema.d.ts.map +1 -0
  185. package/dist/codegen/naming.d.ts +64 -0
  186. package/dist/codegen/naming.d.ts.map +1 -0
  187. package/dist/codegen/package.d.ts +64 -0
  188. package/dist/codegen/package.d.ts.map +1 -0
  189. package/dist/codegen/topo-sort.d.ts +13 -0
  190. package/dist/codegen/topo-sort.d.ts.map +1 -0
  191. package/dist/codegen/typecheck.d.ts +10 -0
  192. package/dist/codegen/typecheck.d.ts.map +1 -0
  193. package/dist/codegen/validate.d.ts +45 -0
  194. package/dist/codegen/validate.d.ts.map +1 -0
  195. package/dist/composite.d.ts +116 -0
  196. package/dist/composite.d.ts.map +1 -0
  197. package/dist/config.d.ts +78 -0
  198. package/dist/config.d.ts.map +1 -0
  199. package/dist/declarable.d.ts +34 -0
  200. package/dist/declarable.d.ts.map +1 -0
  201. package/dist/detectLexicon.d.ts +10 -0
  202. package/dist/detectLexicon.d.ts.map +1 -0
  203. package/dist/discovery/cache.d.ts +36 -0
  204. package/dist/discovery/cache.d.ts.map +1 -0
  205. package/dist/discovery/collect.d.ts +17 -0
  206. package/dist/discovery/collect.d.ts.map +1 -0
  207. package/dist/discovery/cycles.d.ts +23 -0
  208. package/dist/discovery/cycles.d.ts.map +1 -0
  209. package/dist/discovery/files.d.ts +7 -0
  210. package/dist/discovery/files.d.ts.map +1 -0
  211. package/dist/discovery/graph.d.ts +8 -0
  212. package/dist/discovery/graph.d.ts.map +1 -0
  213. package/dist/discovery/import.d.ts +9 -0
  214. package/dist/discovery/import.d.ts.map +1 -0
  215. package/dist/discovery/index.d.ts +25 -0
  216. package/dist/discovery/index.d.ts.map +1 -0
  217. package/dist/discovery/resolve.d.ts +10 -0
  218. package/dist/discovery/resolve.d.ts.map +1 -0
  219. package/dist/errors.d.ts +49 -0
  220. package/dist/errors.d.ts.map +1 -0
  221. package/dist/import/base-parser.d.ts +19 -0
  222. package/dist/import/base-parser.d.ts.map +1 -0
  223. package/dist/import/generator.d.ts +20 -0
  224. package/dist/import/generator.d.ts.map +1 -0
  225. package/dist/import/ir-utils.d.ts +25 -0
  226. package/dist/import/ir-utils.d.ts.map +1 -0
  227. package/dist/import/parser.d.ts +39 -0
  228. package/dist/import/parser.d.ts.map +1 -0
  229. package/dist/index.d.ts +66 -0
  230. package/dist/index.d.ts.map +1 -0
  231. package/dist/intrinsic-interpolation.d.ts +24 -0
  232. package/dist/intrinsic-interpolation.d.ts.map +1 -0
  233. package/dist/intrinsic.d.ts +24 -0
  234. package/dist/intrinsic.d.ts.map +1 -0
  235. package/dist/lexicon-integrity.d.ts +25 -0
  236. package/dist/lexicon-integrity.d.ts.map +1 -0
  237. package/dist/lexicon-manifest.d.ts +9 -0
  238. package/dist/lexicon-manifest.d.ts.map +1 -0
  239. package/dist/lexicon-output.d.ts +66 -0
  240. package/dist/lexicon-output.d.ts.map +1 -0
  241. package/dist/lexicon-plugin-helpers.d.ts +59 -0
  242. package/dist/lexicon-plugin-helpers.d.ts.map +1 -0
  243. package/dist/lexicon-schema.d.ts +82 -0
  244. package/dist/lexicon-schema.d.ts.map +1 -0
  245. package/dist/lexicon.d.ts +383 -0
  246. package/dist/lexicon.d.ts.map +1 -0
  247. package/dist/lifecycle/affected.d.ts +57 -0
  248. package/dist/lifecycle/affected.d.ts.map +1 -0
  249. package/dist/lifecycle/change-set.d.ts +90 -0
  250. package/dist/lifecycle/change-set.d.ts.map +1 -0
  251. package/dist/lifecycle/digest.d.ts +21 -0
  252. package/dist/lifecycle/digest.d.ts.map +1 -0
  253. package/dist/lifecycle/git.d.ts +72 -0
  254. package/dist/lifecycle/git.d.ts.map +1 -0
  255. package/dist/lifecycle/index.d.ts +8 -0
  256. package/dist/lifecycle/index.d.ts.map +1 -0
  257. package/dist/lifecycle/live-diff.d.ts +67 -0
  258. package/dist/lifecycle/live-diff.d.ts.map +1 -0
  259. package/dist/lifecycle/snapshot.d.ts +20 -0
  260. package/dist/lifecycle/snapshot.d.ts.map +1 -0
  261. package/dist/lifecycle/types.d.ts +61 -0
  262. package/dist/lifecycle/types.d.ts.map +1 -0
  263. package/dist/lint/config.d.ts +100 -0
  264. package/dist/lint/config.d.ts.map +1 -0
  265. package/dist/lint/declarative.d.ts +56 -0
  266. package/dist/lint/declarative.d.ts.map +1 -0
  267. package/dist/lint/discover.d.ts +35 -0
  268. package/dist/lint/discover.d.ts.map +1 -0
  269. package/dist/lint/engine.d.ts +21 -0
  270. package/dist/lint/engine.d.ts.map +1 -0
  271. package/dist/lint/named-checks.d.ts +19 -0
  272. package/dist/lint/named-checks.d.ts.map +1 -0
  273. package/dist/lint/parser.d.ts +9 -0
  274. package/dist/lint/parser.d.ts.map +1 -0
  275. package/dist/lint/policy.d.ts +22 -0
  276. package/dist/lint/policy.d.ts.map +1 -0
  277. package/dist/lint/post-synth.d.ts +69 -0
  278. package/dist/lint/post-synth.d.ts.map +1 -0
  279. package/dist/lint/presets/strict.json +19 -0
  280. package/dist/lint/rule-loader.d.ts +16 -0
  281. package/dist/lint/rule-loader.d.ts.map +1 -0
  282. package/dist/lint/rule-registry.d.ts +37 -0
  283. package/dist/lint/rule-registry.d.ts.map +1 -0
  284. package/dist/lint/rule.d.ts +82 -0
  285. package/dist/lint/rule.d.ts.map +1 -0
  286. package/dist/lint/rules/composite-scope.d.ts +18 -0
  287. package/dist/lint/rules/composite-scope.d.ts.map +1 -0
  288. package/dist/lint/rules/cor017-composite-name-match.d.ts +3 -0
  289. package/dist/lint/rules/cor017-composite-name-match.d.ts.map +1 -0
  290. package/dist/lint/rules/cor018-composite-prefer-lexicon-type.d.ts +3 -0
  291. package/dist/lint/rules/cor018-composite-prefer-lexicon-type.d.ts.map +1 -0
  292. package/dist/lint/rules/declarable-naming-convention.d.ts +3 -0
  293. package/dist/lint/rules/declarable-naming-convention.d.ts.map +1 -0
  294. package/dist/lint/rules/evl001-non-literal-expression.d.ts +3 -0
  295. package/dist/lint/rules/evl001-non-literal-expression.d.ts.map +1 -0
  296. package/dist/lint/rules/evl002-control-flow-resource.d.ts +3 -0
  297. package/dist/lint/rules/evl002-control-flow-resource.d.ts.map +1 -0
  298. package/dist/lint/rules/evl003-dynamic-property-access.d.ts +3 -0
  299. package/dist/lint/rules/evl003-dynamic-property-access.d.ts.map +1 -0
  300. package/dist/lint/rules/evl004-spread-non-const.d.ts +3 -0
  301. package/dist/lint/rules/evl004-spread-non-const.d.ts.map +1 -0
  302. package/dist/lint/rules/evl005-resource-block-body.d.ts +3 -0
  303. package/dist/lint/rules/evl005-resource-block-body.d.ts.map +1 -0
  304. package/dist/lint/rules/evl007-invalid-siblings.d.ts +3 -0
  305. package/dist/lint/rules/evl007-invalid-siblings.d.ts.map +1 -0
  306. package/dist/lint/rules/evl009-composite-no-constant.d.ts +3 -0
  307. package/dist/lint/rules/evl009-composite-no-constant.d.ts.map +1 -0
  308. package/dist/lint/rules/evl010-composite-no-transform.d.ts +3 -0
  309. package/dist/lint/rules/evl010-composite-no-transform.d.ts.map +1 -0
  310. package/dist/lint/rules/export-required.d.ts +3 -0
  311. package/dist/lint/rules/export-required.d.ts.map +1 -0
  312. package/dist/lint/rules/file-declarable-limit.d.ts +3 -0
  313. package/dist/lint/rules/file-declarable-limit.d.ts.map +1 -0
  314. package/dist/lint/rules/flat-declarations.d.ts +3 -0
  315. package/dist/lint/rules/flat-declarations.d.ts.map +1 -0
  316. package/dist/lint/rules/index.d.ts +31 -0
  317. package/dist/lint/rules/index.d.ts.map +1 -0
  318. package/dist/lint/rules/no-cyclic-declarable-ref.d.ts +3 -0
  319. package/dist/lint/rules/no-cyclic-declarable-ref.d.ts.map +1 -0
  320. package/dist/lint/rules/no-redundant-type-import.d.ts +10 -0
  321. package/dist/lint/rules/no-redundant-type-import.d.ts.map +1 -0
  322. package/dist/lint/rules/no-redundant-value-cast.d.ts +3 -0
  323. package/dist/lint/rules/no-redundant-value-cast.d.ts.map +1 -0
  324. package/dist/lint/rules/no-string-ref.d.ts +3 -0
  325. package/dist/lint/rules/no-string-ref.d.ts.map +1 -0
  326. package/dist/lint/rules/no-unused-declarable-import.d.ts +3 -0
  327. package/dist/lint/rules/no-unused-declarable-import.d.ts.map +1 -0
  328. package/dist/lint/rules/no-unused-declarable.d.ts +3 -0
  329. package/dist/lint/rules/no-unused-declarable.d.ts.map +1 -0
  330. package/dist/lint/rules/single-concern-file.d.ts +3 -0
  331. package/dist/lint/rules/single-concern-file.d.ts.map +1 -0
  332. package/dist/lint/selectors.d.ts +21 -0
  333. package/dist/lint/selectors.d.ts.map +1 -0
  334. package/dist/lsp/lexicon-providers.d.ts +61 -0
  335. package/dist/lsp/lexicon-providers.d.ts.map +1 -0
  336. package/dist/lsp/types.d.ts +64 -0
  337. package/dist/lsp/types.d.ts.map +1 -0
  338. package/dist/mcp/types.d.ts +21 -0
  339. package/dist/mcp/types.d.ts.map +1 -0
  340. package/dist/op/activity-registry.d.ts +57 -0
  341. package/dist/op/activity-registry.d.ts.map +1 -0
  342. package/dist/op/builders.d.ts +69 -0
  343. package/dist/op/builders.d.ts.map +1 -0
  344. package/dist/op/discover.d.ts +16 -0
  345. package/dist/op/discover.d.ts.map +1 -0
  346. package/dist/op/index.d.ts +11 -0
  347. package/dist/op/index.d.ts.map +1 -0
  348. package/dist/op/local-executor.d.ts +54 -0
  349. package/dist/op/local-executor.d.ts.map +1 -0
  350. package/dist/op/local-output.d.ts +16 -0
  351. package/dist/op/local-output.d.ts.map +1 -0
  352. package/dist/op/resource.d.ts +7 -0
  353. package/dist/op/resource.d.ts.map +1 -0
  354. package/dist/op/types.d.ts +71 -0
  355. package/dist/op/types.d.ts.map +1 -0
  356. package/dist/ownership.d.ts +75 -0
  357. package/dist/ownership.d.ts.map +1 -0
  358. package/dist/project-validation.d.ts +28 -0
  359. package/dist/project-validation.d.ts.map +1 -0
  360. package/dist/provenance.d.ts +26 -0
  361. package/dist/provenance.d.ts.map +1 -0
  362. package/dist/pseudo-parameter.d.ts +32 -0
  363. package/dist/pseudo-parameter.d.ts.map +1 -0
  364. package/dist/resource-attributes.d.ts +17 -0
  365. package/dist/resource-attributes.d.ts.map +1 -0
  366. package/dist/runtime-adapter.d.ts +41 -0
  367. package/dist/runtime-adapter.d.ts.map +1 -0
  368. package/dist/runtime.d.ts +23 -0
  369. package/dist/runtime.d.ts.map +1 -0
  370. package/dist/serializer-walker.d.ts +21 -0
  371. package/dist/serializer-walker.d.ts.map +1 -0
  372. package/dist/serializer.d.ts +64 -0
  373. package/dist/serializer.d.ts.map +1 -0
  374. package/dist/sort.d.ts +8 -0
  375. package/dist/sort.d.ts.map +1 -0
  376. package/dist/stack-output.d.ts +52 -0
  377. package/dist/stack-output.d.ts.map +1 -0
  378. package/dist/toml-emit.d.ts +22 -0
  379. package/dist/toml-emit.d.ts.map +1 -0
  380. package/dist/toml-parse.d.ts +14 -0
  381. package/dist/toml-parse.d.ts.map +1 -0
  382. package/dist/toml-utils.d.ts +20 -0
  383. package/dist/toml-utils.d.ts.map +1 -0
  384. package/dist/toml.d.ts +9 -0
  385. package/dist/toml.d.ts.map +1 -0
  386. package/dist/types.d.ts +47 -0
  387. package/dist/types.d.ts.map +1 -0
  388. package/dist/utils.d.ts +30 -0
  389. package/dist/utils.d.ts.map +1 -0
  390. package/dist/validation.d.ts +42 -0
  391. package/dist/validation.d.ts.map +1 -0
  392. package/dist/yaml.d.ts +42 -0
  393. package/dist/yaml.d.ts.map +1 -0
  394. package/package.json +31 -6
  395. package/src/audit/bundle-decoupling.test.ts +56 -0
  396. package/src/audit/catalog.test.ts +17 -2
  397. package/src/audit/catalog.ts +357 -4
  398. package/src/audit/core.test.ts +15 -0
  399. package/src/audit/core.ts +0 -0
  400. package/src/audit/detect-bundle.test.ts +35 -0
  401. package/src/audit/discover.test.ts +72 -0
  402. package/src/audit/discover.ts +329 -0
  403. package/src/audit/edge-init-safety.test.ts +43 -0
  404. package/src/audit/fetch.test.ts +86 -55
  405. package/src/audit/fetch.ts +118 -87
  406. package/src/audit/remote-discovery.test.ts +80 -0
  407. package/src/audit/report-html.ts +14 -6
  408. package/src/audit/report-model.ts +13 -1
  409. package/src/audit/report.test.ts +3 -1
  410. package/src/audit/report.ts +13 -5
  411. package/src/audit/rules-doc.ts +4 -0
  412. package/src/cli/commands/__fixtures__/audit-aws/stack.yaml +11 -0
  413. package/src/cli/commands/__fixtures__/audit-aws/template.json +7 -0
  414. package/src/cli/commands/__fixtures__/audit-azure/azuredeploy.json +13 -0
  415. package/src/cli/commands/__fixtures__/audit-gcp/bucket.yaml +6 -0
  416. package/src/cli/commands/__fixtures__/audit-gcp/firewall.yaml +9 -0
  417. package/src/cli/commands/__fixtures__/audit-helm/mychart/Chart.yaml +3 -0
  418. package/src/cli/commands/__fixtures__/audit-helm/mychart/templates/deployment.yaml +12 -0
  419. package/src/cli/commands/__fixtures__/audit-helm/mychart/values.yaml +1 -0
  420. package/src/cli/commands/__fixtures__/audit-mixed/.github/workflows/ci.yml +5 -0
  421. package/src/cli/commands/__fixtures__/audit-mixed/Dockerfile +2 -0
  422. package/src/cli/commands/__fixtures__/audit-mixed/cfn/template.json +1 -0
  423. package/src/cli/commands/__fixtures__/audit-mixed/k8s/deploy.yaml +12 -0
  424. package/src/cli/commands/audit.test.ts +80 -14
  425. package/src/cli/commands/audit.ts +28 -126
  426. package/src/codegen/bound-property-types.test.ts +57 -0
  427. package/src/codegen/bound-property-types.ts +110 -0
  428. package/src/codegen/generate-post-synth-barrel.test.ts +37 -0
  429. package/src/codegen/generate-post-synth-barrel.ts +75 -0
  430. package/src/codegen/post-synth-barrel-guards.test.ts +63 -0
  431. package/src/codegen/validate.ts +13 -1
  432. package/src/lint/discover.ts +1 -1
  433. package/src/meta/peer-deps.test.ts +29 -0
@@ -1,21 +1,21 @@
1
1
  /**
2
- * Remote fetch — pull a repo's CI files from a git host so the auditor can run
3
- * on a URL, not just a local path. This is the ONLY audit module that touches
4
- * the network; the core stays pure.
2
+ * Remote fetch — pull a repo's candidate files (all lexicons) from a git host so
3
+ * the auditor can run on a URL, not just a local path. This is the ONLY audit
4
+ * module that touches the network; the core stays pure.
5
5
  *
6
6
  * SSRF posture: only an allowlisted set of hosts is accepted; request URLs are
7
7
  * built from the parsed owner/repo (never a user-controlled host); redirects
8
8
  * are refused; and file count / size / total bytes / time are all capped.
9
9
  */
10
10
 
11
- import type { AuditInput, AuditLexicon } from "./core";
11
+ import type { AuditLexicon } from "./core";
12
12
 
13
13
  export interface FetchOptions {
14
14
  /** Branch/tag/sha; defaults to the repo's default branch. */
15
15
  ref?: string;
16
16
  /** Server-side token (lifts rate limits). Never surfaced to callers. */
17
17
  token?: string;
18
- /** Max number of CI files to fetch (default 50). */
18
+ /** Max number of files to fetch (default 50). */
19
19
  maxFiles?: number;
20
20
  /** Max bytes for a single file; larger files are skipped (default 256 KiB). */
21
21
  maxBytesPerFile?: number;
@@ -87,10 +87,6 @@ function authHeaders(kind: HostKind, token?: string): Record<string, string> {
87
87
  return { "PRIVATE-TOKEN": token };
88
88
  }
89
89
 
90
- function isYaml(name: string): boolean {
91
- return name.endsWith(".yml") || name.endsWith(".yaml");
92
- }
93
-
94
90
  function timeoutSignal(ms: number): AbortSignal | undefined {
95
91
  return typeof AbortSignal !== "undefined" && typeof AbortSignal.timeout === "function"
96
92
  ? AbortSignal.timeout(ms)
@@ -107,7 +103,103 @@ interface ContentsEntry {
107
103
  encoding?: string;
108
104
  }
109
105
 
110
- export async function fetchCiFiles(url: string, opts: FetchOptions = {}): Promise<AuditInput[]> {
106
+ // ── Whole-repo fetch (all lexicons, not just CI) ─────────────────────────────
107
+ // Mirrors the local walk: list the repo tree, keep candidate paths, fetch their
108
+ // contents (capped), and hand the raw {path, content} set to the shared
109
+ // classifier in discover.ts. SSRF posture is unchanged — allowlisted host,
110
+ // URLs built from parsed owner/repo, redirects refused, counts/bytes capped.
111
+
112
+ /** Module-level JSON GET that refuses redirects (reused by the tree-walk). */
113
+ async function getJsonAt(
114
+ url: string,
115
+ headers: Record<string, string>,
116
+ doFetch: typeof fetch,
117
+ timeoutMs: number,
118
+ ): Promise<{ status: number; body: unknown }> {
119
+ let res: Response;
120
+ try {
121
+ res = await doFetch(url, { headers, redirect: "manual", signal: timeoutSignal(timeoutMs) });
122
+ } catch (err) {
123
+ throw new FetchError(`Request failed: ${err instanceof Error ? err.message : String(err)}`);
124
+ }
125
+ if (res.status >= 300 && res.status < 400) throw new FetchError(`Refusing to follow redirect from ${url}`);
126
+ if (res.status === 404) return { status: 404, body: null };
127
+ if (!res.ok) throw new FetchError(`${url} returned ${res.status}`);
128
+ return { status: res.status, body: await res.json() };
129
+ }
130
+
131
+ function projectId(owner: string, repo: string): string {
132
+ return encodeURIComponent(`${owner}/${repo}`);
133
+ }
134
+
135
+ /** The repo's default branch, so the tree request has a concrete ref. */
136
+ async function defaultBranch(host: HostConfig, owner: string, repo: string, doFetch: typeof fetch, headers: Record<string, string>, ms: number): Promise<string> {
137
+ const url =
138
+ host.kind === "gitlab"
139
+ ? `${host.api}/projects/${projectId(owner, repo)}`
140
+ : `${host.api}/repos/${owner}/${repo}`;
141
+ const { body } = await getJsonAt(url, headers, doFetch, ms);
142
+ const branch = (body as { default_branch?: string } | null)?.default_branch;
143
+ return branch && typeof branch === "string" ? branch : "HEAD";
144
+ }
145
+
146
+ interface TreeEntry {
147
+ path: string;
148
+ type: string;
149
+ size?: number;
150
+ }
151
+
152
+ /** List every blob path in the repo (recursive), with size where the host reports it. */
153
+ async function listTree(host: HostConfig, owner: string, repo: string, ref: string, doFetch: typeof fetch, headers: Record<string, string>, ms: number): Promise<TreeEntry[]> {
154
+ if (host.kind === "gitlab") {
155
+ const out: TreeEntry[] = [];
156
+ const MAX_PAGES = 20; // 20 * 100 = 2000 entries — bounded; candidate filter + caps trim further
157
+ for (let page = 1; page <= MAX_PAGES; page++) {
158
+ const url = `${host.api}/projects/${projectId(owner, repo)}/repository/tree?recursive=true&per_page=100&page=${page}&ref=${encodeURIComponent(ref)}`;
159
+ const { body } = await getJsonAt(url, headers, doFetch, ms);
160
+ if (!Array.isArray(body) || body.length === 0) break;
161
+ for (const e of body as Array<{ path: string; type: string }>) {
162
+ if (e.type === "blob") out.push({ path: e.path, type: "blob" });
163
+ }
164
+ if (body.length < 100) break;
165
+ }
166
+ return out;
167
+ }
168
+ // GitHub / Forgejo (Gitea) share the git/trees recursive API.
169
+ const url = `${host.api}/repos/${owner}/${repo}/git/trees/${encodeURIComponent(ref)}?recursive=1`;
170
+ const { body } = await getJsonAt(url, headers, doFetch, ms);
171
+ const tree = (body as { tree?: TreeEntry[] } | null)?.tree;
172
+ if (!Array.isArray(tree)) return [];
173
+ return tree.filter((e) => e.type === "blob").map((e) => ({ path: e.path, type: "blob", size: e.size }));
174
+ }
175
+
176
+ /** Fetch one file's content per host (base64 contents API, or GitLab raw). */
177
+ async function fetchFileContent(host: HostConfig, owner: string, repo: string, path: string, ref: string, doFetch: typeof fetch, headers: Record<string, string>, ms: number): Promise<string | undefined> {
178
+ if (host.kind === "gitlab") {
179
+ const url = `${host.api}/projects/${projectId(owner, repo)}/repository/files/${encodeURIComponent(path)}/raw?ref=${encodeURIComponent(ref)}`;
180
+ let res: Response;
181
+ try {
182
+ res = await doFetch(url, { headers, redirect: "manual", signal: timeoutSignal(ms) });
183
+ } catch {
184
+ return undefined;
185
+ }
186
+ if (!res.ok) return undefined;
187
+ return await res.text();
188
+ }
189
+ const url = `${host.api}/repos/${owner}/${repo}/contents/${path}?ref=${encodeURIComponent(ref)}`;
190
+ const { body } = await getJsonAt(url, headers, doFetch, ms);
191
+ const file = body as ContentsEntry | null;
192
+ if (!file?.content) return undefined;
193
+ return Buffer.from(file.content, (file.encoding as BufferEncoding) ?? "base64").toString("utf-8");
194
+ }
195
+
196
+ /**
197
+ * Fetch every candidate file in a repo (all lexicons), so a URL audit covers the
198
+ * same ground as a local one. Returns raw {path, content}; classification (which
199
+ * needs the lexicon plugins) is the caller's job via `classifyFiles`.
200
+ */
201
+ export async function fetchRepoFiles(url: string, opts: FetchOptions = {}): Promise<Array<{ path: string; content: string }>> {
202
+ const { isCandidatePath } = await import("./discover");
111
203
  const { host, owner, repo } = parseRepoUrl(url);
112
204
  const doFetch = opts.fetchImpl ?? fetch;
113
205
  const cfg = {
@@ -117,58 +209,24 @@ export async function fetchCiFiles(url: string, opts: FetchOptions = {}): Promis
117
209
  timeoutMs: opts.timeoutMs ?? DEFAULTS.timeoutMs,
118
210
  };
119
211
  const headers = authHeaders(host.kind, opts.token);
212
+ const ref = opts.ref ?? (await defaultBranch(host, owner, repo, doFetch, headers, cfg.timeoutMs));
120
213
 
121
- async function getJson(apiUrl: string): Promise<{ status: number; body: unknown }> {
122
- let res: Response;
123
- try {
124
- res = await doFetch(apiUrl, { headers, redirect: "error", signal: timeoutSignal(cfg.timeoutMs) });
125
- } catch (err) {
126
- throw new FetchError(`Request failed: ${err instanceof Error ? err.message : String(err)}`);
127
- }
128
- if (res.status >= 300 && res.status < 400) {
129
- throw new FetchError(`Refusing to follow redirect from ${apiUrl}`);
130
- }
131
- if (res.status === 404) return { status: 404, body: null };
132
- if (!res.ok) throw new FetchError(`${apiUrl} returned ${res.status}`);
133
- return { status: res.status, body: await res.json() };
134
- }
135
-
136
- if (host.kind === "gitlab") {
137
- return fetchGitlab(host, owner, repo, opts.ref, cfg, doFetch, headers);
138
- }
139
-
140
- // GitHub / Forgejo share the Gitea-style contents API.
141
- const dirs = host.kind === "forgejo" ? [".forgejo/workflows", ".github/workflows"] : [".github/workflows"];
142
- const ref = opts.ref ? `?ref=${encodeURIComponent(opts.ref)}` : "";
143
-
144
- const candidates: ContentsEntry[] = [];
145
- const seen = new Set<string>();
146
- for (const dir of dirs) {
147
- const { body } = await getJson(`${host.api}/repos/${owner}/${repo}/contents/${dir}${ref}`);
148
- if (!Array.isArray(body)) continue;
149
- for (const entry of body as ContentsEntry[]) {
150
- if (entry.type === "file" && isYaml(entry.name) && !seen.has(entry.path)) {
151
- seen.add(entry.path);
152
- candidates.push(entry);
153
- }
154
- }
155
- }
214
+ const tree = await listTree(host, owner, repo, ref, doFetch, headers, cfg.timeoutMs);
215
+ const candidates = tree.filter((e) => isCandidatePath(e.path));
156
216
 
157
- const inputs: AuditInput[] = [];
217
+ const files: Array<{ path: string; content: string }> = [];
158
218
  let total = 0;
159
219
  for (const entry of candidates) {
160
- if (inputs.length >= cfg.maxFiles) break;
161
- if ((entry.size ?? 0) > cfg.maxBytesPerFile) continue; // skip oversize
162
- const { body } = await getJson(`${host.api}/repos/${owner}/${repo}/contents/${entry.path}${ref}`);
163
- const file = body as ContentsEntry | null;
164
- if (!file?.content) continue;
165
- const content = Buffer.from(file.content, (file.encoding as BufferEncoding) ?? "base64").toString("utf-8");
220
+ if (files.length >= cfg.maxFiles) break;
221
+ if ((entry.size ?? 0) > cfg.maxBytesPerFile) continue; // skip oversize when the host reports size
222
+ const content = await fetchFileContent(host, owner, repo, entry.path, ref, doFetch, headers, cfg.timeoutMs);
223
+ if (content === undefined) continue;
166
224
  if (content.length > cfg.maxBytesPerFile) continue;
167
225
  total += content.length;
168
- if (total > cfg.maxTotalBytes) throw new FetchError("Repository CI files exceed the total size cap.");
169
- inputs.push({ path: entry.path, content, lexicon: host.lexicon });
226
+ if (total > cfg.maxTotalBytes) throw new FetchError("Repository files exceed the total size cap.");
227
+ files.push({ path: entry.path, content });
170
228
  }
171
- return inputs;
229
+ return files;
172
230
  }
173
231
 
174
232
  const SHA40 = /^[0-9a-f]{40}$/;
@@ -195,7 +253,7 @@ export async function resolveActionSha(
195
253
  Accept: "application/vnd.github+json",
196
254
  ...(opts.token ? { Authorization: `Bearer ${opts.token}` } : {}),
197
255
  },
198
- redirect: "error",
256
+ redirect: "manual",
199
257
  signal: timeoutSignal(opts.timeoutMs ?? DEFAULTS.timeoutMs),
200
258
  });
201
259
  if (!res.ok) return undefined;
@@ -289,16 +347,16 @@ export async function resolveImageDigest(
289
347
  const manifestUrl = `https://${parsed.registry}/v2/${parsed.repository}/manifests/${encodeURIComponent(parsed.tag)}`;
290
348
 
291
349
  try {
292
- let res = await doFetch(manifestUrl, { headers: { Accept: accept }, redirect: "error", signal: timeoutSignal(ms) });
350
+ let res = await doFetch(manifestUrl, { headers: { Accept: accept }, redirect: "manual", signal: timeoutSignal(ms) });
293
351
  if (res.status === 401) {
294
352
  const tokenUrl = tokenUrlFromChallenge(res.headers.get("www-authenticate") ?? "");
295
353
  if (!tokenUrl) return undefined;
296
- const tokRes = await doFetch(tokenUrl, { redirect: "error", signal: timeoutSignal(ms) });
354
+ const tokRes = await doFetch(tokenUrl, { redirect: "manual", signal: timeoutSignal(ms) });
297
355
  if (!tokRes.ok) return undefined;
298
356
  const tok = (await tokRes.json()) as { token?: string; access_token?: string };
299
357
  const bearer = tok.token ?? tok.access_token;
300
358
  if (!bearer) return undefined;
301
- res = await doFetch(manifestUrl, { headers: { Accept: accept, Authorization: `Bearer ${bearer}` }, redirect: "error", signal: timeoutSignal(ms) });
359
+ res = await doFetch(manifestUrl, { headers: { Accept: accept, Authorization: `Bearer ${bearer}` }, redirect: "manual", signal: timeoutSignal(ms) });
302
360
  }
303
361
  if (!res.ok) return undefined;
304
362
  const digest = res.headers.get("docker-content-digest");
@@ -331,7 +389,7 @@ export async function resolveRepoCommit(
331
389
  ? `${host.api}/projects/${encodeURIComponent(`${owner}/${repo}`)}/repository/commits?per_page=1`
332
390
  : `${host.api}/repos/${owner}/${repo}/commits?per_page=1&limit=1`;
333
391
  try {
334
- const res = await doFetch(apiUrl, { headers, redirect: "error", signal: timeoutSignal(opts.timeoutMs ?? DEFAULTS.timeoutMs) });
392
+ const res = await doFetch(apiUrl, { headers, redirect: "manual", signal: timeoutSignal(opts.timeoutMs ?? DEFAULTS.timeoutMs) });
335
393
  if (!res.ok) return undefined;
336
394
  const body = (await res.json()) as Array<{ sha?: string; id?: string }>;
337
395
  if (!Array.isArray(body) || body.length === 0) return undefined;
@@ -342,30 +400,3 @@ export async function resolveRepoCommit(
342
400
  }
343
401
  }
344
402
 
345
- async function fetchGitlab(
346
- host: HostConfig,
347
- owner: string,
348
- repo: string,
349
- ref: string | undefined,
350
- cfg: { maxBytesPerFile: number; maxTotalBytes: number; timeoutMs: number },
351
- doFetch: typeof fetch,
352
- headers: Record<string, string>,
353
- ): Promise<AuditInput[]> {
354
- const projectId = encodeURIComponent(`${owner}/${repo}`);
355
- const refQ = `?ref=${encodeURIComponent(ref ?? "HEAD")}`;
356
- const apiUrl = `${host.api}/projects/${projectId}/repository/files/${encodeURIComponent(".gitlab-ci.yml")}/raw${refQ}`;
357
- let res: Response;
358
- try {
359
- res = await doFetch(apiUrl, { headers, redirect: "error", signal: timeoutSignal(cfg.timeoutMs) });
360
- } catch (err) {
361
- throw new FetchError(`Request failed: ${err instanceof Error ? err.message : String(err)}`);
362
- }
363
- if (res.status >= 300 && res.status < 400) throw new FetchError(`Refusing to follow redirect from ${apiUrl}`);
364
- if (res.status === 404) return [];
365
- if (!res.ok) throw new FetchError(`${apiUrl} returned ${res.status}`);
366
- const content = await res.text();
367
- if (content.length > cfg.maxBytesPerFile || content.length > cfg.maxTotalBytes) {
368
- throw new FetchError("Pipeline file exceeds the size cap.");
369
- }
370
- return [{ path: ".gitlab-ci.yml", content, lexicon: "gitlab" }];
371
- }
@@ -0,0 +1,80 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { fileURLToPath } from "url";
3
+ import { readdirSync, readFileSync, statSync } from "fs";
4
+ import { join, relative } from "path";
5
+ import { fetchRepoFiles, FetchError } from "./fetch";
6
+ import { discoverByDetection, classifyFiles } from "./discover";
7
+ import { loadAuditPlugins } from "./discover";
8
+
9
+ const MIXED = fileURLToPath(new URL("../cli/commands/__fixtures__/audit-mixed", import.meta.url));
10
+
11
+ /** Read a dir into relative-path → content (the "repo" a mock will serve). */
12
+ function readDir(root: string): Record<string, string> {
13
+ const out: Record<string, string> = {};
14
+ const walk = (dir: string) => {
15
+ for (const e of readdirSync(dir, { withFileTypes: true })) {
16
+ const full = join(dir, e.name);
17
+ if (e.isDirectory()) walk(full);
18
+ else out[relative(root, full)] = readFileSync(full, "utf-8");
19
+ }
20
+ };
21
+ walk(root);
22
+ return out;
23
+ }
24
+
25
+ /** A GitHub-shaped mock: repo-info default_branch, recursive git/trees, base64 contents. */
26
+ function mockGithub(files: Record<string, string>): typeof fetch {
27
+ const b64 = (s: string) => Buffer.from(s, "utf-8").toString("base64");
28
+ return (async (url: string | URL | Request) => {
29
+ const u = String(url);
30
+ if (u.includes("/git/trees/")) {
31
+ const tree = Object.keys(files).map((path) => ({ path, type: "blob", size: files[path].length }));
32
+ return new Response(JSON.stringify({ tree }), { status: 200 });
33
+ }
34
+ const cm = u.match(/\/contents\/(.+?)\?/);
35
+ if (cm) {
36
+ const path = decodeURIComponent(cm[1]);
37
+ const content = files[path];
38
+ if (content === undefined) return new Response("not found", { status: 404 });
39
+ return new Response(JSON.stringify({ path, type: "file", content: b64(content), encoding: "base64" }), { status: 200 });
40
+ }
41
+ if (/\/repos\/[^/]+\/[^/]+(\?|$)/.test(u)) return new Response(JSON.stringify({ default_branch: "main" }), { status: 200 });
42
+ return new Response("not found", { status: 404 });
43
+ }) as unknown as typeof fetch;
44
+ }
45
+
46
+ const targets = (inputs: { lexicon: string; path: string }[]) => inputs.map((i) => `${i.lexicon}:${i.path}`).sort();
47
+
48
+ describe("remote audit covers all lexicons (not just CI)", () => {
49
+ test("a URL audit detects github + k8s + docker + aws in one repo", async () => {
50
+ const plugins = await loadAuditPlugins();
51
+ const files = await fetchRepoFiles("https://github.com/acme/widgets", { fetchImpl: mockGithub(readDir(MIXED)) });
52
+ const lexicons = new Set(classifyFiles(files, plugins).map((i) => i.lexicon));
53
+ expect([...lexicons].sort()).toEqual(["aws", "docker", "github", "k8s"]);
54
+ });
55
+
56
+ test("remote classification matches a local audit of the same repo (parity)", async () => {
57
+ const plugins = await loadAuditPlugins();
58
+ const local = discoverByDetection(MIXED, plugins);
59
+ const remote = classifyFiles(await fetchRepoFiles("https://github.com/acme/widgets", { fetchImpl: mockGithub(readDir(MIXED)) }), plugins);
60
+ expect(targets(remote)).toEqual(targets(local));
61
+ });
62
+
63
+ test("caps the number of files fetched", async () => {
64
+ const files = await fetchRepoFiles("https://github.com/acme/widgets", { fetchImpl: mockGithub(readDir(MIXED)), maxFiles: 1 });
65
+ expect(files.length).toBe(1);
66
+ });
67
+
68
+ test("rejects a non-allowlisted host before any fetch", async () => {
69
+ await expect(fetchRepoFiles("https://evil.example.com/o/r")).rejects.toThrow(/Host not allowed/);
70
+ });
71
+
72
+ test("refuses to follow a redirect on the tree request", async () => {
73
+ const impl = (async (url: string | URL | Request) => {
74
+ const u = String(url);
75
+ if (u.includes("/git/trees/")) return new Response(null, { status: 302 });
76
+ return new Response(JSON.stringify({ default_branch: "main" }), { status: 200 });
77
+ }) as unknown as typeof fetch;
78
+ await expect(fetchRepoFiles("https://github.com/acme/widgets", { fetchImpl: impl })).rejects.toThrow(/redirect/i);
79
+ });
80
+ });
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import type { AuditFinding } from "./core";
12
- import { ruleDocUrl } from "./catalog";
12
+ import { ruleDocUrl, type RuleMeta } from "./catalog";
13
13
  import { buildReportModel, buildReportJson, type AuditSnapshot, type BuildModelOptions, type EnrichedFinding, type GuidanceCluster, type QuickWinFile, type ReportCounts } from "./report-model";
14
14
 
15
15
  export type { AuditSnapshot } from "./report-model";
@@ -34,7 +34,7 @@ export interface RenderHtmlOptions extends BuildModelOptions {
34
34
  }
35
35
 
36
36
  const DEFAULT_ACCENT = "#7c3aed";
37
- const DEFAULT_TITLE = "CI Security Audit";
37
+ const DEFAULT_TITLE = "chant audit";
38
38
  const DEFAULT_FOOTER = 'Generated by <a href="https://intentius.io/chant/cli/audit/">chant audit</a>.';
39
39
 
40
40
  function esc(s: string): string {
@@ -46,6 +46,13 @@ function ruleLink(id: string): string {
46
46
  return `<a class="rule-id" href="${esc(ruleDocUrl(id))}">${esc(id)}</a>`;
47
47
  }
48
48
 
49
+ /** External backing for a merge-worthy rule (OSSF Scorecard, CIS, vendor docs). */
50
+ function authorityLinks(meta: RuleMeta): string {
51
+ if (!meta.authority?.length) return "";
52
+ const links = meta.authority.map((a) => `<a href="${esc(a.url)}">${esc(a.name)}</a>`).join(", ");
53
+ return ` <span class="muted">per ${links}</span>`;
54
+ }
55
+
49
56
  function renderDiff(diff: string): string {
50
57
  const rows = diff
51
58
  .split("\n")
@@ -60,11 +67,11 @@ function renderDiff(diff: string): string {
60
67
  function renderQuickWins(files: QuickWinFile[]): string {
61
68
  const cards = files
62
69
  .map((qw) => {
63
- const chips = qw.addressed.map((m) => `<span class="chip">${ruleLink(m.id)} ${esc(m.title)}</span>`).join(" ");
70
+ const chips = qw.addressed.map((m) => `<span class="chip">${ruleLink(m.id)} ${esc(m.title)}${authorityLinks(m)}</span>`).join(" ");
64
71
  const diff = qw.diff ? renderDiff(qw.diff) : "";
65
72
  const needs = qw.needsInput.length
66
73
  ? `<div class="needs"><strong>Needs a value to auto-patch:</strong><ul>${qw.needsInput
67
- .map((f) => `<li>${ruleLink(f.checkId)}${f.entity ? ` (<code>${esc(f.entity)}</code>)` : ""} — ${esc(f.meta.remediation)}</li>`)
74
+ .map((f) => `<li>${ruleLink(f.checkId)}${f.entity ? ` (<code>${esc(f.entity)}</code>)` : ""} — ${esc(f.meta.remediation)}${authorityLinks(f.meta)}</li>`)
68
75
  .join("")}</ul></div>`
69
76
  : "";
70
77
  return `<div class="card"><div class="card-head"><code class="file">${esc(qw.file)}</code> ${chips}</div>${diff}${needs}</div>`;
@@ -82,7 +89,7 @@ function renderNeedsReview(clusters: GuidanceCluster[], n: number): string {
82
89
  const locs = findings
83
90
  .map((f) => `<li><code>${esc(f.file)}</code>${f.entity ? ` (<code>${esc(f.entity)}</code>)` : ""} — ${esc(f.message)}</li>`)
84
91
  .join("");
85
- return `<div class="rule"><div><span class="sev ${findings[0].severity}"></span><strong>${ruleLink(meta.id)}</strong> — ${esc(meta.title)}. <span class="muted">${esc(meta.remediation)}</span></div><ul>${locs}</ul></div>`;
92
+ return `<div class="rule"><div><span class="sev ${findings[0].severity}"></span><strong>${ruleLink(meta.id)}</strong> — ${esc(meta.title)}. <span class="muted">${esc(meta.remediation)}</span>${authorityLinks(meta)}</div><ul>${locs}</ul></div>`;
86
93
  })
87
94
  .join("");
88
95
  return `<div class="cluster"><h3>${head}</h3>${rules}</div>`;
@@ -101,6 +108,7 @@ function renderReportOnly(findings: EnrichedFinding[], n: number): string {
101
108
  function renderHeader(counts: ReportCounts, snapshot: AuditSnapshot | undefined, notes: string[]): string {
102
109
  const sev = `<span class="sev error"></span>${counts.errors} error <span class="sev warning"></span>${counts.warnings} warning <span class="sev info"></span>${counts.infos} info`;
103
110
  const tiers = `<span class="chip">${counts.quickWin} quick-win</span> <span class="chip">${counts.needsReview} needs-review</span> <span class="chip">${counts.reportOnly} hygiene</span>`;
111
+ const cats = `<span class="chip">${counts.security} security</span> <span class="chip">${counts.correctness} correctness</span> <span class="chip">${counts.bestPractice} best-practice</span>`;
104
112
  const meta: string[] = [];
105
113
  if (snapshot) {
106
114
  if (snapshot.host) meta.push(esc(snapshot.host));
@@ -113,7 +121,7 @@ function renderHeader(counts: ReportCounts, snapshot: AuditSnapshot | undefined,
113
121
  meta.push(`chant ${esc(snapshot.toolVersion)}`);
114
122
  }
115
123
  const noteHtml = notes.map((n) => `<div class="note">${esc(n)}</div>`).join("");
116
- return `<div class="summary"><div class="meta">${meta.join(" · ")}</div><div class="counts">${sev}</div><div class="tiers">${tiers}</div>${noteHtml}</div>`;
124
+ return `<div class="summary"><div class="meta">${meta.join(" · ")}</div><div class="counts">${sev}</div><div class="tiers">${tiers}</div><div class="tiers">${cats}</div>${noteHtml}</div>`;
117
125
  }
118
126
 
119
127
  const STYLES = `
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { AuditFinding } from "./core";
9
- import { RULE_CATALOG, ruleDocUrl, type Authority, type FixKind, type RuleMeta, type Tier } from "./catalog";
9
+ import { RULE_CATALOG, ruleDocUrl, type Authority, type Category, type FixKind, type RuleMeta, type Tier } from "./catalog";
10
10
  import { proveFix, unifiedDiff, type ProveOptions } from "./proof";
11
11
  import type { Severity } from "../lint/rule";
12
12
 
@@ -16,6 +16,8 @@ import type { Severity } from "../lint/rule";
16
16
  * major. Consumers should check the major and ignore unknown fields. See
17
17
  * `docs/cli/audit`.
18
18
  */
19
+ // Additive changes (new fields like `category`, #415) keep the same version per
20
+ // the stability contract; only renamed/removed fields bump it.
19
21
  export const REPORT_SCHEMA_VERSION = "1.0";
20
22
 
21
23
  export const SEVERITY_WEIGHT: Record<Severity, number> = { error: 0, warning: 1, info: 2 };
@@ -55,6 +57,10 @@ export interface ReportCounts {
55
57
  errors: number;
56
58
  warnings: number;
57
59
  infos: number;
60
+ /** Findings by category (#415) — what kind of issue, not how confident the fix. */
61
+ security: number;
62
+ correctness: number;
63
+ bestPractice: number;
58
64
  }
59
65
 
60
66
  export interface ReportModel {
@@ -88,6 +94,8 @@ export interface SerializedFinding {
88
94
  lexicon: string;
89
95
  tier: Tier;
90
96
  fixKind: FixKind;
97
+ /** What kind of finding this is (security / correctness / best-practice). */
98
+ category: Category;
91
99
  title: string;
92
100
  remediation: string;
93
101
  authority: Authority[];
@@ -237,6 +245,9 @@ export function buildReportModel(findings: AuditFinding[], opts: BuildModelOptio
237
245
  errors: shown.filter((f) => f.severity === "error").length,
238
246
  warnings: shown.filter((f) => f.severity === "warning").length,
239
247
  infos: shown.filter((f) => f.severity === "info").length,
248
+ security: shown.filter((f) => f.meta.category === "security").length,
249
+ correctness: shown.filter((f) => f.meta.category === "correctness").length,
250
+ bestPractice: shown.filter((f) => f.meta.category === "best-practice").length,
240
251
  };
241
252
 
242
253
  return {
@@ -266,6 +277,7 @@ export function buildReportJson(findings: AuditFinding[], opts: { snapshot?: Aud
266
277
  lexicon: f.lexicon,
267
278
  tier: f.meta.tier,
268
279
  fixKind: f.meta.fixKind,
280
+ category: f.meta.category,
269
281
  title: f.meta.title,
270
282
  remediation: f.meta.remediation,
271
283
  authority: f.meta.authority ?? [],
@@ -31,7 +31,9 @@ describe("renderMarkdown — reworked structure", () => {
31
31
  test("quick wins show a real combined diff when file content is provided", () => {
32
32
  const out = renderMarkdown(FINDINGS, { files: [{ path: ".github/workflows/ci.yml", content: CI }] });
33
33
  expect(out).toContain("## Quick wins (deterministic)");
34
- expect(out).toContain("Addresses [GHA033](https://intentius.io/chant/lint-rules/audit-rules/#gha033) (Blanket write-all permissions):");
34
+ expect(out).toContain("Addresses [GHA033](https://intentius.io/chant/lint-rules/audit-rules/#gha033) (Blanket write-all permissions)");
35
+ // merge-worthy findings cite their external authority (#351 links surfaced in the layout)
36
+ expect(out).toContain("per [OSSF Scorecard — Token-Permissions](https://github.com/ossf/scorecard");
35
37
  expect(out).toContain("```diff");
36
38
  expect(out).toContain("-permissions: write-all");
37
39
  expect(out).toContain("+permissions:");
@@ -11,7 +11,7 @@
11
11
  */
12
12
 
13
13
  import type { AuditFinding } from "./core";
14
- import { ruleDocUrl } from "./catalog";
14
+ import { ruleDocUrl, type RuleMeta } from "./catalog";
15
15
  import type { ProveOptions } from "./proof";
16
16
  import { buildReportModel, type EnrichedFinding, type GuidanceCluster, type QuickWinFile } from "./report-model";
17
17
 
@@ -20,6 +20,12 @@ function ruleLink(id: string): string {
20
20
  return `[${id}](${ruleDocUrl(id)})`;
21
21
  }
22
22
 
23
+ /** External backing for a merge-worthy rule (OSSF Scorecard, CIS, vendor docs). */
24
+ function authorityLinks(meta: RuleMeta): string {
25
+ if (!meta.authority?.length) return "";
26
+ return ` — per ${meta.authority.map((a) => `[${a.name}](${a.url})`).join(", ")}`;
27
+ }
28
+
23
29
  export interface RenderOptions {
24
30
  /** Repo URL or path shown in the header. */
25
31
  target?: string;
@@ -42,14 +48,14 @@ function renderQuickWins(files: QuickWinFile[]): string[] {
42
48
  for (const qw of files) {
43
49
  lines.push(`### \`${qw.file}\``);
44
50
  if (qw.diff) {
45
- const labels = qw.addressed.map((m) => `${ruleLink(m.id)} (${m.title})`).join(", ");
51
+ const labels = qw.addressed.map((m) => `${ruleLink(m.id)} (${m.title})${authorityLinks(m)}`).join(", ");
46
52
  lines.push("", `Addresses ${labels}:`, "", "```diff", qw.diff, "```");
47
53
  }
48
54
  if (qw.needsInput.length > 0) {
49
55
  lines.push("", "Needs a value before it can be auto-patched:");
50
56
  for (const f of qw.needsInput) {
51
57
  const where = f.entity ? ` (\`${f.entity}\`)` : "";
52
- lines.push(`- **${ruleLink(f.checkId)}**${where} — ${f.meta.remediation}`);
58
+ lines.push(`- **${ruleLink(f.checkId)}**${where} — ${f.meta.remediation}${authorityLinks(f.meta)}`);
53
59
  }
54
60
  }
55
61
  lines.push("");
@@ -62,7 +68,7 @@ function renderNeedsReview(clusters: GuidanceCluster[]): string[] {
62
68
  for (const cluster of clusters) {
63
69
  lines.push(`### ${cluster.url ? `[${cluster.name}](${cluster.url})` : cluster.name}`, "");
64
70
  for (const { meta, findings } of cluster.rules) {
65
- lines.push(`- **${ruleLink(meta.id)}** — ${meta.title} (${findings[0].severity}). ${meta.remediation}`);
71
+ lines.push(`- **${ruleLink(meta.id)}** — ${meta.title} (${findings[0].severity}). ${meta.remediation}${authorityLinks(meta)}`);
66
72
  for (const f of findings) {
67
73
  const where = f.entity ? `\`${f.file}\` (\`${f.entity}\`)` : `\`${f.file}\``;
68
74
  lines.push(` - ${where} — ${escapeCell(f.message)}`);
@@ -87,7 +93,7 @@ export function renderMarkdown(findings: AuditFinding[], opts: RenderOptions = {
87
93
  const model = buildReportModel(findings, opts);
88
94
  const { counts } = model;
89
95
 
90
- const lines: string[] = ["# CI security audit"];
96
+ const lines: string[] = ["# chant audit"];
91
97
  if (opts.target) lines.push("", `Target: ${opts.target}`);
92
98
  lines.push("");
93
99
  for (const note of opts.notes ?? []) lines.push(`> Note: ${note}`, "");
@@ -102,6 +108,8 @@ export function renderMarkdown(findings: AuditFinding[], opts: RenderOptions = {
102
108
  `${counts.quickWin} quick-win, ${counts.needsReview} needs-review, ${counts.reportOnly} report-only ` +
103
109
  `(${counts.errors} error, ${counts.warnings} warning, ${counts.infos} info).`,
104
110
  "",
111
+ `By category: ${counts.security} security, ${counts.correctness} correctness, ${counts.bestPractice} best-practice.`,
112
+ "",
105
113
  );
106
114
 
107
115
  if (model.quickWins.length > 0) lines.push(...renderQuickWins(model.quickWins));
@@ -15,6 +15,10 @@ const GROUPS: Array<{ heading: string; prefixes: string[]; blurb: string }> = [
15
15
  { heading: "Forgejo (WFJ)", prefixes: ["WFJ"], blurb: "" },
16
16
  { heading: "Kubernetes (WK8 / ARGO)", prefixes: ["WK8", "ARGO"], blurb: "Run against Kubernetes manifests." },
17
17
  { heading: "Docker (DKRD)", prefixes: ["DKRD"], blurb: "Run against Dockerfiles and Compose files." },
18
+ { heading: "AWS CloudFormation (WAW / COR / EXT)", prefixes: ["WAW", "COR", "EXT"], blurb: "Run against CloudFormation templates (JSON or YAML)." },
19
+ { heading: "Azure ARM (AZR)", prefixes: ["AZR"], blurb: "Run against ARM deployment templates (JSON)." },
20
+ { heading: "GCP Config Connector (WGC)", prefixes: ["WGC"], blurb: "Run against Config Connector (cnrm.cloud.google.com) manifests." },
21
+ { heading: "Helm (WHM)", prefixes: ["WHM"], blurb: "Run against Helm charts (Chart.yaml + templates)." },
18
22
  ];
19
23
 
20
24
  function ruleBlock(m: RuleMeta): string {
@@ -0,0 +1,11 @@
1
+ AWSTemplateFormatVersion: "2010-09-09"
2
+ Resources:
3
+ Sg:
4
+ Type: AWS::EC2::SecurityGroup
5
+ Properties:
6
+ GroupDescription: web
7
+ SecurityGroupIngress:
8
+ - IpProtocol: tcp
9
+ FromPort: 22
10
+ ToPort: 22
11
+ CidrIp: 0.0.0.0/0
@@ -0,0 +1,7 @@
1
+ {
2
+ "AWSTemplateFormatVersion": "2010-09-09",
3
+ "Resources": {
4
+ "Data": { "Type": "AWS::S3::Bucket", "Properties": {} },
5
+ "Db": { "Type": "AWS::RDS::DBInstance", "Properties": { "Engine": "postgres" } }
6
+ }
7
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
3
+ "contentVersion": "1.0.0.0",
4
+ "resources": [
5
+ {
6
+ "type": "Microsoft.Storage/storageAccounts",
7
+ "apiVersion": "2022-09-01",
8
+ "name": "data",
9
+ "location": "eastus",
10
+ "properties": { "allowBlobPublicAccess": true }
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,6 @@
1
+ apiVersion: storage.cnrm.cloud.google.com/v1beta1
2
+ kind: StorageBucket
3
+ metadata:
4
+ name: data
5
+ spec:
6
+ location: US
@@ -0,0 +1,9 @@
1
+ apiVersion: compute.cnrm.cloud.google.com/v1beta1
2
+ kind: ComputeFirewall
3
+ metadata:
4
+ name: allow-all
5
+ spec:
6
+ sourceRanges:
7
+ - 0.0.0.0/0
8
+ allow:
9
+ - protocol: tcp
@@ -0,0 +1,3 @@
1
+ apiVersion: v2
2
+ name: mychart
3
+ version: 0.1.0
@@ -0,0 +1,12 @@
1
+ apiVersion: apps/v1
2
+ kind: Deployment
3
+ metadata:
4
+ name: {{ .Release.Name }}
5
+ spec:
6
+ template:
7
+ spec:
8
+ containers:
9
+ - name: app
10
+ image: nginx:latest
11
+ securityContext:
12
+ privileged: true
@@ -0,0 +1 @@
1
+ image: myapp:latest