@dommaker/harness 1.7.0 → 1.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/CHANGELOG.md +37 -0
- package/bin/harness.js +17 -1
- package/dist/agents/index.d.ts +6 -2
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +6 -16
- package/dist/agents/index.js.map +1 -1
- package/dist/cli/commands/__tests__/gate-decision.d.ts +18 -0
- package/dist/cli/commands/__tests__/gate-decision.d.ts.map +1 -0
- package/dist/cli/commands/__tests__/gate-decision.js +26 -0
- package/dist/cli/commands/__tests__/gate-decision.js.map +1 -0
- package/dist/cli/commands/acceptance.d.ts.map +1 -1
- package/dist/cli/commands/acceptance.js +17 -28
- package/dist/cli/commands/acceptance.js.map +1 -1
- package/dist/cli/commands/check.d.ts +11 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +26 -11
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/command.d.ts +0 -1
- package/dist/cli/commands/command.d.ts.map +1 -1
- package/dist/cli/commands/command.js +8 -6
- package/dist/cli/commands/command.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +36 -57
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/contract.d.ts.map +1 -1
- package/dist/cli/commands/contract.js +36 -42
- package/dist/cli/commands/contract.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts +2 -0
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +11 -6
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/failure.d.ts.map +1 -1
- package/dist/cli/commands/failure.js +16 -2
- package/dist/cli/commands/failure.js.map +1 -1
- package/dist/cli/commands/init.d.ts +14 -8
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +172 -364
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/knowledge-view.d.ts +90 -0
- package/dist/cli/commands/knowledge-view.d.ts.map +1 -0
- package/dist/cli/commands/knowledge-view.js +198 -0
- package/dist/cli/commands/knowledge-view.js.map +1 -0
- package/dist/cli/commands/knowledge.d.ts +137 -52
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +534 -400
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/passes-gate.d.ts +4 -1
- package/dist/cli/commands/passes-gate.d.ts.map +1 -1
- package/dist/cli/commands/passes-gate.js +11 -2
- package/dist/cli/commands/passes-gate.js.map +1 -1
- package/dist/cli/commands/performance.d.ts.map +1 -1
- package/dist/cli/commands/performance.js +19 -31
- package/dist/cli/commands/performance.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +2 -1
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/review.d.ts.map +1 -1
- package/dist/cli/commands/review.js +13 -25
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +73 -0
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -0
- package/dist/cli/commands/scaffold-templates.js +424 -0
- package/dist/cli/commands/scaffold-templates.js.map +1 -0
- package/dist/cli/commands/scaffold.d.ts +105 -0
- package/dist/cli/commands/scaffold.d.ts.map +1 -0
- package/dist/cli/commands/scaffold.js +263 -0
- package/dist/cli/commands/scaffold.js.map +1 -0
- package/dist/cli/commands/security.d.ts.map +1 -1
- package/dist/cli/commands/security.js +22 -24
- package/dist/cli/commands/security.js.map +1 -1
- package/dist/cli/commands/spec-baseline-check.d.ts +43 -0
- package/dist/cli/commands/spec-baseline-check.d.ts.map +1 -1
- package/dist/cli/commands/spec-baseline-check.js +109 -34
- package/dist/cli/commands/spec-baseline-check.js.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.d.ts +21 -2
- package/dist/cli/commands/sync-docs/context-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/context-syncer.js +112 -28
- package/dist/cli/commands/sync-docs/context-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +86 -27
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts +24 -1
- package/dist/cli/commands/sync-docs/project-reader.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/project-reader.js +33 -7
- package/dist/cli/commands/sync-docs/project-reader.js.map +1 -1
- package/dist/cli/commands/validate.d.ts +0 -16
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/cli/commands/validate.js +2 -97
- package/dist/cli/commands/validate.js.map +1 -1
- package/dist/cli/gate-command.d.ts +37 -0
- package/dist/cli/gate-command.d.ts.map +1 -0
- package/dist/cli/gate-command.js +64 -0
- package/dist/cli/gate-command.js.map +1 -0
- package/dist/completion-checkers/index.d.ts +1 -1
- package/dist/completion-checkers/index.d.ts.map +1 -1
- package/dist/completion-checkers/index.js +0 -15
- package/dist/completion-checkers/index.js.map +1 -1
- package/dist/context/knowledge-injector.d.ts +4 -2
- package/dist/context/knowledge-injector.d.ts.map +1 -1
- package/dist/context/knowledge-injector.js +13 -6
- package/dist/context/knowledge-injector.js.map +1 -1
- package/dist/core/constraints/agent-prompt-renderer.js +2 -2
- package/dist/core/constraints/agent-prompt-renderer.js.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.d.ts +20 -0
- package/dist/core/constraints/capabilities-reconcile.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-reconcile.js +26 -4
- package/dist/core/constraints/capabilities-reconcile.js.map +1 -1
- package/dist/core/constraints/check-cache.d.ts +1 -1
- package/dist/core/constraints/check-cache.js +1 -1
- package/dist/core/constraints/checker.d.ts +8 -3
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +30 -19
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +3 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +17 -24
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/context-doc-sync.js +9 -6
- package/dist/core/constraints/checkers/context-doc-sync.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +24 -30
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +10 -4
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +10 -6
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +7 -3
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/context-builder.d.ts +11 -10
- package/dist/core/constraints/context-builder.d.ts.map +1 -1
- package/dist/core/constraints/context-builder.js +31 -27
- package/dist/core/constraints/context-builder.js.map +1 -1
- package/dist/core/constraints/context-reconcile.d.ts +75 -0
- package/dist/core/constraints/context-reconcile.d.ts.map +1 -0
- package/dist/core/constraints/context-reconcile.js +161 -0
- package/dist/core/constraints/context-reconcile.js.map +1 -0
- package/dist/core/constraints/doc-freshness/runner.d.ts +7 -0
- package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
- package/dist/core/constraints/doc-freshness/runner.js +8 -3
- package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
- package/dist/core/constraints/git-evidence.js +1 -1
- package/dist/core/constraints/git-evidence.js.map +1 -1
- package/dist/core/constraints/injection-drift.d.ts +7 -2
- package/dist/core/constraints/injection-drift.d.ts.map +1 -1
- package/dist/core/constraints/injection-drift.js +13 -7
- package/dist/core/constraints/injection-drift.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +92 -0
- package/dist/core/constraints/run-env.d.ts.map +1 -0
- package/dist/core/constraints/run-env.js +175 -0
- package/dist/core/constraints/run-env.js.map +1 -0
- package/dist/core/constraints/triggers.d.ts +22 -0
- package/dist/core/constraints/triggers.d.ts.map +1 -0
- package/dist/core/constraints/triggers.js +31 -0
- package/dist/core/constraints/triggers.js.map +1 -0
- package/dist/core/effective-constraints.d.ts +13 -4
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +16 -6
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/effective-set.d.ts +8 -15
- package/dist/core/effective-set.d.ts.map +1 -1
- package/dist/core/effective-set.js +7 -12
- package/dist/core/effective-set.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +39 -17
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +51 -122
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/check-handlers/http.d.ts +2 -2
- package/dist/core/validators/check-handlers/http.d.ts.map +1 -1
- package/dist/core/validators/check-handlers/http.js +12 -7
- package/dist/core/validators/check-handlers/http.js.map +1 -1
- package/dist/failure/classifier.d.ts +1 -1
- package/dist/failure/classifier.d.ts.map +1 -1
- package/dist/failure/classifier.js +8 -8
- package/dist/failure/classifier.js.map +1 -1
- package/dist/failure/index.d.ts +2 -1
- package/dist/failure/index.d.ts.map +1 -1
- package/dist/failure/index.js +9 -17
- package/dist/failure/index.js.map +1 -1
- package/dist/failure/recorder.d.ts +1 -1
- package/dist/failure/recorder.d.ts.map +1 -1
- package/dist/gates/checker-gate.d.ts +2 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +3 -2
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/command.d.ts +10 -4
- package/dist/gates/command.d.ts.map +1 -1
- package/dist/gates/command.js +36 -62
- package/dist/gates/command.js.map +1 -1
- package/dist/gates/contract.d.ts.map +1 -1
- package/dist/gates/contract.js +0 -4
- package/dist/gates/contract.js.map +1 -1
- package/dist/gates/definitions.d.ts +2 -1
- package/dist/gates/definitions.d.ts.map +1 -1
- package/dist/gates/definitions.js +6 -5
- package/dist/gates/definitions.js.map +1 -1
- package/dist/gates/index.d.ts +0 -4
- package/dist/gates/index.d.ts.map +1 -1
- package/dist/gates/index.js +1 -6
- package/dist/gates/index.js.map +1 -1
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +0 -4
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/runner.d.ts +1 -1
- package/dist/gates/runner.js +1 -1
- package/dist/gates/security.d.ts +1 -1
- package/dist/gates/security.d.ts.map +1 -1
- package/dist/gates/security.js +5 -6
- package/dist/gates/security.js.map +1 -1
- package/dist/gates/types.d.ts +14 -17
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/gates/types.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +6 -4
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +25 -13
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/config.d.ts +6 -2
- package/dist/hooks/config.d.ts.map +1 -1
- package/dist/hooks/config.js +5 -1
- package/dist/hooks/config.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +0 -15
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/pipeline.d.ts.map +1 -1
- package/dist/hooks/pipeline.js +2 -4
- package/dist/hooks/pipeline.js.map +1 -1
- package/dist/hooks/registry.d.ts +11 -8
- package/dist/hooks/registry.d.ts.map +1 -1
- package/dist/hooks/registry.js +22 -7
- package/dist/hooks/registry.js.map +1 -1
- package/dist/hooks/types.d.ts +23 -7
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/knowledge/audit-scoring.d.ts +87 -0
- package/dist/knowledge/audit-scoring.d.ts.map +1 -0
- package/dist/knowledge/audit-scoring.js +470 -0
- package/dist/knowledge/audit-scoring.js.map +1 -0
- package/dist/knowledge/audit.d.ts +11 -59
- package/dist/knowledge/audit.d.ts.map +1 -1
- package/dist/knowledge/audit.js +47 -522
- package/dist/knowledge/audit.js.map +1 -1
- package/dist/knowledge/import.d.ts.map +1 -1
- package/dist/knowledge/import.js +2 -0
- package/dist/knowledge/import.js.map +1 -1
- package/dist/knowledge/index-generator.d.ts.map +1 -1
- package/dist/knowledge/index-generator.js +4 -5
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/knowledge/index.d.ts +3 -2
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/knowledge/index.js +3 -16
- package/dist/knowledge/index.js.map +1 -1
- package/dist/knowledge/ingest.js +1 -1
- package/dist/knowledge/ingest.js.map +1 -1
- package/dist/knowledge/lifecycle.d.ts.map +1 -1
- package/dist/knowledge/lifecycle.js +4 -2
- package/dist/knowledge/lifecycle.js.map +1 -1
- package/dist/knowledge/migration.d.ts +3 -0
- package/dist/knowledge/migration.d.ts.map +1 -1
- package/dist/knowledge/migration.js +8 -2
- package/dist/knowledge/migration.js.map +1 -1
- package/dist/knowledge/query.d.ts +6 -0
- package/dist/knowledge/query.d.ts.map +1 -1
- package/dist/knowledge/query.js +8 -2
- package/dist/knowledge/query.js.map +1 -1
- package/dist/knowledge/reference-tracker.d.ts.map +1 -1
- package/dist/knowledge/reference-tracker.js +4 -6
- package/dist/knowledge/reference-tracker.js.map +1 -1
- package/dist/knowledge/store.d.ts +22 -16
- package/dist/knowledge/store.d.ts.map +1 -1
- package/dist/knowledge/store.js +53 -12
- package/dist/knowledge/store.js.map +1 -1
- package/dist/knowledge/tree-walker.d.ts +19 -0
- package/dist/knowledge/tree-walker.d.ts.map +1 -0
- package/dist/knowledge/tree-walker.js +30 -0
- package/dist/knowledge/tree-walker.js.map +1 -0
- package/dist/knowledge/types.d.ts +16 -0
- package/dist/knowledge/types.d.ts.map +1 -1
- package/dist/monitoring/traces.d.ts +5 -3
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +19 -5
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/test-setup/jsonl-fake-fs.d.ts +30 -0
- package/dist/test-setup/jsonl-fake-fs.d.ts.map +1 -0
- package/dist/test-setup/jsonl-fake-fs.js +49 -0
- package/dist/test-setup/jsonl-fake-fs.js.map +1 -0
- package/dist/tools/index.d.ts +4 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +7 -15
- package/dist/tools/index.js.map +1 -1
- package/dist/types/checkpoint.d.ts +5 -0
- package/dist/types/checkpoint.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +18 -0
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/trace.d.ts +13 -4
- package/dist/types/trace.d.ts.map +1 -1
- package/dist/types/trace.js +4 -3
- package/dist/types/trace.js.map +1 -1
- package/dist/utils/exec.d.ts +4 -27
- package/dist/utils/exec.d.ts.map +1 -1
- package/dist/utils/exec.js +4 -94
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/jsonl.d.ts +17 -7
- package/dist/utils/jsonl.d.ts.map +1 -1
- package/dist/utils/jsonl.js +144 -17
- package/dist/utils/jsonl.js.map +1 -1
- package/dist/utils/numeric-flag.d.ts +44 -0
- package/dist/utils/numeric-flag.d.ts.map +1 -0
- package/dist/utils/numeric-flag.js +62 -0
- package/dist/utils/numeric-flag.js.map +1 -0
- package/package.json +1 -1
- package/src/CONTEXT.md +5 -2
- package/src/__tests__/acceptance-gate.test.ts +7 -4
- package/src/__tests__/checker-extra.test.ts +3 -2
- package/src/__tests__/checker.test.ts +4 -3
- package/src/__tests__/checkpoint.test.ts +26 -9
- package/src/__tests__/context-files-resolution.test.ts +29 -15
- package/src/__tests__/contract-gate.test.ts +19 -40
- package/src/__tests__/error-classifier.test.ts +1 -1
- package/src/__tests__/exec.test.ts +45 -107
- package/src/__tests__/failure-recorder.test.ts +7 -5
- package/src/__tests__/hooks-pipeline.test.ts +34 -47
- package/src/__tests__/layering.test.ts +217 -25
- package/src/__tests__/passes-gate.test.ts +9 -4
- package/src/__tests__/performance-gate.test.ts +3 -15
- package/src/__tests__/project-config-loader-extra.test.ts +31 -57
- package/src/__tests__/project-config-loader.test.ts +6 -3
- package/src/__tests__/public-exports.test.ts +0 -1
- package/src/__tests__/public-type-surface.test.ts +5 -4
- package/src/__tests__/public-value-surface.test.ts +182 -0
- package/src/__tests__/review-gate.test.ts +4 -6
- package/src/__tests__/security-gate.test.ts +5 -20
- package/src/__tests__/skip-semantics.test.ts +5 -3
- package/src/__tests__/spec-validator.test.ts +3 -2
- package/src/__tests__/sub-barrels-explicit.test.ts +87 -0
- package/src/__tests__/test-fixture-hygiene.test.ts +53 -0
- package/src/__tests__/trace-analyzer-extra.test.ts +5 -3
- package/src/__tests__/trace-analyzer.test.ts +5 -3
- package/src/__tests__/trace-collector.test.ts +5 -3
- package/src/agents/index.ts +13 -2
- package/src/cli/__tests__/gate-command.test.ts +162 -0
- package/src/cli/commands/CONTEXT.md +29 -5
- package/src/cli/commands/__tests__/acceptance.test.ts +24 -25
- package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +15 -0
- package/src/cli/commands/__tests__/check-drift.test.ts +42 -1
- package/src/cli/commands/__tests__/check-read-count.test.ts +167 -0
- package/src/cli/commands/__tests__/check-skip-output.test.ts +4 -1
- package/src/cli/commands/__tests__/check.test.ts +43 -36
- package/src/cli/commands/__tests__/command-gate-dead-options.test.ts +60 -0
- package/src/cli/commands/__tests__/command.test.ts +33 -17
- package/src/cli/commands/__tests__/constraints-retire.test.ts +52 -0
- package/src/cli/commands/__tests__/context-syncer.test.ts +141 -0
- package/src/cli/commands/__tests__/contract.test.ts +20 -20
- package/src/cli/commands/__tests__/failure.test.ts +5 -4
- package/src/cli/commands/__tests__/gate-decision.ts +30 -0
- package/src/cli/commands/__tests__/init-dead-options.test.ts +50 -0
- package/src/cli/commands/__tests__/init-governance-io.test.ts +139 -0
- package/src/cli/commands/__tests__/init-injection.test.ts +3 -3
- package/src/cli/commands/__tests__/init-ondisk.test.ts +867 -0
- package/src/cli/commands/__tests__/init.test.ts +82 -6
- package/src/cli/commands/__tests__/knowledge-audit-threshold.test.ts +146 -0
- package/src/cli/commands/__tests__/knowledge-flywheel-consistency.test.ts +1 -1
- package/src/cli/commands/__tests__/knowledge-tone-mapping.test.ts +244 -0
- package/src/cli/commands/__tests__/knowledge-view.test.ts +839 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +33 -15
- package/src/cli/commands/__tests__/numeric-flag-assembly.test.ts +368 -0
- package/src/cli/commands/__tests__/performance.test.ts +19 -21
- package/src/cli/commands/__tests__/posteval-plan.test.ts +5 -3
- package/src/cli/commands/__tests__/pre-push-hook.test.ts +181 -0
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +95 -2
- package/src/cli/commands/__tests__/project-path-convention.test.ts +103 -25
- package/src/cli/commands/__tests__/registry.test.ts +2 -0
- package/src/cli/commands/__tests__/report.test.ts +23 -1
- package/src/cli/commands/__tests__/review.test.ts +10 -17
- package/src/cli/commands/__tests__/scaffold.test.ts +505 -0
- package/src/cli/commands/__tests__/security-default-scan.test.ts +75 -0
- package/src/cli/commands/__tests__/security.test.ts +21 -24
- package/src/cli/commands/__tests__/spec-baseline-check.test.ts +3 -2
- package/src/cli/commands/__tests__/spec-baseline-index-cap.test.ts +131 -0
- package/src/cli/commands/__tests__/spec-baseline-read-count.test.ts +325 -0
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +19 -4
- package/src/cli/commands/__tests__/sync-docs-context-drift.test.ts +232 -0
- package/src/cli/commands/__tests__/sync-docs-module-order.test.ts +111 -0
- package/src/cli/commands/__tests__/sync-docs-read-count.test.ts +192 -0
- package/src/cli/commands/__tests__/sync-docs.test.ts +3 -2
- package/src/cli/commands/__tests__/validate.test.ts +1 -48
- package/src/cli/commands/acceptance.ts +24 -29
- package/src/cli/commands/check.ts +40 -17
- package/src/cli/commands/command.ts +9 -8
- package/src/cli/commands/constraints-retire.ts +57 -70
- package/src/cli/commands/contract.ts +44 -47
- package/src/cli/commands/definitions.ts +12 -6
- package/src/cli/commands/failure.ts +16 -3
- package/src/cli/commands/init.ts +242 -389
- package/src/cli/commands/knowledge-view.ts +224 -0
- package/src/cli/commands/knowledge.ts +643 -448
- package/src/cli/commands/passes-gate.ts +12 -3
- package/src/cli/commands/performance.ts +31 -38
- package/src/cli/commands/report.ts +3 -2
- package/src/cli/commands/review.ts +22 -28
- package/src/cli/commands/scaffold-templates.ts +406 -0
- package/src/cli/commands/scaffold.ts +302 -0
- package/src/cli/commands/security.ts +27 -23
- package/src/cli/commands/spec-baseline-check.ts +158 -33
- package/src/cli/commands/sync-docs/__tests__/context-export-surface.test.ts +102 -0
- package/src/cli/commands/sync-docs/__tests__/module-descriptions.test.ts +196 -0
- package/src/cli/commands/sync-docs/context-syncer.ts +137 -31
- package/src/cli/commands/sync-docs/index.ts +103 -28
- package/src/cli/commands/sync-docs/project-reader.ts +51 -7
- package/src/cli/commands/validate.ts +1 -104
- package/src/cli/gate-command.ts +87 -0
- package/src/completion-checkers/index.ts +10 -1
- package/src/context/CONTEXT.md +2 -1
- package/src/context/__tests__/knowledge-injector.test.ts +27 -0
- package/src/context/__tests__/session-manager.test.ts +19 -12
- package/src/context/knowledge-injector.ts +14 -7
- package/src/core/CONTEXT.md +10 -7
- package/src/core/__tests__/effective-set.test.ts +36 -60
- package/src/core/constraints/__tests__/context-reconcile.test.ts +246 -0
- package/src/core/constraints/__tests__/run-env.test.ts +377 -0
- package/src/core/constraints/__tests__/triggers.test.ts +70 -0
- package/src/core/constraints/agent-prompt-renderer.ts +1 -1
- package/src/core/constraints/capabilities-reconcile.ts +28 -4
- package/src/core/constraints/check-cache.ts +1 -1
- package/src/core/constraints/checker.ts +30 -14
- package/src/core/constraints/checkers/__tests__/context-doc-sync.test.ts +8 -3
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +16 -7
- package/src/core/constraints/checkers/__tests__/no-hardcoded-credentials.test.ts +2 -1
- package/src/core/constraints/checkers/capability-sync.ts +27 -28
- package/src/core/constraints/checkers/context-doc-sync.ts +9 -7
- package/src/core/constraints/checkers/docs-freshness.ts +28 -33
- package/src/core/constraints/checkers/governance-presence.ts +10 -6
- package/src/core/constraints/checkers/types.ts +14 -8
- package/src/core/constraints/context-builder.ts +35 -26
- package/src/core/constraints/context-reconcile.ts +200 -0
- package/src/core/constraints/doc-freshness/__tests__/runner.test.ts +25 -0
- package/src/core/constraints/doc-freshness/runner.ts +18 -4
- package/src/core/constraints/git-evidence.ts +1 -1
- package/src/core/constraints/injection-drift.ts +19 -8
- package/src/core/constraints/run-env.ts +207 -0
- package/src/core/constraints/triggers.ts +33 -0
- package/src/core/effective-constraints.ts +17 -6
- package/src/core/effective-set.ts +8 -24
- package/src/core/index.ts +2 -0
- package/src/core/project-config-loader.ts +54 -102
- package/src/core/validators/__tests__/http-retry-sleep.test.ts +231 -0
- package/src/core/validators/check-handlers/http.ts +18 -7
- package/src/failure/CONTEXT.md +2 -1
- package/src/failure/classifier.ts +2 -2
- package/src/failure/index.ts +14 -2
- package/src/failure/recorder.ts +1 -1
- package/src/gates/CONTEXT.md +13 -7
- package/src/gates/__tests__/checker-gate.test.ts +1 -1
- package/src/gates/__tests__/command-single-predicate.test.ts +135 -0
- package/src/gates/__tests__/command.test.ts +8 -15
- package/src/gates/__tests__/contract.test.ts +5 -5
- package/src/gates/__tests__/gate-interface.test.ts +0 -17
- package/src/gates/__tests__/performance.test.ts +0 -13
- package/src/gates/__tests__/review.test.ts +0 -1
- package/src/gates/__tests__/runner.test.ts +1 -1
- package/src/gates/__tests__/security-default-scan.test.ts +97 -0
- package/src/gates/__tests__/security.test.ts +0 -13
- package/src/gates/checker-gate.ts +3 -2
- package/src/gates/command.ts +51 -74
- package/src/gates/contract.ts +0 -5
- package/src/gates/definitions.ts +6 -5
- package/src/gates/index.ts +0 -5
- package/src/gates/performance.ts +1 -6
- package/src/gates/runner.ts +1 -1
- package/src/gates/security.ts +9 -11
- package/src/gates/types.ts +20 -23
- package/src/hooks/CONTEXT.md +10 -9
- package/src/hooks/__tests__/bootstrap.test.ts +29 -6
- package/src/hooks/__tests__/pipeline.test.ts +28 -17
- package/src/hooks/__tests__/registry.test.ts +51 -3
- package/src/hooks/bootstrap.ts +35 -15
- package/src/hooks/config.ts +6 -2
- package/src/hooks/index.ts +10 -1
- package/src/hooks/pipeline.ts +4 -6
- package/src/hooks/registry.ts +31 -12
- package/src/hooks/types.ts +24 -7
- package/src/index.ts +1 -2
- package/src/knowledge/CONTEXT.md +14 -7
- package/src/knowledge/__tests__/audit-labels.test.ts +8 -5
- package/src/knowledge/__tests__/audit-scoring.test.ts +230 -0
- package/src/knowledge/__tests__/audit-write-count.test.ts +131 -0
- package/src/knowledge/__tests__/audit.test.ts +291 -208
- package/src/knowledge/__tests__/doctor.test.ts +3 -2
- package/src/knowledge/__tests__/ingest.test.ts +3 -2
- package/src/knowledge/__tests__/lifecycle.test.ts +57 -2
- package/src/knowledge/__tests__/migration.test.ts +35 -6
- package/src/knowledge/__tests__/query.test.ts +3 -2
- package/src/knowledge/__tests__/reference-tracker.test.ts +23 -2
- package/src/knowledge/__tests__/store.test.ts +149 -4
- package/src/knowledge/__tests__/tree-walker.test.ts +132 -0
- package/src/knowledge/audit-scoring.ts +589 -0
- package/src/knowledge/audit.ts +55 -599
- package/src/knowledge/import.ts +2 -0
- package/src/knowledge/index-generator.ts +4 -6
- package/src/knowledge/index.ts +27 -2
- package/src/knowledge/ingest.ts +1 -1
- package/src/knowledge/lifecycle.ts +5 -2
- package/src/knowledge/migration.ts +8 -2
- package/src/knowledge/query.ts +8 -1
- package/src/knowledge/reference-tracker.ts +5 -7
- package/src/knowledge/store.ts +75 -16
- package/src/knowledge/tree-walker.ts +29 -0
- package/src/knowledge/types.ts +19 -0
- package/src/monitoring/CONTEXT.md +5 -2
- package/src/monitoring/__tests__/context-tracker.test.ts +16 -8
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +80 -0
- package/src/monitoring/traces.ts +19 -5
- package/src/release/CONTEXT.md +1 -1
- package/src/release/__tests__/integrity.test.ts +21 -12
- package/src/test-setup/__tests__/jsonl-fake-fs.test.ts +98 -0
- package/src/test-setup/jsonl-fake-fs.ts +59 -0
- package/src/tools/index.ts +5 -1
- package/src/types/checkpoint.ts +6 -0
- package/src/types/project-config.ts +21 -0
- package/src/types/trace.ts +14 -4
- package/src/utils/__tests__/file-walk.test.ts +3 -2
- package/src/utils/__tests__/jsonl-bounded-read.test.ts +106 -0
- package/src/utils/__tests__/jsonl-skip-disposition.test.ts +10 -6
- package/src/utils/__tests__/jsonl.test.ts +61 -14
- package/src/utils/__tests__/numeric-flag.test.ts +80 -0
- package/src/utils/__tests__/package-version.test.ts +2 -2
- package/src/utils/exec.ts +4 -64
- package/src/utils/jsonl.ts +159 -20
- package/src/utils/numeric-flag.ts +64 -0
- package/dist/failure/types.d.ts +0 -9
- package/dist/failure/types.d.ts.map +0 -1
- package/dist/failure/types.js +0 -25
- package/dist/failure/types.js.map +0 -1
- package/dist/gates/effective-gates.d.ts +0 -30
- package/dist/gates/effective-gates.d.ts.map +0 -1
- package/dist/gates/effective-gates.js +0 -81
- package/dist/gates/effective-gates.js.map +0 -1
- package/src/failure/types.ts +0 -9
- package/src/gates/__tests__/effective-gates.test.ts +0 -77
- package/src/gates/effective-gates.ts +0 -99
package/src/failure/recorder.ts
CHANGED
package/src/gates/CONTEXT.md
CHANGED
|
@@ -5,25 +5,30 @@
|
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
7
|
- 统一协议:`Gate{id, order, evaluate(ctx)}` → `GateDecision{status: deny|abstain|ask, result: GateResult}`(`types.ts`;`decisionFromResult` 报告→决策映射,浅冻结)
|
|
8
|
-
- 注册表:`gateRegistry`(`registry.ts`)——定义即注册 + 构建期双向闭环(定义无实现/实现无定义/重复 id → 加载期抛错);`getGate`(未注册抛错)/`listRegisteredGates`/`registeredGateCount`
|
|
8
|
+
- 注册表:`gateRegistry`(`registry.ts`)——定义即注册 + 构建期双向闭环(定义无实现/实现无定义/重复 id → 加载期抛错);`getGate`(未注册抛错)/`listRegisteredGates`/`registeredGateCount`;闭环校验本体是 `assertGateRegistryClosed(definitions, implementations)`(本模块加载时对 `GATE_DEFINITIONS` × `IMPLEMENTATIONS` 自跑一次,消费方亦可自带两张表调用)
|
|
9
9
|
- 定义表:`GATE_DEFINITIONS`(`definitions.ts`)——id/description/默认 order/CLI 元数据;CLI 元数据形状即 `CommandDefinition`(ADR-0007:门禁与非门禁共用同一命令定义形状,bin 单引擎单循环;门禁特有语义 = `subcommandStrict:false` 未知子命令落回默认 action + `bareRunsAction:true` 裸跑执行 action);bin/harness.js 注册表驱动生成 6 门禁命令(纯数据模块,禁 import 实现,保 --help 懒加载;CLI 实现引用为 module+export,per-command 懒加载,H5)
|
|
10
10
|
- 执行器:`runGates`(`runner.ts`)——按 order 升序;deny 单调(下游 abstain 不得改回 allow)+ ask 枚举预留 fail-closed = deny;决策浅冻结
|
|
11
|
-
-
|
|
12
|
-
- checker-as-guard 接线点:`createCheckerGate(check)`(`checker-gate.ts`)——ConstraintCheck → Gate(studio #129 随动);判定经唯一归一点 `normalizeCheckOutcome`(harness#119/ADR-0016):`false` 或 `CheckDetail.pass=false` → deny(证据行随理由带出),`true`(含 pass+提示)/'skip' → abstain;env 经 `buildCheckEnv(..., 'none')` 构造(显式不接证据,语义见 core/constraints/checkers/types.ts 工厂 doc)
|
|
11
|
+
- 生效集(已删除):原 `getEffectiveGates` + config.yml `gates.order` / `gates.<id>.enabled` 三重空——无调用方、无任何项目配置过该段、README 无示例且加载器对顶层键名拼错静默通过。`order` 字段与 `runGates` 保留(链内默认序 + 复开点),裁决理由记在 ADR-0002 文末「后续变更」
|
|
12
|
+
- checker-as-guard 接线点:`createCheckerGate(check)`(`checker-gate.ts`)——ConstraintCheck → Gate(studio #129 随动);判定经唯一归一点 `normalizeCheckOutcome`(harness#119/ADR-0016):`false` 或 `CheckDetail.pass=false` → deny(证据行随理由带出),`true`(含 pass+提示)/'skip' → abstain;env 经 `buildCheckEnv(..., 'none')` 构造(显式不接证据,语义见 core/constraints/checkers/types.ts 工厂 doc);`ctx.runEnv` 传入即整条守卫链共用一枚运行级观察面——项目配置文件整链至多读一次,不传则每道门禁自造一枚一次性观察面(判定不变,ADR-0023)
|
|
13
13
|
- 门禁类:`ReviewGate` / `SecurityGate` / `PerformanceGate` / `ContractGate` / `SpecAcceptanceGate` / `CommandGate`(各自执行细节私有,保留 `check()`/`scan()` 报告方法);SpecAcceptanceGate 的 runner 输出解读与 e2e 判负都从 `core/validators/test-output.ts` 的 `judgeTestRun` 取(解析收口 ADR-0012、判定依据 ADR-0014,本层既不自带正则也不自写判定 `if`)
|
|
14
|
-
-
|
|
14
|
+
- 命令黑名单的模块级出口(`command.ts`):规则表正本 `DEFAULT_COMMAND_BLACKLIST`(每台 `CommandGate` 实例构造时拷它作基线,运行时扩展点是 `addRule()`)、默认实例单例出口 `getCommandGate()`、快捷判定 `isCommandAllowed(command)` → boolean 与 `getCommandRiskLevel(command)` → `'high' | 'medium' | 'low'`(后三者都经包根对下游公开,ADR-0003 显式清单)。仓内不靠它们做判定:CLI `command` 用带配置的那台实例(#135/ADR-0024,`command-gate-dead-options.test.ts` 钉住 CLI 源码零引用 `getCommandGate(`/`getCommandRiskLevel`),hook 恒裸构造(见「约定」);`DEFAULT_COMMAND_BLACKLIST` 的仓内消费者是 `harness command --list` 的展示
|
|
15
|
+
- `types.ts` — GateResult(报告结构,保留)/ GateContext / GateDecision 等公共类型 + 报告构造器 `pass` / `fail` / `fromError`(动态 passed 走 `gateResult`):timestamp 与 duration 口径唯一落点,门禁实现禁止手写 GateResult 字面量(duration 是可选字段,漏写无编译期报错)。**`GateContext` 只带运行信息(在哪跑 / 跑什么),配置一律走构造器**——曾有 6 个与构造器平行的配置型字段(`projectId` 等),填了不生效,已删(架构评审候选1)
|
|
15
16
|
- 便捷工厂函数:createReviewGate / createSecurityGate / createPerformanceGate / createContractGate / createSpecAcceptanceGate / createCommandGate
|
|
16
17
|
|
|
17
18
|
## 依赖关系
|
|
18
|
-
- 依赖 `src/core/`(
|
|
19
|
+
- 依赖 `src/core/`(checker-gate 适配 checkers 注册表类型并经 `buildCheckEnv` 构造执行环境)
|
|
19
20
|
- 依赖 `src/types/` 公共类型
|
|
20
21
|
|
|
21
22
|
## 约定
|
|
22
23
|
- 新门禁必须:① 在 `definitions.ts` 补 GateDefinition(含 CLI 元数据)② 在 `registry.ts` IMPLEMENTATIONS 注册实现(缺一 → 加载期抛错)③ 实现统一 Gate 接口(evaluate 产三态决策,报告由 `types.ts` 的 `pass`/`fail`/`fromError`/`gateResult` 构造,不手写字面量)④ 配 CLI 命令(命令实现文件 + CLI 元数据的 module+export 引用,bin 由定义表驱动生成,不再手写块)+ 测试文件
|
|
23
24
|
- deny 单调是接口契约:决策浅冻结,下游不得改写上游决策
|
|
24
|
-
- `
|
|
25
|
+
- **统一接口 `evaluate()` 的生产消费者 = 6 个门禁 CLI 命令**(架构评审候选1 步骤 2):命令一律穿过 `evaluate()`,`GateDecision → CommandResult` 的映射与失败措辞收在 `src/cli/gate-command.ts` 一处,不再各写一遍(此前是 6 份 `<id> gate denied` + 4 份 `<id> gate error`)。报告面 `check()`/`scan()` 仍是各门禁私有实现,只展示不判断的子命令(`security audit` 等)直读它
|
|
26
|
+
- `runGates` 是 Gate **链**语义的参考实现(deny 单调 / ask fail-closed / 全决策浅冻结):链级无生产调用方——6 个命令各跑一项(CLI 文件驱动逐命令执行),studio `runCompletionGuards` 是自己的三个 checker、不 import 本层,故消费方只有测试。「deny 后短路跳过剩余门禁」类优化在出现真实链消费者前不做——它与「按执行顺序的全部决策」报告契约直接冲突,短路等于改 GateResult 语义(harness#115 裁决,防后续评审重复提议);`order` 字段与本执行器即为该复开点保留(config.yml 的声明式顺序/开关面按 ADR-0022 口径收缩,理由记在 ADR-0002 文末「后续变更」)
|
|
25
27
|
- ask 枚举预留:暂无实现,runGates fail-closed 按 deny 计
|
|
26
|
-
- **收到的根要传到自己每个 IO/执行点(harness#95)**:门禁只认 `context.projectPath`,内部相对子路径一律锚到它(`SpecAcceptanceGate` 的 tasksPath、`ContractGate` 的 contractPath、e2e/scan 的 `cwd`),禁止「根已传入却又取 cwd」、禁止 `xxxPath: './…'` 相对默认值——否则 `-p` 半失效且表现为假绿(acceptance 的「无 tasks.yml 即跳过」= passed:true)。本层唯一保留的 cwd 站点是 `checker-gate` 对 `ctx.projectPath`
|
|
28
|
+
- **收到的根要传到自己每个 IO/执行点(harness#95)**:门禁只认 `context.projectPath`,内部相对子路径一律锚到它(`SpecAcceptanceGate` 的 tasksPath、`ContractGate` 的 contractPath、e2e/scan 的 `cwd`),禁止「根已传入却又取 cwd」、禁止 `xxxPath: './…'` 相对默认值——否则 `-p` 半失效且表现为假绿(acceptance 的「无 tasks.yml 即跳过」= passed:true)。本层唯一保留的 cwd 站点是 `checker-gate` 对 `ctx.projectPath` 的缺省兜底(豁免理由见守护表)。适用域自 #139 起覆盖到 monitoring 侧的落点默认值:约定正本是「相对片段(`DEFAULT_TRACE_FILE`)必须由消费点锚根」,闸 3 的键名后缀由 `Path` 扩到 `Path|File|Log` 并新增 `xxxFile: SOME_CONSTANT` 常量引用臂(本层曾漏检的形状正是它)。约定正本与四道机器可检的闸见 `src/cli/commands/CONTEXT.md`
|
|
29
|
+
- **CommandGate 的匹配语义只有一个落点(#135/ADR-0024)**:谓词 `matchCommand`(规则表遍历 + 类别忽略 + 模式测试)与折裁点 `judge`(级别 → 三桶 + `allowed` + `riskLevel`)同为私有,`check()` / `isAllowed()` / `getRiskLevel()` 是这份裁决的三个投影——禁止在投影里再写匹配循环。等级取命中集合里的最高档而非首条命中,不变式「存在阻断级命中 ⟺ `check` 不通过 ⟺ `isAllowed` false ⟺ 等级 high」由 `__tests__/command-single-predicate.test.ts` 的源形状闸(`pattern.test(` / `ignoreCategories.includes(` / 遍历规则表的循环体各恰好一处)+ 三投影一致性闸钉住
|
|
30
|
+
- **hook 只用出厂规则(#135 裁决,不是疏漏)**:`src/pretool-use-hook.ts` 恒裸构造 `new CommandGate()`,不装项目配置——hook 是 fail-open 的纵深防御一道,阻断级之外的命中同样放行(`isAllowed` 只看阻断级);配置装载只属于 CLI 侧(`harness command`,判定与 `--level` 共用它创建的那台实例)。两条入口是同一决策模块的两个 adapter,不得在 hook 路径另起一套判定
|
|
31
|
+
- **本入口的值面(24 项运行时导出)逐字冻结在 `src/__tests__/public-value-surface.test.ts`**(ADR-0022 追记第 5 条收口):删掉或新增任一值导出——含上面 6 个 `create*Gate` 工厂(其中 5 个是本入口内联声明)——即红且指名 `./gates` 与符号,「这个函数不在公开面上、删它非 breaking」不再靠人 grep。本入口的值导出恰好全数是包根 `.` 的子集,删除会连带撞 `public-exports.test.ts`,但那个套件报的是 `.`、分不清入口归属;类型面另归 `public-type-surface.test.ts`。增删属公共面 breaking,须走发布级别裁决(ADR-0003/0022)
|
|
27
32
|
|
|
28
33
|
## 注意事项
|
|
29
34
|
- 门禁系统不包含业务逻辑,只提供检查能力
|
|
@@ -32,3 +37,4 @@
|
|
|
32
37
|
- `PerformanceGate` 只执法有真实现的维度(coverage 走 json-summary、bundleSize 走 dist 测量);responseTime/memoryUsage/throughput 三维无真实现(原 Math.random 伪造 + 未接线的 runBenchmark)已随 benchmark 机制整体删除(ADR-0018,架构评审候选2)
|
|
33
38
|
- config.yml gates 段为新增面:引用未注册 id 直接抛错(无历史残留配置需兼容)
|
|
34
39
|
- CommandGate 为命令黑名单检查(SEC-006)
|
|
40
|
+
- `SecurityGate` 的扫描命令是**三级优先级**(`scan()` 内 `??` 链,自上而下取第一个已提供值):① `context.securityScanCommand` ② 构造时传入的 `scanCommand` ③ `detectScanCommand()` 自动探测(现为常量 `npm audit --json`)。缺省「未提供」的唯一表示是 `undefined`——构造器把 `''` 归一成 `undefined`(`getConfig()`/`this.config` 因此是 `SecurityGateConfig` 而非 `Required<…>`,`scanCommand` 保持可选);曾因兜成空串令 `??` 链恒停在第二级、第三级探测不可达,`harness security` 不带 `--scan-command` 必抛 `The argument 'file' cannot be empty`(#138)。按包管理器探测(读 lock 选 pnpm/yarn)与 `auditDetails` 的吞错各另开票,本层不改 `detectScanCommand` 常量返回
|
|
@@ -6,7 +6,7 @@ import { createCheckerGate } from '../checker-gate';
|
|
|
6
6
|
import { contextFlag, contextEvidenceFlag } from '../../core/constraints/checkers/types';
|
|
7
7
|
|
|
8
8
|
describe('createCheckerGate', () => {
|
|
9
|
-
const ctx = {
|
|
9
|
+
const ctx = {projectPath: '/tmp/project' };
|
|
10
10
|
|
|
11
11
|
it('checker 通过(true)→ abstain', async () => {
|
|
12
12
|
const gate = createCheckerGate(contextFlag('flag-pass', () => true), 7);
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CommandGate 单一匹配谓词闸(#135,架构评审候选 6)
|
|
3
|
+
*
|
|
4
|
+
* 三条逐行同构的匹配循环(`check()` 经 `checkBlacklist()` / `isAllowed()` / `getRiskLevel()`)
|
|
5
|
+
* 收成一个谓词「命令串 → 命中规则集合」,三个入口退化成它之上的投影。本文件钉两件事:
|
|
6
|
+
* ① 源形状闸——匹配语义(模式测试 / 类别忽略)在文件内只有一处落点,grep 可证;
|
|
7
|
+
* ② 一致性闸——同一条目经三个投影得到的裁决互相自洽。
|
|
8
|
+
*
|
|
9
|
+
* ② 用 `addRule()` 挂的合成规则跑,不涉出厂规则表内容(对外措辞纪律 PIT-021)。
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as fs from 'fs';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
import { CommandGate } from '../command';
|
|
15
|
+
import type { CommandBlacklistRule } from '../types';
|
|
16
|
+
|
|
17
|
+
const source = fs.readFileSync(path.join(__dirname, '..', 'command.ts'), 'utf-8');
|
|
18
|
+
|
|
19
|
+
/** 数一个针脚在源文件里出现几次(`split` 计数,不做正则解释) */
|
|
20
|
+
function count(needle: string): number {
|
|
21
|
+
return source.split(needle).length - 1;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** 合成规则:三条各自只匹配自己的记号,级别覆盖 block/warn/audit 三档 */
|
|
25
|
+
const SYN_BLOCK: CommandBlacklistRule = {
|
|
26
|
+
id: 'syn-block',
|
|
27
|
+
pattern: /\bSYN_BLOCK\b/,
|
|
28
|
+
level: 'block',
|
|
29
|
+
message: '合成阻断',
|
|
30
|
+
category: 'syn-a',
|
|
31
|
+
};
|
|
32
|
+
const SYN_WARN: CommandBlacklistRule = {
|
|
33
|
+
id: 'syn-warn',
|
|
34
|
+
pattern: /\bSYN_WARN\b/,
|
|
35
|
+
level: 'warn',
|
|
36
|
+
message: '合成警告',
|
|
37
|
+
category: 'syn-b',
|
|
38
|
+
};
|
|
39
|
+
const SYN_AUDIT: CommandBlacklistRule = {
|
|
40
|
+
id: 'syn-audit',
|
|
41
|
+
pattern: /\bSYN_AUDIT\b/,
|
|
42
|
+
level: 'audit',
|
|
43
|
+
message: '合成审计',
|
|
44
|
+
category: 'syn-c',
|
|
45
|
+
};
|
|
46
|
+
const SYN_IGNORED: CommandBlacklistRule = {
|
|
47
|
+
id: 'syn-ignored',
|
|
48
|
+
pattern: /\bSYN_IGNORED\b/,
|
|
49
|
+
level: 'block',
|
|
50
|
+
message: '合成阻断(类别被忽略)',
|
|
51
|
+
category: 'syn-d',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/** 装配一台只认合成规则的门禁:出厂规则整体移除,投影一致性判定的输入面因此可控 */
|
|
55
|
+
function syntheticGate(): CommandGate {
|
|
56
|
+
const gate = new CommandGate();
|
|
57
|
+
for (const rule of gate.getBlacklist()) gate.removeRule(rule.id);
|
|
58
|
+
gate.addRule(SYN_WARN);
|
|
59
|
+
gate.addRule(SYN_BLOCK);
|
|
60
|
+
gate.addRule(SYN_AUDIT);
|
|
61
|
+
gate.addRule(SYN_IGNORED);
|
|
62
|
+
return gate;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
describe('CommandGate 匹配循环唯一化(#135)', () => {
|
|
66
|
+
describe('源形状闸:匹配语义只有一处落点', () => {
|
|
67
|
+
it('规则模式测试恰好一处(此前三条循环各写一遍)', () => {
|
|
68
|
+
expect(count('pattern.test(')).toBe(1);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('类别忽略判定恰好一处', () => {
|
|
72
|
+
expect(count('ignoreCategories.includes(')).toBe(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('遍历规则表的循环体恰好一处,且不再有逐条 switch 分派', () => {
|
|
76
|
+
expect(count('for (const rule of this.blacklist')).toBe(1);
|
|
77
|
+
expect(count('switch (rule.level)')).toBe(0);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('三投影对同一输入互相自洽', () => {
|
|
82
|
+
it('单条命中:check 的裁决 / isAllowed 的布尔 / getRiskLevel 的等级三者一致', async () => {
|
|
83
|
+
const gate = syntheticGate();
|
|
84
|
+
const cases: Array<{ command: string; passed: boolean; allowed: boolean; level: string }> = [
|
|
85
|
+
{ command: 'SYN_BLOCK', passed: false, allowed: false, level: 'high' },
|
|
86
|
+
{ command: 'SYN_WARN', passed: true, allowed: true, level: 'medium' },
|
|
87
|
+
{ command: 'SYN_AUDIT', passed: true, allowed: true, level: 'low' },
|
|
88
|
+
{ command: 'nothing matches', passed: true, allowed: true, level: 'low' },
|
|
89
|
+
];
|
|
90
|
+
|
|
91
|
+
for (const { command, passed, allowed, level } of cases) {
|
|
92
|
+
const result = await gate.check(command);
|
|
93
|
+
expect(result.passed).toBe(passed);
|
|
94
|
+
expect(gate.isAllowed(command)).toBe(allowed);
|
|
95
|
+
expect(gate.getRiskLevel(command)).toBe(level);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('不变式:blocked 级命中 ⟺ check 不通过 ⟺ isAllowed false ⟺ 等级 high', async () => {
|
|
100
|
+
const gate = syntheticGate();
|
|
101
|
+
const corpus = ['SYN_BLOCK', 'SYN_WARN', 'SYN_AUDIT', 'SYN_WARN SYN_AUDIT', 'clean'];
|
|
102
|
+
|
|
103
|
+
for (const command of corpus) {
|
|
104
|
+
const result = await gate.check(command);
|
|
105
|
+
const allowed = gate.isAllowed(command);
|
|
106
|
+
const level = gate.getRiskLevel(command);
|
|
107
|
+
const blocked = (result.details?.blocked ?? []).length > 0;
|
|
108
|
+
|
|
109
|
+
expect(allowed).toBe(!blocked);
|
|
110
|
+
expect(result.passed).toBe(!blocked);
|
|
111
|
+
expect(level === 'high').toBe(blocked);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('等级取命中集合里的最高档,与规则表次序无关(首条命中即返回的旧语义不再存在)', async () => {
|
|
116
|
+
const gate = syntheticGate();
|
|
117
|
+
// 合成装配里 warn 排在 block 之前:只取首条命中会报 medium 而 check/isAllowed 判阻断
|
|
118
|
+
const command = 'SYN_WARN SYN_BLOCK';
|
|
119
|
+
|
|
120
|
+
expect(gate.getRiskLevel(command)).toBe('high');
|
|
121
|
+
expect(gate.isAllowed(command)).toBe(false);
|
|
122
|
+
expect((await gate.check(command)).passed).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('忽略类别对三投影同样生效(同一谓词,不会一处忽略一处不忽略)', async () => {
|
|
126
|
+
const gate = new CommandGate({ ignoreCategories: ['syn-d'] });
|
|
127
|
+
for (const rule of gate.getBlacklist()) gate.removeRule(rule.id);
|
|
128
|
+
gate.addRule(SYN_IGNORED);
|
|
129
|
+
|
|
130
|
+
expect(gate.isAllowed('SYN_IGNORED')).toBe(true);
|
|
131
|
+
expect(gate.getRiskLevel('SYN_IGNORED')).toBe('low');
|
|
132
|
+
expect((await gate.check('SYN_IGNORED')).passed).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -87,21 +87,14 @@ describe('CommandGate', () => {
|
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
it('should
|
|
91
|
-
const gate = new CommandGate(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
customBlacklist: [{
|
|
99
|
-
id: 'custom-block',
|
|
100
|
-
pattern: /\bmy-custom-dangerous-command\b/i,
|
|
101
|
-
level: 'block',
|
|
102
|
-
message: 'Custom dangerous command',
|
|
103
|
-
category: 'custom',
|
|
104
|
-
}],
|
|
90
|
+
it('should support runtime rules via addRule(配置位 customBlacklist 已删,见 ADR-0024)', async () => {
|
|
91
|
+
const gate = new CommandGate();
|
|
92
|
+
gate.addRule({
|
|
93
|
+
id: 'custom-block',
|
|
94
|
+
pattern: /\bmy-custom-dangerous-command\b/i,
|
|
95
|
+
level: 'block',
|
|
96
|
+
message: 'Custom dangerous command',
|
|
97
|
+
category: 'custom',
|
|
105
98
|
});
|
|
106
99
|
const result = await gate.check('my-custom-dangerous-command --run');
|
|
107
100
|
expect(result.passed).toBe(false);
|
|
@@ -32,18 +32,18 @@ describe('ContractGate', () => {
|
|
|
32
32
|
|
|
33
33
|
it('有效 OpenAPI → check 通过', async () => {
|
|
34
34
|
fs.writeFileSync(path.join(dir, 'openapi.yaml'), VALID_SPEC);
|
|
35
|
-
const result = await new ContractGate().check({
|
|
35
|
+
const result = await new ContractGate().check({projectPath: dir });
|
|
36
36
|
expect(result.passed).toBe(true);
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
it('缺 openapi 版本 → check 失败', async () => {
|
|
40
40
|
fs.writeFileSync(path.join(dir, 'openapi.yaml'), INVALID_SPEC);
|
|
41
|
-
const result = await new ContractGate().check({
|
|
41
|
+
const result = await new ContractGate().check({projectPath: dir });
|
|
42
42
|
expect(result.passed).toBe(false);
|
|
43
43
|
});
|
|
44
44
|
|
|
45
45
|
it('契约文件缺失 → 跳过(passed)', async () => {
|
|
46
|
-
const result = await new ContractGate().check({
|
|
46
|
+
const result = await new ContractGate().check({projectPath: dir });
|
|
47
47
|
expect(result.passed).toBe(true);
|
|
48
48
|
expect(result.message).toContain('未找到契约文件');
|
|
49
49
|
});
|
|
@@ -51,7 +51,7 @@ describe('ContractGate', () => {
|
|
|
51
51
|
describe('统一接口 evaluate', () => {
|
|
52
52
|
it('有效契约 → abstain(冻结决策)', async () => {
|
|
53
53
|
fs.writeFileSync(path.join(dir, 'openapi.yaml'), VALID_SPEC);
|
|
54
|
-
const decision = await new ContractGate().evaluate({
|
|
54
|
+
const decision = await new ContractGate().evaluate({projectPath: dir });
|
|
55
55
|
expect(decision.status).toBe('abstain');
|
|
56
56
|
expect(decision.result.gate).toBe('contract');
|
|
57
57
|
expect(Object.isFrozen(decision)).toBe(true);
|
|
@@ -59,7 +59,7 @@ describe('ContractGate', () => {
|
|
|
59
59
|
|
|
60
60
|
it('无效契约 → deny', async () => {
|
|
61
61
|
fs.writeFileSync(path.join(dir, 'openapi.yaml'), INVALID_SPEC);
|
|
62
|
-
const decision = await new ContractGate().evaluate({
|
|
62
|
+
const decision = await new ContractGate().evaluate({projectPath: dir });
|
|
63
63
|
expect(decision.status).toBe('deny');
|
|
64
64
|
expect(decision.result.passed).toBe(false);
|
|
65
65
|
});
|
|
@@ -33,7 +33,6 @@ describe('ReviewGate 统一接口', () => {
|
|
|
33
33
|
it('本地模式 requireApproval=true → deny(三态映射 + 冻结)', async () => {
|
|
34
34
|
mockExec.mockImplementation(callExec);
|
|
35
35
|
const decision = await new ReviewGate().evaluate({
|
|
36
|
-
projectId: 'p',
|
|
37
36
|
projectPath: '/test/project',
|
|
38
37
|
});
|
|
39
38
|
expect(decision.status).toBe('deny');
|
|
@@ -45,7 +44,6 @@ describe('ReviewGate 统一接口', () => {
|
|
|
45
44
|
it('本地模式 requireApproval=false → abstain', async () => {
|
|
46
45
|
mockExec.mockImplementation(callExec);
|
|
47
46
|
const decision = await new ReviewGate({ requireApproval: false }).evaluate({
|
|
48
|
-
projectId: 'p',
|
|
49
47
|
projectPath: '/test/project',
|
|
50
48
|
});
|
|
51
49
|
expect(decision.status).toBe('abstain');
|
|
@@ -67,7 +65,6 @@ describe('SecurityGate 统一接口', () => {
|
|
|
67
65
|
callback(null, { stdout: JSON.stringify({ audit: { advisories: {} } }) })
|
|
68
66
|
);
|
|
69
67
|
const decision = await new SecurityGate().evaluate({
|
|
70
|
-
projectId: 'p',
|
|
71
68
|
projectPath: '/test/project',
|
|
72
69
|
});
|
|
73
70
|
expect(decision.status).toBe('abstain');
|
|
@@ -87,7 +84,6 @@ describe('SecurityGate 统一接口', () => {
|
|
|
87
84
|
})
|
|
88
85
|
);
|
|
89
86
|
const decision = await new SecurityGate().evaluate({
|
|
90
|
-
projectId: 'p',
|
|
91
87
|
projectPath: '/test/project',
|
|
92
88
|
});
|
|
93
89
|
expect(decision.status).toBe('deny');
|
|
@@ -102,15 +98,6 @@ describe('SecurityGate 统一接口', () => {
|
|
|
102
98
|
});
|
|
103
99
|
|
|
104
100
|
describe('PerformanceGate 统一接口', () => {
|
|
105
|
-
it('enabled=false → abstain(禁用即放行)', async () => {
|
|
106
|
-
const decision = await new PerformanceGate({ enabled: false }).evaluate({
|
|
107
|
-
projectId: 'p',
|
|
108
|
-
projectPath: '/test/project',
|
|
109
|
-
});
|
|
110
|
-
expect(decision.status).toBe('abstain');
|
|
111
|
-
expect(decision.result.passed).toBe(true);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
101
|
it('id/order 字段齐备', () => {
|
|
115
102
|
const gate = new PerformanceGate();
|
|
116
103
|
expect(gate.id).toBe('performance');
|
|
@@ -131,7 +118,6 @@ describe('SpecAcceptanceGate 统一接口', () => {
|
|
|
131
118
|
|
|
132
119
|
it('tasks.yml 不存在 → abstain(跳过)', async () => {
|
|
133
120
|
const decision = await new SpecAcceptanceGate().evaluate({
|
|
134
|
-
projectId: 'p',
|
|
135
121
|
projectPath: dir,
|
|
136
122
|
tasksPath: path.join(dir, 'no-such-tasks.yml'),
|
|
137
123
|
});
|
|
@@ -157,7 +143,6 @@ describe('SpecAcceptanceGate 统一接口', () => {
|
|
|
157
143
|
].join('\n')
|
|
158
144
|
);
|
|
159
145
|
const decision = await new SpecAcceptanceGate().evaluate({
|
|
160
|
-
projectId: 'p',
|
|
161
146
|
projectPath: dir,
|
|
162
147
|
tasksPath,
|
|
163
148
|
});
|
|
@@ -176,7 +161,6 @@ describe('SpecAcceptanceGate 统一接口', () => {
|
|
|
176
161
|
describe('CommandGate 统一接口', () => {
|
|
177
162
|
it('黑名单命令 → deny', async () => {
|
|
178
163
|
const decision = await new CommandGate().evaluate({
|
|
179
|
-
projectId: 'p',
|
|
180
164
|
projectPath: '/test/project',
|
|
181
165
|
command: 'rm -rf /',
|
|
182
166
|
});
|
|
@@ -186,7 +170,6 @@ describe('CommandGate 统一接口', () => {
|
|
|
186
170
|
|
|
187
171
|
it('安全命令 → abstain', async () => {
|
|
188
172
|
const decision = await new CommandGate().evaluate({
|
|
189
|
-
projectId: 'p',
|
|
190
173
|
projectPath: '/test/project',
|
|
191
174
|
command: 'echo hello',
|
|
192
175
|
});
|
|
@@ -23,7 +23,6 @@ const mockFs = fs as jest.Mocked<typeof fs>;
|
|
|
23
23
|
describe('PerformanceGate', () => {
|
|
24
24
|
let gate: PerformanceGate;
|
|
25
25
|
const baseContext = {
|
|
26
|
-
projectId: 'test-project',
|
|
27
26
|
projectPath: '/test/project',
|
|
28
27
|
};
|
|
29
28
|
|
|
@@ -36,31 +35,19 @@ describe('PerformanceGate', () => {
|
|
|
36
35
|
it('should use default config', () => {
|
|
37
36
|
const defaultGate = new PerformanceGate();
|
|
38
37
|
const config = defaultGate.getConfig();
|
|
39
|
-
expect(config.enabled).toBe(true);
|
|
40
38
|
expect(config.thresholds).toEqual({});
|
|
41
39
|
});
|
|
42
40
|
|
|
43
41
|
it('should accept custom config', () => {
|
|
44
42
|
const customGate = new PerformanceGate({
|
|
45
|
-
enabled: false,
|
|
46
43
|
coverageTimeout: 60000,
|
|
47
44
|
});
|
|
48
45
|
const config = customGate.getConfig();
|
|
49
|
-
expect(config.enabled).toBe(false);
|
|
50
46
|
expect(config.coverageTimeout).toBe(60000);
|
|
51
47
|
});
|
|
52
48
|
});
|
|
53
49
|
|
|
54
50
|
describe('check()', () => {
|
|
55
|
-
it('should pass when gate is disabled', async () => {
|
|
56
|
-
const disabledGate = new PerformanceGate({ enabled: false });
|
|
57
|
-
|
|
58
|
-
const result = await disabledGate.check(baseContext);
|
|
59
|
-
|
|
60
|
-
expect(result.passed).toBe(true);
|
|
61
|
-
expect(result.message).toContain('已禁用');
|
|
62
|
-
});
|
|
63
|
-
|
|
64
51
|
it('should pass when no thresholds defined', async () => {
|
|
65
52
|
const result = await gate.check(baseContext);
|
|
66
53
|
|
|
@@ -23,7 +23,7 @@ function fakeGate(id: string, order: number, status: GateDecisionStatus): Gate {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const ctx: GateContext = {
|
|
26
|
+
const ctx: GateContext = {projectPath: '/tmp/project' };
|
|
27
27
|
|
|
28
28
|
describe('runGates', () => {
|
|
29
29
|
it('deny 单调:先 deny 后 abstain,终态 deny(下游不得改回 allow)', async () => {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #138 缺省扫描命令断链回归测试
|
|
3
|
+
*
|
|
4
|
+
* 断点:SecurityGate 构造器曾把缺省 scanCommand 兜成空串,判定链用 `??`
|
|
5
|
+
* (空串非 nullish),三级兜底恒停在第二级 → runScan('') 抛
|
|
6
|
+
* 「The argument 'file' cannot be empty」,detectScanCommand() 全仓不可达。
|
|
7
|
+
*
|
|
8
|
+
* 替身落点:这里【不 mock 整个 child_process】——那正是缺省路径一直亮绿灯的机制
|
|
9
|
+
* (`security.test.ts` 顶层 jest.mock('child_process') 后,exec('') 照样回调桩数据、
|
|
10
|
+
* 永不抛错,把断点盖住了)。改用 exec 的唯一出口 `src/utils/exec` 的 `execAsync` 作缝:
|
|
11
|
+
* 记录门禁「实际下发去执行的那条命令字符串」,据此断言链真的落到第三级
|
|
12
|
+
* (detectScanCommand 的返回值),并对空串复现真实的 exec 抛错(保持红/绿的分界诚实)。
|
|
13
|
+
* CLI 命令级的缺省路径覆盖见 `src/cli/commands/__tests__/security-default-scan.test.ts`。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { execAsync } from '../../utils/exec';
|
|
17
|
+
import { SecurityGate } from '../security';
|
|
18
|
+
|
|
19
|
+
// 缝替身:只替换 execAsync,模块其余导出保持真实
|
|
20
|
+
jest.mock('../../utils/exec', () => {
|
|
21
|
+
const actual = jest.requireActual('../../utils/exec');
|
|
22
|
+
return { ...actual, execAsync: jest.fn() };
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const mockExecAsync = execAsync as unknown as jest.Mock;
|
|
26
|
+
|
|
27
|
+
/** detectScanCommand() 现返回值——链落到第三级的唯一判据 */
|
|
28
|
+
const DETECTED = 'npm audit --json';
|
|
29
|
+
const projectPath = '/probe/project';
|
|
30
|
+
|
|
31
|
+
describe('SecurityGate 缺省扫描命令(#138 断链)', () => {
|
|
32
|
+
/** 门禁本轮实际下发执行的命令(按调用顺序) */
|
|
33
|
+
const issued = (): string[] => mockExecAsync.mock.calls.map((c) => String(c[0]));
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
jest.clearAllMocks();
|
|
37
|
+
// 缝替身:空命令复现真实 exec 抛错;非空则回零漏洞 JSON(判定为通过)
|
|
38
|
+
mockExecAsync.mockImplementation((cmd: string) => {
|
|
39
|
+
if (!cmd || !cmd.trim()) {
|
|
40
|
+
return Promise.reject(
|
|
41
|
+
new TypeError("The argument 'file' cannot be empty. Received ''")
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return Promise.resolve({ stdout: JSON.stringify({ vulnerabilities: {} }), stderr: '' });
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('三级兜底链可达性', () => {
|
|
49
|
+
it('不给任何扫描命令 → 下发的是 detectScanCommand() 的返回值而非空串', async () => {
|
|
50
|
+
const result = await new SecurityGate().scan({ projectPath });
|
|
51
|
+
|
|
52
|
+
expect(issued()).toEqual([DETECTED]); // 链走到第三级并把它交给 exec
|
|
53
|
+
expect(result.details?.scanCommand).toBe(DETECTED);
|
|
54
|
+
expect(result.passed).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('evaluate() 缺省路径 → abstain 判定,不再报错(harness security 复现路径)', async () => {
|
|
58
|
+
const decision = await new SecurityGate().evaluate({ projectPath });
|
|
59
|
+
|
|
60
|
+
expect(issued()).toEqual([DETECTED]);
|
|
61
|
+
expect(decision.status).toBe('abstain');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('context.securityScanCommand 优先级最高,不触达探测', async () => {
|
|
65
|
+
const result = await new SecurityGate().scan({
|
|
66
|
+
projectPath,
|
|
67
|
+
securityScanCommand: 'yarn audit --json',
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(issued()).toEqual(['yarn audit --json']);
|
|
71
|
+
expect(result.details?.scanCommand).toBe('yarn audit --json');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('构造时显式 scanCommand 优先于探测,不触达第三级', async () => {
|
|
75
|
+
const result = await new SecurityGate({ scanCommand: 'pnpm audit --json' }).scan({
|
|
76
|
+
projectPath,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(issued()).toEqual(['pnpm audit --json']);
|
|
80
|
+
expect(result.details?.scanCommand).toBe('pnpm audit --json');
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('空串归一(--scan-command "")', () => {
|
|
85
|
+
it('显式空串视为未提供 → 归一为 undefined 并落到探测', async () => {
|
|
86
|
+
const result = await new SecurityGate({ scanCommand: '' }).scan({ projectPath });
|
|
87
|
+
|
|
88
|
+
expect(issued()).toEqual([DETECTED]);
|
|
89
|
+
expect(result.details?.scanCommand).toBe(DETECTED);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('构造器不再出现 scanCommand 的空串缺省', () => {
|
|
93
|
+
expect(new SecurityGate().getConfig().scanCommand).toBeUndefined();
|
|
94
|
+
expect(new SecurityGate({ scanCommand: '' }).getConfig().scanCommand).toBeUndefined();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -15,7 +15,6 @@ const mockExec = exec as unknown as jest.Mock;
|
|
|
15
15
|
describe('SecurityGate', () => {
|
|
16
16
|
let gate: SecurityGate;
|
|
17
17
|
const baseContext = {
|
|
18
|
-
projectId: 'test-project',
|
|
19
18
|
projectPath: '/test/project',
|
|
20
19
|
};
|
|
21
20
|
|
|
@@ -28,7 +27,6 @@ describe('SecurityGate', () => {
|
|
|
28
27
|
it('should use default config', () => {
|
|
29
28
|
const defaultGate = new SecurityGate();
|
|
30
29
|
const config = defaultGate.getConfig();
|
|
31
|
-
expect(config.enabled).toBe(true);
|
|
32
30
|
expect(config.severityThreshold).toBe('high');
|
|
33
31
|
expect(config.ignoreWarnings).toBe(false);
|
|
34
32
|
expect(config.ignoreDevDependencies).toBe(false);
|
|
@@ -36,27 +34,16 @@ describe('SecurityGate', () => {
|
|
|
36
34
|
|
|
37
35
|
it('should accept custom config', () => {
|
|
38
36
|
const customGate = new SecurityGate({
|
|
39
|
-
enabled: false,
|
|
40
37
|
severityThreshold: 'critical',
|
|
41
38
|
ignoreWarnings: true,
|
|
42
39
|
});
|
|
43
40
|
const config = customGate.getConfig();
|
|
44
|
-
expect(config.enabled).toBe(false);
|
|
45
41
|
expect(config.severityThreshold).toBe('critical');
|
|
46
42
|
expect(config.ignoreWarnings).toBe(true);
|
|
47
43
|
});
|
|
48
44
|
});
|
|
49
45
|
|
|
50
46
|
describe('scan()', () => {
|
|
51
|
-
it('should pass when gate is disabled', async () => {
|
|
52
|
-
const disabledGate = new SecurityGate({ enabled: false });
|
|
53
|
-
|
|
54
|
-
const result = await disabledGate.scan(baseContext);
|
|
55
|
-
|
|
56
|
-
expect(result.passed).toBe(true);
|
|
57
|
-
expect(result.message).toContain('已禁用');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
47
|
it('should pass when no vulnerabilities found', async () => {
|
|
61
48
|
mockExec.mockImplementationOnce((cmd, opts, callback) => {
|
|
62
49
|
callback(null, { stdout: JSON.stringify({ vulnerabilities: {} }) });
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* 把已注册的 ConstraintCheck 适配为统一 Gate,供守卫链(如 studio
|
|
5
5
|
* runCompletionGuards)复用 runGates 的 deny 单调 / ask fail-closed 语义。
|
|
6
6
|
*
|
|
7
|
-
* env 经 buildCheckEnv(..., 'none') 构造(显式不接证据),语义见工厂 doc
|
|
7
|
+
* env 经 buildCheckEnv(..., 'none') 构造(显式不接证据),语义见工厂 doc;
|
|
8
|
+
* `ctx.runEnv` 传入即与本 run 其余消费方共用同一份上行数据读取(ADR-0023)。
|
|
8
9
|
*/
|
|
9
10
|
|
|
10
11
|
import type { ConstraintCheck } from '../core/constraints/checkers';
|
|
@@ -29,7 +30,7 @@ export function createCheckerGate(check: ConstraintCheck, order = 0): Gate {
|
|
|
29
30
|
async evaluate(ctx: GateContext): Promise<GateDecision> {
|
|
30
31
|
const startTime = Date.now();
|
|
31
32
|
const projectPath = ctx.projectPath || process.cwd();
|
|
32
|
-
const env = buildCheckEnv({ operation: 'manual', projectPath }, 'none');
|
|
33
|
+
const env = buildCheckEnv({ operation: 'manual', projectPath }, 'none', ctx.runEnv);
|
|
33
34
|
const outcome = normalizeCheckOutcome(await check.evaluate(env));
|
|
34
35
|
const evidence = outcome.evidence.map((e) => `\n - ${e}`).join('');
|
|
35
36
|
const reason = outcome.skipped
|