@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.
- 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 +17 -2
- package/src/audit/catalog.ts +357 -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/audit/rules-doc.ts +4 -0
- package/src/cli/commands/__fixtures__/audit-aws/stack.yaml +11 -0
- package/src/cli/commands/__fixtures__/audit-aws/template.json +7 -0
- package/src/cli/commands/__fixtures__/audit-azure/azuredeploy.json +13 -0
- package/src/cli/commands/__fixtures__/audit-gcp/bucket.yaml +6 -0
- package/src/cli/commands/__fixtures__/audit-gcp/firewall.yaml +9 -0
- package/src/cli/commands/__fixtures__/audit-helm/mychart/Chart.yaml +3 -0
- package/src/cli/commands/__fixtures__/audit-helm/mychart/templates/deployment.yaml +12 -0
- package/src/cli/commands/__fixtures__/audit-helm/mychart/values.yaml +1 -0
- 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 +80 -14
- package/src/cli/commands/audit.ts +28 -126
- 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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"AWSTemplateFormatVersion":"2010-09-09","Resources":{"B":{"Type":"AWS::S3::Bucket"}}}
|
|
@@ -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");
|
|
@@ -58,8 +64,66 @@ describe("auditCommand", () => {
|
|
|
58
64
|
expect(ids).toContain("DKRD003"); // compose exposes SSH port 22
|
|
59
65
|
});
|
|
60
66
|
|
|
61
|
-
test("discovers
|
|
62
|
-
const
|
|
67
|
+
test("discovers and audits CloudFormation (JSON and YAML)", async () => {
|
|
68
|
+
const repo = fileURLToPath(new URL("./__fixtures__/audit-aws", import.meta.url));
|
|
69
|
+
const files = await discoverLexicon(repo, "aws");
|
|
70
|
+
const paths = files.map((f) => f.path).sort();
|
|
71
|
+
expect(paths).toContain("template.json");
|
|
72
|
+
expect(paths).toContain("stack.yaml");
|
|
73
|
+
// YAML is normalized to a JSON string the aws checks can JSON.parse.
|
|
74
|
+
expect(() => JSON.parse(files.find((f) => f.path === "stack.yaml")!.content)).not.toThrow();
|
|
75
|
+
|
|
76
|
+
const result = await auditCommand({ path: repo, format: "stylish" });
|
|
77
|
+
expect(result.success).toBe(true);
|
|
78
|
+
const ids = new Set(result.findings.map((f) => f.checkId));
|
|
79
|
+
expect(ids).toContain("WAW018"); // S3 missing public access block (JSON template)
|
|
80
|
+
expect(ids).toContain("WAW021"); // RDS not encrypted (JSON template)
|
|
81
|
+
expect(ids).toContain("WAW019"); // SG open SSH (YAML template — proves YAML works)
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("discovers and audits Azure ARM templates", async () => {
|
|
85
|
+
const repo = fileURLToPath(new URL("./__fixtures__/audit-azure", import.meta.url));
|
|
86
|
+
expect((await discoverLexicon(repo, "azure")).map((f) => f.path)).toContain("azuredeploy.json");
|
|
87
|
+
const result = await auditCommand({ path: repo, format: "stylish" });
|
|
88
|
+
expect(result.success).toBe(true);
|
|
89
|
+
const ids = new Set(result.findings.map((f) => f.checkId));
|
|
90
|
+
expect(ids).toContain("AZR014"); // storage allows public blob access
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("discovers and audits GCP Config Connector (not misclassified as k8s)", async () => {
|
|
94
|
+
const repo = fileURLToPath(new URL("./__fixtures__/audit-gcp", import.meta.url));
|
|
95
|
+
const gcp = await discoverLexicon(repo, "gcp");
|
|
96
|
+
expect(gcp.map((f) => f.path).sort()).toEqual(["bucket.yaml", "firewall.yaml"]);
|
|
97
|
+
// cnrm manifests must NOT also be picked up as k8s.
|
|
98
|
+
expect(await discoverLexicon(repo, "k8s")).toEqual([]);
|
|
99
|
+
|
|
100
|
+
const result = await auditCommand({ path: repo, format: "stylish" });
|
|
101
|
+
expect(result.success).toBe(true);
|
|
102
|
+
const ids = new Set(result.findings.map((f) => f.checkId));
|
|
103
|
+
expect(ids).toContain("WGC109"); // firewall open to 0.0.0.0/0
|
|
104
|
+
// k8s checks did not run on these.
|
|
105
|
+
expect([...ids].some((id) => id.startsWith("WK8"))).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test("discovers and audits a Helm chart (as a bundle, not loose manifests)", async () => {
|
|
109
|
+
const repo = fileURLToPath(new URL("./__fixtures__/audit-helm", import.meta.url));
|
|
110
|
+
const charts = await discoverLexicon(repo, "helm");
|
|
111
|
+
expect(charts).toHaveLength(1);
|
|
112
|
+
expect(charts[0].lexicon).toBe("helm");
|
|
113
|
+
expect(charts[0].files!["Chart.yaml"]).toContain("name: mychart");
|
|
114
|
+
expect(charts[0].files!["templates/deployment.yaml"]).toContain("privileged");
|
|
115
|
+
|
|
116
|
+
const result = await auditCommand({ path: repo, format: "stylish" });
|
|
117
|
+
expect(result.success).toBe(true);
|
|
118
|
+
const ids = new Set(result.findings.map((f) => f.checkId));
|
|
119
|
+
expect(ids).toContain("WHM401"); // :latest image in the chart
|
|
120
|
+
expect(ids).toContain("WHM404"); // privileged container in a template
|
|
121
|
+
// the chart's template was NOT double-audited as a loose k8s manifest
|
|
122
|
+
expect([...ids].some((id) => id.startsWith("WK8"))).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("discovers CI files under a repo root", async () => {
|
|
126
|
+
const files = await discoverLexicon(REPO, "github");
|
|
63
127
|
expect(files.map((f) => f.path)).toContain(".github/workflows/ci.yml");
|
|
64
128
|
expect(files.every((f) => f.lexicon === "github")).toBe(true);
|
|
65
129
|
});
|
|
@@ -110,7 +174,7 @@ describe("auditCommand", () => {
|
|
|
110
174
|
expect(result.success).toBe(true);
|
|
111
175
|
expect(result.wroteTo).toBe(out);
|
|
112
176
|
expect(existsSync(out)).toBe(true);
|
|
113
|
-
expect(readFileSync(out, "utf-8")).toContain("#
|
|
177
|
+
expect(readFileSync(out, "utf-8")).toContain("# chant audit");
|
|
114
178
|
rmSync(out);
|
|
115
179
|
});
|
|
116
180
|
|
|
@@ -133,7 +197,7 @@ describe("auditCommand", () => {
|
|
|
133
197
|
const result = await auditCommand({ path: tmp });
|
|
134
198
|
expect(result.success).toBe(true);
|
|
135
199
|
expect(result.exitCode).toBe(0);
|
|
136
|
-
expect(result.output).toContain("No
|
|
200
|
+
expect(result.output).toContain("No auditable files found");
|
|
137
201
|
rmSync(tmp, { recursive: true, force: true });
|
|
138
202
|
});
|
|
139
203
|
|
|
@@ -142,12 +206,13 @@ describe("auditCommand", () => {
|
|
|
142
206
|
const yaml = "name: CI\non:\n push:\npermissions: write-all\njobs:\n build:\n runs-on: ubuntu-latest\n";
|
|
143
207
|
const impl = (async (url: string | URL | Request) => {
|
|
144
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
|
+
}
|
|
145
212
|
if (u.includes("/contents/.github/workflows/ci.yml")) {
|
|
146
213
|
return new Response(JSON.stringify({ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", content: b64(yaml), encoding: "base64" }), { status: 200 });
|
|
147
214
|
}
|
|
148
|
-
if (u.
|
|
149
|
-
return new Response(JSON.stringify([{ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", size: 100 }]), { status: 200 });
|
|
150
|
-
}
|
|
215
|
+
if (/\/repos\/acme\/widgets(\?|$)/.test(u)) return new Response(JSON.stringify({ default_branch: "main" }), { status: 200 });
|
|
151
216
|
return new Response("not found", { status: 404 });
|
|
152
217
|
}) as unknown as typeof fetch;
|
|
153
218
|
|
|
@@ -164,12 +229,13 @@ describe("auditCommand", () => {
|
|
|
164
229
|
const impl = (async (url: string | URL | Request) => {
|
|
165
230
|
const u = String(url);
|
|
166
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
|
+
}
|
|
167
235
|
if (u.includes("/contents/.github/workflows/ci.yml")) {
|
|
168
236
|
return new Response(JSON.stringify({ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", content: b64(yaml), encoding: "base64" }), { status: 200 });
|
|
169
237
|
}
|
|
170
|
-
if (u.
|
|
171
|
-
return new Response(JSON.stringify([{ name: "ci.yml", path: ".github/workflows/ci.yml", type: "file", size: 100 }]), { status: 200 });
|
|
172
|
-
}
|
|
238
|
+
if (/\/repos\/acme\/widgets(\?|$)/.test(u)) return new Response(JSON.stringify({ default_branch: "main" }), { status: 200 });
|
|
173
239
|
return new Response("not found", { status: 404 });
|
|
174
240
|
}) as unknown as typeof fetch;
|
|
175
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,122 +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
|
-
/** True if any YAML document in the content is a Kubernetes manifest. */
|
|
150
|
-
function looksLikeK8s(content: string): boolean {
|
|
151
|
-
for (const doc of content.split(/\n---\n/)) {
|
|
152
|
-
const t = doc.trim();
|
|
153
|
-
if (!t) continue;
|
|
154
|
-
try {
|
|
155
|
-
const obj = parseYAML(t) as Record<string, unknown>;
|
|
156
|
-
if (obj && typeof obj.apiVersion === "string" && typeof obj.kind === "string") return true;
|
|
157
|
-
} catch {
|
|
158
|
-
// not parseable as a single doc — skip
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** Discover Kubernetes manifest files under a repo root (content-detected). */
|
|
165
|
-
export function discoverManifests(root: string): AuditInput[] {
|
|
166
|
-
const files: string[] = [];
|
|
167
|
-
walkFiles(root, files);
|
|
168
|
-
const inputs: AuditInput[] = [];
|
|
169
|
-
for (const full of files) {
|
|
170
|
-
if (!isYaml(basename(full))) continue;
|
|
171
|
-
const content = readSafe(full);
|
|
172
|
-
if (content !== undefined && looksLikeK8s(content)) {
|
|
173
|
-
inputs.push({ path: relative(root, full), content, lexicon: "k8s" });
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return inputs;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function isDockerfileName(name: string): boolean {
|
|
180
|
-
return name === "Dockerfile" || name.startsWith("Dockerfile.") || name.endsWith(".Dockerfile") || name.endsWith(".dockerfile");
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function looksLikeCompose(content: string): boolean {
|
|
184
|
-
try {
|
|
185
|
-
const obj = parseYAML(content) as Record<string, unknown>;
|
|
186
|
-
return Boolean(obj) && typeof obj === "object" && "services" in obj;
|
|
187
|
-
} catch {
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/** Discover Docker artifacts: Dockerfiles (by name) and Compose files (by `services:`). */
|
|
193
|
-
export function discoverDocker(root: string): AuditInput[] {
|
|
194
|
-
const files: string[] = [];
|
|
195
|
-
walkFiles(root, files);
|
|
196
|
-
const inputs: AuditInput[] = [];
|
|
197
|
-
for (const full of files) {
|
|
198
|
-
const name = basename(full);
|
|
199
|
-
const content = readSafe(full);
|
|
200
|
-
if (content === undefined) continue;
|
|
201
|
-
if (isDockerfileName(name) || (isYaml(name) && looksLikeCompose(content))) {
|
|
202
|
-
inputs.push({ path: relative(root, full), content, lexicon: "docker" });
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return inputs;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
91
|
function isMergeWorthy(f: AuditFinding): boolean {
|
|
209
92
|
return RULE_CATALOG[f.checkId]?.tier === "merge-worthy";
|
|
210
93
|
}
|
|
@@ -240,7 +123,7 @@ function renderStylish(findings: AuditFinding[], scanned: string[], notes: strin
|
|
|
240
123
|
const mw = findings.filter(isMergeWorthy);
|
|
241
124
|
const ro = findings.filter((f) => !isMergeWorthy(f));
|
|
242
125
|
lines.push(
|
|
243
|
-
`Audited ${scanned.length}
|
|
126
|
+
`Audited ${scanned.length} file${scanned.length === 1 ? "" : "s"} — ` +
|
|
244
127
|
`${findings.length} finding${findings.length === 1 ? "" : "s"} ` +
|
|
245
128
|
`(${mw.length} merge-worthy, ${ro.length} report-only).`,
|
|
246
129
|
);
|
|
@@ -323,12 +206,19 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
323
206
|
const isUrl = /^https?:\/\//.test(options.path);
|
|
324
207
|
|
|
325
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 = "";
|
|
326
213
|
if (isUrl) {
|
|
327
214
|
try {
|
|
328
|
-
|
|
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, {
|
|
329
218
|
token: options.token ?? tokenForHost(options.path),
|
|
330
219
|
fetchImpl: options.fetchImpl,
|
|
331
220
|
});
|
|
221
|
+
inputs = classifyFiles(files, await loadAuditPlugins());
|
|
332
222
|
} catch (err) {
|
|
333
223
|
const msg = err instanceof FetchError ? err.message : err instanceof Error ? err.message : String(err);
|
|
334
224
|
return { success: false, output: "", findings: [], scanned: [], exitCode: 1, error: msg };
|
|
@@ -337,13 +227,25 @@ export async function auditCommand(options: AuditCommandOptions): Promise<AuditC
|
|
|
337
227
|
if (!existsSync(options.path)) {
|
|
338
228
|
return { success: false, output: "", findings: [], scanned: [], exitCode: 1, error: `Path not found: ${options.path}` };
|
|
339
229
|
}
|
|
340
|
-
|
|
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);
|
|
341
243
|
}
|
|
342
244
|
|
|
343
245
|
const scanned = inputs.map((i) => i.path);
|
|
344
246
|
|
|
345
247
|
if (inputs.length === 0) {
|
|
346
|
-
return { success: true, output: `No
|
|
248
|
+
return { success: true, output: `No auditable files found under ${options.path}.${missingHint}`, findings: [], scanned: [], exitCode: 0 };
|
|
347
249
|
}
|
|
348
250
|
|
|
349
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
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import { generatePostSynthBarrel, type BarrelEntry } from "./generate-post-synth-barrel";
|
|
3
|
+
|
|
4
|
+
describe("generatePostSynthBarrel", () => {
|
|
5
|
+
test("emits a DO NOT EDIT banner, static imports, and an id-sorted array", () => {
|
|
6
|
+
const entries: BarrelEntry[] = [
|
|
7
|
+
{ module: "./gha021", exportName: "gha021", id: "GHA021" },
|
|
8
|
+
{ module: "./apt-no-recommends", exportName: "dkrd010", id: "DKRD010" },
|
|
9
|
+
{ module: "./gha006", exportName: "gha006", id: "GHA006" },
|
|
10
|
+
];
|
|
11
|
+
const out = generatePostSynthBarrel(entries);
|
|
12
|
+
|
|
13
|
+
expect(out).toContain("// Code generated by chant generate. DO NOT EDIT.");
|
|
14
|
+
expect(out).toContain('import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";');
|
|
15
|
+
// Export name may differ from the module name (docker/helm/temporal).
|
|
16
|
+
expect(out).toContain('import { dkrd010 } from "./apt-no-recommends";');
|
|
17
|
+
// Array is ordered by id, not insertion order.
|
|
18
|
+
const arrayBody = out.slice(out.indexOf("postSynthChecks: PostSynthCheck[] = ["));
|
|
19
|
+
expect(arrayBody.indexOf("dkrd010")).toBeLessThan(arrayBody.indexOf("gha006"));
|
|
20
|
+
expect(arrayBody.indexOf("gha006")).toBeLessThan(arrayBody.indexOf("gha021"));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("is deterministic regardless of input order", () => {
|
|
24
|
+
const a: BarrelEntry[] = [
|
|
25
|
+
{ module: "./b", exportName: "b", id: "X2" },
|
|
26
|
+
{ module: "./a", exportName: "a", id: "X1" },
|
|
27
|
+
];
|
|
28
|
+
const b = [...a].reverse();
|
|
29
|
+
expect(generatePostSynthBarrel(a)).toBe(generatePostSynthBarrel(b));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test("empty input yields an empty array, still valid", () => {
|
|
33
|
+
const out = generatePostSynthBarrel([]);
|
|
34
|
+
expect(out).toContain("export const postSynthChecks: PostSynthCheck[] = [");
|
|
35
|
+
expect(out).toContain("];");
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate a static, importable barrel of a lexicon's post-synth checks.
|
|
3
|
+
*
|
|
4
|
+
* The runtime loader (`discoverPostSynthChecks`) readdir-globs the directory and
|
|
5
|
+
* `require()`s each file via tsx — fs- and tsx-coupled, so it can't run in a
|
|
6
|
+
* bundle or on an edge runtime (Cloudflare Workers). This generator produces a
|
|
7
|
+
* committed `lint/post-synth/index.ts` with explicit static imports + an exported
|
|
8
|
+
* `postSynthChecks` array, which bundlers can follow with no fs/tsx/require. See #409.
|
|
9
|
+
*
|
|
10
|
+
* Detection reuses the runtime's own `isPostSynthCheck`, so the barrel contains
|
|
11
|
+
* exactly the checks discovery would — including files whose export name differs
|
|
12
|
+
* from the filename (docker/helm/temporal) and excluding helper modules
|
|
13
|
+
* (cf-refs, arm-refs, …) that export no check.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { createRequire } from "module";
|
|
17
|
+
import { listRuleFiles } from "../lint/discover";
|
|
18
|
+
import { isPostSynthCheck } from "../lint/post-synth";
|
|
19
|
+
|
|
20
|
+
export interface BarrelEntry {
|
|
21
|
+
/** Module specifier relative to the barrel, e.g. "./gha021" or "./apt-no-recommends". */
|
|
22
|
+
module: string;
|
|
23
|
+
/** Exported binding holding the check, e.g. "gha021" or "dkrd010". */
|
|
24
|
+
exportName: string;
|
|
25
|
+
/** The check's id (e.g. "GHA021"), used for stable ordering. */
|
|
26
|
+
id: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Scan a post-synth directory and return one entry per exported check.
|
|
31
|
+
* Runs at codegen time on Node (tsx-powered require), never in the shipped path.
|
|
32
|
+
*/
|
|
33
|
+
export function scanPostSynthChecks(dir: string, importMetaUrl: string): BarrelEntry[] {
|
|
34
|
+
const require = createRequire(importMetaUrl);
|
|
35
|
+
const entries: BarrelEntry[] = [];
|
|
36
|
+
for (const file of listRuleFiles(dir)) {
|
|
37
|
+
const base = file.replace(/\.ts$/, "");
|
|
38
|
+
let mod: Record<string, unknown>;
|
|
39
|
+
try {
|
|
40
|
+
mod = require(`${dir}/${base}`) as Record<string, unknown>;
|
|
41
|
+
} catch {
|
|
42
|
+
continue; // a file that fails to load contributes nothing
|
|
43
|
+
}
|
|
44
|
+
for (const [exportName, value] of Object.entries(mod)) {
|
|
45
|
+
if (isPostSynthCheck(value)) {
|
|
46
|
+
entries.push({ module: `./${base}`, exportName, id: value.id });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return entries;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Render the barrel file content from scanned entries (pure — easy to unit test). */
|
|
54
|
+
export function generatePostSynthBarrel(entries: BarrelEntry[]): string {
|
|
55
|
+
const sorted = [...entries].sort((a, b) => a.id.localeCompare(b.id));
|
|
56
|
+
const lines: string[] = [];
|
|
57
|
+
lines.push("// Code generated by chant generate. DO NOT EDIT.");
|
|
58
|
+
lines.push('import type { PostSynthCheck } from "@intentius/chant/lint/post-synth";');
|
|
59
|
+
for (const e of sorted) {
|
|
60
|
+
lines.push(`import { ${e.exportName} } from ${JSON.stringify(e.module)};`);
|
|
61
|
+
}
|
|
62
|
+
lines.push("");
|
|
63
|
+
lines.push("export const postSynthChecks: PostSynthCheck[] = [");
|
|
64
|
+
for (const e of sorted) {
|
|
65
|
+
lines.push(` ${e.exportName},`);
|
|
66
|
+
}
|
|
67
|
+
lines.push("];");
|
|
68
|
+
lines.push("");
|
|
69
|
+
return lines.join("\n");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Scan a directory and render its barrel in one step. */
|
|
73
|
+
export function renderPostSynthBarrelForDir(dir: string, importMetaUrl: string): string {
|
|
74
|
+
return generatePostSynthBarrel(scanPostSynthChecks(dir, importMetaUrl));
|
|
75
|
+
}
|