@intentius/chant 0.7.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.
- package/dist/attrref.d.ts +35 -0
- package/dist/attrref.d.ts.map +1 -0
- package/dist/audit/catalog.d.ts +65 -0
- package/dist/audit/catalog.d.ts.map +1 -0
- package/dist/audit/core.d.ts +76 -0
- package/dist/audit/core.d.ts.map +1 -0
- package/dist/audit/discover.d.ts +76 -0
- package/dist/audit/discover.d.ts.map +1 -0
- package/dist/audit/fetch.d.ts +90 -0
- package/dist/audit/fetch.d.ts.map +1 -0
- package/dist/audit/proof.d.ts +53 -0
- package/dist/audit/proof.d.ts.map +1 -0
- package/dist/audit/report-html.d.ts +32 -0
- package/dist/audit/report-html.d.ts.map +1 -0
- package/dist/audit/report-model.d.ts +119 -0
- package/dist/audit/report-model.d.ts.map +1 -0
- package/dist/audit/report.d.ts +31 -0
- package/dist/audit/report.d.ts.map +1 -0
- package/dist/audit/rules-doc.d.ts +11 -0
- package/dist/audit/rules-doc.d.ts.map +1 -0
- package/dist/build.d.ts +120 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/builder.d.ts +56 -0
- package/dist/builder.d.ts.map +1 -0
- package/dist/child-project.d.ts +34 -0
- package/dist/child-project.d.ts.map +1 -0
- package/dist/cli/commands/audit.d.ts +60 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/build.d.ts +70 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/check-lexicon.d.ts +19 -0
- package/dist/cli/commands/check-lexicon.d.ts.map +1 -0
- package/dist/cli/commands/describe.d.ts +39 -0
- package/dist/cli/commands/describe.d.ts.map +1 -0
- package/dist/cli/commands/diff.d.ts +32 -0
- package/dist/cli/commands/diff.d.ts.map +1 -0
- package/dist/cli/commands/doctor.d.ts +11 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/import.d.ts +68 -0
- package/dist/cli/commands/import.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts +9 -0
- package/dist/cli/commands/init-lexicon/templates/codegen.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts +9 -0
- package/dist/cli/commands/init-lexicon/templates/docs.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/examples.d.ts +8 -0
- package/dist/cli/commands/init-lexicon/templates/examples.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/lint.d.ts +8 -0
- package/dist/cli/commands/init-lexicon/templates/lint.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/lsp.d.ts +6 -0
- package/dist/cli/commands/init-lexicon/templates/lsp.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/plugin.d.ts +12 -0
- package/dist/cli/commands/init-lexicon/templates/plugin.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts +19 -0
- package/dist/cli/commands/init-lexicon/templates/project.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/spec.d.ts +6 -0
- package/dist/cli/commands/init-lexicon/templates/spec.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon/templates/tests.d.ts +12 -0
- package/dist/cli/commands/init-lexicon/templates/tests.d.ts.map +1 -0
- package/dist/cli/commands/init-lexicon.d.ts +37 -0
- package/dist/cli/commands/init-lexicon.d.ts.map +1 -0
- package/dist/cli/commands/init.d.ts +48 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/licenses.d.ts +23 -0
- package/dist/cli/commands/licenses.d.ts.map +1 -0
- package/dist/cli/commands/lint.d.ts +52 -0
- package/dist/cli/commands/lint.d.ts.map +1 -0
- package/dist/cli/commands/list.d.ts +38 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/migrate.d.ts +51 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/onboard.d.ts +19 -0
- package/dist/cli/commands/onboard.d.ts.map +1 -0
- package/dist/cli/commands/update.d.ts +29 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/vendor.d.ts +84 -0
- package/dist/cli/commands/vendor.d.ts.map +1 -0
- package/dist/cli/conflict-check.d.ts +18 -0
- package/dist/cli/conflict-check.d.ts.map +1 -0
- package/dist/cli/debug.d.ts +5 -0
- package/dist/cli/debug.d.ts.map +1 -0
- package/dist/cli/format.d.ts +36 -0
- package/dist/cli/format.d.ts.map +1 -0
- package/dist/cli/handlers/build.d.ts +3 -0
- package/dist/cli/handlers/build.d.ts.map +1 -0
- package/dist/cli/handlers/dev.d.ts +7 -0
- package/dist/cli/handlers/dev.d.ts.map +1 -0
- package/dist/cli/handlers/graph.d.ts +8 -0
- package/dist/cli/handlers/graph.d.ts.map +1 -0
- package/dist/cli/handlers/init.d.ts +4 -0
- package/dist/cli/handlers/init.d.ts.map +1 -0
- package/dist/cli/handlers/lifecycle.d.ts +39 -0
- package/dist/cli/handlers/lifecycle.d.ts.map +1 -0
- package/dist/cli/handlers/lint.d.ts +3 -0
- package/dist/cli/handlers/lint.d.ts.map +1 -0
- package/dist/cli/handlers/migrate.d.ts +3 -0
- package/dist/cli/handlers/migrate.d.ts.map +1 -0
- package/dist/cli/handlers/misc.d.ts +8 -0
- package/dist/cli/handlers/misc.d.ts.map +1 -0
- package/dist/cli/handlers/run-client.d.ts +107 -0
- package/dist/cli/handlers/run-client.d.ts.map +1 -0
- package/dist/cli/handlers/run-report.d.ts +5 -0
- package/dist/cli/handlers/run-report.d.ts.map +1 -0
- package/dist/cli/handlers/run.d.ts +27 -0
- package/dist/cli/handlers/run.d.ts.map +1 -0
- package/dist/cli/handlers/serve.d.ts +5 -0
- package/dist/cli/handlers/serve.d.ts.map +1 -0
- package/dist/cli/handlers/vendor.d.ts +9 -0
- package/dist/cli/handlers/vendor.d.ts.map +1 -0
- package/dist/cli/index.d.ts +4 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/lsp/capabilities.d.ts +21 -0
- package/dist/cli/lsp/capabilities.d.ts.map +1 -0
- package/dist/cli/lsp/diagnostics.d.ts +30 -0
- package/dist/cli/lsp/diagnostics.d.ts.map +1 -0
- package/dist/cli/lsp/server.d.ts +106 -0
- package/dist/cli/lsp/server.d.ts.map +1 -0
- package/dist/cli/main.d.ts +7 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/mcp/lifecycle-tools.d.ts +15 -0
- package/dist/cli/mcp/lifecycle-tools.d.ts.map +1 -0
- package/dist/cli/mcp/op-tools.d.ts +7 -0
- package/dist/cli/mcp/op-tools.d.ts.map +1 -0
- package/dist/cli/mcp/resource-handlers.d.ts +28 -0
- package/dist/cli/mcp/resource-handlers.d.ts.map +1 -0
- package/dist/cli/mcp/resources/context.d.ts +6 -0
- package/dist/cli/mcp/resources/context.d.ts.map +1 -0
- package/dist/cli/mcp/server.d.ts +44 -0
- package/dist/cli/mcp/server.d.ts.map +1 -0
- package/dist/cli/mcp/tools/build.d.ts +31 -0
- package/dist/cli/mcp/tools/build.d.ts.map +1 -0
- package/dist/cli/mcp/tools/explain.d.ts +27 -0
- package/dist/cli/mcp/tools/explain.d.ts.map +1 -0
- package/dist/cli/mcp/tools/import.d.ts +26 -0
- package/dist/cli/mcp/tools/import.d.ts.map +1 -0
- package/dist/cli/mcp/tools/lint.d.ts +26 -0
- package/dist/cli/mcp/tools/lint.d.ts.map +1 -0
- package/dist/cli/mcp/tools/scaffold.d.ts +31 -0
- package/dist/cli/mcp/tools/scaffold.d.ts.map +1 -0
- package/dist/cli/mcp/tools/search.d.ts +31 -0
- package/dist/cli/mcp/tools/search.d.ts.map +1 -0
- package/dist/cli/mcp/types.d.ts +42 -0
- package/dist/cli/mcp/types.d.ts.map +1 -0
- package/dist/cli/plugins.d.ts +21 -0
- package/dist/cli/plugins.d.ts.map +1 -0
- package/dist/cli/registry.d.ts +100 -0
- package/dist/cli/registry.d.ts.map +1 -0
- package/dist/cli/reporters/stylish.d.ts +26 -0
- package/dist/cli/reporters/stylish.d.ts.map +1 -0
- package/dist/cli/watch.d.ts +29 -0
- package/dist/cli/watch.d.ts.map +1 -0
- package/dist/codegen/bound-property-types.d.ts +38 -0
- package/dist/codegen/bound-property-types.d.ts.map +1 -0
- package/dist/codegen/coverage.d.ts +57 -0
- package/dist/codegen/coverage.d.ts.map +1 -0
- package/dist/codegen/docs-file-markers.d.ts +16 -0
- package/dist/codegen/docs-file-markers.d.ts.map +1 -0
- package/dist/codegen/docs-rule-scanning.d.ts +14 -0
- package/dist/codegen/docs-rule-scanning.d.ts.map +1 -0
- package/dist/codegen/docs-rules.d.ts +17 -0
- package/dist/codegen/docs-rules.d.ts.map +1 -0
- package/dist/codegen/docs-sections.d.ts +11 -0
- package/dist/codegen/docs-sections.d.ts.map +1 -0
- package/dist/codegen/docs-sidebar.d.ts +6 -0
- package/dist/codegen/docs-sidebar.d.ts.map +1 -0
- package/dist/codegen/docs-types.d.ts +84 -0
- package/dist/codegen/docs-types.d.ts.map +1 -0
- package/dist/codegen/docs.d.ts +28 -0
- package/dist/codegen/docs.d.ts.map +1 -0
- package/dist/codegen/fetch.d.ts +76 -0
- package/dist/codegen/fetch.d.ts.map +1 -0
- package/dist/codegen/generate-post-synth-barrel.d.ts +32 -0
- package/dist/codegen/generate-post-synth-barrel.d.ts.map +1 -0
- package/dist/codegen/generate-registry.d.ts +40 -0
- package/dist/codegen/generate-registry.d.ts.map +1 -0
- package/dist/codegen/generate-runtime-index.d.ts +40 -0
- package/dist/codegen/generate-runtime-index.d.ts.map +1 -0
- package/dist/codegen/generate-typescript.d.ts +48 -0
- package/dist/codegen/generate-typescript.d.ts.map +1 -0
- package/dist/codegen/generate.d.ts +92 -0
- package/dist/codegen/generate.d.ts.map +1 -0
- package/dist/codegen/json-patch.d.ts +12 -0
- package/dist/codegen/json-patch.d.ts.map +1 -0
- package/dist/codegen/json-schema.d.ts +82 -0
- package/dist/codegen/json-schema.d.ts.map +1 -0
- package/dist/codegen/naming.d.ts +64 -0
- package/dist/codegen/naming.d.ts.map +1 -0
- package/dist/codegen/package.d.ts +64 -0
- package/dist/codegen/package.d.ts.map +1 -0
- package/dist/codegen/topo-sort.d.ts +13 -0
- package/dist/codegen/topo-sort.d.ts.map +1 -0
- package/dist/codegen/typecheck.d.ts +10 -0
- package/dist/codegen/typecheck.d.ts.map +1 -0
- package/dist/codegen/validate.d.ts +45 -0
- package/dist/codegen/validate.d.ts.map +1 -0
- package/dist/composite.d.ts +116 -0
- package/dist/composite.d.ts.map +1 -0
- package/dist/config.d.ts +78 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/declarable.d.ts +34 -0
- package/dist/declarable.d.ts.map +1 -0
- package/dist/detectLexicon.d.ts +10 -0
- package/dist/detectLexicon.d.ts.map +1 -0
- package/dist/discovery/cache.d.ts +36 -0
- package/dist/discovery/cache.d.ts.map +1 -0
- package/dist/discovery/collect.d.ts +17 -0
- package/dist/discovery/collect.d.ts.map +1 -0
- package/dist/discovery/cycles.d.ts +23 -0
- package/dist/discovery/cycles.d.ts.map +1 -0
- package/dist/discovery/files.d.ts +7 -0
- package/dist/discovery/files.d.ts.map +1 -0
- package/dist/discovery/graph.d.ts +8 -0
- package/dist/discovery/graph.d.ts.map +1 -0
- package/dist/discovery/import.d.ts +9 -0
- package/dist/discovery/import.d.ts.map +1 -0
- package/dist/discovery/index.d.ts +25 -0
- package/dist/discovery/index.d.ts.map +1 -0
- package/dist/discovery/resolve.d.ts +10 -0
- package/dist/discovery/resolve.d.ts.map +1 -0
- package/dist/errors.d.ts +49 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/import/base-parser.d.ts +19 -0
- package/dist/import/base-parser.d.ts.map +1 -0
- package/dist/import/generator.d.ts +20 -0
- package/dist/import/generator.d.ts.map +1 -0
- package/dist/import/ir-utils.d.ts +25 -0
- package/dist/import/ir-utils.d.ts.map +1 -0
- package/dist/import/parser.d.ts +39 -0
- package/dist/import/parser.d.ts.map +1 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/intrinsic-interpolation.d.ts +24 -0
- package/dist/intrinsic-interpolation.d.ts.map +1 -0
- package/dist/intrinsic.d.ts +24 -0
- package/dist/intrinsic.d.ts.map +1 -0
- package/dist/lexicon-integrity.d.ts +25 -0
- package/dist/lexicon-integrity.d.ts.map +1 -0
- package/dist/lexicon-manifest.d.ts +9 -0
- package/dist/lexicon-manifest.d.ts.map +1 -0
- package/dist/lexicon-output.d.ts +66 -0
- package/dist/lexicon-output.d.ts.map +1 -0
- package/dist/lexicon-plugin-helpers.d.ts +59 -0
- package/dist/lexicon-plugin-helpers.d.ts.map +1 -0
- package/dist/lexicon-schema.d.ts +82 -0
- package/dist/lexicon-schema.d.ts.map +1 -0
- package/dist/lexicon.d.ts +383 -0
- package/dist/lexicon.d.ts.map +1 -0
- package/dist/lifecycle/affected.d.ts +57 -0
- package/dist/lifecycle/affected.d.ts.map +1 -0
- package/dist/lifecycle/change-set.d.ts +90 -0
- package/dist/lifecycle/change-set.d.ts.map +1 -0
- package/dist/lifecycle/digest.d.ts +21 -0
- package/dist/lifecycle/digest.d.ts.map +1 -0
- package/dist/lifecycle/git.d.ts +72 -0
- package/dist/lifecycle/git.d.ts.map +1 -0
- package/dist/lifecycle/index.d.ts +8 -0
- package/dist/lifecycle/index.d.ts.map +1 -0
- package/dist/lifecycle/live-diff.d.ts +67 -0
- package/dist/lifecycle/live-diff.d.ts.map +1 -0
- package/dist/lifecycle/snapshot.d.ts +20 -0
- package/dist/lifecycle/snapshot.d.ts.map +1 -0
- package/dist/lifecycle/types.d.ts +61 -0
- package/dist/lifecycle/types.d.ts.map +1 -0
- package/dist/lint/config.d.ts +100 -0
- package/dist/lint/config.d.ts.map +1 -0
- package/dist/lint/declarative.d.ts +56 -0
- package/dist/lint/declarative.d.ts.map +1 -0
- package/dist/lint/discover.d.ts +35 -0
- package/dist/lint/discover.d.ts.map +1 -0
- package/dist/lint/engine.d.ts +21 -0
- package/dist/lint/engine.d.ts.map +1 -0
- package/dist/lint/named-checks.d.ts +19 -0
- package/dist/lint/named-checks.d.ts.map +1 -0
- package/dist/lint/parser.d.ts +9 -0
- package/dist/lint/parser.d.ts.map +1 -0
- package/dist/lint/policy.d.ts +22 -0
- package/dist/lint/policy.d.ts.map +1 -0
- package/dist/lint/post-synth.d.ts +69 -0
- package/dist/lint/post-synth.d.ts.map +1 -0
- package/dist/lint/presets/strict.json +19 -0
- package/dist/lint/rule-loader.d.ts +16 -0
- package/dist/lint/rule-loader.d.ts.map +1 -0
- package/dist/lint/rule-registry.d.ts +37 -0
- package/dist/lint/rule-registry.d.ts.map +1 -0
- package/dist/lint/rule.d.ts +82 -0
- package/dist/lint/rule.d.ts.map +1 -0
- package/dist/lint/rules/composite-scope.d.ts +18 -0
- package/dist/lint/rules/composite-scope.d.ts.map +1 -0
- package/dist/lint/rules/cor017-composite-name-match.d.ts +3 -0
- package/dist/lint/rules/cor017-composite-name-match.d.ts.map +1 -0
- package/dist/lint/rules/cor018-composite-prefer-lexicon-type.d.ts +3 -0
- package/dist/lint/rules/cor018-composite-prefer-lexicon-type.d.ts.map +1 -0
- package/dist/lint/rules/declarable-naming-convention.d.ts +3 -0
- package/dist/lint/rules/declarable-naming-convention.d.ts.map +1 -0
- package/dist/lint/rules/evl001-non-literal-expression.d.ts +3 -0
- package/dist/lint/rules/evl001-non-literal-expression.d.ts.map +1 -0
- package/dist/lint/rules/evl002-control-flow-resource.d.ts +3 -0
- package/dist/lint/rules/evl002-control-flow-resource.d.ts.map +1 -0
- package/dist/lint/rules/evl003-dynamic-property-access.d.ts +3 -0
- package/dist/lint/rules/evl003-dynamic-property-access.d.ts.map +1 -0
- package/dist/lint/rules/evl004-spread-non-const.d.ts +3 -0
- package/dist/lint/rules/evl004-spread-non-const.d.ts.map +1 -0
- package/dist/lint/rules/evl005-resource-block-body.d.ts +3 -0
- package/dist/lint/rules/evl005-resource-block-body.d.ts.map +1 -0
- package/dist/lint/rules/evl007-invalid-siblings.d.ts +3 -0
- package/dist/lint/rules/evl007-invalid-siblings.d.ts.map +1 -0
- package/dist/lint/rules/evl009-composite-no-constant.d.ts +3 -0
- package/dist/lint/rules/evl009-composite-no-constant.d.ts.map +1 -0
- package/dist/lint/rules/evl010-composite-no-transform.d.ts +3 -0
- package/dist/lint/rules/evl010-composite-no-transform.d.ts.map +1 -0
- package/dist/lint/rules/export-required.d.ts +3 -0
- package/dist/lint/rules/export-required.d.ts.map +1 -0
- package/dist/lint/rules/file-declarable-limit.d.ts +3 -0
- package/dist/lint/rules/file-declarable-limit.d.ts.map +1 -0
- package/dist/lint/rules/flat-declarations.d.ts +3 -0
- package/dist/lint/rules/flat-declarations.d.ts.map +1 -0
- package/dist/lint/rules/index.d.ts +31 -0
- package/dist/lint/rules/index.d.ts.map +1 -0
- package/dist/lint/rules/no-cyclic-declarable-ref.d.ts +3 -0
- package/dist/lint/rules/no-cyclic-declarable-ref.d.ts.map +1 -0
- package/dist/lint/rules/no-redundant-type-import.d.ts +10 -0
- package/dist/lint/rules/no-redundant-type-import.d.ts.map +1 -0
- package/dist/lint/rules/no-redundant-value-cast.d.ts +3 -0
- package/dist/lint/rules/no-redundant-value-cast.d.ts.map +1 -0
- package/dist/lint/rules/no-string-ref.d.ts +3 -0
- package/dist/lint/rules/no-string-ref.d.ts.map +1 -0
- package/dist/lint/rules/no-unused-declarable-import.d.ts +3 -0
- package/dist/lint/rules/no-unused-declarable-import.d.ts.map +1 -0
- package/dist/lint/rules/no-unused-declarable.d.ts +3 -0
- package/dist/lint/rules/no-unused-declarable.d.ts.map +1 -0
- package/dist/lint/rules/single-concern-file.d.ts +3 -0
- package/dist/lint/rules/single-concern-file.d.ts.map +1 -0
- package/dist/lint/selectors.d.ts +21 -0
- package/dist/lint/selectors.d.ts.map +1 -0
- package/dist/lsp/lexicon-providers.d.ts +61 -0
- package/dist/lsp/lexicon-providers.d.ts.map +1 -0
- package/dist/lsp/types.d.ts +64 -0
- package/dist/lsp/types.d.ts.map +1 -0
- package/dist/mcp/types.d.ts +21 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/op/activity-registry.d.ts +57 -0
- package/dist/op/activity-registry.d.ts.map +1 -0
- package/dist/op/builders.d.ts +69 -0
- package/dist/op/builders.d.ts.map +1 -0
- package/dist/op/discover.d.ts +16 -0
- package/dist/op/discover.d.ts.map +1 -0
- package/dist/op/index.d.ts +11 -0
- package/dist/op/index.d.ts.map +1 -0
- package/dist/op/local-executor.d.ts +54 -0
- package/dist/op/local-executor.d.ts.map +1 -0
- package/dist/op/local-output.d.ts +16 -0
- package/dist/op/local-output.d.ts.map +1 -0
- package/dist/op/resource.d.ts +7 -0
- package/dist/op/resource.d.ts.map +1 -0
- package/dist/op/types.d.ts +71 -0
- package/dist/op/types.d.ts.map +1 -0
- package/dist/ownership.d.ts +75 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/project-validation.d.ts +28 -0
- package/dist/project-validation.d.ts.map +1 -0
- package/dist/provenance.d.ts +26 -0
- package/dist/provenance.d.ts.map +1 -0
- package/dist/pseudo-parameter.d.ts +32 -0
- package/dist/pseudo-parameter.d.ts.map +1 -0
- package/dist/resource-attributes.d.ts +17 -0
- package/dist/resource-attributes.d.ts.map +1 -0
- package/dist/runtime-adapter.d.ts +41 -0
- package/dist/runtime-adapter.d.ts.map +1 -0
- package/dist/runtime.d.ts +23 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/serializer-walker.d.ts +21 -0
- package/dist/serializer-walker.d.ts.map +1 -0
- package/dist/serializer.d.ts +64 -0
- package/dist/serializer.d.ts.map +1 -0
- package/dist/sort.d.ts +8 -0
- package/dist/sort.d.ts.map +1 -0
- package/dist/stack-output.d.ts +52 -0
- package/dist/stack-output.d.ts.map +1 -0
- package/dist/toml-emit.d.ts +22 -0
- package/dist/toml-emit.d.ts.map +1 -0
- package/dist/toml-parse.d.ts +14 -0
- package/dist/toml-parse.d.ts.map +1 -0
- package/dist/toml-utils.d.ts +20 -0
- package/dist/toml-utils.d.ts.map +1 -0
- package/dist/toml.d.ts +9 -0
- package/dist/toml.d.ts.map +1 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils.d.ts +30 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/validation.d.ts +42 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/yaml.d.ts +42 -0
- package/dist/yaml.d.ts.map +1 -0
- package/package.json +31 -6
- package/src/audit/bundle-decoupling.test.ts +56 -0
- package/src/audit/catalog.test.ts +16 -1
- package/src/audit/catalog.ts +244 -4
- package/src/audit/core.test.ts +15 -0
- package/src/audit/core.ts +0 -0
- package/src/audit/detect-bundle.test.ts +35 -0
- package/src/audit/discover.test.ts +72 -0
- package/src/audit/discover.ts +329 -0
- package/src/audit/edge-init-safety.test.ts +43 -0
- package/src/audit/fetch.test.ts +86 -55
- package/src/audit/fetch.ts +118 -87
- package/src/audit/remote-discovery.test.ts +80 -0
- package/src/audit/report-html.ts +14 -6
- package/src/audit/report-model.ts +13 -1
- package/src/audit/report.test.ts +3 -1
- package/src/audit/report.ts +13 -5
- package/src/cli/commands/__fixtures__/audit-mixed/.github/workflows/ci.yml +5 -0
- package/src/cli/commands/__fixtures__/audit-mixed/Dockerfile +2 -0
- package/src/cli/commands/__fixtures__/audit-mixed/cfn/template.json +1 -0
- package/src/cli/commands/__fixtures__/audit-mixed/k8s/deploy.yaml +12 -0
- package/src/cli/commands/audit.test.ts +27 -19
- package/src/cli/commands/audit.ts +28 -272
- package/src/codegen/bound-property-types.test.ts +57 -0
- package/src/codegen/bound-property-types.ts +110 -0
- package/src/codegen/generate-post-synth-barrel.test.ts +37 -0
- package/src/codegen/generate-post-synth-barrel.ts +75 -0
- package/src/codegen/post-synth-barrel-guards.test.ts +63 -0
- package/src/codegen/validate.ts +13 -1
- package/src/lint/discover.ts +1 -1
- package/src/meta/peer-deps.test.ts +29 -0
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { describe, test, expect } from "vitest";
|
|
2
2
|
import { fileURLToPath } from "url";
|
|
3
|
-
import { auditCommand,
|
|
4
|
-
import {
|
|
3
|
+
import { auditCommand, tokenForHost, coverageNotes } from "./audit";
|
|
4
|
+
import { discoverByDetection, loadAuditPlugins } from "../../audit/discover";
|
|
5
|
+
import { MissingLexiconError, type AuditInput, type AuditLexicon } from "../../audit/core";
|
|
6
|
+
|
|
7
|
+
/** Discover with all audit lexicons loaded, then keep one lexicon's inputs. */
|
|
8
|
+
async function discoverLexicon(repo: string, lexicon: AuditLexicon): Promise<AuditInput[]> {
|
|
9
|
+
return discoverByDetection(repo, await loadAuditPlugins()).filter((i) => i.lexicon === lexicon);
|
|
10
|
+
}
|
|
5
11
|
import { readFileSync, existsSync, rmSync } from "fs";
|
|
6
12
|
import { tmpdir } from "os";
|
|
7
13
|
import { join } from "path";
|
|
@@ -32,7 +38,7 @@ describe("auditCommand", () => {
|
|
|
32
38
|
|
|
33
39
|
test("discovers and audits Kubernetes manifests", async () => {
|
|
34
40
|
const repo = fileURLToPath(new URL("./__fixtures__/audit-k8s", import.meta.url));
|
|
35
|
-
const files =
|
|
41
|
+
const files = await discoverLexicon(repo, "k8s");
|
|
36
42
|
expect(files.map((f) => f.path)).toContain("manifests/deploy.yaml");
|
|
37
43
|
expect(files.every((f) => f.lexicon === "k8s")).toBe(true);
|
|
38
44
|
|
|
@@ -45,7 +51,7 @@ describe("auditCommand", () => {
|
|
|
45
51
|
|
|
46
52
|
test("discovers and audits Docker artifacts (nested Dockerfile + compose)", async () => {
|
|
47
53
|
const repo = fileURLToPath(new URL("./__fixtures__/audit-docker", import.meta.url));
|
|
48
|
-
const files =
|
|
54
|
+
const files = await discoverLexicon(repo, "docker");
|
|
49
55
|
const paths = files.map((f) => f.path).sort();
|
|
50
56
|
expect(paths).toContain("app/Dockerfile");
|
|
51
57
|
expect(paths).toContain("docker-compose.yml");
|
|
@@ -60,7 +66,7 @@ describe("auditCommand", () => {
|
|
|
60
66
|
|
|
61
67
|
test("discovers and audits CloudFormation (JSON and YAML)", async () => {
|
|
62
68
|
const repo = fileURLToPath(new URL("./__fixtures__/audit-aws", import.meta.url));
|
|
63
|
-
const files =
|
|
69
|
+
const files = await discoverLexicon(repo, "aws");
|
|
64
70
|
const paths = files.map((f) => f.path).sort();
|
|
65
71
|
expect(paths).toContain("template.json");
|
|
66
72
|
expect(paths).toContain("stack.yaml");
|
|
@@ -77,7 +83,7 @@ describe("auditCommand", () => {
|
|
|
77
83
|
|
|
78
84
|
test("discovers and audits Azure ARM templates", async () => {
|
|
79
85
|
const repo = fileURLToPath(new URL("./__fixtures__/audit-azure", import.meta.url));
|
|
80
|
-
expect(
|
|
86
|
+
expect((await discoverLexicon(repo, "azure")).map((f) => f.path)).toContain("azuredeploy.json");
|
|
81
87
|
const result = await auditCommand({ path: repo, format: "stylish" });
|
|
82
88
|
expect(result.success).toBe(true);
|
|
83
89
|
const ids = new Set(result.findings.map((f) => f.checkId));
|
|
@@ -86,10 +92,10 @@ describe("auditCommand", () => {
|
|
|
86
92
|
|
|
87
93
|
test("discovers and audits GCP Config Connector (not misclassified as k8s)", async () => {
|
|
88
94
|
const repo = fileURLToPath(new URL("./__fixtures__/audit-gcp", import.meta.url));
|
|
89
|
-
const gcp =
|
|
95
|
+
const gcp = await discoverLexicon(repo, "gcp");
|
|
90
96
|
expect(gcp.map((f) => f.path).sort()).toEqual(["bucket.yaml", "firewall.yaml"]);
|
|
91
97
|
// cnrm manifests must NOT also be picked up as k8s.
|
|
92
|
-
expect(
|
|
98
|
+
expect(await discoverLexicon(repo, "k8s")).toEqual([]);
|
|
93
99
|
|
|
94
100
|
const result = await auditCommand({ path: repo, format: "stylish" });
|
|
95
101
|
expect(result.success).toBe(true);
|
|
@@ -101,7 +107,7 @@ describe("auditCommand", () => {
|
|
|
101
107
|
|
|
102
108
|
test("discovers and audits a Helm chart (as a bundle, not loose manifests)", async () => {
|
|
103
109
|
const repo = fileURLToPath(new URL("./__fixtures__/audit-helm", import.meta.url));
|
|
104
|
-
const charts =
|
|
110
|
+
const charts = await discoverLexicon(repo, "helm");
|
|
105
111
|
expect(charts).toHaveLength(1);
|
|
106
112
|
expect(charts[0].lexicon).toBe("helm");
|
|
107
113
|
expect(charts[0].files!["Chart.yaml"]).toContain("name: mychart");
|
|
@@ -116,8 +122,8 @@ describe("auditCommand", () => {
|
|
|
116
122
|
expect([...ids].some((id) => id.startsWith("WK8"))).toBe(false);
|
|
117
123
|
});
|
|
118
124
|
|
|
119
|
-
test("discovers CI files under a repo root", () => {
|
|
120
|
-
const files =
|
|
125
|
+
test("discovers CI files under a repo root", async () => {
|
|
126
|
+
const files = await discoverLexicon(REPO, "github");
|
|
121
127
|
expect(files.map((f) => f.path)).toContain(".github/workflows/ci.yml");
|
|
122
128
|
expect(files.every((f) => f.lexicon === "github")).toBe(true);
|
|
123
129
|
});
|
|
@@ -168,7 +174,7 @@ describe("auditCommand", () => {
|
|
|
168
174
|
expect(result.success).toBe(true);
|
|
169
175
|
expect(result.wroteTo).toBe(out);
|
|
170
176
|
expect(existsSync(out)).toBe(true);
|
|
171
|
-
expect(readFileSync(out, "utf-8")).toContain("#
|
|
177
|
+
expect(readFileSync(out, "utf-8")).toContain("# chant audit");
|
|
172
178
|
rmSync(out);
|
|
173
179
|
});
|
|
174
180
|
|
|
@@ -191,7 +197,7 @@ describe("auditCommand", () => {
|
|
|
191
197
|
const result = await auditCommand({ path: tmp });
|
|
192
198
|
expect(result.success).toBe(true);
|
|
193
199
|
expect(result.exitCode).toBe(0);
|
|
194
|
-
expect(result.output).toContain("No
|
|
200
|
+
expect(result.output).toContain("No auditable files found");
|
|
195
201
|
rmSync(tmp, { recursive: true, force: true });
|
|
196
202
|
});
|
|
197
203
|
|
|
@@ -200,12 +206,13 @@ describe("auditCommand", () => {
|
|
|
200
206
|
const yaml = "name: CI\non:\n push:\npermissions: write-all\njobs:\n build:\n runs-on: ubuntu-latest\n";
|
|
201
207
|
const impl = (async (url: string | URL | Request) => {
|
|
202
208
|
const u = String(url);
|
|
209
|
+
if (u.includes("/git/trees/")) {
|
|
210
|
+
return new Response(JSON.stringify({ tree: [{ path: ".github/workflows/ci.yml", type: "blob", size: 100 }] }), { status: 200 });
|
|
211
|
+
}
|
|
203
212
|
if (u.includes("/contents/.github/workflows/ci.yml")) {
|
|
204
213
|
return new Response(JSON.stringify({ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", content: b64(yaml), encoding: "base64" }), { status: 200 });
|
|
205
214
|
}
|
|
206
|
-
if (u.
|
|
207
|
-
return new Response(JSON.stringify([{ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", size: 100 }]), { status: 200 });
|
|
208
|
-
}
|
|
215
|
+
if (/\/repos\/acme\/widgets(\?|$)/.test(u)) return new Response(JSON.stringify({ default_branch: "main" }), { status: 200 });
|
|
209
216
|
return new Response("not found", { status: 404 });
|
|
210
217
|
}) as unknown as typeof fetch;
|
|
211
218
|
|
|
@@ -222,12 +229,13 @@ describe("auditCommand", () => {
|
|
|
222
229
|
const impl = (async (url: string | URL | Request) => {
|
|
223
230
|
const u = String(url);
|
|
224
231
|
if (u.includes("/commits/v4")) return new Response(JSON.stringify({ sha }), { status: 200 });
|
|
232
|
+
if (u.includes("/git/trees/")) {
|
|
233
|
+
return new Response(JSON.stringify({ tree: [{ path: ".github/workflows/ci.yml", type: "blob", size: 100 }] }), { status: 200 });
|
|
234
|
+
}
|
|
225
235
|
if (u.includes("/contents/.github/workflows/ci.yml")) {
|
|
226
236
|
return new Response(JSON.stringify({ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", content: b64(yaml), encoding: "base64" }), { status: 200 });
|
|
227
237
|
}
|
|
228
|
-
if (u.
|
|
229
|
-
return new Response(JSON.stringify([{ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", size: 100 }]), { status: 200 });
|
|
230
|
-
}
|
|
238
|
+
if (/\/repos\/acme\/widgets(\?|$)/.test(u)) return new Response(JSON.stringify({ default_branch: "main" }), { status: 200 });
|
|
231
239
|
return new Response("not found", { status: 404 });
|
|
232
240
|
}) as unknown as typeof fetch;
|
|
233
241
|
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `chant audit` — run chant's
|
|
2
|
+
* `chant audit` — run chant's security, correctness, and best-practice checks against an existing repo's
|
|
3
3
|
* pipeline YAML and emit a tiered report. Does not require a chant project;
|
|
4
4
|
* it reads `.github/workflows`, `.gitlab-ci.yml`, and `.forgejo/workflows`
|
|
5
5
|
* directly and runs the real post-synth checks via the audit core.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { existsSync,
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import { auditFiles, type AuditInput, type AuditFinding, type AuditLexicon, type ChecksProvider } from "../../audit/core";
|
|
8
|
+
import { existsSync, statSync, writeFileSync } from "fs";
|
|
9
|
+
import { auditFiles, type AuditInput, type AuditFinding, type ChecksProvider } from "../../audit/core";
|
|
10
|
+
import { discoverByDetection, classifyFiles, loadAuditPlugins, AUDIT_LEXICONS } from "../../audit/discover";
|
|
12
11
|
import { RULE_CATALOG } from "../../audit/catalog";
|
|
13
12
|
import { renderMarkdown } from "../../audit/report";
|
|
14
13
|
import { renderHtml, type ReportTheme } from "../../audit/report-html";
|
|
15
14
|
import { buildReportJson, type AuditSnapshot } from "../../audit/report-model";
|
|
16
|
-
import {
|
|
15
|
+
import { fetchRepoFiles, resolveActionSha, resolveImageDigest, resolveRepoCommit, parseRepoUrl, FetchError } from "../../audit/fetch";
|
|
17
16
|
import { extractUnpinnedActions, extractUnpinnedImages } from "../../audit/proof";
|
|
18
17
|
import type { ProveOptions } from "../../audit/proof";
|
|
19
18
|
import type { Severity } from "../../lint/rule";
|
|
@@ -89,255 +88,6 @@ function githubToken(env: NodeJS.ProcessEnv = process.env): string | undefined {
|
|
|
89
88
|
return env.GITHUB_TOKEN ?? env.CHANT_AUDIT_GITHUB_TOKEN;
|
|
90
89
|
}
|
|
91
90
|
|
|
92
|
-
function isYaml(name: string): boolean {
|
|
93
|
-
return name.endsWith(".yml") || name.endsWith(".yaml");
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function collectDir(root: string, dir: string, lexicon: AuditLexicon, out: AuditInput[]): void {
|
|
97
|
-
const abs = join(root, dir);
|
|
98
|
-
if (!existsSync(abs) || !statSync(abs).isDirectory()) return;
|
|
99
|
-
for (const name of readdirSync(abs).sort()) {
|
|
100
|
-
if (!isYaml(name)) continue;
|
|
101
|
-
const full = join(abs, name);
|
|
102
|
-
if (!statSync(full).isFile()) continue;
|
|
103
|
-
out.push({ path: relative(root, full), content: readFileSync(full, "utf-8"), lexicon });
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** Discover CI files under a repo root. */
|
|
108
|
-
export function discoverCiFiles(root: string): AuditInput[] {
|
|
109
|
-
const inputs: AuditInput[] = [];
|
|
110
|
-
collectDir(root, ".github/workflows", "github", inputs);
|
|
111
|
-
collectDir(root, ".forgejo/workflows", "forgejo", inputs);
|
|
112
|
-
const gitlab = join(root, ".gitlab-ci.yml");
|
|
113
|
-
if (existsSync(gitlab) && statSync(gitlab).isFile()) {
|
|
114
|
-
inputs.push({ path: ".gitlab-ci.yml", content: readFileSync(gitlab, "utf-8"), lexicon: "gitlab" });
|
|
115
|
-
}
|
|
116
|
-
return inputs;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const WALK_SKIP = new Set(["node_modules", ".git", "dist", ".github", ".forgejo"]);
|
|
120
|
-
const MAX_WALK_FILES = 1000;
|
|
121
|
-
|
|
122
|
-
/** Recursively collect file paths under a root, skipping noise/dot dirs. */
|
|
123
|
-
function walkFiles(dir: string, out: string[]): void {
|
|
124
|
-
if (out.length >= MAX_WALK_FILES) return;
|
|
125
|
-
let entries;
|
|
126
|
-
try {
|
|
127
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
128
|
-
} catch {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
for (const e of entries.sort((a, b) => (a.name < b.name ? -1 : 1))) {
|
|
132
|
-
if (out.length >= MAX_WALK_FILES) return;
|
|
133
|
-
if (e.name.startsWith(".") && e.isDirectory()) continue;
|
|
134
|
-
if (WALK_SKIP.has(e.name)) continue;
|
|
135
|
-
const full = join(dir, e.name);
|
|
136
|
-
if (e.isDirectory()) walkFiles(full, out);
|
|
137
|
-
else out.push(full);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function readSafe(full: string): string | undefined {
|
|
142
|
-
try {
|
|
143
|
-
return readFileSync(full, "utf-8");
|
|
144
|
-
} catch {
|
|
145
|
-
return undefined;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const CNRM = "cnrm.cloud.google.com";
|
|
150
|
-
|
|
151
|
-
/** True if any YAML doc is a Kubernetes manifest (excluding GCP Config Connector). */
|
|
152
|
-
function looksLikeK8s(content: string): boolean {
|
|
153
|
-
for (const doc of content.split(/\n---\n/)) {
|
|
154
|
-
const t = doc.trim();
|
|
155
|
-
if (!t) continue;
|
|
156
|
-
try {
|
|
157
|
-
const obj = parseYAML(t) as Record<string, unknown>;
|
|
158
|
-
const apiVersion = obj?.apiVersion;
|
|
159
|
-
if (typeof apiVersion === "string" && typeof obj.kind === "string" && !apiVersion.includes(CNRM)) return true;
|
|
160
|
-
} catch {
|
|
161
|
-
// not parseable as a single doc — skip
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
/** True if any YAML doc is a GCP Config Connector resource (cnrm.cloud.google.com). */
|
|
168
|
-
function looksLikeGcp(content: string): boolean {
|
|
169
|
-
for (const doc of content.split(/\n---\n/)) {
|
|
170
|
-
const t = doc.trim();
|
|
171
|
-
if (!t) continue;
|
|
172
|
-
try {
|
|
173
|
-
const obj = parseYAML(t) as Record<string, unknown>;
|
|
174
|
-
if (typeof obj?.apiVersion === "string" && obj.apiVersion.includes(CNRM)) return true;
|
|
175
|
-
} catch {
|
|
176
|
-
// skip
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/** Discover GCP Config Connector manifests under a repo root. */
|
|
183
|
-
export function discoverGcp(root: string): AuditInput[] {
|
|
184
|
-
const files: string[] = [];
|
|
185
|
-
walkFiles(root, files);
|
|
186
|
-
const inputs: AuditInput[] = [];
|
|
187
|
-
for (const full of files) {
|
|
188
|
-
if (!isYaml(basename(full))) continue;
|
|
189
|
-
const content = readSafe(full);
|
|
190
|
-
if (content !== undefined && looksLikeGcp(content)) {
|
|
191
|
-
inputs.push({ path: relative(root, full), content, lexicon: "gcp" });
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return inputs;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/** Discover Kubernetes manifest files under a repo root (content-detected). */
|
|
198
|
-
export function discoverManifests(root: string): AuditInput[] {
|
|
199
|
-
const files: string[] = [];
|
|
200
|
-
walkFiles(root, files);
|
|
201
|
-
const inputs: AuditInput[] = [];
|
|
202
|
-
for (const full of files) {
|
|
203
|
-
if (!isYaml(basename(full))) continue;
|
|
204
|
-
const content = readSafe(full);
|
|
205
|
-
if (content !== undefined && looksLikeK8s(content)) {
|
|
206
|
-
inputs.push({ path: relative(root, full), content, lexicon: "k8s" });
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return inputs;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function isDockerfileName(name: string): boolean {
|
|
213
|
-
return name === "Dockerfile" || name.startsWith("Dockerfile.") || name.endsWith(".Dockerfile") || name.endsWith(".dockerfile");
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function looksLikeCompose(content: string): boolean {
|
|
217
|
-
try {
|
|
218
|
-
const obj = parseYAML(content) as Record<string, unknown>;
|
|
219
|
-
return Boolean(obj) && typeof obj === "object" && "services" in obj;
|
|
220
|
-
} catch {
|
|
221
|
-
return false;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/** Parse JSON or YAML content into an object, or undefined. */
|
|
226
|
-
function parseStructured(content: string): Record<string, unknown> | undefined {
|
|
227
|
-
try {
|
|
228
|
-
const j = JSON.parse(content);
|
|
229
|
-
if (j && typeof j === "object") return j as Record<string, unknown>;
|
|
230
|
-
} catch {
|
|
231
|
-
// not JSON — try YAML
|
|
232
|
-
}
|
|
233
|
-
try {
|
|
234
|
-
const y = parseYAML(content) as Record<string, unknown>;
|
|
235
|
-
if (y && typeof y === "object") return y;
|
|
236
|
-
} catch {
|
|
237
|
-
// not YAML either
|
|
238
|
-
}
|
|
239
|
-
return undefined;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/** True if a parsed object is a CloudFormation template. */
|
|
243
|
-
function looksLikeCloudFormation(obj: Record<string, unknown>): boolean {
|
|
244
|
-
if (obj.AWSTemplateFormatVersion !== undefined) return true;
|
|
245
|
-
const resources = obj.Resources;
|
|
246
|
-
if (resources && typeof resources === "object") {
|
|
247
|
-
for (const r of Object.values(resources as Record<string, unknown>)) {
|
|
248
|
-
const type = r && typeof r === "object" ? (r as Record<string, unknown>).Type : undefined;
|
|
249
|
-
if (typeof type === "string" && type.startsWith("AWS::")) return true;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
return false;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/**
|
|
256
|
-
* Discover CloudFormation templates (JSON or YAML, `.json`/`.yaml`/`.yml`/`.template`).
|
|
257
|
-
* The aws checks `JSON.parse` the template, so YAML templates are normalized to a
|
|
258
|
-
* JSON string here (intrinsics like `!Ref` survive as strings — fine for the
|
|
259
|
-
* structural security checks).
|
|
260
|
-
*/
|
|
261
|
-
export function discoverCloudFormation(root: string): AuditInput[] {
|
|
262
|
-
const files: string[] = [];
|
|
263
|
-
walkFiles(root, files);
|
|
264
|
-
const inputs: AuditInput[] = [];
|
|
265
|
-
for (const full of files) {
|
|
266
|
-
const name = basename(full);
|
|
267
|
-
if (!/\.(json|ya?ml|template)$/i.test(name)) continue;
|
|
268
|
-
const content = readSafe(full);
|
|
269
|
-
if (content === undefined) continue;
|
|
270
|
-
const parsed = parseStructured(content);
|
|
271
|
-
if (parsed && looksLikeCloudFormation(parsed)) {
|
|
272
|
-
inputs.push({ path: relative(root, full), content: JSON.stringify(parsed), lexicon: "aws" });
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return inputs;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
/** True if a parsed object is an Azure ARM deployment template. */
|
|
279
|
-
function looksLikeArm(obj: Record<string, unknown>): boolean {
|
|
280
|
-
return typeof obj.$schema === "string" && obj.$schema.includes("deploymentTemplate") && Array.isArray(obj.resources);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
/** Discover Azure ARM templates (`.json`) under a repo root. */
|
|
284
|
-
export function discoverArm(root: string): AuditInput[] {
|
|
285
|
-
const files: string[] = [];
|
|
286
|
-
walkFiles(root, files);
|
|
287
|
-
const inputs: AuditInput[] = [];
|
|
288
|
-
for (const full of files) {
|
|
289
|
-
if (!/\.json$/i.test(basename(full))) continue;
|
|
290
|
-
const content = readSafe(full);
|
|
291
|
-
if (content === undefined) continue;
|
|
292
|
-
const parsed = parseStructured(content);
|
|
293
|
-
if (parsed && looksLikeArm(parsed)) {
|
|
294
|
-
inputs.push({ path: relative(root, full), content: JSON.stringify(parsed), lexicon: "azure" });
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
return inputs;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
* Discover Helm charts. A chart (directory with Chart.yaml) is one bundle: the
|
|
302
|
-
* helm checks read `output.files` keyed by chart-relative path, so all of the
|
|
303
|
-
* chart's files are collected into a single AuditInput.files map.
|
|
304
|
-
*/
|
|
305
|
-
export function discoverHelm(root: string): AuditInput[] {
|
|
306
|
-
const all: string[] = [];
|
|
307
|
-
walkFiles(root, all);
|
|
308
|
-
const chartDirs = all.filter((f) => basename(f) === "Chart.yaml").map((f) => f.slice(0, -("/Chart.yaml".length)));
|
|
309
|
-
const inputs: AuditInput[] = [];
|
|
310
|
-
for (const dir of chartDirs) {
|
|
311
|
-
const prefix = dir + "/";
|
|
312
|
-
const files: Record<string, string> = {};
|
|
313
|
-
for (const f of all) {
|
|
314
|
-
if (!f.startsWith(prefix)) continue;
|
|
315
|
-
const content = readSafe(f);
|
|
316
|
-
if (content !== undefined) files[f.slice(prefix.length)] = content;
|
|
317
|
-
}
|
|
318
|
-
if (files["Chart.yaml"] === undefined) continue;
|
|
319
|
-
const chartPath = relative(root, dir) || ".";
|
|
320
|
-
inputs.push({ path: chartPath, content: files["Chart.yaml"], lexicon: "helm", files });
|
|
321
|
-
}
|
|
322
|
-
return inputs;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
/** Discover Docker artifacts: Dockerfiles (by name) and Compose files (by `services:`). */
|
|
326
|
-
export function discoverDocker(root: string): AuditInput[] {
|
|
327
|
-
const files: string[] = [];
|
|
328
|
-
walkFiles(root, files);
|
|
329
|
-
const inputs: AuditInput[] = [];
|
|
330
|
-
for (const full of files) {
|
|
331
|
-
const name = basename(full);
|
|
332
|
-
const content = readSafe(full);
|
|
333
|
-
if (content === undefined) continue;
|
|
334
|
-
if (isDockerfileName(name) || (isYaml(name) && looksLikeCompose(content))) {
|
|
335
|
-
inputs.push({ path: relative(root, full), content, lexicon: "docker" });
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
return inputs;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
91
|
function isMergeWorthy(f: AuditFinding): boolean {
|
|
342
92
|
return RULE_CATALOG[f.checkId]?.tier === "merge-worthy";
|
|
343
93
|
}
|
|
@@ -373,7 +123,7 @@ function renderStylish(findings: AuditFinding[], scanned: string[], notes: strin
|
|
|
373
123
|
const mw = findings.filter(isMergeWorthy);
|
|
374
124
|
const ro = findings.filter((f) => !isMergeWorthy(f));
|
|
375
125
|
lines.push(
|
|
376
|
-
`Audited ${scanned.length}
|
|
126
|
+
`Audited ${scanned.length} file${scanned.length === 1 ? "" : "s"} — ` +
|
|
377
127
|
`${findings.length} finding${findings.length === 1 ? "" : "s"} ` +
|
|
378
128
|
`(${mw.length} merge-worthy, ${ro.length} report-only).`,
|
|
379
129
|
);
|
|
@@ -456,12 +206,19 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
456
206
|
const isUrl = /^https?:\/\//.test(options.path);
|
|
457
207
|
|
|
458
208
|
let inputs: AuditInput[];
|
|
209
|
+
// Content-detected lexicons whose package isn't installed can't be detected
|
|
210
|
+
// (detection needs the plugin's `detectTemplate`), so a local audit would
|
|
211
|
+
// silently find nothing. Track the missing packages to turn that into a hint.
|
|
212
|
+
let missingHint = "";
|
|
459
213
|
if (isUrl) {
|
|
460
214
|
try {
|
|
461
|
-
|
|
215
|
+
// Fetch the whole repo's candidate files (all lexicons, not just CI) and
|
|
216
|
+
// run them through the same classifier the local path uses (#420).
|
|
217
|
+
const files = await fetchRepoFiles(options.path, {
|
|
462
218
|
token: options.token ?? tokenForHost(options.path),
|
|
463
219
|
fetchImpl: options.fetchImpl,
|
|
464
220
|
});
|
|
221
|
+
inputs = classifyFiles(files, await loadAuditPlugins());
|
|
465
222
|
} catch (err) {
|
|
466
223
|
const msg = err instanceof FetchError ? err.message : err instanceof Error ? err.message : String(err);
|
|
467
224
|
return { success: false, output: "", findings: [], scanned: [], exitCode: 1, error: msg };
|
|
@@ -470,26 +227,25 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
470
227
|
if (!existsSync(options.path)) {
|
|
471
228
|
return { success: false, output: "", findings: [], scanned: [], exitCode: 1, error: `Path not found: ${options.path}` };
|
|
472
229
|
}
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
const
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
inputs = [...others, ...helm];
|
|
230
|
+
// One walk, plugin-delegated detection. Each lexicon plugin's own
|
|
231
|
+
// `detectTemplate` classifies the files it recognizes; CI (path), Dockerfiles
|
|
232
|
+
// (name), and Helm charts (bundle) are handled by discoverByDetection's
|
|
233
|
+
// special-cases since content shape alone can't disambiguate them.
|
|
234
|
+
const plugins = await loadAuditPlugins();
|
|
235
|
+
const loaded = new Set(plugins.map((p) => p.name));
|
|
236
|
+
const missing = AUDIT_LEXICONS.filter((n) => !loaded.has(n));
|
|
237
|
+
if (missing.length > 0) {
|
|
238
|
+
missingHint =
|
|
239
|
+
` Some lexicon packages are not installed, so files of those types were not detected: ${missing.join(", ")}.` +
|
|
240
|
+
` Install what you need, e.g. npm i ${missing.map((n) => `@intentius/chant-lexicon-${n}`).join(" ")}`;
|
|
241
|
+
}
|
|
242
|
+
inputs = discoverByDetection(options.path, plugins);
|
|
487
243
|
}
|
|
488
244
|
|
|
489
245
|
const scanned = inputs.map((i) => i.path);
|
|
490
246
|
|
|
491
247
|
if (inputs.length === 0) {
|
|
492
|
-
return { success: true, output: `No
|
|
248
|
+
return { success: true, output: `No auditable files found under ${options.path}.${missingHint}`, findings: [], scanned: [], exitCode: 0 };
|
|
493
249
|
}
|
|
494
250
|
|
|
495
251
|
let findings: AuditFinding[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { boundPropertyTypes, type BoundablePropertyType } from "./bound-property-types";
|
|
3
|
+
|
|
4
|
+
interface Prop {
|
|
5
|
+
name: string;
|
|
6
|
+
tsType: string;
|
|
7
|
+
}
|
|
8
|
+
interface PT extends BoundablePropertyType {
|
|
9
|
+
name: string;
|
|
10
|
+
specType: string;
|
|
11
|
+
properties: Prop[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function pt(name: string, props: Array<[string, string]>): PT {
|
|
15
|
+
return { name, specType: name.split("_")[1] ?? name, properties: props.map(([n, t]) => ({ name: n, tsType: t })) };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe("boundPropertyTypes", () => {
|
|
19
|
+
test("drops property types unreachable from resource props", () => {
|
|
20
|
+
const props: Prop[] = [{ name: "sku", tsType: "R_Sku" }];
|
|
21
|
+
const types = [pt("R_Sku", [["name", "string"]]), pt("R_Unused", [["x", "string"]])];
|
|
22
|
+
const kept = boundPropertyTypes("R", props, types, new Set(), { maxDepth: 2 });
|
|
23
|
+
expect(kept.map((t) => t.name)).toEqual(["R_Sku"]);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("caps nesting depth and loosens references beyond the cap", () => {
|
|
27
|
+
const props: Prop[] = [{ name: "a", tsType: "R_A" }];
|
|
28
|
+
const types = [
|
|
29
|
+
pt("R_A", [["b", "R_B"]]),
|
|
30
|
+
pt("R_B", [["c", "R_C"]]),
|
|
31
|
+
pt("R_C", [["d", "string"]]),
|
|
32
|
+
];
|
|
33
|
+
const kept = boundPropertyTypes("R", props, types, new Set(), { maxDepth: 1 });
|
|
34
|
+
// depth 1: R_A kept, its child R_B not expanded -> loosened, R_C dropped
|
|
35
|
+
expect(kept.map((t) => t.name)).toEqual(["R_A"]);
|
|
36
|
+
expect(kept[0].properties[0].tsType).toBe("Record<string, unknown>");
|
|
37
|
+
expect(props[0].tsType).toBe("R_A"); // resource prop ref to a kept type is preserved
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("force-keeps curated property types by substring regardless of reachability", () => {
|
|
41
|
+
const props: Prop[] = [];
|
|
42
|
+
const types = [pt("R_SecurityRulePropertiesFormat", [["x", "string"]]), pt("R_Other", [["y", "string"]])];
|
|
43
|
+
const kept = boundPropertyTypes("R", props, types, new Set(), {
|
|
44
|
+
maxDepth: 1,
|
|
45
|
+
curatedDefs: ["SecurityRule"],
|
|
46
|
+
});
|
|
47
|
+
expect(kept.map((t) => t.name)).toEqual(["R_SecurityRulePropertiesFormat"]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("preserves enum references when loosening", () => {
|
|
51
|
+
const props: Prop[] = [{ name: "a", tsType: "R_A" }];
|
|
52
|
+
const types = [pt("R_A", [["mode", "R_ModeEnum"], ["nested", "R_Deep"]]), pt("R_Deep", [["x", "string"]])];
|
|
53
|
+
const kept = boundPropertyTypes("R", props, types, new Set(["R_ModeEnum"]), { maxDepth: 1 });
|
|
54
|
+
expect(kept[0].properties[0].tsType).toBe("R_ModeEnum"); // enum kept
|
|
55
|
+
expect(kept[0].properties[1].tsType).toBe("Record<string, unknown>"); // deep type loosened
|
|
56
|
+
});
|
|
57
|
+
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bound the set of generated property-type interfaces a lexicon emits.
|
|
3
|
+
*
|
|
4
|
+
* Cloud provider schemas (ARM, CloudFormation, …) embed a named definition for
|
|
5
|
+
* every nested object. Fully expanding all of them per-resource produces huge
|
|
6
|
+
* `.d.ts` declarations (azure was ~273MB — see #438). This helper keeps only
|
|
7
|
+
* the property types reachable from a resource's top-level properties within a
|
|
8
|
+
* depth bound, plus a curated force-keep set; references that fall outside the
|
|
9
|
+
* kept set are rewritten to `Record<string, unknown>`. Enum references are
|
|
10
|
+
* preserved. The input property refs are mutated in place. (#438, #440)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** A reference carrying a TypeScript type string, mutated in place when loosened. */
|
|
14
|
+
export interface BoundablePropertyRef {
|
|
15
|
+
tsType: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A generated property type (named interface) and its properties. */
|
|
19
|
+
export interface BoundablePropertyType {
|
|
20
|
+
name: string;
|
|
21
|
+
specType: string;
|
|
22
|
+
properties: BoundablePropertyRef[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface BoundPropertyTypeOptions {
|
|
26
|
+
/** Maximum nesting depth of emitted property-type interfaces. */
|
|
27
|
+
maxDepth: number;
|
|
28
|
+
/**
|
|
29
|
+
* Definition-name substrings to always keep regardless of reachability/depth
|
|
30
|
+
* (e.g. common nested shapes worth typing for authoring ergonomics). Matched
|
|
31
|
+
* by substring against each property type's `specType`.
|
|
32
|
+
*/
|
|
33
|
+
curatedDefs?: Iterable<string>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function escapeRegExp(s: string): string {
|
|
37
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Extract `${shortName}_Ident` type-reference tokens from a tsType string. */
|
|
41
|
+
function referencedTypeNames(tsType: string, shortName: string): string[] {
|
|
42
|
+
const re = new RegExp("\\b" + escapeRegExp(shortName) + "_[A-Za-z0-9_]+", "g");
|
|
43
|
+
return tsType.match(re) ?? [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Filter `propertyTypes` to the bounded set and loosen out-of-set references.
|
|
48
|
+
* Mutates the `tsType` of `resourceProps` and retained property types; returns
|
|
49
|
+
* the retained property types.
|
|
50
|
+
*/
|
|
51
|
+
export function boundPropertyTypes<PT extends BoundablePropertyType>(
|
|
52
|
+
shortName: string,
|
|
53
|
+
resourceProps: BoundablePropertyRef[],
|
|
54
|
+
propertyTypes: PT[],
|
|
55
|
+
enumNames: Set<string>,
|
|
56
|
+
options: BoundPropertyTypeOptions,
|
|
57
|
+
): PT[] {
|
|
58
|
+
const { maxDepth } = options;
|
|
59
|
+
const curated = options.curatedDefs ? [...options.curatedDefs] : [];
|
|
60
|
+
const byName = new Map(propertyTypes.map((pt) => [pt.name, pt] as const));
|
|
61
|
+
const reached = new Set<string>();
|
|
62
|
+
const queue: Array<{ name: string; depth: number }> = [];
|
|
63
|
+
|
|
64
|
+
const seed = (tsType: string, depth: number) => {
|
|
65
|
+
for (const n of referencedTypeNames(tsType, shortName)) {
|
|
66
|
+
if (byName.has(n) && !reached.has(n)) {
|
|
67
|
+
reached.add(n);
|
|
68
|
+
queue.push({ name: n, depth });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
for (const p of resourceProps) seed(p.tsType, 1);
|
|
74
|
+
// Force-keep curated property types (depth-1 seeds), matched by substring so
|
|
75
|
+
// schema variant names (e.g. "SecurityRulePropertiesFormat") are retained.
|
|
76
|
+
if (curated.length > 0) {
|
|
77
|
+
for (const pt of propertyTypes) {
|
|
78
|
+
if (!reached.has(pt.name) && curated.some((c) => pt.specType.includes(c))) {
|
|
79
|
+
reached.add(pt.name);
|
|
80
|
+
queue.push({ name: pt.name, depth: 1 });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
while (queue.length) {
|
|
85
|
+
const { name, depth } = queue.shift()!;
|
|
86
|
+
if (depth >= maxDepth) continue; // do not expand children beyond the cap
|
|
87
|
+
const pt = byName.get(name);
|
|
88
|
+
if (!pt) continue;
|
|
89
|
+
for (const p of pt.properties) seed(p.tsType, depth + 1);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Rewrite references to property types we are not emitting (keep enums).
|
|
93
|
+
const loosen = (tsType: string): string => {
|
|
94
|
+
let out = tsType;
|
|
95
|
+
for (const n of referencedTypeNames(tsType, shortName)) {
|
|
96
|
+
if (reached.has(n) || enumNames.has(n) || !byName.has(n)) continue;
|
|
97
|
+
out = out.replace(new RegExp("\\b" + escapeRegExp(n) + "\\b", "g"), "Record<string, unknown>");
|
|
98
|
+
}
|
|
99
|
+
return out;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
for (const p of resourceProps) p.tsType = loosen(p.tsType);
|
|
103
|
+
const retained: PT[] = [];
|
|
104
|
+
for (const pt of propertyTypes) {
|
|
105
|
+
if (!reached.has(pt.name)) continue;
|
|
106
|
+
for (const p of pt.properties) p.tsType = loosen(p.tsType);
|
|
107
|
+
retained.push(pt);
|
|
108
|
+
}
|
|
109
|
+
return retained;
|
|
110
|
+
}
|