driftdetect-core 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/boundaries/types.d.ts +1 -1
- package/dist/boundaries/types.d.ts.map +1 -1
- package/dist/call-graph/analysis/graph-builder.d.ts.map +1 -1
- package/dist/call-graph/analysis/graph-builder.js +1 -0
- package/dist/call-graph/analysis/graph-builder.js.map +1 -1
- package/dist/call-graph/extractors/go-data-access-extractor.d.ts +80 -0
- package/dist/call-graph/extractors/go-data-access-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/go-data-access-extractor.js +457 -0
- package/dist/call-graph/extractors/go-data-access-extractor.js.map +1 -0
- package/dist/call-graph/extractors/go-extractor.d.ts +103 -0
- package/dist/call-graph/extractors/go-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/go-extractor.js +509 -0
- package/dist/call-graph/extractors/go-extractor.js.map +1 -0
- package/dist/call-graph/extractors/go-hybrid-extractor.d.ts +97 -0
- package/dist/call-graph/extractors/go-hybrid-extractor.d.ts.map +1 -0
- package/dist/call-graph/extractors/go-hybrid-extractor.js +496 -0
- package/dist/call-graph/extractors/go-hybrid-extractor.js.map +1 -0
- package/dist/call-graph/extractors/regex/go-regex.d.ts +51 -0
- package/dist/call-graph/extractors/regex/go-regex.d.ts.map +1 -0
- package/dist/call-graph/extractors/regex/go-regex.js +435 -0
- package/dist/call-graph/extractors/regex/go-regex.js.map +1 -0
- package/dist/call-graph/extractors/regex/index.d.ts +1 -0
- package/dist/call-graph/extractors/regex/index.d.ts.map +1 -1
- package/dist/call-graph/extractors/regex/index.js +8 -0
- package/dist/call-graph/extractors/regex/index.js.map +1 -1
- package/dist/call-graph/index.d.ts +3 -0
- package/dist/call-graph/index.d.ts.map +1 -1
- package/dist/call-graph/index.js +5 -0
- package/dist/call-graph/index.js.map +1 -1
- package/dist/call-graph/types.d.ts +1 -1
- package/dist/call-graph/types.d.ts.map +1 -1
- package/dist/constraints/extraction/constraint-synthesizer.d.ts +72 -0
- package/dist/constraints/extraction/constraint-synthesizer.d.ts.map +1 -0
- package/dist/constraints/extraction/constraint-synthesizer.js +336 -0
- package/dist/constraints/extraction/constraint-synthesizer.js.map +1 -0
- package/dist/constraints/extraction/index.d.ts +10 -0
- package/dist/constraints/extraction/index.d.ts.map +1 -0
- package/dist/constraints/extraction/index.js +10 -0
- package/dist/constraints/extraction/index.js.map +1 -0
- package/dist/constraints/extraction/invariant-detector.d.ts +109 -0
- package/dist/constraints/extraction/invariant-detector.d.ts.map +1 -0
- package/dist/constraints/extraction/invariant-detector.js +979 -0
- package/dist/constraints/extraction/invariant-detector.js.map +1 -0
- package/dist/constraints/index.d.ts +15 -0
- package/dist/constraints/index.d.ts.map +1 -0
- package/dist/constraints/index.js +16 -0
- package/dist/constraints/index.js.map +1 -0
- package/dist/constraints/store/constraint-store.d.ts +110 -0
- package/dist/constraints/store/constraint-store.d.ts.map +1 -0
- package/dist/constraints/store/constraint-store.js +584 -0
- package/dist/constraints/store/constraint-store.js.map +1 -0
- package/dist/constraints/types.d.ts +605 -0
- package/dist/constraints/types.d.ts.map +1 -0
- package/dist/constraints/types.js +57 -0
- package/dist/constraints/types.js.map +1 -0
- package/dist/constraints/verification/constraint-verifier.d.ts +100 -0
- package/dist/constraints/verification/constraint-verifier.d.ts.map +1 -0
- package/dist/constraints/verification/constraint-verifier.js +638 -0
- package/dist/constraints/verification/constraint-verifier.js.map +1 -0
- package/dist/constraints/verification/index.d.ts +8 -0
- package/dist/constraints/verification/index.d.ts.map +1 -0
- package/dist/constraints/verification/index.js +8 -0
- package/dist/constraints/verification/index.js.map +1 -0
- package/dist/decisions/analyzer/decision-mining-analyzer.d.ts +121 -0
- package/dist/decisions/analyzer/decision-mining-analyzer.d.ts.map +1 -0
- package/dist/decisions/analyzer/decision-mining-analyzer.js +904 -0
- package/dist/decisions/analyzer/decision-mining-analyzer.js.map +1 -0
- package/dist/decisions/analyzer/index.d.ts +5 -0
- package/dist/decisions/analyzer/index.d.ts.map +1 -0
- package/dist/decisions/analyzer/index.js +5 -0
- package/dist/decisions/analyzer/index.js.map +1 -0
- package/dist/decisions/extractors/base-commit-extractor.d.ts +104 -0
- package/dist/decisions/extractors/base-commit-extractor.d.ts.map +1 -0
- package/dist/decisions/extractors/base-commit-extractor.js +305 -0
- package/dist/decisions/extractors/base-commit-extractor.js.map +1 -0
- package/dist/decisions/extractors/csharp-commit-extractor.d.ts +18 -0
- package/dist/decisions/extractors/csharp-commit-extractor.d.ts.map +1 -0
- package/dist/decisions/extractors/csharp-commit-extractor.js +281 -0
- package/dist/decisions/extractors/csharp-commit-extractor.js.map +1 -0
- package/dist/decisions/extractors/index.d.ts +31 -0
- package/dist/decisions/extractors/index.d.ts.map +1 -0
- package/dist/decisions/extractors/index.js +63 -0
- package/dist/decisions/extractors/index.js.map +1 -0
- package/dist/decisions/extractors/java-commit-extractor.d.ts +18 -0
- package/dist/decisions/extractors/java-commit-extractor.d.ts.map +1 -0
- package/dist/decisions/extractors/java-commit-extractor.js +248 -0
- package/dist/decisions/extractors/java-commit-extractor.js.map +1 -0
- package/dist/decisions/extractors/php-commit-extractor.d.ts +18 -0
- package/dist/decisions/extractors/php-commit-extractor.d.ts.map +1 -0
- package/dist/decisions/extractors/php-commit-extractor.js +280 -0
- package/dist/decisions/extractors/php-commit-extractor.js.map +1 -0
- package/dist/decisions/extractors/python-commit-extractor.d.ts +18 -0
- package/dist/decisions/extractors/python-commit-extractor.d.ts.map +1 -0
- package/dist/decisions/extractors/python-commit-extractor.js +248 -0
- package/dist/decisions/extractors/python-commit-extractor.js.map +1 -0
- package/dist/decisions/extractors/typescript-commit-extractor.d.ts +37 -0
- package/dist/decisions/extractors/typescript-commit-extractor.d.ts.map +1 -0
- package/dist/decisions/extractors/typescript-commit-extractor.js +229 -0
- package/dist/decisions/extractors/typescript-commit-extractor.js.map +1 -0
- package/dist/decisions/git/commit-parser.d.ts +58 -0
- package/dist/decisions/git/commit-parser.d.ts.map +1 -0
- package/dist/decisions/git/commit-parser.js +344 -0
- package/dist/decisions/git/commit-parser.js.map +1 -0
- package/dist/decisions/git/diff-analyzer.d.ts +30 -0
- package/dist/decisions/git/diff-analyzer.d.ts.map +1 -0
- package/dist/decisions/git/diff-analyzer.js +492 -0
- package/dist/decisions/git/diff-analyzer.js.map +1 -0
- package/dist/decisions/git/git-walker.d.ts +64 -0
- package/dist/decisions/git/git-walker.d.ts.map +1 -0
- package/dist/decisions/git/git-walker.js +441 -0
- package/dist/decisions/git/git-walker.js.map +1 -0
- package/dist/decisions/git/index.d.ts +11 -0
- package/dist/decisions/git/index.d.ts.map +1 -0
- package/dist/decisions/git/index.js +13 -0
- package/dist/decisions/git/index.js.map +1 -0
- package/dist/decisions/git/types.d.ts +223 -0
- package/dist/decisions/git/types.d.ts.map +1 -0
- package/dist/decisions/git/types.js +7 -0
- package/dist/decisions/git/types.js.map +1 -0
- package/dist/decisions/index.d.ts +27 -0
- package/dist/decisions/index.d.ts.map +1 -0
- package/dist/decisions/index.js +45 -0
- package/dist/decisions/index.js.map +1 -0
- package/dist/decisions/types.d.ts +530 -0
- package/dist/decisions/types.d.ts.map +1 -0
- package/dist/decisions/types.js +8 -0
- package/dist/decisions/types.js.map +1 -0
- package/dist/go/go-analyzer.d.ts +203 -0
- package/dist/go/go-analyzer.d.ts.map +1 -0
- package/dist/go/go-analyzer.js +509 -0
- package/dist/go/go-analyzer.js.map +1 -0
- package/dist/go/index.d.ts +7 -0
- package/dist/go/index.d.ts.map +1 -0
- package/dist/go/index.js +8 -0
- package/dist/go/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -1
- package/dist/parsers/tree-sitter/go-loader.d.ts +50 -0
- package/dist/parsers/tree-sitter/go-loader.d.ts.map +1 -0
- package/dist/parsers/tree-sitter/go-loader.js +161 -0
- package/dist/parsers/tree-sitter/go-loader.js.map +1 -0
- package/dist/parsers/tree-sitter/tree-sitter-go-parser.d.ts +250 -0
- package/dist/parsers/tree-sitter/tree-sitter-go-parser.d.ts.map +1 -0
- package/dist/parsers/tree-sitter/tree-sitter-go-parser.js +707 -0
- package/dist/parsers/tree-sitter/tree-sitter-go-parser.js.map +1 -0
- package/dist/parsers/types.d.ts +1 -1
- package/dist/parsers/types.d.ts.map +1 -1
- package/dist/simulation/approach-generator.d.ts +73 -0
- package/dist/simulation/approach-generator.d.ts.map +1 -0
- package/dist/simulation/approach-generator.js +371 -0
- package/dist/simulation/approach-generator.js.map +1 -0
- package/dist/simulation/index.d.ts +14 -0
- package/dist/simulation/index.d.ts.map +1 -0
- package/dist/simulation/index.js +19 -0
- package/dist/simulation/index.js.map +1 -0
- package/dist/simulation/language-strategies/csharp-strategies.d.ts +17 -0
- package/dist/simulation/language-strategies/csharp-strategies.d.ts.map +1 -0
- package/dist/simulation/language-strategies/csharp-strategies.js +118 -0
- package/dist/simulation/language-strategies/csharp-strategies.js.map +1 -0
- package/dist/simulation/language-strategies/index.d.ts +41 -0
- package/dist/simulation/language-strategies/index.d.ts.map +1 -0
- package/dist/simulation/language-strategies/index.js +92 -0
- package/dist/simulation/language-strategies/index.js.map +1 -0
- package/dist/simulation/language-strategies/java-strategies.d.ts +17 -0
- package/dist/simulation/language-strategies/java-strategies.d.ts.map +1 -0
- package/dist/simulation/language-strategies/java-strategies.js +119 -0
- package/dist/simulation/language-strategies/java-strategies.js.map +1 -0
- package/dist/simulation/language-strategies/php-strategies.d.ts +17 -0
- package/dist/simulation/language-strategies/php-strategies.d.ts.map +1 -0
- package/dist/simulation/language-strategies/php-strategies.js +129 -0
- package/dist/simulation/language-strategies/php-strategies.js.map +1 -0
- package/dist/simulation/language-strategies/python-strategies.d.ts +17 -0
- package/dist/simulation/language-strategies/python-strategies.d.ts.map +1 -0
- package/dist/simulation/language-strategies/python-strategies.js +208 -0
- package/dist/simulation/language-strategies/python-strategies.js.map +1 -0
- package/dist/simulation/language-strategies/types.d.ts +67 -0
- package/dist/simulation/language-strategies/types.d.ts.map +1 -0
- package/dist/simulation/language-strategies/types.js +23 -0
- package/dist/simulation/language-strategies/types.js.map +1 -0
- package/dist/simulation/language-strategies/typescript-strategies.d.ts +17 -0
- package/dist/simulation/language-strategies/typescript-strategies.d.ts.map +1 -0
- package/dist/simulation/language-strategies/typescript-strategies.js +251 -0
- package/dist/simulation/language-strategies/typescript-strategies.js.map +1 -0
- package/dist/simulation/scorers/friction-scorer.d.ts +45 -0
- package/dist/simulation/scorers/friction-scorer.d.ts.map +1 -0
- package/dist/simulation/scorers/friction-scorer.js +336 -0
- package/dist/simulation/scorers/friction-scorer.js.map +1 -0
- package/dist/simulation/scorers/impact-scorer.d.ts +56 -0
- package/dist/simulation/scorers/impact-scorer.d.ts.map +1 -0
- package/dist/simulation/scorers/impact-scorer.js +273 -0
- package/dist/simulation/scorers/impact-scorer.js.map +1 -0
- package/dist/simulation/scorers/index.d.ts +12 -0
- package/dist/simulation/scorers/index.d.ts.map +1 -0
- package/dist/simulation/scorers/index.js +12 -0
- package/dist/simulation/scorers/index.js.map +1 -0
- package/dist/simulation/scorers/pattern-alignment-scorer.d.ts +83 -0
- package/dist/simulation/scorers/pattern-alignment-scorer.d.ts.map +1 -0
- package/dist/simulation/scorers/pattern-alignment-scorer.js +367 -0
- package/dist/simulation/scorers/pattern-alignment-scorer.js.map +1 -0
- package/dist/simulation/scorers/security-scorer.d.ts +63 -0
- package/dist/simulation/scorers/security-scorer.d.ts.map +1 -0
- package/dist/simulation/scorers/security-scorer.js +346 -0
- package/dist/simulation/scorers/security-scorer.js.map +1 -0
- package/dist/simulation/simulation-engine.d.ts +89 -0
- package/dist/simulation/simulation-engine.d.ts.map +1 -0
- package/dist/simulation/simulation-engine.js +480 -0
- package/dist/simulation/simulation-engine.js.map +1 -0
- package/dist/simulation/types.d.ts +200 -0
- package/dist/simulation/types.d.ts.map +1 -0
- package/dist/simulation/types.js +23 -0
- package/dist/simulation/types.js.map +1 -0
- package/dist/speculative/approach-generator.d.ts +2 -0
- package/dist/speculative/approach-generator.d.ts.map +1 -0
- package/dist/speculative/approach-generator.js +2 -0
- package/dist/speculative/approach-generator.js.map +1 -0
- package/dist/speculative/templates/types.d.ts +133 -0
- package/dist/speculative/templates/types.d.ts.map +1 -0
- package/dist/speculative/templates/types.js +208 -0
- package/dist/speculative/templates/types.js.map +1 -0
- package/dist/speculative/types.d.ts +523 -0
- package/dist/speculative/types.d.ts.map +1 -0
- package/dist/speculative/types.js +71 -0
- package/dist/speculative/types.js.map +1 -0
- package/dist/test-topology/extractors/base-test-extractor.d.ts +1 -1
- package/dist/test-topology/extractors/base-test-extractor.d.ts.map +1 -1
- package/dist/test-topology/extractors/base-test-extractor.js.map +1 -1
- package/dist/test-topology/extractors/go-test-extractor.d.ts +33 -0
- package/dist/test-topology/extractors/go-test-extractor.d.ts.map +1 -0
- package/dist/test-topology/extractors/go-test-extractor.js +436 -0
- package/dist/test-topology/extractors/go-test-extractor.js.map +1 -0
- package/dist/test-topology/extractors/index.d.ts +1 -0
- package/dist/test-topology/extractors/index.d.ts.map +1 -1
- package/dist/test-topology/extractors/index.js +1 -0
- package/dist/test-topology/extractors/index.js.map +1 -1
- package/dist/test-topology/extractors/regex/go-test-regex.d.ts +62 -0
- package/dist/test-topology/extractors/regex/go-test-regex.d.ts.map +1 -0
- package/dist/test-topology/extractors/regex/go-test-regex.js +528 -0
- package/dist/test-topology/extractors/regex/go-test-regex.js.map +1 -0
- package/dist/test-topology/extractors/regex/index.d.ts +1 -0
- package/dist/test-topology/extractors/regex/index.d.ts.map +1 -1
- package/dist/test-topology/extractors/regex/index.js +4 -0
- package/dist/test-topology/extractors/regex/index.js.map +1 -1
- package/dist/test-topology/hybrid-test-topology-analyzer.d.ts.map +1 -1
- package/dist/test-topology/hybrid-test-topology-analyzer.js +1 -0
- package/dist/test-topology/hybrid-test-topology-analyzer.js.map +1 -1
- package/dist/test-topology/test-topology-analyzer.d.ts.map +1 -1
- package/dist/test-topology/test-topology-analyzer.js +1 -0
- package/dist/test-topology/test-topology-analyzer.js.map +1 -1
- package/dist/test-topology/types.d.ts +2 -2
- package/dist/test-topology/types.d.ts.map +1 -1
- package/dist/unified-provider/matching/database-sql-matcher.d.ts +42 -0
- package/dist/unified-provider/matching/database-sql-matcher.d.ts.map +1 -0
- package/dist/unified-provider/matching/database-sql-matcher.js +282 -0
- package/dist/unified-provider/matching/database-sql-matcher.js.map +1 -0
- package/dist/unified-provider/matching/gorm-matcher.d.ts +41 -0
- package/dist/unified-provider/matching/gorm-matcher.d.ts.map +1 -0
- package/dist/unified-provider/matching/gorm-matcher.js +222 -0
- package/dist/unified-provider/matching/gorm-matcher.js.map +1 -0
- package/dist/unified-provider/matching/index.d.ts +3 -0
- package/dist/unified-provider/matching/index.d.ts.map +1 -1
- package/dist/unified-provider/matching/index.js +4 -0
- package/dist/unified-provider/matching/index.js.map +1 -1
- package/dist/unified-provider/matching/matcher-registry.d.ts.map +1 -1
- package/dist/unified-provider/matching/matcher-registry.js +7 -0
- package/dist/unified-provider/matching/matcher-registry.js.map +1 -1
- package/dist/unified-provider/matching/sqlx-matcher.d.ts +40 -0
- package/dist/unified-provider/matching/sqlx-matcher.d.ts.map +1 -0
- package/dist/unified-provider/matching/sqlx-matcher.js +214 -0
- package/dist/unified-provider/matching/sqlx-matcher.js.map +1 -0
- package/dist/unified-provider/normalization/go-normalizer.d.ts +62 -0
- package/dist/unified-provider/normalization/go-normalizer.d.ts.map +1 -0
- package/dist/unified-provider/normalization/go-normalizer.js +608 -0
- package/dist/unified-provider/normalization/go-normalizer.js.map +1 -0
- package/dist/unified-provider/normalization/index.d.ts +1 -0
- package/dist/unified-provider/normalization/index.d.ts.map +1 -1
- package/dist/unified-provider/normalization/index.js +6 -1
- package/dist/unified-provider/normalization/index.js.map +1 -1
- package/dist/unified-provider/types.d.ts +1 -1
- package/dist/unified-provider/types.d.ts.map +1 -1
- package/dist/wpf/extractors/binding-error-detector.d.ts +75 -0
- package/dist/wpf/extractors/binding-error-detector.d.ts.map +1 -0
- package/dist/wpf/extractors/binding-error-detector.js +290 -0
- package/dist/wpf/extractors/binding-error-detector.js.map +1 -0
- package/dist/wpf/extractors/code-behind-linker.d.ts +70 -0
- package/dist/wpf/extractors/code-behind-linker.d.ts.map +1 -0
- package/dist/wpf/extractors/code-behind-linker.js +172 -0
- package/dist/wpf/extractors/code-behind-linker.js.map +1 -0
- package/dist/wpf/extractors/dependency-property-extractor.d.ts +61 -0
- package/dist/wpf/extractors/dependency-property-extractor.d.ts.map +1 -0
- package/dist/wpf/extractors/dependency-property-extractor.js +201 -0
- package/dist/wpf/extractors/dependency-property-extractor.js.map +1 -0
- package/dist/wpf/extractors/regex/viewmodel-regex.d.ts +82 -0
- package/dist/wpf/extractors/regex/viewmodel-regex.d.ts.map +1 -0
- package/dist/wpf/extractors/regex/viewmodel-regex.js +412 -0
- package/dist/wpf/extractors/regex/viewmodel-regex.js.map +1 -0
- package/dist/wpf/extractors/regex/xaml-regex.d.ts +77 -0
- package/dist/wpf/extractors/regex/xaml-regex.d.ts.map +1 -0
- package/dist/wpf/extractors/regex/xaml-regex.js +353 -0
- package/dist/wpf/extractors/regex/xaml-regex.js.map +1 -0
- package/dist/wpf/extractors/resource-dictionary-parser.d.ts +103 -0
- package/dist/wpf/extractors/resource-dictionary-parser.d.ts.map +1 -0
- package/dist/wpf/extractors/resource-dictionary-parser.js +296 -0
- package/dist/wpf/extractors/resource-dictionary-parser.js.map +1 -0
- package/dist/wpf/extractors/value-converter-extractor.d.ts +128 -0
- package/dist/wpf/extractors/value-converter-extractor.d.ts.map +1 -0
- package/dist/wpf/extractors/value-converter-extractor.js +312 -0
- package/dist/wpf/extractors/value-converter-extractor.js.map +1 -0
- package/dist/wpf/extractors/viewmodel-hybrid-extractor.d.ts +104 -0
- package/dist/wpf/extractors/viewmodel-hybrid-extractor.d.ts.map +1 -0
- package/dist/wpf/extractors/viewmodel-hybrid-extractor.js +513 -0
- package/dist/wpf/extractors/viewmodel-hybrid-extractor.js.map +1 -0
- package/dist/wpf/extractors/xaml-hybrid-extractor.d.ts +98 -0
- package/dist/wpf/extractors/xaml-hybrid-extractor.d.ts.map +1 -0
- package/dist/wpf/extractors/xaml-hybrid-extractor.js +444 -0
- package/dist/wpf/extractors/xaml-hybrid-extractor.js.map +1 -0
- package/dist/wpf/index.d.ts +25 -0
- package/dist/wpf/index.d.ts.map +1 -0
- package/dist/wpf/index.js +31 -0
- package/dist/wpf/index.js.map +1 -0
- package/dist/wpf/integration/wpf-callgraph-adapter.d.ts +82 -0
- package/dist/wpf/integration/wpf-callgraph-adapter.d.ts.map +1 -0
- package/dist/wpf/integration/wpf-callgraph-adapter.js +311 -0
- package/dist/wpf/integration/wpf-callgraph-adapter.js.map +1 -0
- package/dist/wpf/integration/wpf-data-flow-tracer.d.ts +129 -0
- package/dist/wpf/integration/wpf-data-flow-tracer.d.ts.map +1 -0
- package/dist/wpf/integration/wpf-data-flow-tracer.js +367 -0
- package/dist/wpf/integration/wpf-data-flow-tracer.js.map +1 -0
- package/dist/wpf/linkers/datacontext-resolver.d.ts +66 -0
- package/dist/wpf/linkers/datacontext-resolver.d.ts.map +1 -0
- package/dist/wpf/linkers/datacontext-resolver.js +240 -0
- package/dist/wpf/linkers/datacontext-resolver.js.map +1 -0
- package/dist/wpf/linkers/viewmodel-linker.d.ts +71 -0
- package/dist/wpf/linkers/viewmodel-linker.d.ts.map +1 -0
- package/dist/wpf/linkers/viewmodel-linker.js +268 -0
- package/dist/wpf/linkers/viewmodel-linker.js.map +1 -0
- package/dist/wpf/types.d.ts +451 -0
- package/dist/wpf/types.d.ts.map +1 -0
- package/dist/wpf/types.js +8 -0
- package/dist/wpf/types.js.map +1 -0
- package/dist/wpf/wpf-analyzer.d.ts +114 -0
- package/dist/wpf/wpf-analyzer.d.ts.map +1 -0
- package/dist/wpf/wpf-analyzer.js +332 -0
- package/dist/wpf/wpf-analyzer.js.map +1 -0
- package/dist/wrappers/integration/adapter.d.ts.map +1 -1
- package/dist/wrappers/integration/adapter.js +1 -0
- package/dist/wrappers/integration/adapter.js.map +1 -1
- package/package.json +29 -18
- package/LICENSE +0 -21
|
@@ -0,0 +1,979 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant Detector
|
|
3
|
+
*
|
|
4
|
+
* Analyzes Drift's existing data (patterns, call graphs, boundaries, test topology,
|
|
5
|
+
* error handling) to discover architectural invariants that can become constraints.
|
|
6
|
+
*
|
|
7
|
+
* This is the semantic analysis layer that mines invariants from:
|
|
8
|
+
* - Pattern data (high-confidence approved patterns)
|
|
9
|
+
* - Call graph (auth-before-data-access, validation patterns)
|
|
10
|
+
* - Boundaries (data access layer invariants)
|
|
11
|
+
* - Test topology (coverage requirements)
|
|
12
|
+
* - Error handling (error boundary patterns)
|
|
13
|
+
*/
|
|
14
|
+
// =============================================================================
|
|
15
|
+
// Invariant Detector
|
|
16
|
+
// =============================================================================
|
|
17
|
+
export class InvariantDetector {
|
|
18
|
+
config;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Detect invariants from all available Drift data
|
|
24
|
+
*/
|
|
25
|
+
async detectAll(options = {}) {
|
|
26
|
+
const invariants = [];
|
|
27
|
+
const categories = options.categories ?? [
|
|
28
|
+
'api', 'auth', 'data', 'error', 'test', 'security', 'structural'
|
|
29
|
+
];
|
|
30
|
+
// Detect from patterns
|
|
31
|
+
if (this.config.patternStore) {
|
|
32
|
+
const patternInvariants = await this.detectFromPatterns(categories, options);
|
|
33
|
+
invariants.push(...patternInvariants);
|
|
34
|
+
}
|
|
35
|
+
// Detect from call graph
|
|
36
|
+
if (this.config.callGraphStore) {
|
|
37
|
+
const callGraphInvariants = await this.detectFromCallGraph(options);
|
|
38
|
+
invariants.push(...callGraphInvariants);
|
|
39
|
+
}
|
|
40
|
+
// Detect from boundaries
|
|
41
|
+
if (this.config.boundaryStore) {
|
|
42
|
+
const boundaryInvariants = await this.detectFromBoundaries(options);
|
|
43
|
+
invariants.push(...boundaryInvariants);
|
|
44
|
+
}
|
|
45
|
+
// Detect from test topology
|
|
46
|
+
if (this.config.testTopologyAnalyzer) {
|
|
47
|
+
const testInvariants = await this.detectFromTestTopology(options);
|
|
48
|
+
invariants.push(...testInvariants);
|
|
49
|
+
}
|
|
50
|
+
// Detect from error handling
|
|
51
|
+
if (this.config.errorHandlingAnalyzer) {
|
|
52
|
+
const errorInvariants = await this.detectFromErrorHandling(options);
|
|
53
|
+
invariants.push(...errorInvariants);
|
|
54
|
+
}
|
|
55
|
+
// Filter by confidence threshold
|
|
56
|
+
const minConfidence = options.minConfidence ?? 0.90;
|
|
57
|
+
return invariants.filter(inv => inv.constraint.confidence.score >= minConfidence);
|
|
58
|
+
}
|
|
59
|
+
// ===========================================================================
|
|
60
|
+
// Pattern-Based Detection
|
|
61
|
+
// ===========================================================================
|
|
62
|
+
/**
|
|
63
|
+
* Detect invariants from pattern data
|
|
64
|
+
*/
|
|
65
|
+
async detectFromPatterns(categories, options) {
|
|
66
|
+
const invariants = [];
|
|
67
|
+
const store = this.config.patternStore;
|
|
68
|
+
// Get all approved patterns with high confidence
|
|
69
|
+
const patterns = store.getAll().filter(p => p.status === 'approved' &&
|
|
70
|
+
p.confidence.score >= 0.85 &&
|
|
71
|
+
categories.includes(p.category));
|
|
72
|
+
for (const pattern of patterns) {
|
|
73
|
+
const detected = this.patternToInvariant(pattern, options);
|
|
74
|
+
if (detected) {
|
|
75
|
+
invariants.push(detected);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return invariants;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Convert a high-confidence pattern to an invariant
|
|
82
|
+
*/
|
|
83
|
+
patternToInvariant(pattern, options) {
|
|
84
|
+
// Only convert patterns with enough evidence
|
|
85
|
+
if (pattern.locations.length < 3)
|
|
86
|
+
return null;
|
|
87
|
+
const category = pattern.category;
|
|
88
|
+
const language = this.detectLanguageFromPattern(pattern);
|
|
89
|
+
// Build predicate based on pattern type
|
|
90
|
+
const predicate = this.buildPredicateFromPattern(pattern);
|
|
91
|
+
// Calculate confidence
|
|
92
|
+
const violations = pattern.outliers ?? [];
|
|
93
|
+
const conforming = pattern.locations.length;
|
|
94
|
+
const violating = violations.length;
|
|
95
|
+
const confidence = conforming / (conforming + violating);
|
|
96
|
+
// Build violation details
|
|
97
|
+
const violationDetails = violations.map((o) => ({
|
|
98
|
+
file: o.file,
|
|
99
|
+
line: o.line,
|
|
100
|
+
reason: o.reason ?? 'Outlier from pattern',
|
|
101
|
+
}));
|
|
102
|
+
// Build confidence object conditionally to satisfy exactOptionalPropertyTypes
|
|
103
|
+
const constraintConfidence = {
|
|
104
|
+
score: confidence,
|
|
105
|
+
evidence: conforming,
|
|
106
|
+
violations: violating,
|
|
107
|
+
lastVerified: new Date().toISOString(),
|
|
108
|
+
};
|
|
109
|
+
if (options.includeViolationDetails && violationDetails.length > 0) {
|
|
110
|
+
constraintConfidence.violationDetails = violationDetails;
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
constraint: {
|
|
114
|
+
name: `${pattern.name} Invariant`,
|
|
115
|
+
description: `All ${pattern.name} instances must follow the established pattern`,
|
|
116
|
+
category,
|
|
117
|
+
derivedFrom: {
|
|
118
|
+
patterns: [pattern.id],
|
|
119
|
+
callGraphPaths: [],
|
|
120
|
+
boundaries: [],
|
|
121
|
+
},
|
|
122
|
+
invariant: {
|
|
123
|
+
type: this.inferConstraintType(pattern),
|
|
124
|
+
condition: pattern.description ?? `Must follow ${pattern.name} pattern`,
|
|
125
|
+
predicate,
|
|
126
|
+
},
|
|
127
|
+
scope: this.buildScopeFromPattern(pattern),
|
|
128
|
+
confidence: constraintConfidence,
|
|
129
|
+
enforcement: {
|
|
130
|
+
level: confidence >= 0.95 ? 'error' : 'warning',
|
|
131
|
+
guidance: `Follow the ${pattern.name} pattern as established in the codebase`,
|
|
132
|
+
},
|
|
133
|
+
status: 'discovered',
|
|
134
|
+
language,
|
|
135
|
+
},
|
|
136
|
+
evidence: {
|
|
137
|
+
conforming,
|
|
138
|
+
violating,
|
|
139
|
+
conformingLocations: pattern.locations.slice(0, 5).map((l) => `${l.file}:${l.line}`),
|
|
140
|
+
violatingLocations: violations.slice(0, 5).map((o) => `${o.file}:${o.line}`),
|
|
141
|
+
sources: [`pattern:${pattern.id}`],
|
|
142
|
+
},
|
|
143
|
+
violations: violationDetails,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// ===========================================================================
|
|
147
|
+
// Call Graph-Based Detection
|
|
148
|
+
// ===========================================================================
|
|
149
|
+
/**
|
|
150
|
+
* Detect invariants from call graph analysis
|
|
151
|
+
*/
|
|
152
|
+
async detectFromCallGraph(_options) {
|
|
153
|
+
const invariants = [];
|
|
154
|
+
const store = this.config.callGraphStore;
|
|
155
|
+
// Load call graph
|
|
156
|
+
const graph = await store.load();
|
|
157
|
+
if (!graph)
|
|
158
|
+
return invariants;
|
|
159
|
+
// Detect auth-before-data-access invariants
|
|
160
|
+
const authInvariants = this.detectAuthBeforeDataAccess(graph);
|
|
161
|
+
invariants.push(...authInvariants);
|
|
162
|
+
// Detect validation patterns
|
|
163
|
+
const validationInvariants = this.detectValidationPatterns(graph);
|
|
164
|
+
invariants.push(...validationInvariants);
|
|
165
|
+
return invariants;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Detect auth-before-data-access patterns
|
|
169
|
+
*/
|
|
170
|
+
detectAuthBeforeDataAccess(graph) {
|
|
171
|
+
const invariants = [];
|
|
172
|
+
// Find entry points that access data
|
|
173
|
+
const entryPointIds = graph.entryPoints ?? [];
|
|
174
|
+
const entryPointsWithData = [];
|
|
175
|
+
for (const id of entryPointIds) {
|
|
176
|
+
const func = graph.functions.get(id);
|
|
177
|
+
if (func && func.dataAccess && func.dataAccess.length > 0) {
|
|
178
|
+
entryPointsWithData.push(func);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (entryPointsWithData.length < 3)
|
|
182
|
+
return invariants;
|
|
183
|
+
// Group by whether they have auth in the call chain
|
|
184
|
+
const withAuth = [];
|
|
185
|
+
const withoutAuth = [];
|
|
186
|
+
for (const entry of entryPointsWithData) {
|
|
187
|
+
const hasAuth = this.hasAuthInCallChain(entry, graph);
|
|
188
|
+
if (hasAuth) {
|
|
189
|
+
withAuth.push(entry);
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
withoutAuth.push(entry);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// If most entry points have auth, create an invariant
|
|
196
|
+
const total = withAuth.length + withoutAuth.length;
|
|
197
|
+
if (total >= 3 && withAuth.length / total >= 0.8) {
|
|
198
|
+
const confidence = withAuth.length / total;
|
|
199
|
+
invariants.push({
|
|
200
|
+
constraint: {
|
|
201
|
+
name: 'Auth Before Data Access',
|
|
202
|
+
description: 'Entry points that access data must have authentication in the call chain',
|
|
203
|
+
category: 'auth',
|
|
204
|
+
derivedFrom: {
|
|
205
|
+
patterns: [],
|
|
206
|
+
callGraphPaths: entryPointsWithData.map(e => e.id),
|
|
207
|
+
boundaries: [],
|
|
208
|
+
},
|
|
209
|
+
invariant: {
|
|
210
|
+
type: 'must_precede',
|
|
211
|
+
condition: 'Authentication must occur before data access',
|
|
212
|
+
predicate: {
|
|
213
|
+
callChain: {
|
|
214
|
+
from: 'entryPoint',
|
|
215
|
+
to: 'dataAccess',
|
|
216
|
+
mustInclude: ['authenticate', 'authorize', 'checkAuth', 'requireAuth'],
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
scope: {
|
|
221
|
+
entryPoints: true,
|
|
222
|
+
dataAccessors: true,
|
|
223
|
+
},
|
|
224
|
+
confidence: {
|
|
225
|
+
score: confidence,
|
|
226
|
+
evidence: withAuth.length,
|
|
227
|
+
violations: withoutAuth.length,
|
|
228
|
+
lastVerified: new Date().toISOString(),
|
|
229
|
+
},
|
|
230
|
+
enforcement: {
|
|
231
|
+
level: 'error',
|
|
232
|
+
guidance: 'Add authentication middleware or check before accessing data',
|
|
233
|
+
},
|
|
234
|
+
status: 'discovered',
|
|
235
|
+
language: 'all',
|
|
236
|
+
},
|
|
237
|
+
evidence: {
|
|
238
|
+
conforming: withAuth.length,
|
|
239
|
+
violating: withoutAuth.length,
|
|
240
|
+
conformingLocations: withAuth.slice(0, 5).map(f => `${f.file}:${f.startLine}`),
|
|
241
|
+
violatingLocations: withoutAuth.slice(0, 5).map(f => `${f.file}:${f.startLine}`),
|
|
242
|
+
sources: ['callGraph'],
|
|
243
|
+
},
|
|
244
|
+
violations: withoutAuth.map(f => ({
|
|
245
|
+
file: f.file,
|
|
246
|
+
line: f.startLine,
|
|
247
|
+
reason: 'Entry point accesses data without authentication in call chain',
|
|
248
|
+
})),
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
return invariants;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Check if a function has auth in its call chain
|
|
255
|
+
*/
|
|
256
|
+
hasAuthInCallChain(func, graph) {
|
|
257
|
+
const authPatterns = [
|
|
258
|
+
/auth/i, /authenticate/i, /authorize/i, /checkAuth/i,
|
|
259
|
+
/requireAuth/i, /isAuthenticated/i, /verifyToken/i,
|
|
260
|
+
/validateToken/i, /checkPermission/i, /hasRole/i,
|
|
261
|
+
];
|
|
262
|
+
const visited = new Set();
|
|
263
|
+
const queue = [func.id];
|
|
264
|
+
while (queue.length > 0) {
|
|
265
|
+
const current = queue.shift();
|
|
266
|
+
if (visited.has(current))
|
|
267
|
+
continue;
|
|
268
|
+
visited.add(current);
|
|
269
|
+
const node = graph.functions.get(current);
|
|
270
|
+
if (!node)
|
|
271
|
+
continue;
|
|
272
|
+
// Check if this function is auth-related
|
|
273
|
+
if (authPatterns.some(p => p.test(node.name))) {
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
// Check decorators/annotations
|
|
277
|
+
if (node.decorators?.some(d => authPatterns.some(p => p.test(d)))) {
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
// Add callees to queue
|
|
281
|
+
for (const call of node.calls ?? []) {
|
|
282
|
+
if (call.calleeId) {
|
|
283
|
+
queue.push(call.calleeId);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Detect validation patterns in call graph
|
|
291
|
+
*/
|
|
292
|
+
detectValidationPatterns(graph) {
|
|
293
|
+
const invariants = [];
|
|
294
|
+
// Find entry points
|
|
295
|
+
const entryPointIds = graph.entryPoints ?? [];
|
|
296
|
+
const entryPoints = [];
|
|
297
|
+
for (const id of entryPointIds) {
|
|
298
|
+
const func = graph.functions.get(id);
|
|
299
|
+
if (func)
|
|
300
|
+
entryPoints.push(func);
|
|
301
|
+
}
|
|
302
|
+
if (entryPoints.length < 3)
|
|
303
|
+
return invariants;
|
|
304
|
+
// Check for validation patterns
|
|
305
|
+
const validationPatterns = [
|
|
306
|
+
/validate/i, /sanitize/i, /check/i, /verify/i,
|
|
307
|
+
/parse/i, /schema/i, /zod/i, /yup/i, /joi/i,
|
|
308
|
+
];
|
|
309
|
+
const withValidation = [];
|
|
310
|
+
const withoutValidation = [];
|
|
311
|
+
for (const entry of entryPoints) {
|
|
312
|
+
const hasValidation = this.hasPatternInCallChain(entry, graph, validationPatterns);
|
|
313
|
+
if (hasValidation) {
|
|
314
|
+
withValidation.push(entry);
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
withoutValidation.push(entry);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const total = withValidation.length + withoutValidation.length;
|
|
321
|
+
if (total >= 3 && withValidation.length / total >= 0.7) {
|
|
322
|
+
const confidence = withValidation.length / total;
|
|
323
|
+
invariants.push({
|
|
324
|
+
constraint: {
|
|
325
|
+
name: 'Input Validation Required',
|
|
326
|
+
description: 'Entry points must validate input before processing',
|
|
327
|
+
category: 'validation',
|
|
328
|
+
derivedFrom: {
|
|
329
|
+
patterns: [],
|
|
330
|
+
callGraphPaths: entryPoints.map(e => e.id),
|
|
331
|
+
boundaries: [],
|
|
332
|
+
},
|
|
333
|
+
invariant: {
|
|
334
|
+
type: 'must_precede',
|
|
335
|
+
condition: 'Input validation must occur at entry points',
|
|
336
|
+
predicate: {
|
|
337
|
+
entryPointMustHave: {
|
|
338
|
+
inCallChain: ['validate', 'sanitize', 'parse', 'schema'],
|
|
339
|
+
position: 'before_handler',
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
scope: {
|
|
344
|
+
entryPoints: true,
|
|
345
|
+
},
|
|
346
|
+
confidence: {
|
|
347
|
+
score: confidence,
|
|
348
|
+
evidence: withValidation.length,
|
|
349
|
+
violations: withoutValidation.length,
|
|
350
|
+
lastVerified: new Date().toISOString(),
|
|
351
|
+
},
|
|
352
|
+
enforcement: {
|
|
353
|
+
level: 'warning',
|
|
354
|
+
guidance: 'Add input validation at the entry point',
|
|
355
|
+
},
|
|
356
|
+
status: 'discovered',
|
|
357
|
+
language: 'all',
|
|
358
|
+
},
|
|
359
|
+
evidence: {
|
|
360
|
+
conforming: withValidation.length,
|
|
361
|
+
violating: withoutValidation.length,
|
|
362
|
+
conformingLocations: withValidation.slice(0, 5).map(f => `${f.file}:${f.startLine}`),
|
|
363
|
+
violatingLocations: withoutValidation.slice(0, 5).map(f => `${f.file}:${f.startLine}`),
|
|
364
|
+
sources: ['callGraph'],
|
|
365
|
+
},
|
|
366
|
+
violations: withoutValidation.map(f => ({
|
|
367
|
+
file: f.file,
|
|
368
|
+
line: f.startLine,
|
|
369
|
+
reason: 'Entry point lacks input validation',
|
|
370
|
+
})),
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
return invariants;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Check if a function has a pattern in its call chain
|
|
377
|
+
*/
|
|
378
|
+
hasPatternInCallChain(func, graph, patterns) {
|
|
379
|
+
const visited = new Set();
|
|
380
|
+
const queue = [func.id];
|
|
381
|
+
while (queue.length > 0) {
|
|
382
|
+
const current = queue.shift();
|
|
383
|
+
if (visited.has(current))
|
|
384
|
+
continue;
|
|
385
|
+
visited.add(current);
|
|
386
|
+
const node = graph.functions.get(current);
|
|
387
|
+
if (!node)
|
|
388
|
+
continue;
|
|
389
|
+
if (patterns.some(p => p.test(node.name))) {
|
|
390
|
+
return true;
|
|
391
|
+
}
|
|
392
|
+
if (node.decorators?.some(d => patterns.some(p => p.test(d)))) {
|
|
393
|
+
return true;
|
|
394
|
+
}
|
|
395
|
+
for (const call of node.calls ?? []) {
|
|
396
|
+
if (call.calleeId) {
|
|
397
|
+
queue.push(call.calleeId);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
// ===========================================================================
|
|
404
|
+
// Boundary-Based Detection
|
|
405
|
+
// ===========================================================================
|
|
406
|
+
/**
|
|
407
|
+
* Detect invariants from boundary data
|
|
408
|
+
*/
|
|
409
|
+
async detectFromBoundaries(_options) {
|
|
410
|
+
const invariants = [];
|
|
411
|
+
const store = this.config.boundaryStore;
|
|
412
|
+
try {
|
|
413
|
+
await store.initialize();
|
|
414
|
+
const accessMap = store.getAccessMap();
|
|
415
|
+
if (!accessMap)
|
|
416
|
+
return invariants;
|
|
417
|
+
// Detect data access layer invariants from access points
|
|
418
|
+
const accessPoints = Object.values(accessMap.accessPoints);
|
|
419
|
+
if (accessPoints.length < 3)
|
|
420
|
+
return invariants;
|
|
421
|
+
// Group by table
|
|
422
|
+
const byTable = new Map();
|
|
423
|
+
for (const point of accessPoints) {
|
|
424
|
+
const table = point.table;
|
|
425
|
+
const list = byTable.get(table) ?? [];
|
|
426
|
+
list.push({
|
|
427
|
+
file: point.file,
|
|
428
|
+
accessor: point.context ?? 'direct',
|
|
429
|
+
line: point.line,
|
|
430
|
+
});
|
|
431
|
+
byTable.set(table, list);
|
|
432
|
+
}
|
|
433
|
+
// For each table with enough accesses, check for consistent access layer
|
|
434
|
+
for (const [table, accesses] of byTable) {
|
|
435
|
+
if (accesses.length < 3)
|
|
436
|
+
continue;
|
|
437
|
+
// Extract access layers
|
|
438
|
+
const layerCounts = new Map();
|
|
439
|
+
for (const access of accesses) {
|
|
440
|
+
const layer = this.extractAccessLayer(access.accessor);
|
|
441
|
+
layerCounts.set(layer, (layerCounts.get(layer) ?? 0) + 1);
|
|
442
|
+
}
|
|
443
|
+
// Find dominant layer
|
|
444
|
+
let dominantLayer = '';
|
|
445
|
+
let dominantCount = 0;
|
|
446
|
+
for (const [layer, count] of layerCounts) {
|
|
447
|
+
if (count > dominantCount) {
|
|
448
|
+
dominantLayer = layer;
|
|
449
|
+
dominantCount = count;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
const confidence = dominantCount / accesses.length;
|
|
453
|
+
if (confidence >= 0.8 && dominantLayer && dominantLayer !== 'direct') {
|
|
454
|
+
const violations = accesses.filter(a => this.extractAccessLayer(a.accessor) !== dominantLayer);
|
|
455
|
+
invariants.push({
|
|
456
|
+
constraint: {
|
|
457
|
+
name: `${table} Access Layer`,
|
|
458
|
+
description: `Access to ${table} must go through ${dominantLayer}`,
|
|
459
|
+
category: 'data',
|
|
460
|
+
derivedFrom: {
|
|
461
|
+
patterns: [],
|
|
462
|
+
callGraphPaths: [],
|
|
463
|
+
boundaries: [table],
|
|
464
|
+
},
|
|
465
|
+
invariant: {
|
|
466
|
+
type: 'data_flow',
|
|
467
|
+
condition: `All access to ${table} must go through ${dominantLayer}`,
|
|
468
|
+
predicate: {
|
|
469
|
+
dataAccess: {
|
|
470
|
+
table,
|
|
471
|
+
mustGoThrough: [dominantLayer],
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
scope: {
|
|
476
|
+
dataAccessors: true,
|
|
477
|
+
},
|
|
478
|
+
confidence: {
|
|
479
|
+
score: confidence,
|
|
480
|
+
evidence: dominantCount,
|
|
481
|
+
violations: violations.length,
|
|
482
|
+
lastVerified: new Date().toISOString(),
|
|
483
|
+
},
|
|
484
|
+
enforcement: {
|
|
485
|
+
level: 'warning',
|
|
486
|
+
guidance: `Access ${table} through ${dominantLayer} instead of directly`,
|
|
487
|
+
},
|
|
488
|
+
status: 'discovered',
|
|
489
|
+
language: 'all',
|
|
490
|
+
},
|
|
491
|
+
evidence: {
|
|
492
|
+
conforming: dominantCount,
|
|
493
|
+
violating: violations.length,
|
|
494
|
+
conformingLocations: accesses
|
|
495
|
+
.filter(a => this.extractAccessLayer(a.accessor) === dominantLayer)
|
|
496
|
+
.slice(0, 5)
|
|
497
|
+
.map(a => `${a.file}:${a.line}`),
|
|
498
|
+
violatingLocations: violations.slice(0, 5).map(a => `${a.file}:${a.line}`),
|
|
499
|
+
sources: ['boundaries'],
|
|
500
|
+
},
|
|
501
|
+
violations: violations.map(a => ({
|
|
502
|
+
file: a.file,
|
|
503
|
+
line: a.line,
|
|
504
|
+
reason: `Accesses ${table} directly instead of through ${dominantLayer}`,
|
|
505
|
+
})),
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
// Detect sensitive data access patterns
|
|
510
|
+
const sensitiveInvariants = this.detectSensitiveDataInvariants(accessMap);
|
|
511
|
+
invariants.push(...sensitiveInvariants);
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
// Boundary store not available or error
|
|
515
|
+
}
|
|
516
|
+
return invariants;
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* Detect invariants for sensitive data access
|
|
520
|
+
*/
|
|
521
|
+
detectSensitiveDataInvariants(accessMap) {
|
|
522
|
+
const invariants = [];
|
|
523
|
+
if (!accessMap.sensitiveFields || accessMap.sensitiveFields.length === 0) {
|
|
524
|
+
return invariants;
|
|
525
|
+
}
|
|
526
|
+
// Group sensitive fields by table
|
|
527
|
+
const sensitiveByTable = new Map();
|
|
528
|
+
for (const field of accessMap.sensitiveFields) {
|
|
529
|
+
if (!field.table)
|
|
530
|
+
continue; // Skip fields without table info
|
|
531
|
+
const fields = sensitiveByTable.get(field.table) ?? [];
|
|
532
|
+
fields.push(field.field);
|
|
533
|
+
sensitiveByTable.set(field.table, fields);
|
|
534
|
+
}
|
|
535
|
+
for (const [table, fields] of sensitiveByTable) {
|
|
536
|
+
invariants.push({
|
|
537
|
+
constraint: {
|
|
538
|
+
name: `${table} Sensitive Data Protection`,
|
|
539
|
+
description: `Sensitive fields in ${table} require special handling`,
|
|
540
|
+
category: 'security',
|
|
541
|
+
derivedFrom: {
|
|
542
|
+
patterns: [],
|
|
543
|
+
callGraphPaths: [],
|
|
544
|
+
boundaries: [table],
|
|
545
|
+
},
|
|
546
|
+
invariant: {
|
|
547
|
+
type: 'must_have',
|
|
548
|
+
condition: `Access to sensitive fields (${fields.join(', ')}) must be audited`,
|
|
549
|
+
predicate: {
|
|
550
|
+
dataAccess: {
|
|
551
|
+
table,
|
|
552
|
+
sensitiveFields: fields,
|
|
553
|
+
requiresAuth: true,
|
|
554
|
+
},
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
scope: {
|
|
558
|
+
dataAccessors: true,
|
|
559
|
+
},
|
|
560
|
+
confidence: {
|
|
561
|
+
score: 0.95,
|
|
562
|
+
evidence: fields.length,
|
|
563
|
+
violations: 0,
|
|
564
|
+
lastVerified: new Date().toISOString(),
|
|
565
|
+
},
|
|
566
|
+
enforcement: {
|
|
567
|
+
level: 'error',
|
|
568
|
+
guidance: `Ensure proper authorization and audit logging when accessing ${fields.join(', ')}`,
|
|
569
|
+
},
|
|
570
|
+
status: 'discovered',
|
|
571
|
+
language: 'all',
|
|
572
|
+
},
|
|
573
|
+
evidence: {
|
|
574
|
+
conforming: fields.length,
|
|
575
|
+
violating: 0,
|
|
576
|
+
conformingLocations: [],
|
|
577
|
+
violatingLocations: [],
|
|
578
|
+
sources: ['boundaries:sensitiveFields'],
|
|
579
|
+
},
|
|
580
|
+
violations: [],
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
return invariants;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Extract the access layer from an accessor context
|
|
587
|
+
*/
|
|
588
|
+
extractAccessLayer(accessor) {
|
|
589
|
+
const match = accessor.match(/(\w+)(Repository|Service|DAO|Store|Manager)/i);
|
|
590
|
+
return match ? match[0] : 'direct';
|
|
591
|
+
}
|
|
592
|
+
// ===========================================================================
|
|
593
|
+
// Test Topology-Based Detection
|
|
594
|
+
// ===========================================================================
|
|
595
|
+
/**
|
|
596
|
+
* Detect invariants from test topology
|
|
597
|
+
*/
|
|
598
|
+
async detectFromTestTopology(_options) {
|
|
599
|
+
const invariants = [];
|
|
600
|
+
const analyzer = this.config.testTopologyAnalyzer;
|
|
601
|
+
// Get summary statistics
|
|
602
|
+
const summary = analyzer.getSummary();
|
|
603
|
+
if (!summary || summary.totalFunctions < 5)
|
|
604
|
+
return invariants;
|
|
605
|
+
// If most functions are tested, create a test coverage invariant
|
|
606
|
+
const coverageRatio = summary.coveredFunctions / summary.totalFunctions;
|
|
607
|
+
if (coverageRatio >= 0.7) {
|
|
608
|
+
invariants.push({
|
|
609
|
+
constraint: {
|
|
610
|
+
name: 'Function Test Coverage',
|
|
611
|
+
description: 'Functions should have at least one test',
|
|
612
|
+
category: 'test',
|
|
613
|
+
derivedFrom: {
|
|
614
|
+
patterns: [],
|
|
615
|
+
callGraphPaths: [],
|
|
616
|
+
boundaries: [],
|
|
617
|
+
testTopology: ['coverage'],
|
|
618
|
+
},
|
|
619
|
+
invariant: {
|
|
620
|
+
type: 'must_have',
|
|
621
|
+
condition: 'Each function must have at least one test',
|
|
622
|
+
predicate: {
|
|
623
|
+
testCoverage: {
|
|
624
|
+
minCoverage: 1,
|
|
625
|
+
types: ['unit'],
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
scope: {
|
|
630
|
+
functions: ['.*'],
|
|
631
|
+
exclude: {
|
|
632
|
+
files: ['**/test/**', '**/*.test.*', '**/*.spec.*'],
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
confidence: {
|
|
636
|
+
score: coverageRatio,
|
|
637
|
+
evidence: summary.coveredFunctions,
|
|
638
|
+
violations: summary.totalFunctions - summary.coveredFunctions,
|
|
639
|
+
lastVerified: new Date().toISOString(),
|
|
640
|
+
},
|
|
641
|
+
enforcement: {
|
|
642
|
+
level: 'warning',
|
|
643
|
+
guidance: 'Add unit tests for this function',
|
|
644
|
+
},
|
|
645
|
+
status: 'discovered',
|
|
646
|
+
language: 'all',
|
|
647
|
+
},
|
|
648
|
+
evidence: {
|
|
649
|
+
conforming: summary.coveredFunctions,
|
|
650
|
+
violating: summary.totalFunctions - summary.coveredFunctions,
|
|
651
|
+
conformingLocations: [],
|
|
652
|
+
violatingLocations: [],
|
|
653
|
+
sources: ['testTopology'],
|
|
654
|
+
},
|
|
655
|
+
violations: [],
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
// Check mock ratio patterns
|
|
659
|
+
if (summary.avgMockRatio < 0.5) {
|
|
660
|
+
invariants.push({
|
|
661
|
+
constraint: {
|
|
662
|
+
name: 'Reasonable Mock Usage',
|
|
663
|
+
description: 'Tests should not over-mock - prefer integration over isolation',
|
|
664
|
+
category: 'test',
|
|
665
|
+
derivedFrom: {
|
|
666
|
+
patterns: [],
|
|
667
|
+
callGraphPaths: [],
|
|
668
|
+
boundaries: [],
|
|
669
|
+
testTopology: ['mocks'],
|
|
670
|
+
},
|
|
671
|
+
invariant: {
|
|
672
|
+
type: 'must_not_have',
|
|
673
|
+
condition: 'Tests should have mock ratio below 70%',
|
|
674
|
+
predicate: {
|
|
675
|
+
testCoverage: {
|
|
676
|
+
maxMockRatio: 0.7,
|
|
677
|
+
},
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
scope: {
|
|
681
|
+
files: ['**/*.test.*', '**/*.spec.*'],
|
|
682
|
+
},
|
|
683
|
+
confidence: {
|
|
684
|
+
score: 1 - summary.avgMockRatio,
|
|
685
|
+
evidence: summary.testCases,
|
|
686
|
+
violations: 0,
|
|
687
|
+
lastVerified: new Date().toISOString(),
|
|
688
|
+
},
|
|
689
|
+
enforcement: {
|
|
690
|
+
level: 'info',
|
|
691
|
+
guidance: 'Consider reducing mocks in favor of integration tests',
|
|
692
|
+
},
|
|
693
|
+
status: 'discovered',
|
|
694
|
+
language: 'all',
|
|
695
|
+
},
|
|
696
|
+
evidence: {
|
|
697
|
+
conforming: summary.testCases,
|
|
698
|
+
violating: 0,
|
|
699
|
+
conformingLocations: [],
|
|
700
|
+
violatingLocations: [],
|
|
701
|
+
sources: ['testTopology:mocks'],
|
|
702
|
+
},
|
|
703
|
+
violations: [],
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
return invariants;
|
|
707
|
+
}
|
|
708
|
+
// ===========================================================================
|
|
709
|
+
// Error Handling-Based Detection
|
|
710
|
+
// ===========================================================================
|
|
711
|
+
/**
|
|
712
|
+
* Detect invariants from error handling analysis
|
|
713
|
+
*/
|
|
714
|
+
async detectFromErrorHandling(_options) {
|
|
715
|
+
const invariants = [];
|
|
716
|
+
const analyzer = this.config.errorHandlingAnalyzer;
|
|
717
|
+
// Get topology and summary
|
|
718
|
+
const topology = analyzer.getTopology();
|
|
719
|
+
const summary = analyzer.getSummary();
|
|
720
|
+
if (!topology || !summary || summary.totalFunctions < 5)
|
|
721
|
+
return invariants;
|
|
722
|
+
// Detect async error handling patterns
|
|
723
|
+
const asyncFunctions = [];
|
|
724
|
+
for (const [_funcId, profile] of topology.functions) {
|
|
725
|
+
if (profile.isAsync) {
|
|
726
|
+
asyncFunctions.push({
|
|
727
|
+
file: profile.file,
|
|
728
|
+
line: profile.line,
|
|
729
|
+
name: profile.qualifiedName,
|
|
730
|
+
hasHandling: profile.hasTryCatch || (profile.asyncHandling?.hasAsyncTryCatch ?? false),
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
if (asyncFunctions.length >= 5) {
|
|
735
|
+
const withHandling = asyncFunctions.filter(f => f.hasHandling);
|
|
736
|
+
const withoutHandling = asyncFunctions.filter(f => !f.hasHandling);
|
|
737
|
+
const ratio = withHandling.length / asyncFunctions.length;
|
|
738
|
+
if (ratio >= 0.7) {
|
|
739
|
+
invariants.push({
|
|
740
|
+
constraint: {
|
|
741
|
+
name: 'Async Error Handling',
|
|
742
|
+
description: 'Async functions must have error handling',
|
|
743
|
+
category: 'error',
|
|
744
|
+
derivedFrom: {
|
|
745
|
+
patterns: [],
|
|
746
|
+
callGraphPaths: [],
|
|
747
|
+
boundaries: [],
|
|
748
|
+
errorHandling: ['topology'],
|
|
749
|
+
},
|
|
750
|
+
invariant: {
|
|
751
|
+
type: 'must_have',
|
|
752
|
+
condition: 'Async functions must have try-catch or error handling',
|
|
753
|
+
predicate: {
|
|
754
|
+
functionMustHave: {
|
|
755
|
+
errorHandling: true,
|
|
756
|
+
isAsync: true,
|
|
757
|
+
},
|
|
758
|
+
},
|
|
759
|
+
},
|
|
760
|
+
scope: {
|
|
761
|
+
functions: ['.*'],
|
|
762
|
+
},
|
|
763
|
+
confidence: {
|
|
764
|
+
score: ratio,
|
|
765
|
+
evidence: withHandling.length,
|
|
766
|
+
violations: withoutHandling.length,
|
|
767
|
+
lastVerified: new Date().toISOString(),
|
|
768
|
+
},
|
|
769
|
+
enforcement: {
|
|
770
|
+
level: 'warning',
|
|
771
|
+
guidance: 'Add try-catch or error handling to this async function',
|
|
772
|
+
},
|
|
773
|
+
status: 'discovered',
|
|
774
|
+
language: 'all',
|
|
775
|
+
},
|
|
776
|
+
evidence: {
|
|
777
|
+
conforming: withHandling.length,
|
|
778
|
+
violating: withoutHandling.length,
|
|
779
|
+
conformingLocations: withHandling.slice(0, 5).map(f => `${f.file}:${f.line}`),
|
|
780
|
+
violatingLocations: withoutHandling.slice(0, 5).map(f => `${f.file}:${f.line}`),
|
|
781
|
+
sources: ['errorHandling'],
|
|
782
|
+
},
|
|
783
|
+
violations: withoutHandling.slice(0, 20).map(f => ({
|
|
784
|
+
file: f.file,
|
|
785
|
+
line: f.line,
|
|
786
|
+
reason: 'Async function lacks error handling',
|
|
787
|
+
})),
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
// Detect error boundary patterns
|
|
792
|
+
const boundaries = topology.boundaries ?? [];
|
|
793
|
+
if (boundaries.length >= 2) {
|
|
794
|
+
const frameworkBoundaries = boundaries.filter(b => b.isFrameworkBoundary);
|
|
795
|
+
if (frameworkBoundaries.length > 0) {
|
|
796
|
+
invariants.push({
|
|
797
|
+
constraint: {
|
|
798
|
+
name: 'Error Boundary Pattern',
|
|
799
|
+
description: 'Use framework error boundaries for centralized error handling',
|
|
800
|
+
category: 'error',
|
|
801
|
+
derivedFrom: {
|
|
802
|
+
patterns: [],
|
|
803
|
+
callGraphPaths: [],
|
|
804
|
+
boundaries: [],
|
|
805
|
+
errorHandling: ['boundaries'],
|
|
806
|
+
},
|
|
807
|
+
invariant: {
|
|
808
|
+
type: 'must_have',
|
|
809
|
+
condition: 'Critical paths should be protected by error boundaries',
|
|
810
|
+
predicate: {
|
|
811
|
+
callChain: {
|
|
812
|
+
from: 'entryPoint',
|
|
813
|
+
to: 'dataAccess',
|
|
814
|
+
mustInclude: frameworkBoundaries.map(b => b.name),
|
|
815
|
+
},
|
|
816
|
+
},
|
|
817
|
+
},
|
|
818
|
+
scope: {
|
|
819
|
+
entryPoints: true,
|
|
820
|
+
},
|
|
821
|
+
confidence: {
|
|
822
|
+
score: 0.85,
|
|
823
|
+
evidence: frameworkBoundaries.length,
|
|
824
|
+
violations: 0,
|
|
825
|
+
lastVerified: new Date().toISOString(),
|
|
826
|
+
},
|
|
827
|
+
enforcement: {
|
|
828
|
+
level: 'info',
|
|
829
|
+
guidance: 'Ensure error boundaries protect critical code paths',
|
|
830
|
+
},
|
|
831
|
+
status: 'discovered',
|
|
832
|
+
language: 'all',
|
|
833
|
+
},
|
|
834
|
+
evidence: {
|
|
835
|
+
conforming: frameworkBoundaries.length,
|
|
836
|
+
violating: 0,
|
|
837
|
+
conformingLocations: frameworkBoundaries.slice(0, 5).map(b => `${b.file}:${b.line}`),
|
|
838
|
+
violatingLocations: [],
|
|
839
|
+
sources: ['errorHandling:boundaries'],
|
|
840
|
+
},
|
|
841
|
+
violations: [],
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
// Detect swallowed error anti-pattern
|
|
846
|
+
const swallowedCount = summary.topIssues?.find(i => i.type === 'swallowed')?.count ?? 0;
|
|
847
|
+
if (swallowedCount > 0 && summary.totalFunctions > 10) {
|
|
848
|
+
const swallowRatio = swallowedCount / summary.totalFunctions;
|
|
849
|
+
if (swallowRatio < 0.1) { // Less than 10% swallow errors = good pattern
|
|
850
|
+
invariants.push({
|
|
851
|
+
constraint: {
|
|
852
|
+
name: 'No Swallowed Errors',
|
|
853
|
+
description: 'Errors must not be silently swallowed',
|
|
854
|
+
category: 'error',
|
|
855
|
+
derivedFrom: {
|
|
856
|
+
patterns: [],
|
|
857
|
+
callGraphPaths: [],
|
|
858
|
+
boundaries: [],
|
|
859
|
+
errorHandling: ['gaps'],
|
|
860
|
+
},
|
|
861
|
+
invariant: {
|
|
862
|
+
type: 'must_not_have',
|
|
863
|
+
condition: 'Catch blocks must not swallow errors silently',
|
|
864
|
+
predicate: {
|
|
865
|
+
functionMustHave: {
|
|
866
|
+
bodyMustNotContain: ['catch {}', 'catch (e) {}', 'except: pass'],
|
|
867
|
+
},
|
|
868
|
+
},
|
|
869
|
+
},
|
|
870
|
+
scope: {
|
|
871
|
+
functions: ['.*'],
|
|
872
|
+
},
|
|
873
|
+
confidence: {
|
|
874
|
+
score: 1 - swallowRatio,
|
|
875
|
+
evidence: summary.totalFunctions - swallowedCount,
|
|
876
|
+
violations: swallowedCount,
|
|
877
|
+
lastVerified: new Date().toISOString(),
|
|
878
|
+
},
|
|
879
|
+
enforcement: {
|
|
880
|
+
level: 'error',
|
|
881
|
+
guidance: 'Log the error or rethrow it instead of swallowing',
|
|
882
|
+
},
|
|
883
|
+
status: 'discovered',
|
|
884
|
+
language: 'all',
|
|
885
|
+
},
|
|
886
|
+
evidence: {
|
|
887
|
+
conforming: summary.totalFunctions - swallowedCount,
|
|
888
|
+
violating: swallowedCount,
|
|
889
|
+
conformingLocations: [],
|
|
890
|
+
violatingLocations: [],
|
|
891
|
+
sources: ['errorHandling:gaps'],
|
|
892
|
+
},
|
|
893
|
+
violations: [],
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
return invariants;
|
|
898
|
+
}
|
|
899
|
+
// ===========================================================================
|
|
900
|
+
// Helper Methods
|
|
901
|
+
// ===========================================================================
|
|
902
|
+
detectLanguageFromPattern(pattern) {
|
|
903
|
+
const extensions = new Set();
|
|
904
|
+
for (const loc of pattern.locations) {
|
|
905
|
+
const ext = loc.file.split('.').pop()?.toLowerCase();
|
|
906
|
+
if (ext)
|
|
907
|
+
extensions.add(ext);
|
|
908
|
+
}
|
|
909
|
+
if (extensions.has('ts') || extensions.has('tsx'))
|
|
910
|
+
return 'typescript';
|
|
911
|
+
if (extensions.has('js') || extensions.has('jsx'))
|
|
912
|
+
return 'javascript';
|
|
913
|
+
if (extensions.has('py'))
|
|
914
|
+
return 'python';
|
|
915
|
+
if (extensions.has('java'))
|
|
916
|
+
return 'java';
|
|
917
|
+
if (extensions.has('cs'))
|
|
918
|
+
return 'csharp';
|
|
919
|
+
if (extensions.has('php'))
|
|
920
|
+
return 'php';
|
|
921
|
+
return 'all';
|
|
922
|
+
}
|
|
923
|
+
buildPredicateFromPattern(pattern) {
|
|
924
|
+
const predicate = {};
|
|
925
|
+
const config = pattern.detector;
|
|
926
|
+
if (config?.config && typeof config.config === 'object') {
|
|
927
|
+
const detectorConfig = config.config;
|
|
928
|
+
if (Array.isArray(detectorConfig['decorators']) && detectorConfig['decorators'].length > 0) {
|
|
929
|
+
predicate.functionMustHave = {
|
|
930
|
+
decorator: detectorConfig['decorators'],
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
if (typeof detectorConfig['namePattern'] === 'string') {
|
|
934
|
+
predicate.naming = {
|
|
935
|
+
pattern: detectorConfig['namePattern'],
|
|
936
|
+
scope: 'function',
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return predicate;
|
|
941
|
+
}
|
|
942
|
+
inferConstraintType(pattern) {
|
|
943
|
+
const category = pattern.category;
|
|
944
|
+
switch (category) {
|
|
945
|
+
case 'auth':
|
|
946
|
+
return 'must_precede';
|
|
947
|
+
case 'errors':
|
|
948
|
+
return 'must_wrap';
|
|
949
|
+
case 'logging':
|
|
950
|
+
return 'must_have';
|
|
951
|
+
case 'testing':
|
|
952
|
+
return 'must_have';
|
|
953
|
+
case 'security':
|
|
954
|
+
return 'must_have';
|
|
955
|
+
default:
|
|
956
|
+
return 'must_have';
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
buildScopeFromPattern(pattern) {
|
|
960
|
+
const scope = {};
|
|
961
|
+
const directories = new Set();
|
|
962
|
+
for (const loc of pattern.locations) {
|
|
963
|
+
const dir = loc.file.split('/').slice(0, -1).join('/');
|
|
964
|
+
if (dir)
|
|
965
|
+
directories.add(dir);
|
|
966
|
+
}
|
|
967
|
+
if (directories.size > 0) {
|
|
968
|
+
scope.files = Array.from(directories).map(d => `${d}/**/*`);
|
|
969
|
+
}
|
|
970
|
+
return scope;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
// =============================================================================
|
|
974
|
+
// Factory
|
|
975
|
+
// =============================================================================
|
|
976
|
+
export function createInvariantDetector(config) {
|
|
977
|
+
return new InvariantDetector(config);
|
|
978
|
+
}
|
|
979
|
+
//# sourceMappingURL=invariant-detector.js.map
|