@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,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON.parse Detection
|
|
3
|
+
*
|
|
4
|
+
* Source-aware detection of JSON.parse usage with severity classification
|
|
5
|
+
* based on the data source and error handling context.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Vulnerability, VulnerabilitySeverity } from '../../types'
|
|
9
|
+
import { isComment, isTestOrMockFile } from '../../utils/context-helpers'
|
|
10
|
+
import { isInsideTryCatch, hasTryCatchNearby } from './utils/control-flow'
|
|
11
|
+
import { hasSchemaValidationNearby } from './utils/schema-validation'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* JSON.parse source classification
|
|
15
|
+
* Determines if the input is user-controlled or internal data
|
|
16
|
+
*/
|
|
17
|
+
export type JSONParseSource =
|
|
18
|
+
| 'user_input'
|
|
19
|
+
| 'local_storage'
|
|
20
|
+
| 'database'
|
|
21
|
+
| 'config'
|
|
22
|
+
| 'migration'
|
|
23
|
+
| 'internal'
|
|
24
|
+
| 'test_fixture'
|
|
25
|
+
| 'ui_state'
|
|
26
|
+
| 'unknown'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Check if file path indicates a low-risk context for JSON.parse
|
|
30
|
+
*/
|
|
31
|
+
export function isLowRiskJSONParseFile(filePath: string): JSONParseSource | null {
|
|
32
|
+
// Test/mock files - skip or info only
|
|
33
|
+
if (isTestOrMockFile(filePath)) {
|
|
34
|
+
return 'test_fixture'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Settings/preferences components - internal UI state
|
|
38
|
+
if (/\/(components|pages)\/(settings|preferences|config)/i.test(filePath)) {
|
|
39
|
+
return 'ui_state'
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Provider/context files - typically storing state in localStorage
|
|
43
|
+
if (/Provider\.(ts|tsx|js|jsx)$/i.test(filePath)) {
|
|
44
|
+
return 'ui_state'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Modal/Dialog components - typically internal state
|
|
48
|
+
if (/(Modal|Dialog|Settings|Preferences)\.(ts|tsx|js|jsx)$/i.test(filePath)) {
|
|
49
|
+
return 'ui_state'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// __mocks__ directory
|
|
53
|
+
if (/__mocks__/i.test(filePath)) {
|
|
54
|
+
return 'test_fixture'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// fixtures directory
|
|
58
|
+
if (/\/(fixtures?|stubs?|mocks?)\//i.test(filePath)) {
|
|
59
|
+
return 'test_fixture'
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// scripts/tools directories (internal tooling)
|
|
63
|
+
if (/\/(scripts?|tools?|cli)\//i.test(filePath)) {
|
|
64
|
+
return 'internal'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Migration files
|
|
68
|
+
if (/migration/i.test(filePath)) {
|
|
69
|
+
return 'migration'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Config files
|
|
73
|
+
if (/\/(config|settings|constants)\.(ts|js)/i.test(filePath)) {
|
|
74
|
+
return 'config'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if JSON.parse is parsing a trusted SDK response
|
|
82
|
+
* These are well-defined responses from known APIs and are safe to parse
|
|
83
|
+
*/
|
|
84
|
+
export function isTrustedSDKResponse(lineContent: string, content: string): boolean {
|
|
85
|
+
const trustedPatterns = [
|
|
86
|
+
// OpenAI SDK responses
|
|
87
|
+
/JSON\.parse\s*\(\s*(?:response|completion|result|message)\.(?:content|text|data)/i,
|
|
88
|
+
/JSON\.parse\s*\(\s*(?:openai|anthropic|client)\./i,
|
|
89
|
+
// Fetch response.json() result (already parsed by fetch)
|
|
90
|
+
/JSON\.parse\s*\(\s*await\s+.*\.json\s*\(\s*\)\s*\)/i,
|
|
91
|
+
// SDK method results
|
|
92
|
+
/JSON\.parse\s*\(\s*(?:result|response)\.(?:choices|content|data|body)\[/i,
|
|
93
|
+
// AI SDK streaming results
|
|
94
|
+
/JSON\.parse\s*\(\s*(?:chunk|delta|part)\.(?:content|text)/i,
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
if (trustedPatterns.some(p => p.test(lineContent))) {
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check surrounding context for SDK usage
|
|
102
|
+
const sdkContextPatterns = [
|
|
103
|
+
/openai\..*\.create/i,
|
|
104
|
+
/anthropic\..*\.create/i,
|
|
105
|
+
/\.chat\.completions/i,
|
|
106
|
+
/\.messages\.create/i,
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
return sdkContextPatterns.some(p => p.test(content))
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Classify the source of data being passed to JSON.parse
|
|
114
|
+
*/
|
|
115
|
+
export function classifyJSONParseSource(
|
|
116
|
+
lineContent: string,
|
|
117
|
+
filePath: string
|
|
118
|
+
): JSONParseSource {
|
|
119
|
+
// First check file path for low-risk contexts
|
|
120
|
+
const fileBasedSource = isLowRiskJSONParseFile(filePath)
|
|
121
|
+
if (fileBasedSource) {
|
|
122
|
+
return fileBasedSource
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// User input - potentially dangerous
|
|
126
|
+
const userInputPatterns = [
|
|
127
|
+
/JSON\.parse\s*\(\s*(req|request)\.(body|query|params)/i,
|
|
128
|
+
/JSON\.parse\s*\(\s*event\.(body|queryStringParameters)/i, // AWS Lambda
|
|
129
|
+
/JSON\.parse\s*\(\s*ctx\.(request|body|query)/i, // Koa
|
|
130
|
+
/JSON\.parse\s*\(\s*(input|userInput|rawInput|payload)/i,
|
|
131
|
+
/JSON\.parse\s*\(\s*body\b/i, // Generic 'body' often means request body
|
|
132
|
+
]
|
|
133
|
+
if (userInputPatterns.some(p => p.test(lineContent))) {
|
|
134
|
+
return 'user_input'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// localStorage/sessionStorage - client-side storage
|
|
138
|
+
const storagePatterns = [
|
|
139
|
+
/JSON\.parse\s*\(\s*localStorage\.getItem/i,
|
|
140
|
+
/JSON\.parse\s*\(\s*sessionStorage\.getItem/i,
|
|
141
|
+
/JSON\.parse\s*\(\s*window\.localStorage/i,
|
|
142
|
+
/JSON\.parse\s*\(\s*storage\.get/i,
|
|
143
|
+
/JSON\.parse\s*\(\s*saved\b/i, // Common pattern: const saved = localStorage.getItem(...); JSON.parse(saved)
|
|
144
|
+
/JSON\.parse\s*\(\s*stored\b/i,
|
|
145
|
+
]
|
|
146
|
+
if (storagePatterns.some(p => p.test(lineContent))) {
|
|
147
|
+
return 'local_storage'
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Database results - internal data
|
|
151
|
+
const databasePatterns = [
|
|
152
|
+
/JSON\.parse\s*\(\s*(row|result|record|doc|document)\./i,
|
|
153
|
+
/JSON\.parse\s*\(\s*\w+\.(data|json|metadata|embedding)\)/i,
|
|
154
|
+
/JSON\.parse\s*\(\s*\w+\[['"]?\w+['"]?\]\.(data|json|embedding)/i,
|
|
155
|
+
/JSON\.parse\s*\(\s*item\.\w+\)/i, // ORM iteration: items.map(item => JSON.parse(item.field))
|
|
156
|
+
/JSON\.parse\s*\(\s*\w+\.content\)/i, // Parsing content field from DB
|
|
157
|
+
]
|
|
158
|
+
if (databasePatterns.some(p => p.test(lineContent))) {
|
|
159
|
+
return 'database'
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Editor state, internal caches, UI state
|
|
163
|
+
const internalPatterns = [
|
|
164
|
+
/JSON\.parse\s*\(\s*(state|cache|stored|saved|cached)/i,
|
|
165
|
+
/JSON\.parse\s*\(\s*this\.(state|cache|data)/i,
|
|
166
|
+
/JSON\.parse\s*\(\s*\w+State\)/i,
|
|
167
|
+
/JSON\.parse\s*\(\s*editorState/i,
|
|
168
|
+
/JSON\.parse\s*\(\s*parsed\b/i, // JSON.parse(parsed) - likely already validated
|
|
169
|
+
/JSON\.parse\s*\(\s*settings\b/i, // Settings data
|
|
170
|
+
/JSON\.parse\s*\(\s*preferences\b/i,
|
|
171
|
+
]
|
|
172
|
+
if (internalPatterns.some(p => p.test(lineContent))) {
|
|
173
|
+
return 'internal'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Node content in editor apps (e.g., noda-os nodes have JSON content)
|
|
177
|
+
if (
|
|
178
|
+
/JSON\.parse\s*\(\s*(node|note|document|entry)\.(content|body|data)\)/i.test(
|
|
179
|
+
lineContent
|
|
180
|
+
)
|
|
181
|
+
) {
|
|
182
|
+
return 'database'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return 'unknown'
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Detect JSON.parse usage with source-aware severity
|
|
190
|
+
* Much smarter than simple pattern matching - considers try/catch and data source
|
|
191
|
+
*/
|
|
192
|
+
export function detectJSONParseSafe(
|
|
193
|
+
content: string,
|
|
194
|
+
filePath: string,
|
|
195
|
+
isTestFile: boolean,
|
|
196
|
+
vulnerabilities: Vulnerability[]
|
|
197
|
+
): void {
|
|
198
|
+
const lines = content.split('\n')
|
|
199
|
+
const jsonParsePattern = /JSON\.parse\s*\(/gi
|
|
200
|
+
|
|
201
|
+
// Track instances per file to aggregate noisy patterns
|
|
202
|
+
const instances: {
|
|
203
|
+
lineNumber: number
|
|
204
|
+
lineContent: string
|
|
205
|
+
source: JSONParseSource
|
|
206
|
+
}[] = []
|
|
207
|
+
|
|
208
|
+
lines.forEach((line, index) => {
|
|
209
|
+
if (isComment(line)) return
|
|
210
|
+
|
|
211
|
+
jsonParsePattern.lastIndex = 0
|
|
212
|
+
if (!jsonParsePattern.test(line)) return
|
|
213
|
+
|
|
214
|
+
const jsonSource = classifyJSONParseSource(line, filePath)
|
|
215
|
+
|
|
216
|
+
// Skip migration files entirely - they're internal tooling
|
|
217
|
+
if (jsonSource === 'migration') return
|
|
218
|
+
|
|
219
|
+
// Skip test fixtures entirely - they're intentionally parsing test data
|
|
220
|
+
if (jsonSource === 'test_fixture') return
|
|
221
|
+
|
|
222
|
+
// Skip trusted SDK responses - these are well-defined and safe to parse
|
|
223
|
+
if (isTrustedSDKResponse(line, content)) return
|
|
224
|
+
|
|
225
|
+
// Check if JSON.parse is inside a try-catch block
|
|
226
|
+
const insideTryCatch =
|
|
227
|
+
isInsideTryCatch(content, index) || hasTryCatchNearby(content, index)
|
|
228
|
+
|
|
229
|
+
// Check if schema validation is applied after JSON.parse
|
|
230
|
+
const hasSchemaValidation = hasSchemaValidationNearby(content, index)
|
|
231
|
+
|
|
232
|
+
// If inside try-catch with safe source, suppress entirely - this is perfectly fine
|
|
233
|
+
if (
|
|
234
|
+
insideTryCatch &&
|
|
235
|
+
['local_storage', 'database', 'config', 'internal', 'ui_state'].includes(
|
|
236
|
+
jsonSource
|
|
237
|
+
)
|
|
238
|
+
) {
|
|
239
|
+
return
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// If schema validation is present, this is properly handled
|
|
243
|
+
if (hasSchemaValidation) {
|
|
244
|
+
return
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// UI state (settings, providers, modals) - very low risk, aggregate or skip
|
|
248
|
+
if (jsonSource === 'ui_state') {
|
|
249
|
+
// Only track for aggregation, don't report individually
|
|
250
|
+
instances.push({
|
|
251
|
+
lineNumber: index + 1,
|
|
252
|
+
lineContent: line.trim(),
|
|
253
|
+
source: jsonSource,
|
|
254
|
+
})
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Determine severity based on source and error handling
|
|
259
|
+
let severity: VulnerabilitySeverity
|
|
260
|
+
let description: string
|
|
261
|
+
let suggestedFix: string
|
|
262
|
+
let confidence: 'high' | 'medium' | 'low' = 'medium'
|
|
263
|
+
|
|
264
|
+
if (insideTryCatch) {
|
|
265
|
+
// Already has error handling
|
|
266
|
+
switch (jsonSource) {
|
|
267
|
+
case 'user_input':
|
|
268
|
+
severity = 'low'
|
|
269
|
+
description =
|
|
270
|
+
'JSON.parse on user input is wrapped in try-catch. Consider adding schema validation (zod/yup) to validate the parsed structure.'
|
|
271
|
+
suggestedFix =
|
|
272
|
+
'Add schema validation after parsing: const validated = schema.parse(JSON.parse(input))'
|
|
273
|
+
confidence = 'low'
|
|
274
|
+
break
|
|
275
|
+
default:
|
|
276
|
+
// With try-catch and non-user source, this is fine - don't report
|
|
277
|
+
return
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
// No try-catch
|
|
281
|
+
switch (jsonSource) {
|
|
282
|
+
case 'user_input':
|
|
283
|
+
severity = 'medium'
|
|
284
|
+
description =
|
|
285
|
+
'JSON.parse on user input without schema validation. Malformed input will crash; malicious input may have unexpected shape.'
|
|
286
|
+
suggestedFix =
|
|
287
|
+
'Use a schema validation library (zod, yup, joi): try { const data = schema.parse(JSON.parse(body)) } catch (e) { return 400 }'
|
|
288
|
+
confidence = 'high'
|
|
289
|
+
break
|
|
290
|
+
case 'local_storage':
|
|
291
|
+
severity = 'info'
|
|
292
|
+
description =
|
|
293
|
+
'JSON.parse on localStorage data. Consider adding try-catch for robustness against corrupted data.'
|
|
294
|
+
suggestedFix =
|
|
295
|
+
'Wrap in try-catch to handle corrupted localStorage gracefully.'
|
|
296
|
+
confidence = 'low'
|
|
297
|
+
break
|
|
298
|
+
case 'database':
|
|
299
|
+
// Database content parsing is very common and low-risk
|
|
300
|
+
instances.push({
|
|
301
|
+
lineNumber: index + 1,
|
|
302
|
+
lineContent: line.trim(),
|
|
303
|
+
source: jsonSource,
|
|
304
|
+
})
|
|
305
|
+
return // Will be aggregated below
|
|
306
|
+
case 'config':
|
|
307
|
+
case 'internal':
|
|
308
|
+
severity = 'info'
|
|
309
|
+
description = `JSON.parse on ${jsonSource.replace('_', ' ')} data without error handling. Low risk but consider defensive coding.`
|
|
310
|
+
suggestedFix = 'Consider adding try-catch for robustness.'
|
|
311
|
+
confidence = 'low'
|
|
312
|
+
break
|
|
313
|
+
default:
|
|
314
|
+
// Unknown source - track for potential aggregation
|
|
315
|
+
instances.push({
|
|
316
|
+
lineNumber: index + 1,
|
|
317
|
+
lineContent: line.trim(),
|
|
318
|
+
source: jsonSource,
|
|
319
|
+
})
|
|
320
|
+
return // Will be evaluated below based on aggregation
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Downgrade test files
|
|
325
|
+
if (isTestFile) {
|
|
326
|
+
severity = 'info'
|
|
327
|
+
confidence = 'low'
|
|
328
|
+
description += ' (in test file)'
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
vulnerabilities.push({
|
|
332
|
+
id: `json-parse-${filePath}-${index + 1}`,
|
|
333
|
+
filePath,
|
|
334
|
+
lineNumber: index + 1,
|
|
335
|
+
lineContent: line.trim(),
|
|
336
|
+
severity,
|
|
337
|
+
category: 'dangerous_function',
|
|
338
|
+
title: 'JSON.parse usage',
|
|
339
|
+
description,
|
|
340
|
+
suggestedFix,
|
|
341
|
+
confidence,
|
|
342
|
+
layer: 2,
|
|
343
|
+
})
|
|
344
|
+
})
|
|
345
|
+
|
|
346
|
+
// Aggregate low-risk JSON.parse instances if there are many
|
|
347
|
+
if (instances.length >= 3) {
|
|
348
|
+
// Create single aggregated finding instead of N individual findings
|
|
349
|
+
const lineNumbers = instances.map(i => i.lineNumber).slice(0, 5)
|
|
350
|
+
const moreText =
|
|
351
|
+
instances.length > 5 ? `... (${instances.length} total)` : ''
|
|
352
|
+
|
|
353
|
+
vulnerabilities.push({
|
|
354
|
+
id: `json-parse-aggregated-${filePath}`,
|
|
355
|
+
filePath,
|
|
356
|
+
lineNumber: instances[0].lineNumber,
|
|
357
|
+
lineContent: `${instances.length} instances across this file`,
|
|
358
|
+
severity: 'info',
|
|
359
|
+
category: 'dangerous_function',
|
|
360
|
+
title: `JSON.parse usage (${instances.length} instances)`,
|
|
361
|
+
description: `JSON.parse detected. Consider adding error handling and schema validation if parsing user input.${isTestFile ? ' (in test file)' : ''}\n\nFound ${instances.length} occurrences at lines: ${lineNumbers.join(', ')}${moreText}`,
|
|
362
|
+
suggestedFix:
|
|
363
|
+
'Add try-catch for error handling. If parsing user input, add schema validation.',
|
|
364
|
+
confidence: 'low',
|
|
365
|
+
layer: 2,
|
|
366
|
+
})
|
|
367
|
+
} else if (instances.length > 0 && instances.length < 3) {
|
|
368
|
+
// Report individually for small counts
|
|
369
|
+
for (const instance of instances) {
|
|
370
|
+
vulnerabilities.push({
|
|
371
|
+
id: `json-parse-${filePath}-${instance.lineNumber}`,
|
|
372
|
+
filePath,
|
|
373
|
+
lineNumber: instance.lineNumber,
|
|
374
|
+
lineContent: instance.lineContent,
|
|
375
|
+
severity: 'info',
|
|
376
|
+
category: 'dangerous_function',
|
|
377
|
+
title: 'JSON.parse usage',
|
|
378
|
+
description: `JSON.parse on ${instance.source.replace('_', ' ')} data without error handling. Low risk but consider defensive coding.${isTestFile ? ' (in test file)' : ''}`,
|
|
379
|
+
suggestedFix: 'Consider adding try-catch for robustness.',
|
|
380
|
+
confidence: 'low',
|
|
381
|
+
layer: 2,
|
|
382
|
+
})
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|