@oculum/scanner 1.0.9 → 1.0.11
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/baseline/diff.d.ts +32 -0
- package/dist/baseline/diff.d.ts.map +1 -0
- package/dist/baseline/diff.js +119 -0
- package/dist/baseline/diff.js.map +1 -0
- package/dist/baseline/index.d.ts +9 -0
- package/dist/baseline/index.d.ts.map +1 -0
- package/dist/baseline/index.js +19 -0
- package/dist/baseline/index.js.map +1 -0
- package/dist/baseline/manager.d.ts +67 -0
- package/dist/baseline/manager.d.ts.map +1 -0
- package/dist/baseline/manager.js +180 -0
- package/dist/baseline/manager.js.map +1 -0
- package/dist/baseline/types.d.ts +91 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +12 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/formatters/cli-terminal.d.ts +38 -0
- package/dist/formatters/cli-terminal.d.ts.map +1 -1
- package/dist/formatters/cli-terminal.js +365 -42
- package/dist/formatters/cli-terminal.js.map +1 -1
- package/dist/formatters/github-comment.d.ts +1 -1
- package/dist/formatters/github-comment.d.ts.map +1 -1
- package/dist/formatters/github-comment.js +75 -11
- package/dist/formatters/github-comment.js.map +1 -1
- package/dist/formatters/index.d.ts +1 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +4 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +155 -16
- package/dist/index.js.map +1 -1
- package/dist/layer1/config-audit.d.ts.map +1 -1
- package/dist/layer1/config-audit.js +20 -3
- package/dist/layer1/config-audit.js.map +1 -1
- package/dist/layer1/config-mcp-audit.d.ts +20 -0
- package/dist/layer1/config-mcp-audit.d.ts.map +1 -0
- package/dist/layer1/config-mcp-audit.js +239 -0
- package/dist/layer1/config-mcp-audit.js.map +1 -0
- package/dist/layer1/index.d.ts +1 -0
- package/dist/layer1/index.d.ts.map +1 -1
- package/dist/layer1/index.js +9 -1
- package/dist/layer1/index.js.map +1 -1
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
- package/dist/layer2/ai-agent-tools.js +303 -0
- package/dist/layer2/ai-agent-tools.js.map +1 -1
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
- package/dist/layer2/ai-endpoint-protection.js +17 -3
- package/dist/layer2/ai-endpoint-protection.js.map +1 -1
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
- package/dist/layer2/ai-execution-sinks.js +462 -12
- package/dist/layer2/ai-execution-sinks.js.map +1 -1
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
- package/dist/layer2/ai-fingerprinting.js +3 -0
- package/dist/layer2/ai-fingerprinting.js.map +1 -1
- package/dist/layer2/ai-mcp-security.d.ts +17 -0
- package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
- package/dist/layer2/ai-mcp-security.js +679 -0
- package/dist/layer2/ai-mcp-security.js.map +1 -0
- package/dist/layer2/ai-package-hallucination.d.ts +19 -0
- package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
- package/dist/layer2/ai-package-hallucination.js +696 -0
- package/dist/layer2/ai-package-hallucination.js.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
- package/dist/layer2/ai-prompt-hygiene.js +495 -9
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
- package/dist/layer2/ai-rag-safety.js +372 -1
- package/dist/layer2/ai-rag-safety.js.map +1 -1
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
- package/dist/layer2/auth-antipatterns.js +4 -0
- package/dist/layer2/auth-antipatterns.js.map +1 -1
- package/dist/layer2/byok-patterns.d.ts.map +1 -1
- package/dist/layer2/byok-patterns.js +3 -0
- package/dist/layer2/byok-patterns.js.map +1 -1
- package/dist/layer2/dangerous-functions/child-process.d.ts +16 -0
- package/dist/layer2/dangerous-functions/child-process.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/child-process.js +74 -0
- package/dist/layer2/dangerous-functions/child-process.js.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts +29 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.js +179 -0
- package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
- package/dist/layer2/dangerous-functions/index.d.ts +13 -0
- package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/index.js +621 -0
- package/dist/layer2/dangerous-functions/index.js.map +1 -0
- package/dist/layer2/dangerous-functions/json-parse.d.ts +31 -0
- package/dist/layer2/dangerous-functions/json-parse.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/json-parse.js +319 -0
- package/dist/layer2/dangerous-functions/json-parse.js.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts +61 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.js +459 -0
- package/dist/layer2/dangerous-functions/math-random.js.map +1 -0
- package/dist/layer2/dangerous-functions/patterns.d.ts +21 -0
- package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/patterns.js +161 -0
- package/dist/layer2/dangerous-functions/patterns.js.map +1 -0
- package/dist/layer2/dangerous-functions/request-validation.d.ts +13 -0
- package/dist/layer2/dangerous-functions/request-validation.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/request-validation.js +119 -0
- package/dist/layer2/dangerous-functions/request-validation.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +23 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js +149 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/helpers.d.ts +31 -0
- package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/helpers.js +124 -0
- package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/index.d.ts +9 -0
- package/dist/layer2/dangerous-functions/utils/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/index.js +23 -0
- package/dist/layer2/dangerous-functions/utils/index.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +22 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js +89 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
- package/dist/layer2/data-exposure.d.ts.map +1 -1
- package/dist/layer2/data-exposure.js +3 -0
- package/dist/layer2/data-exposure.js.map +1 -1
- package/dist/layer2/framework-checks.d.ts.map +1 -1
- package/dist/layer2/framework-checks.js +3 -0
- package/dist/layer2/framework-checks.js.map +1 -1
- package/dist/layer2/index.d.ts +3 -0
- package/dist/layer2/index.d.ts.map +1 -1
- package/dist/layer2/index.js +61 -2
- package/dist/layer2/index.js.map +1 -1
- package/dist/layer2/logic-gates.d.ts.map +1 -1
- package/dist/layer2/logic-gates.js +4 -0
- package/dist/layer2/logic-gates.js.map +1 -1
- package/dist/layer2/model-supply-chain.d.ts +20 -0
- package/dist/layer2/model-supply-chain.d.ts.map +1 -0
- package/dist/layer2/model-supply-chain.js +376 -0
- package/dist/layer2/model-supply-chain.js.map +1 -0
- package/dist/layer2/risky-imports.d.ts.map +1 -1
- package/dist/layer2/risky-imports.js +4 -0
- package/dist/layer2/risky-imports.js.map +1 -1
- package/dist/layer2/variables.d.ts.map +1 -1
- package/dist/layer2/variables.js +4 -0
- package/dist/layer2/variables.js.map +1 -1
- package/dist/layer3/anthropic/auto-dismiss.d.ts +24 -0
- package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -0
- package/dist/layer3/anthropic/auto-dismiss.js +188 -0
- package/dist/layer3/anthropic/auto-dismiss.js.map +1 -0
- package/dist/layer3/anthropic/clients.d.ts +44 -0
- package/dist/layer3/anthropic/clients.d.ts.map +1 -0
- package/dist/layer3/anthropic/clients.js +81 -0
- package/dist/layer3/anthropic/clients.js.map +1 -0
- package/dist/layer3/anthropic/index.d.ts +41 -0
- package/dist/layer3/anthropic/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/index.js +141 -0
- package/dist/layer3/anthropic/index.js.map +1 -0
- package/dist/layer3/anthropic/prompts/index.d.ts +8 -0
- package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/index.js +14 -0
- package/dist/layer3/anthropic/prompts/index.js.map +1 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +15 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.js +169 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +1 -0
- package/dist/layer3/anthropic/prompts/validation.d.ts +12 -0
- package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/validation.js +421 -0
- package/dist/layer3/anthropic/prompts/validation.js.map +1 -0
- package/dist/layer3/anthropic/providers/anthropic.d.ts +21 -0
- package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/anthropic.js +266 -0
- package/dist/layer3/anthropic/providers/anthropic.js.map +1 -0
- package/dist/layer3/anthropic/providers/index.d.ts +8 -0
- package/dist/layer3/anthropic/providers/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/index.js +15 -0
- package/dist/layer3/anthropic/providers/index.js.map +1 -0
- package/dist/layer3/anthropic/providers/openai.d.ts +18 -0
- package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/openai.js +340 -0
- package/dist/layer3/anthropic/providers/openai.js.map +1 -0
- package/dist/layer3/anthropic/request-builder.d.ts +20 -0
- package/dist/layer3/anthropic/request-builder.d.ts.map +1 -0
- package/dist/layer3/anthropic/request-builder.js +134 -0
- package/dist/layer3/anthropic/request-builder.js.map +1 -0
- package/dist/layer3/anthropic/types.d.ts +88 -0
- package/dist/layer3/anthropic/types.d.ts.map +1 -0
- package/dist/layer3/anthropic/types.js +38 -0
- package/dist/layer3/anthropic/types.js.map +1 -0
- package/dist/layer3/anthropic/utils/index.d.ts +9 -0
- package/dist/layer3/anthropic/utils/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/index.js +24 -0
- package/dist/layer3/anthropic/utils/index.js.map +1 -0
- package/dist/layer3/anthropic/utils/path-helpers.d.ts +21 -0
- package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/path-helpers.js +69 -0
- package/dist/layer3/anthropic/utils/path-helpers.js.map +1 -0
- package/dist/layer3/anthropic/utils/response-parser.d.ts +40 -0
- package/dist/layer3/anthropic/utils/response-parser.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/response-parser.js +285 -0
- package/dist/layer3/anthropic/utils/response-parser.js.map +1 -0
- package/dist/layer3/anthropic/utils/retry.d.ts +15 -0
- package/dist/layer3/anthropic/utils/retry.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/retry.js +62 -0
- package/dist/layer3/anthropic/utils/retry.js.map +1 -0
- package/dist/layer3/index.d.ts +1 -0
- package/dist/layer3/index.d.ts.map +1 -1
- package/dist/layer3/index.js +16 -6
- package/dist/layer3/index.js.map +1 -1
- package/dist/layer3/osv-check.d.ts +75 -0
- package/dist/layer3/osv-check.d.ts.map +1 -0
- package/dist/layer3/osv-check.js +308 -0
- package/dist/layer3/osv-check.js.map +1 -0
- package/dist/rules/framework-fixes.d.ts +48 -0
- package/dist/rules/framework-fixes.d.ts.map +1 -0
- package/dist/rules/framework-fixes.js +439 -0
- package/dist/rules/framework-fixes.js.map +1 -0
- package/dist/rules/index.d.ts +8 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +18 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/metadata.d.ts +43 -0
- package/dist/rules/metadata.d.ts.map +1 -0
- package/dist/rules/metadata.js +734 -0
- package/dist/rules/metadata.js.map +1 -0
- package/dist/suppression/config-loader.d.ts +74 -0
- package/dist/suppression/config-loader.d.ts.map +1 -0
- package/dist/suppression/config-loader.js +424 -0
- package/dist/suppression/config-loader.js.map +1 -0
- package/dist/suppression/hash.d.ts +48 -0
- package/dist/suppression/hash.d.ts.map +1 -0
- package/dist/suppression/hash.js +88 -0
- package/dist/suppression/hash.js.map +1 -0
- package/dist/suppression/index.d.ts +11 -0
- package/dist/suppression/index.d.ts.map +1 -0
- package/dist/suppression/index.js +39 -0
- package/dist/suppression/index.js.map +1 -0
- package/dist/suppression/inline-parser.d.ts +39 -0
- package/dist/suppression/inline-parser.d.ts.map +1 -0
- package/dist/suppression/inline-parser.js +218 -0
- package/dist/suppression/inline-parser.js.map +1 -0
- package/dist/suppression/manager.d.ts +94 -0
- package/dist/suppression/manager.d.ts.map +1 -0
- package/dist/suppression/manager.js +292 -0
- package/dist/suppression/manager.js.map +1 -0
- package/dist/suppression/types.d.ts +151 -0
- package/dist/suppression/types.d.ts.map +1 -0
- package/dist/suppression/types.js +28 -0
- package/dist/suppression/types.js.map +1 -0
- package/dist/tiers.d.ts +1 -1
- package/dist/tiers.d.ts.map +1 -1
- package/dist/tiers.js +27 -0
- package/dist/tiers.js.map +1 -1
- package/dist/types.d.ts +62 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/context-helpers.d.ts +4 -0
- package/dist/utils/context-helpers.d.ts.map +1 -1
- package/dist/utils/context-helpers.js +13 -9
- package/dist/utils/context-helpers.js.map +1 -1
- package/package.json +4 -2
- package/src/__tests__/benchmark/fixtures/layer1/mcp-config-audit.json +31 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1489 -82
- package/src/__tests__/benchmark/fixtures/layer2/ai-mcp-security.ts +495 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-package-hallucination.ts +255 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +300 -1
- package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +139 -0
- package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +7 -0
- package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +63 -0
- package/src/__tests__/benchmark/fixtures/layer2/excessive-agency.ts +221 -0
- package/src/__tests__/benchmark/fixtures/layer2/index.ts +18 -0
- package/src/__tests__/benchmark/fixtures/layer2/model-supply-chain.ts +204 -0
- package/src/__tests__/benchmark/fixtures/layer2/phase1-enhancements.ts +157 -0
- package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +758 -0
- package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
- package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
- package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
- package/src/baseline/__tests__/diff.test.ts +261 -0
- package/src/baseline/__tests__/manager.test.ts +225 -0
- package/src/baseline/diff.ts +135 -0
- package/src/baseline/index.ts +29 -0
- package/src/baseline/manager.ts +230 -0
- package/src/baseline/types.ts +97 -0
- package/src/formatters/cli-terminal.ts +444 -41
- package/src/formatters/github-comment.ts +79 -11
- package/src/formatters/index.ts +4 -0
- package/src/index.ts +197 -14
- package/src/layer1/config-audit.ts +24 -3
- package/src/layer1/config-mcp-audit.ts +276 -0
- package/src/layer1/index.ts +16 -6
- package/src/layer2/ai-agent-tools.ts +336 -0
- package/src/layer2/ai-endpoint-protection.ts +16 -3
- package/src/layer2/ai-execution-sinks.ts +516 -12
- package/src/layer2/ai-fingerprinting.ts +5 -1
- package/src/layer2/ai-mcp-security.ts +730 -0
- package/src/layer2/ai-package-hallucination.ts +791 -0
- package/src/layer2/ai-prompt-hygiene.ts +547 -9
- package/src/layer2/ai-rag-safety.ts +382 -3
- package/src/layer2/auth-antipatterns.ts +5 -0
- package/src/layer2/byok-patterns.ts +5 -1
- package/src/layer2/dangerous-functions/child-process.ts +98 -0
- package/src/layer2/dangerous-functions/dom-xss.ts +220 -0
- package/src/layer2/dangerous-functions/index.ts +949 -0
- package/src/layer2/dangerous-functions/json-parse.ts +385 -0
- package/src/layer2/dangerous-functions/math-random.ts +537 -0
- package/src/layer2/dangerous-functions/patterns.ts +174 -0
- package/src/layer2/dangerous-functions/request-validation.ts +145 -0
- package/src/layer2/dangerous-functions/utils/control-flow.ts +162 -0
- package/src/layer2/dangerous-functions/utils/helpers.ts +170 -0
- package/src/layer2/dangerous-functions/utils/index.ts +25 -0
- package/src/layer2/dangerous-functions/utils/schema-validation.ts +91 -0
- package/src/layer2/data-exposure.ts +5 -1
- package/src/layer2/framework-checks.ts +5 -0
- package/src/layer2/index.ts +63 -1
- package/src/layer2/logic-gates.ts +5 -0
- package/src/layer2/model-supply-chain.ts +456 -0
- package/src/layer2/risky-imports.ts +5 -0
- package/src/layer2/variables.ts +5 -0
- package/src/layer3/__tests__/osv-check.test.ts +384 -0
- package/src/layer3/anthropic/auto-dismiss.ts +212 -0
- package/src/layer3/anthropic/clients.ts +84 -0
- package/src/layer3/anthropic/index.ts +170 -0
- package/src/layer3/anthropic/prompts/index.ts +14 -0
- package/src/layer3/anthropic/prompts/semantic-analysis.ts +173 -0
- package/src/layer3/anthropic/prompts/validation.ts +419 -0
- package/src/layer3/anthropic/providers/anthropic.ts +310 -0
- package/src/layer3/anthropic/providers/index.ts +8 -0
- package/src/layer3/anthropic/providers/openai.ts +384 -0
- package/src/layer3/anthropic/request-builder.ts +150 -0
- package/src/layer3/anthropic/types.ts +148 -0
- package/src/layer3/anthropic/utils/index.ts +26 -0
- package/src/layer3/anthropic/utils/path-helpers.ts +68 -0
- package/src/layer3/anthropic/utils/response-parser.ts +322 -0
- package/src/layer3/anthropic/utils/retry.ts +75 -0
- package/src/layer3/index.ts +18 -5
- package/src/layer3/osv-check.ts +420 -0
- package/src/rules/__tests__/framework-fixes.test.ts +689 -0
- package/src/rules/__tests__/metadata.test.ts +218 -0
- package/src/rules/framework-fixes.ts +470 -0
- package/src/rules/index.ts +21 -0
- package/src/rules/metadata.ts +831 -0
- package/src/suppression/__tests__/config-loader.test.ts +382 -0
- package/src/suppression/__tests__/hash.test.ts +166 -0
- package/src/suppression/__tests__/inline-parser.test.ts +212 -0
- package/src/suppression/__tests__/manager.test.ts +415 -0
- package/src/suppression/config-loader.ts +462 -0
- package/src/suppression/hash.ts +95 -0
- package/src/suppression/index.ts +51 -0
- package/src/suppression/inline-parser.ts +273 -0
- package/src/suppression/manager.ts +379 -0
- package/src/suppression/types.ts +174 -0
- package/src/tiers.ts +36 -0
- package/src/types.ts +90 -0
- package/src/utils/context-helpers.ts +13 -9
- package/dist/layer2/dangerous-functions.d.ts +0 -7
- package/dist/layer2/dangerous-functions.d.ts.map +0 -1
- package/dist/layer2/dangerous-functions.js +0 -1701
- package/dist/layer2/dangerous-functions.js.map +0 -1
- package/dist/layer3/anthropic.d.ts +0 -87
- package/dist/layer3/anthropic.d.ts.map +0 -1
- package/dist/layer3/anthropic.js +0 -1948
- package/dist/layer3/anthropic.js.map +0 -1
- package/dist/layer3/openai.d.ts +0 -25
- package/dist/layer3/openai.d.ts.map +0 -1
- package/dist/layer3/openai.js +0 -238
- package/dist/layer3/openai.js.map +0 -1
- package/src/layer2/dangerous-functions.ts +0 -1940
- package/src/layer3/anthropic.ts +0 -2257
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility Functions Index
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all utility functions from the anthropic module.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
normalizePathForComparison,
|
|
9
|
+
findMatchingFilePath,
|
|
10
|
+
getLanguageFromPath,
|
|
11
|
+
} from './path-helpers'
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
makeAnthropicRequestWithRetry,
|
|
15
|
+
makeOpenAIRequestWithRetry,
|
|
16
|
+
} from './retry'
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
parseAIResponse,
|
|
20
|
+
parseValidationResponse,
|
|
21
|
+
parseMultiFileValidationResponse,
|
|
22
|
+
applyValidationResults,
|
|
23
|
+
validateSeverity,
|
|
24
|
+
validateCategory,
|
|
25
|
+
getLineContent,
|
|
26
|
+
} from './response-parser'
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Path Normalization Helpers
|
|
3
|
+
*
|
|
4
|
+
* Functions for normalizing and matching file paths in AI responses.
|
|
5
|
+
* AI models may return paths in different formats than expected.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a file path for comparison purposes.
|
|
10
|
+
* Handles common variations: ./src/file.ts, src/file.ts, /src/file.ts
|
|
11
|
+
*/
|
|
12
|
+
export function normalizePathForComparison(path: string): string {
|
|
13
|
+
return path
|
|
14
|
+
.replace(/^\.\//, '') // Remove leading ./
|
|
15
|
+
.replace(/^\//, '') // Remove leading /
|
|
16
|
+
.replace(/\\/g, '/') // Normalize Windows backslashes
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Find a matching file path from expected paths, handling path format variations.
|
|
21
|
+
* AI responses may use different path formats than what we sent.
|
|
22
|
+
*/
|
|
23
|
+
export function findMatchingFilePath(responsePath: string, expectedPaths: string[]): string | null {
|
|
24
|
+
// Exact match first
|
|
25
|
+
if (expectedPaths.includes(responsePath)) return responsePath
|
|
26
|
+
|
|
27
|
+
// Normalized match
|
|
28
|
+
const normalized = normalizePathForComparison(responsePath)
|
|
29
|
+
for (const expected of expectedPaths) {
|
|
30
|
+
if (normalizePathForComparison(expected) === normalized) {
|
|
31
|
+
console.log(`[AI Validation] Path fuzzy matched: "${responsePath}" -> "${expected}"`)
|
|
32
|
+
return expected
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Basename match (only if unique) - handles cases like "file.ts" matching "src/api/file.ts"
|
|
37
|
+
const basename = responsePath.split('/').pop() || responsePath
|
|
38
|
+
const matches = expectedPaths.filter(p => (p.split('/').pop() || p) === basename)
|
|
39
|
+
if (matches.length === 1) {
|
|
40
|
+
console.log(`[AI Validation] Path basename matched: "${responsePath}" -> "${matches[0]}"`)
|
|
41
|
+
return matches[0]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get language identifier from file path extension
|
|
49
|
+
*/
|
|
50
|
+
export function getLanguageFromPath(path: string): string {
|
|
51
|
+
const ext = path.split('.').pop()?.toLowerCase()
|
|
52
|
+
const langMap: Record<string, string> = {
|
|
53
|
+
ts: 'typescript',
|
|
54
|
+
tsx: 'tsx',
|
|
55
|
+
js: 'javascript',
|
|
56
|
+
jsx: 'jsx',
|
|
57
|
+
py: 'python',
|
|
58
|
+
rb: 'ruby',
|
|
59
|
+
go: 'go',
|
|
60
|
+
java: 'java',
|
|
61
|
+
php: 'php',
|
|
62
|
+
cs: 'csharp',
|
|
63
|
+
json: 'json',
|
|
64
|
+
yaml: 'yaml',
|
|
65
|
+
yml: 'yaml',
|
|
66
|
+
}
|
|
67
|
+
return langMap[ext || ''] || ext || 'text'
|
|
68
|
+
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Response Parsing Utilities
|
|
3
|
+
*
|
|
4
|
+
* Functions for parsing validation responses from AI models.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { VulnerabilitySeverity, VulnerabilityCategory, Vulnerability, ValidationStatus } from '../../../types'
|
|
8
|
+
import type { ValidationResult, AIFinding } from '../types'
|
|
9
|
+
import { findMatchingFilePath } from './path-helpers'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Extract the first top-level JSON array from text.
|
|
13
|
+
* The model may include prose before/after the JSON.
|
|
14
|
+
*/
|
|
15
|
+
function extractTopLevelArray(text: string): string | null {
|
|
16
|
+
const startIndex = text.indexOf('[')
|
|
17
|
+
if (startIndex === -1) return null
|
|
18
|
+
|
|
19
|
+
let depth = 0
|
|
20
|
+
let inString = false
|
|
21
|
+
let stringChar: '"' | "'" | null = null
|
|
22
|
+
let escape = false
|
|
23
|
+
|
|
24
|
+
for (let i = startIndex; i < text.length; i++) {
|
|
25
|
+
const ch = text[i]
|
|
26
|
+
|
|
27
|
+
if (inString) {
|
|
28
|
+
if (escape) {
|
|
29
|
+
escape = false
|
|
30
|
+
continue
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (ch === '\\') {
|
|
34
|
+
escape = true
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (stringChar && ch === stringChar) {
|
|
39
|
+
inString = false
|
|
40
|
+
stringChar = null
|
|
41
|
+
}
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (ch === '"' || ch === "'") {
|
|
46
|
+
inString = true
|
|
47
|
+
stringChar = ch as '"' | "'"
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (ch === '[') {
|
|
52
|
+
depth++
|
|
53
|
+
} else if (ch === ']') {
|
|
54
|
+
depth--
|
|
55
|
+
if (depth === 0) {
|
|
56
|
+
return text.slice(startIndex, i + 1)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return null
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parse AI response for single file validation
|
|
66
|
+
*/
|
|
67
|
+
export function parseAIResponse(response: string): AIFinding[] {
|
|
68
|
+
try {
|
|
69
|
+
// Try to extract JSON from the response
|
|
70
|
+
const jsonMatch = response.match(/\[[\s\S]*\]/)
|
|
71
|
+
if (!jsonMatch) {
|
|
72
|
+
return []
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const parsed = JSON.parse(jsonMatch[0])
|
|
76
|
+
|
|
77
|
+
// Validate the structure
|
|
78
|
+
if (!Array.isArray(parsed)) {
|
|
79
|
+
return []
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return parsed.filter(item =>
|
|
83
|
+
typeof item.lineNumber === 'number' &&
|
|
84
|
+
typeof item.severity === 'string' &&
|
|
85
|
+
typeof item.category === 'string' &&
|
|
86
|
+
typeof item.title === 'string' &&
|
|
87
|
+
typeof item.description === 'string'
|
|
88
|
+
).map(item => ({
|
|
89
|
+
lineNumber: item.lineNumber,
|
|
90
|
+
severity: validateSeverity(item.severity),
|
|
91
|
+
category: validateCategory(item.category),
|
|
92
|
+
title: item.title,
|
|
93
|
+
description: item.description,
|
|
94
|
+
suggestedFix: item.suggestedFix || 'Review and fix the security issue',
|
|
95
|
+
}))
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error('Failed to parse AI response:', error)
|
|
98
|
+
return []
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parse single-file validation response
|
|
104
|
+
*/
|
|
105
|
+
export function parseValidationResponse(response: string): ValidationResult[] {
|
|
106
|
+
try {
|
|
107
|
+
const jsonSlice = extractTopLevelArray(response)
|
|
108
|
+
if (!jsonSlice) return []
|
|
109
|
+
|
|
110
|
+
const parsed = JSON.parse(jsonSlice)
|
|
111
|
+
if (!Array.isArray(parsed)) return []
|
|
112
|
+
|
|
113
|
+
return parsed
|
|
114
|
+
.filter(item =>
|
|
115
|
+
typeof item.index === 'number' &&
|
|
116
|
+
typeof item.keep === 'boolean'
|
|
117
|
+
)
|
|
118
|
+
.map(item => {
|
|
119
|
+
// Normalize notes field: prefer new 'notes', fallback to legacy 'reason' or 'validationNotes'
|
|
120
|
+
const notes = item.notes || item.validationNotes || item.reason || undefined
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
index: item.index,
|
|
124
|
+
keep: item.keep,
|
|
125
|
+
notes,
|
|
126
|
+
adjustedSeverity: item.adjustedSeverity || null,
|
|
127
|
+
// Keep legacy fields for backward compatibility
|
|
128
|
+
reason: item.reason,
|
|
129
|
+
validationNotes: item.validationNotes,
|
|
130
|
+
// Actionable output fields (PRO-82)
|
|
131
|
+
impact: item.impact || undefined,
|
|
132
|
+
fixSuggestion: item.fixSuggestion || undefined,
|
|
133
|
+
}
|
|
134
|
+
})
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error('Failed to parse validation response:', error)
|
|
137
|
+
return []
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parse multi-file validation response (Phase 2)
|
|
143
|
+
* Returns a map of file path -> validation results
|
|
144
|
+
*/
|
|
145
|
+
export function parseMultiFileValidationResponse(
|
|
146
|
+
response: string,
|
|
147
|
+
expectedFiles: string[]
|
|
148
|
+
): Map<string, ValidationResult[]> {
|
|
149
|
+
const resultMap = new Map<string, ValidationResult[]>()
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const jsonSlice = extractTopLevelArray(response)
|
|
153
|
+
if (!jsonSlice) {
|
|
154
|
+
console.error('[AI Validation] Multi-file: No JSON array found in response')
|
|
155
|
+
return resultMap
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const parsed = JSON.parse(jsonSlice)
|
|
159
|
+
if (!Array.isArray(parsed)) {
|
|
160
|
+
console.error('[AI Validation] Multi-file: Parsed result is not an array')
|
|
161
|
+
return resultMap
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Process each file's results
|
|
165
|
+
for (const fileResult of parsed) {
|
|
166
|
+
if (!fileResult.file || !Array.isArray(fileResult.validations)) {
|
|
167
|
+
console.warn('[AI Validation] Multi-file: Invalid file result structure, skipping')
|
|
168
|
+
continue
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Use path normalization to match AI response paths to expected paths
|
|
172
|
+
const responsePath = fileResult.file
|
|
173
|
+
const matchedPath = findMatchingFilePath(responsePath, expectedFiles)
|
|
174
|
+
|
|
175
|
+
if (!matchedPath) {
|
|
176
|
+
console.warn(`[AI Validation] Multi-file: Could not match path "${responsePath}" to any expected file`)
|
|
177
|
+
continue
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const validations: ValidationResult[] = fileResult.validations
|
|
181
|
+
.filter((item: any) =>
|
|
182
|
+
typeof item.index === 'number' &&
|
|
183
|
+
typeof item.keep === 'boolean'
|
|
184
|
+
)
|
|
185
|
+
.map((item: any) => {
|
|
186
|
+
// Normalize notes field: prefer new 'notes', fallback to legacy 'reason' or 'validationNotes'
|
|
187
|
+
const notes = item.notes || item.validationNotes || item.reason || undefined
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
index: item.index,
|
|
191
|
+
keep: item.keep,
|
|
192
|
+
notes,
|
|
193
|
+
adjustedSeverity: item.adjustedSeverity || null,
|
|
194
|
+
// Keep legacy fields for backward compatibility
|
|
195
|
+
reason: item.reason,
|
|
196
|
+
validationNotes: item.validationNotes,
|
|
197
|
+
// Actionable output fields (PRO-82)
|
|
198
|
+
impact: item.impact || undefined,
|
|
199
|
+
fixSuggestion: item.fixSuggestion || undefined,
|
|
200
|
+
}
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
resultMap.set(matchedPath, validations)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Log any files that weren't in the response (these will be REJECTED by default)
|
|
207
|
+
const missingFiles = expectedFiles.filter(f => !resultMap.has(f))
|
|
208
|
+
if (missingFiles.length > 0) {
|
|
209
|
+
console.warn(`[AI Validation] Multi-file: Missing ${missingFiles.length} files from response: ${missingFiles.join(', ')}`)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
} catch (error) {
|
|
213
|
+
console.error('[AI Validation] Multi-file: Failed to parse response:', error)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return resultMap
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Apply validation results to findings
|
|
221
|
+
*/
|
|
222
|
+
export function applyValidationResults(
|
|
223
|
+
findings: Vulnerability[],
|
|
224
|
+
validationResults: ValidationResult[]
|
|
225
|
+
): { processed: Vulnerability[]; dismissedCount: number } {
|
|
226
|
+
const processed: Vulnerability[] = []
|
|
227
|
+
let dismissedCount = 0
|
|
228
|
+
|
|
229
|
+
for (let i = 0; i < findings.length; i++) {
|
|
230
|
+
const finding = findings[i]
|
|
231
|
+
const validation = validationResults.find(v => v.index === i)
|
|
232
|
+
|
|
233
|
+
if (!validation) {
|
|
234
|
+
// No validation result - REJECT by default (conservative approach)
|
|
235
|
+
// If AI doesn't explicitly validate a finding, assume it's a false positive
|
|
236
|
+
console.warn(`[AI Validation] No result for finding ${i}: ${finding.title} - REJECTING`)
|
|
237
|
+
dismissedCount++
|
|
238
|
+
continue // Don't add to processed - finding is removed
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (validation.keep) {
|
|
242
|
+
// Keep the finding
|
|
243
|
+
const adjustedFinding: Vulnerability = {
|
|
244
|
+
...finding,
|
|
245
|
+
validatedByAI: true,
|
|
246
|
+
confidence: 'high',
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Extract notes from optimized or legacy format
|
|
250
|
+
const validationNotes = validation.notes || validation.validationNotes || validation.reason || undefined
|
|
251
|
+
|
|
252
|
+
if (validation.adjustedSeverity && validation.adjustedSeverity !== finding.severity) {
|
|
253
|
+
// Severity was adjusted
|
|
254
|
+
adjustedFinding.originalSeverity = finding.severity
|
|
255
|
+
adjustedFinding.severity = validation.adjustedSeverity
|
|
256
|
+
adjustedFinding.validationStatus = 'downgraded' as ValidationStatus
|
|
257
|
+
adjustedFinding.validationNotes = validationNotes || 'Severity adjusted by AI validation'
|
|
258
|
+
} else {
|
|
259
|
+
// Confirmed at original severity
|
|
260
|
+
adjustedFinding.validationStatus = 'confirmed' as ValidationStatus
|
|
261
|
+
adjustedFinding.validationNotes = validationNotes
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Apply AI-generated actionable fields (PRO-82)
|
|
265
|
+
if (validation.impact) {
|
|
266
|
+
adjustedFinding.impact = validation.impact
|
|
267
|
+
adjustedFinding.aiEnhanced = true
|
|
268
|
+
}
|
|
269
|
+
if (validation.fixSuggestion) {
|
|
270
|
+
// AI-generated fix becomes the primary fix step
|
|
271
|
+
adjustedFinding.fixSteps = [validation.fixSuggestion]
|
|
272
|
+
adjustedFinding.aiEnhanced = true
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
processed.push(adjustedFinding)
|
|
276
|
+
} else {
|
|
277
|
+
// Finding was dismissed - only log in debug mode to reduce noise
|
|
278
|
+
if (process.env.DEBUG || process.env.OCULUM_DEBUG) {
|
|
279
|
+
console.log(`[AI Validation] Rejected: ${finding.title} at ${finding.filePath}:${finding.lineNumber}`)
|
|
280
|
+
}
|
|
281
|
+
dismissedCount++
|
|
282
|
+
// Don't add to processed - finding is removed
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return { processed, dismissedCount }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Validate severity value from AI response
|
|
291
|
+
*/
|
|
292
|
+
export function validateSeverity(severity: string): VulnerabilitySeverity {
|
|
293
|
+
const valid: VulnerabilitySeverity[] = ['critical', 'high', 'medium', 'low', 'info']
|
|
294
|
+
return valid.includes(severity as VulnerabilitySeverity)
|
|
295
|
+
? severity as VulnerabilitySeverity
|
|
296
|
+
: 'medium'
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Validate category value from AI response
|
|
301
|
+
*/
|
|
302
|
+
export function validateCategory(category: string): VulnerabilityCategory {
|
|
303
|
+
const valid: VulnerabilityCategory[] = [
|
|
304
|
+
'sql_injection', 'xss', 'command_injection', 'missing_auth',
|
|
305
|
+
'dangerous_function', 'hardcoded_secret', 'high_entropy_string',
|
|
306
|
+
'sensitive_variable', 'security_bypass', 'insecure_config',
|
|
307
|
+
'suspicious_package', 'cors_misconfiguration', 'root_container',
|
|
308
|
+
'weak_crypto', 'sensitive_url', 'ai_pattern', 'dangerous_file',
|
|
309
|
+
'data_exposure', // For logging/exposing sensitive data
|
|
310
|
+
]
|
|
311
|
+
return valid.includes(category as VulnerabilityCategory)
|
|
312
|
+
? category as VulnerabilityCategory
|
|
313
|
+
: 'dangerous_function'
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Get line content from file content by line number
|
|
318
|
+
*/
|
|
319
|
+
export function getLineContent(content: string, lineNumber: number): string {
|
|
320
|
+
const lines = content.split('\n')
|
|
321
|
+
return lines[lineNumber - 1]?.trim() || ''
|
|
322
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry Logic for AI API Calls
|
|
3
|
+
*
|
|
4
|
+
* Implements exponential backoff for rate limit handling.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Helper function to make Anthropic API calls with retry logic for rate limiting
|
|
9
|
+
* Implements exponential backoff for 429 (rate limit) errors
|
|
10
|
+
*/
|
|
11
|
+
export async function makeAnthropicRequestWithRetry<T>(
|
|
12
|
+
requestFn: () => Promise<T>,
|
|
13
|
+
maxRetries: number = 3,
|
|
14
|
+
initialDelayMs: number = 1000
|
|
15
|
+
): Promise<T> {
|
|
16
|
+
let lastError: Error | null = null
|
|
17
|
+
|
|
18
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
19
|
+
try {
|
|
20
|
+
return await requestFn()
|
|
21
|
+
} catch (error: any) {
|
|
22
|
+
lastError = error
|
|
23
|
+
|
|
24
|
+
// Check if it's a rate limit error (429)
|
|
25
|
+
const isRateLimit = error?.status === 429 || error?.message?.includes('rate limit')
|
|
26
|
+
|
|
27
|
+
if (isRateLimit && attempt < maxRetries) {
|
|
28
|
+
// Exponential backoff: 1s, 2s, 4s
|
|
29
|
+
const delayMs = initialDelayMs * Math.pow(2, attempt)
|
|
30
|
+
console.log(`[AI Validation] Rate limit hit, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`)
|
|
31
|
+
await new Promise(resolve => setTimeout(resolve, delayMs))
|
|
32
|
+
continue
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If not rate limit or max retries reached, throw
|
|
36
|
+
throw error
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
throw lastError || new Error('Max retries exceeded')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Helper to make OpenAI requests with retry logic for rate limits
|
|
45
|
+
*/
|
|
46
|
+
export async function makeOpenAIRequestWithRetry<T>(
|
|
47
|
+
requestFn: () => Promise<T>,
|
|
48
|
+
maxRetries = 3,
|
|
49
|
+
initialDelayMs = 1000
|
|
50
|
+
): Promise<T> {
|
|
51
|
+
let lastError: Error | null = null
|
|
52
|
+
|
|
53
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
54
|
+
try {
|
|
55
|
+
return await requestFn()
|
|
56
|
+
} catch (error: any) {
|
|
57
|
+
lastError = error
|
|
58
|
+
|
|
59
|
+
// Check if it's a rate limit error (429) - but NOT insufficient_quota
|
|
60
|
+
const isRateLimit = error?.status === 429 && error?.code !== 'insufficient_quota'
|
|
61
|
+
|
|
62
|
+
if (isRateLimit && attempt < maxRetries) {
|
|
63
|
+
const delayMs = initialDelayMs * Math.pow(2, attempt)
|
|
64
|
+
console.log(`[OpenAI Validation] Rate limit hit, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`)
|
|
65
|
+
await new Promise(resolve => setTimeout(resolve, delayMs))
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// If it's a quota error or max retries reached, throw
|
|
70
|
+
throw error
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
throw lastError || new Error('Max retries exceeded')
|
|
75
|
+
}
|
package/src/layer3/index.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import type { Vulnerability, ScanFile, CancellationToken } from '../types'
|
|
7
7
|
import { batchAnalyzeWithAI, type Layer3Context } from './anthropic'
|
|
8
8
|
import { checkPackages } from './package-check'
|
|
9
|
+
import { checkPackageAdvisories } from './osv-check'
|
|
9
10
|
|
|
10
11
|
export interface Layer3Result {
|
|
11
12
|
vulnerabilities: Vulnerability[]
|
|
@@ -58,14 +59,25 @@ export async function runLayer3Scan(
|
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
// 1. Check packages (
|
|
62
|
-
const
|
|
63
|
-
|
|
62
|
+
// 1. Check packages for hallucination/risk (package.json and requirements.txt)
|
|
63
|
+
const packageManifests = files.filter(f =>
|
|
64
|
+
f.path.endsWith('package.json') ||
|
|
65
|
+
f.path.endsWith('requirements.txt')
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
for (const file of packageManifests) {
|
|
64
69
|
// Check for cancellation in package loop
|
|
65
70
|
if (options.cancellationToken?.cancelled) break
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
|
|
72
|
+
// Run hallucination/risk check (package-check only handles package.json)
|
|
73
|
+
if (file.path.endsWith('package.json')) {
|
|
74
|
+
const packageFindings = await checkPackages(file.content, file.path)
|
|
75
|
+
vulnerabilities.push(...packageFindings)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Run OSV advisory check (handles both npm and Python)
|
|
79
|
+
const osvFindings = await checkPackageAdvisories(file.content, file.path)
|
|
80
|
+
vulnerabilities.push(...osvFindings)
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
// Check for cancellation before AI analysis
|
|
@@ -153,3 +165,4 @@ function getAIPriorityScore(file: ScanFile): number {
|
|
|
153
165
|
|
|
154
166
|
export { analyzeWithAI, batchAnalyzeWithAI, type Layer3Context } from './anthropic'
|
|
155
167
|
export { checkPackages } from './package-check'
|
|
168
|
+
export { checkPackageAdvisories } from './osv-check'
|