@oculum/scanner 1.0.10 → 1.0.12
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/ai-context/index.d.ts +6 -0
- package/dist/ai-context/index.d.ts.map +1 -0
- package/dist/ai-context/index.js +13 -0
- package/dist/ai-context/index.js.map +1 -0
- package/dist/ai-context/manager.d.ts +67 -0
- package/dist/ai-context/manager.d.ts.map +1 -0
- package/dist/ai-context/manager.js +104 -0
- package/dist/ai-context/manager.js.map +1 -0
- 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/category-filter.d.ts +125 -0
- package/dist/category-filter.d.ts.map +1 -0
- package/dist/category-filter.js +360 -0
- package/dist/category-filter.js.map +1 -0
- package/dist/filtering/context-adjustments.d.ts +23 -0
- package/dist/filtering/context-adjustments.d.ts.map +1 -0
- package/dist/filtering/context-adjustments.js +100 -0
- package/dist/filtering/context-adjustments.js.map +1 -0
- package/dist/filtering/index.d.ts +3 -0
- package/dist/filtering/index.d.ts.map +1 -0
- package/dist/filtering/index.js +8 -0
- package/dist/filtering/index.js.map +1 -0
- package/dist/filtering/pipeline.d.ts +48 -0
- package/dist/filtering/pipeline.d.ts.map +1 -0
- package/dist/filtering/pipeline.js +76 -0
- package/dist/filtering/pipeline.js.map +1 -0
- package/dist/formatters/ai-context.d.ts +23 -0
- package/dist/formatters/ai-context.d.ts.map +1 -0
- package/dist/formatters/ai-context.js +238 -0
- package/dist/formatters/ai-context.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 +2 -2
- package/dist/formatters/github-comment.d.ts.map +1 -1
- package/dist/formatters/github-comment.js +77 -13
- package/dist/formatters/github-comment.js.map +1 -1
- package/dist/formatters/ide/claude-code.d.ts +17 -0
- package/dist/formatters/ide/claude-code.d.ts.map +1 -0
- package/dist/formatters/ide/claude-code.js +94 -0
- package/dist/formatters/ide/claude-code.js.map +1 -0
- package/dist/formatters/ide/cursor.d.ts +13 -0
- package/dist/formatters/ide/cursor.d.ts.map +1 -0
- package/dist/formatters/ide/cursor.js +125 -0
- package/dist/formatters/ide/cursor.js.map +1 -0
- package/dist/formatters/ide/index.d.ts +62 -0
- package/dist/formatters/ide/index.d.ts.map +1 -0
- package/dist/formatters/ide/index.js +184 -0
- package/dist/formatters/ide/index.js.map +1 -0
- package/dist/formatters/ide/windsurf.d.ts +13 -0
- package/dist/formatters/ide/windsurf.d.ts.map +1 -0
- package/dist/formatters/ide/windsurf.js +117 -0
- package/dist/formatters/ide/windsurf.js.map +1 -0
- package/dist/formatters/index.d.ts +3 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +20 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +423 -56
- package/dist/index.js.map +1 -1
- package/dist/layer1/comments.d.ts +4 -1
- package/dist/layer1/comments.d.ts.map +1 -1
- package/dist/layer1/comments.js +1 -1
- package/dist/layer1/comments.js.map +1 -1
- package/dist/layer1/config-audit.d.ts +4 -1
- package/dist/layer1/config-audit.d.ts.map +1 -1
- package/dist/layer1/config-audit.js +65 -14
- package/dist/layer1/config-audit.js.map +1 -1
- package/dist/layer1/config-mcp-audit.d.ts +23 -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/entropy.d.ts +4 -1
- package/dist/layer1/entropy.d.ts.map +1 -1
- package/dist/layer1/entropy.js +212 -1
- package/dist/layer1/entropy.js.map +1 -1
- package/dist/layer1/file-flags.d.ts +4 -1
- package/dist/layer1/file-flags.d.ts.map +1 -1
- package/dist/layer1/file-flags.js +12 -5
- package/dist/layer1/file-flags.js.map +1 -1
- package/dist/layer1/index.d.ts +1 -0
- package/dist/layer1/index.d.ts.map +1 -1
- package/dist/layer1/index.js +22 -19
- package/dist/layer1/index.js.map +1 -1
- package/dist/layer1/patterns.d.ts +4 -1
- package/dist/layer1/patterns.d.ts.map +1 -1
- package/dist/layer1/patterns.js +34 -4
- package/dist/layer1/patterns.js.map +1 -1
- package/dist/layer1/urls.d.ts +4 -1
- package/dist/layer1/urls.d.ts.map +1 -1
- package/dist/layer1/urls.js +162 -14
- package/dist/layer1/urls.js.map +1 -1
- package/dist/layer1/weak-crypto.d.ts +4 -1
- package/dist/layer1/weak-crypto.d.ts.map +1 -1
- package/dist/layer1/weak-crypto.js +144 -7
- package/dist/layer1/weak-crypto.js.map +1 -1
- package/dist/layer2/ai-agent-tools.d.ts +4 -1
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
- package/dist/layer2/ai-agent-tools.js +964 -2
- package/dist/layer2/ai-agent-tools.js.map +1 -1
- package/dist/layer2/ai-endpoint-protection.d.ts +2 -0
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
- package/dist/layer2/ai-endpoint-protection.js +18 -4
- package/dist/layer2/ai-endpoint-protection.js.map +1 -1
- package/dist/layer2/ai-execution-sinks.d.ts +4 -1
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
- package/dist/layer2/ai-execution-sinks.js +688 -29
- package/dist/layer2/ai-execution-sinks.js.map +1 -1
- package/dist/layer2/ai-fingerprinting.d.ts +4 -1
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
- package/dist/layer2/ai-fingerprinting.js +28 -32
- package/dist/layer2/ai-fingerprinting.js.map +1 -1
- package/dist/layer2/ai-mcp-security.d.ts +20 -0
- package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
- package/dist/layer2/ai-mcp-security.js +877 -0
- package/dist/layer2/ai-mcp-security.js.map +1 -0
- package/dist/layer2/ai-package-hallucination.d.ts +22 -0
- package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
- package/dist/layer2/ai-package-hallucination.js +828 -0
- package/dist/layer2/ai-package-hallucination.js.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts +4 -1
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
- package/dist/layer2/ai-prompt-hygiene.js +817 -17
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
- package/dist/layer2/ai-rag-safety.d.ts +4 -1
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
- package/dist/layer2/ai-rag-safety.js +454 -3
- package/dist/layer2/ai-rag-safety.js.map +1 -1
- package/dist/layer2/ai-schema-validation.d.ts +4 -1
- package/dist/layer2/ai-schema-validation.d.ts.map +1 -1
- package/dist/layer2/ai-schema-validation.js +2 -2
- package/dist/layer2/ai-schema-validation.js.map +1 -1
- package/dist/layer2/auth-antipatterns.d.ts +2 -0
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
- package/dist/layer2/auth-antipatterns.js +209 -20
- package/dist/layer2/auth-antipatterns.js.map +1 -1
- package/dist/layer2/byok-patterns.d.ts +4 -1
- package/dist/layer2/byok-patterns.d.ts.map +1 -1
- package/dist/layer2/byok-patterns.js +5 -2
- 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 +34 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.js +230 -0
- package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
- package/dist/layer2/dangerous-functions/index.d.ts +16 -0
- package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/index.js +1152 -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 +111 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.js +684 -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 +163 -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 +24 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js +70 -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 +147 -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 +102 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
- package/dist/layer2/data-exposure.d.ts +4 -1
- package/dist/layer2/data-exposure.d.ts.map +1 -1
- package/dist/layer2/data-exposure.js +14 -38
- package/dist/layer2/data-exposure.js.map +1 -1
- package/dist/layer2/framework-checks.d.ts +4 -1
- package/dist/layer2/framework-checks.d.ts.map +1 -1
- package/dist/layer2/framework-checks.js +5 -2
- package/dist/layer2/framework-checks.js.map +1 -1
- package/dist/layer2/index.d.ts +12 -1
- package/dist/layer2/index.d.ts.map +1 -1
- package/dist/layer2/index.js +110 -45
- package/dist/layer2/index.js.map +1 -1
- package/dist/layer2/logic-gates.d.ts +4 -1
- package/dist/layer2/logic-gates.d.ts.map +1 -1
- package/dist/layer2/logic-gates.js +58 -20
- package/dist/layer2/logic-gates.js.map +1 -1
- package/dist/layer2/model-supply-chain.d.ts +23 -0
- package/dist/layer2/model-supply-chain.d.ts.map +1 -0
- package/dist/layer2/model-supply-chain.js +444 -0
- package/dist/layer2/model-supply-chain.js.map +1 -0
- package/dist/layer2/risky-imports.d.ts +4 -1
- package/dist/layer2/risky-imports.d.ts.map +1 -1
- package/dist/layer2/risky-imports.js +6 -2
- package/dist/layer2/risky-imports.js.map +1 -1
- package/dist/layer2/variables.d.ts +4 -1
- package/dist/layer2/variables.d.ts.map +1 -1
- package/dist/layer2/variables.js +6 -2
- 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 +199 -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/modes/incremental.js +1 -1
- 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 +3 -3
- package/dist/tiers.d.ts.map +1 -1
- package/dist/tiers.js +34 -7
- package/dist/tiers.js.map +1 -1
- package/dist/types.d.ts +140 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -1
- package/dist/utils/code-analysis.d.ts +39 -0
- package/dist/utils/code-analysis.d.ts.map +1 -0
- package/dist/utils/code-analysis.js +159 -0
- package/dist/utils/code-analysis.js.map +1 -0
- package/dist/utils/comment-analyzer.d.ts +38 -0
- package/dist/utils/comment-analyzer.d.ts.map +1 -0
- package/dist/utils/comment-analyzer.js +218 -0
- package/dist/utils/comment-analyzer.js.map +1 -0
- package/dist/utils/context-helpers.d.ts +112 -1
- package/dist/utils/context-helpers.d.ts.map +1 -1
- package/dist/utils/context-helpers.js +364 -11
- package/dist/utils/context-helpers.js.map +1 -1
- package/dist/utils/environment-context.d.ts +76 -0
- package/dist/utils/environment-context.d.ts.map +1 -0
- package/dist/utils/environment-context.js +271 -0
- package/dist/utils/environment-context.js.map +1 -0
- package/dist/utils/intent-detector.d.ts +66 -0
- package/dist/utils/intent-detector.d.ts.map +1 -0
- package/dist/utils/intent-detector.js +282 -0
- package/dist/utils/intent-detector.js.map +1 -0
- package/dist/utils/parsed-file.d.ts +51 -0
- package/dist/utils/parsed-file.d.ts.map +1 -0
- package/dist/utils/parsed-file.js +95 -0
- package/dist/utils/parsed-file.js.map +1 -0
- package/dist/utils/route-hierarchy.d.ts +50 -0
- package/dist/utils/route-hierarchy.d.ts.map +1 -0
- package/dist/utils/route-hierarchy.js +226 -0
- package/dist/utils/route-hierarchy.js.map +1 -0
- package/dist/utils/schema-semantics.d.ts +45 -0
- package/dist/utils/schema-semantics.d.ts.map +1 -0
- package/dist/utils/schema-semantics.js +193 -0
- package/dist/utils/schema-semantics.js.map +1 -0
- 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 +30 -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__/benchmark/fixtures/layer2/phase5-excessive-agency.ts +580 -0
- package/src/__tests__/benchmark/fixtures/layer2/sprint6-ai-enhancements.ts +515 -0
- package/src/__tests__/benchmark/run-depth-validation.ts +9 -9
- package/src/__tests__/category-filter.test.ts +478 -0
- package/src/__tests__/regression/known-false-positives.test.ts +490 -0
- package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +762 -0
- package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
- package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +0 -9
- package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
- package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
- package/src/__tests__/validation/run-validation.ts +7 -7
- package/src/ai-context/__tests__/manager.test.ts +193 -0
- package/src/ai-context/index.ts +15 -0
- package/src/ai-context/manager.ts +145 -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/category-filter.ts +400 -0
- package/src/filtering/__tests__/pipeline.test.ts +134 -0
- package/src/filtering/context-adjustments.ts +111 -0
- package/src/filtering/index.ts +10 -0
- package/src/filtering/pipeline.ts +130 -0
- package/src/formatters/__tests__/ai-context.test.ts +254 -0
- package/src/formatters/ai-context.ts +302 -0
- package/src/formatters/cli-terminal.ts +444 -41
- package/src/formatters/github-comment.ts +82 -14
- package/src/formatters/ide/__tests__/ide.test.ts +319 -0
- package/src/formatters/ide/claude-code.ts +110 -0
- package/src/formatters/ide/cursor.ts +147 -0
- package/src/formatters/ide/index.ts +216 -0
- package/src/formatters/ide/windsurf.ts +135 -0
- package/src/formatters/index.ts +28 -0
- package/src/index.ts +506 -45
- package/src/layer1/comments.ts +3 -1
- package/src/layer1/config-audit.ts +74 -14
- package/src/layer1/config-mcp-audit.ts +278 -0
- package/src/layer1/entropy.ts +234 -1
- package/src/layer1/file-flags.ts +17 -6
- package/src/layer1/index.ts +29 -23
- package/src/layer1/patterns.ts +42 -4
- package/src/layer1/urls.ts +188 -14
- package/src/layer1/weak-crypto.ts +168 -16
- package/src/layer2/ai-agent-tools.ts +1043 -2
- package/src/layer2/ai-endpoint-protection.ts +19 -4
- package/src/layer2/ai-execution-sinks.ts +755 -29
- package/src/layer2/ai-fingerprinting.ts +33 -33
- package/src/layer2/ai-mcp-security.ts +933 -0
- package/src/layer2/ai-package-hallucination.ts +940 -0
- package/src/layer2/ai-prompt-hygiene.ts +898 -17
- package/src/layer2/ai-rag-safety.ts +467 -5
- package/src/layer2/ai-schema-validation.ts +4 -2
- package/src/layer2/auth-antipatterns.ts +235 -20
- package/src/layer2/byok-patterns.ts +9 -3
- package/src/layer2/dangerous-functions/child-process.ts +98 -0
- package/src/layer2/dangerous-functions/dom-xss.ts +292 -0
- package/src/layer2/dangerous-functions/index.ts +1533 -0
- package/src/layer2/dangerous-functions/json-parse.ts +385 -0
- package/src/layer2/dangerous-functions/math-random.ts +789 -0
- package/src/layer2/dangerous-functions/patterns.ts +176 -0
- package/src/layer2/dangerous-functions/request-validation.ts +145 -0
- package/src/layer2/dangerous-functions/utils/control-flow.ts +35 -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 +106 -0
- package/src/layer2/data-exposure.ts +18 -39
- package/src/layer2/framework-checks.ts +9 -2
- package/src/layer2/index.ts +124 -43
- package/src/layer2/logic-gates.ts +64 -22
- package/src/layer2/model-supply-chain.ts +531 -0
- package/src/layer2/risky-imports.ts +9 -2
- package/src/layer2/variables.ts +9 -2
- package/src/layer3/__tests__/osv-check.test.ts +384 -0
- package/src/layer3/anthropic/auto-dismiss.ts +223 -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/modes/incremental.ts +1 -1
- 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 +45 -9
- package/src/types.ts +212 -8
- package/src/utils/__tests__/code-analysis.test.ts +165 -0
- package/src/utils/__tests__/parsed-file.test.ts +124 -0
- package/src/utils/code-analysis.ts +179 -0
- package/src/utils/comment-analyzer.ts +249 -0
- package/src/utils/context-helpers.ts +421 -11
- package/src/utils/environment-context.ts +304 -0
- package/src/utils/intent-detector.ts +318 -0
- package/src/utils/parsed-file.ts +103 -0
- package/src/utils/route-hierarchy.ts +250 -0
- package/src/utils/schema-semantics.ts +233 -0
- 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,789 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math.random() Detection
|
|
3
|
+
*
|
|
4
|
+
* Context-aware detection of Math.random() usage with intelligent severity
|
|
5
|
+
* classification based on usage context, variable names, and function intent.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
isTestOrMockFile,
|
|
10
|
+
isSeedOrDataGenFile,
|
|
11
|
+
isEducationalVulnerabilityFile,
|
|
12
|
+
} from '../../utils/context-helpers'
|
|
13
|
+
import type { ParsedFile } from '../../utils/parsed-file'
|
|
14
|
+
import { extractFunctionContext } from './utils/control-flow'
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Check if Math.random() is used in a jitter/backoff/retry context
|
|
18
|
+
* These are legitimate uses of Math.random() for network resilience,
|
|
19
|
+
* not security-sensitive randomness.
|
|
20
|
+
*
|
|
21
|
+
* Examples:
|
|
22
|
+
* const delay = baseDelay + Math.random() * jitter
|
|
23
|
+
* setTimeout(retry, delay * Math.random())
|
|
24
|
+
* await sleep(backoff * (1 + Math.random() * 0.1))
|
|
25
|
+
*
|
|
26
|
+
* @param content - Full file content
|
|
27
|
+
* @param lineNumber - The 0-indexed line number where Math.random() was found
|
|
28
|
+
*/
|
|
29
|
+
export function isJitterOrBackoffContext(
|
|
30
|
+
content: string,
|
|
31
|
+
lineNumber: number,
|
|
32
|
+
lines?: string[]
|
|
33
|
+
): boolean {
|
|
34
|
+
const _lines = lines ?? content.split('\n')
|
|
35
|
+
const start = Math.max(0, lineNumber - 10)
|
|
36
|
+
const end = Math.min(_lines.length, lineNumber + 5)
|
|
37
|
+
const context = _lines.slice(start, end).join('\n')
|
|
38
|
+
|
|
39
|
+
// Patterns indicating jitter/backoff/retry context
|
|
40
|
+
const jitterPatterns = [
|
|
41
|
+
// Direct keyword matches
|
|
42
|
+
/\b(jitter|backoff|retry|retries|exponential)\b/i,
|
|
43
|
+
// Delay/timeout with random
|
|
44
|
+
/setTimeout.*Math\.random/i,
|
|
45
|
+
/setInterval.*Math\.random/i,
|
|
46
|
+
/sleep.*Math\.random/i,
|
|
47
|
+
/await.*delay.*Math\.random/i,
|
|
48
|
+
// Common backoff patterns
|
|
49
|
+
/\* Math\.random\(\).*delay/i,
|
|
50
|
+
/delay\s*\*\s*Math\.random/i,
|
|
51
|
+
/Math\.random\(\)\s*\*\s*\d+.*delay/i,
|
|
52
|
+
// Retry-related function names
|
|
53
|
+
/function\s+(retry|withRetry|backoff|withBackoff|exponentialBackoff)/i,
|
|
54
|
+
// Common retry library patterns
|
|
55
|
+
/retryPolicy|retryConfig|retryOptions|maxRetries|retryCount/i,
|
|
56
|
+
// Network resilience patterns
|
|
57
|
+
/\b(throttle|debounce|rateLimit)\b.*Math\.random/i,
|
|
58
|
+
// Sampling/probability for non-security uses
|
|
59
|
+
/sampleRate|samplingRate|probability\s*[<>]=?\s*Math\.random/i,
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
return jitterPatterns.some(p => p.test(context))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if Math.random() is used in a React key prop context
|
|
67
|
+
* This is a common pattern to force re-renders, not a security issue.
|
|
68
|
+
*
|
|
69
|
+
* Examples:
|
|
70
|
+
* key={Math.random()}
|
|
71
|
+
* key={`prefix-${Math.random()}`}
|
|
72
|
+
* key={Math.random().toString()}
|
|
73
|
+
*
|
|
74
|
+
* @param lineContent - The line of code to check
|
|
75
|
+
* @param filePath - The file path (only check JSX/TSX files)
|
|
76
|
+
*/
|
|
77
|
+
export function isReactKeyPattern(lineContent: string, filePath: string): boolean {
|
|
78
|
+
// Only check in JSX/TSX files
|
|
79
|
+
if (!/\.[jt]sx$/.test(filePath)) {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Pattern: key={...Math.random()...}
|
|
84
|
+
// Matches:
|
|
85
|
+
// key={Math.random()}
|
|
86
|
+
// key={`foo-${Math.random()}`}
|
|
87
|
+
// key={Math.random().toString()}
|
|
88
|
+
// key={`${Math.random()}-bar`}
|
|
89
|
+
// key={something + Math.random()}
|
|
90
|
+
const keyPattern = /key\s*=\s*\{[^}]*Math\.random\(\)/
|
|
91
|
+
return keyPattern.test(lineContent)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if Math.random() is used for cosmetic/UI purposes (not security)
|
|
96
|
+
* Cosmetic uses: CSS values, animations, UI variations, demo data
|
|
97
|
+
* Security uses: tokens, IDs, cryptographic operations, session management
|
|
98
|
+
*/
|
|
99
|
+
export function isCosmeticMathRandom(
|
|
100
|
+
lineContent: string,
|
|
101
|
+
content: string,
|
|
102
|
+
lineNumber: number,
|
|
103
|
+
lines?: string[]
|
|
104
|
+
): boolean {
|
|
105
|
+
const _lines = lines ?? content.split('\n')
|
|
106
|
+
|
|
107
|
+
// Check the line itself for cosmetic indicators
|
|
108
|
+
const cosmeticLinePatterns = [
|
|
109
|
+
// CSS/style values
|
|
110
|
+
/['"`]\s*\$\{.*Math\.random.*\}\s*%['"`]/, // `${Math.random() * 40 + 50}%`
|
|
111
|
+
/Math\.random.*\s*\+\s*['"`]%['"`]/, // Math.random() * 40 + '%'
|
|
112
|
+
/Math\.random.*\)\s*\*\s*\d+\s*\+\s*\d+\s*\}\s*%/, // }) * 40 + 50}%
|
|
113
|
+
/return\s+`.*Math\.random.*%`/, // return `${...}%`
|
|
114
|
+
/width:\s*['"`].*Math\.random/i, // width: `${Math.random()...}%`
|
|
115
|
+
/height:\s*['"`].*Math\.random/i, // height: `${Math.random()...}%`
|
|
116
|
+
/opacity:\s*['"`]?.*Math\.random/i, // opacity: Math.random()
|
|
117
|
+
/transform:\s*['"`]?.*Math\.random/i, // transform: translate(...)
|
|
118
|
+
/rotate\(.*Math\.random/i, // rotate(Math.random() * 360)
|
|
119
|
+
/translate\(.*Math\.random/i, // translate(Math.random() * 100)
|
|
120
|
+
/scale\(.*Math\.random/i, // scale(Math.random() * 2)
|
|
121
|
+
// Color/animation values
|
|
122
|
+
/rgba?\(.*Math\.random/i, // rgb(Math.random() * 255, ...)
|
|
123
|
+
/hsl\(.*Math\.random/i, // hsl(Math.random() * 360, ...)
|
|
124
|
+
/Math\.random.*\*\s*360/, // Math.random() * 360 (degrees/hue)
|
|
125
|
+
/Math\.random.*\*\s*255/, // Math.random() * 255 (RGB values)
|
|
126
|
+
// Array/list randomization for UI
|
|
127
|
+
/Math\.floor\(Math\.random.*\.length\)/, // Math.floor(Math.random() * array.length)
|
|
128
|
+
/\[Math\.floor\(Math\.random/, // array[Math.floor(Math.random()...)]
|
|
129
|
+
// Demo/placeholder data
|
|
130
|
+
/Math\.random.*\*\s*\d+\s*\+\s*\d+.*\bpx\b/i, // Math.random() * 100 + 50 + 'px'
|
|
131
|
+
/Math\.random.*\*\s*\d+\s*\+\s*\d+.*\bms\b/i, // Math.random() * 1000 + 500 + 'ms'
|
|
132
|
+
/Math\.random.*\*\s*\d+\s*\+\s*\d+.*\bs\b/i, // Math.random() * 5 + 2 + 's'
|
|
133
|
+
// NOTE: toString patterns removed - now handled by analyzeToStringPattern()
|
|
134
|
+
// which provides more granular severity classification (info/low/medium/high)
|
|
135
|
+
// based on truncation length and context
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
if (cosmeticLinePatterns.some(p => p.test(lineContent))) {
|
|
139
|
+
return true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Check surrounding context (5 lines before and after)
|
|
143
|
+
const contextStart = Math.max(0, lineNumber - 5)
|
|
144
|
+
const contextEnd = Math.min(_lines.length, lineNumber + 5)
|
|
145
|
+
const context = _lines.slice(contextStart, contextEnd).join('\n')
|
|
146
|
+
|
|
147
|
+
// Context indicators of cosmetic use
|
|
148
|
+
const cosmeticContextPatterns = [
|
|
149
|
+
// UI component files - REMOVED, let severity classification handle these
|
|
150
|
+
// Style-related variables/functions
|
|
151
|
+
/\b(style|styles|css|className|animation|transition)/i,
|
|
152
|
+
/\b(width|height|opacity|color|transform|rotate|scale|translate)/i,
|
|
153
|
+
// Demo/example data
|
|
154
|
+
/\b(demo|example|placeholder|mock|fake|sample|test)Data/i,
|
|
155
|
+
/\b(random|shuffle|pick|choose).*\b(color|item|element|option)/i,
|
|
156
|
+
// Animation/timing
|
|
157
|
+
/setTimeout.*Math\.random/i,
|
|
158
|
+
/setInterval.*Math\.random/i,
|
|
159
|
+
/delay.*Math\.random/i,
|
|
160
|
+
/duration.*Math\.random/i,
|
|
161
|
+
// UI state variations
|
|
162
|
+
/\b(variant|theme|layout|position).*Math\.random/i,
|
|
163
|
+
// NOTE: Removed UI identifier patterns (key, id, tempId, etc.) - these should be
|
|
164
|
+
// classified with info/low severity by the severity classification logic, not skipped entirely
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
if (cosmeticContextPatterns.some(p => p.test(context))) {
|
|
168
|
+
return true
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Security-sensitive patterns that override cosmetic detection
|
|
172
|
+
const securityPatterns = [
|
|
173
|
+
/\b(token|secret|key|password|credential|signature)/i,
|
|
174
|
+
/\b(auth|crypto|encrypt|decrypt|hash)/i,
|
|
175
|
+
/\b(session|nonce|salt)\b/i,
|
|
176
|
+
/Math\.random.*\*\s*1e\d+/, // Math.random() * 1e16 (large numbers for IDs)
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
if (securityPatterns.some(p => p.test(lineContent) || p.test(context))) {
|
|
180
|
+
return false // Not cosmetic - this is security-sensitive
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Check for .toString(36) WITHOUT substring/slice/substr (security token pattern)
|
|
184
|
+
// If it has substring/slice/substr, it's already caught by cosmeticLinePatterns above
|
|
185
|
+
const hasToString36WithoutTruncation =
|
|
186
|
+
/Math\.random\(\)\.toString\(36\)/.test(lineContent) &&
|
|
187
|
+
!/\.(substring|substr|slice)\(/.test(lineContent)
|
|
188
|
+
|
|
189
|
+
const hasToString16WithoutTruncation =
|
|
190
|
+
/Math\.random\(\)\.toString\(16\)/.test(lineContent) &&
|
|
191
|
+
!/\.(substring|substr|slice)\(/.test(lineContent)
|
|
192
|
+
|
|
193
|
+
if (hasToString36WithoutTruncation || hasToString16WithoutTruncation) {
|
|
194
|
+
return false // Full-length toString() without truncation - likely security token
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return false // Default to flagging if unclear
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Classify function intent based on function name
|
|
202
|
+
* Used to determine if Math.random() usage is legitimate
|
|
203
|
+
*/
|
|
204
|
+
export function classifyFunctionIntent(
|
|
205
|
+
functionName: string | null
|
|
206
|
+
): 'uuid' | 'captcha' | 'demo' | 'security' | 'unknown' {
|
|
207
|
+
if (!functionName) return 'unknown'
|
|
208
|
+
|
|
209
|
+
const lower = functionName.toLowerCase()
|
|
210
|
+
|
|
211
|
+
// UUID/ID generation (UI correlation, not security)
|
|
212
|
+
// Check for specific UUID patterns and generic ID generation functions
|
|
213
|
+
const uuidPatterns = ['uuid', 'guid', 'uniqueid', 'correlationid', 'tempid', 'temp_id']
|
|
214
|
+
// Match patterns like generateId, generateTempId, createId, etc.
|
|
215
|
+
const idGenerationPatterns = /^(generate|create|make|build)(\w*)?(id|identifier)$/i
|
|
216
|
+
if (
|
|
217
|
+
uuidPatterns.some(p => lower.includes(p)) ||
|
|
218
|
+
idGenerationPatterns.test(lower)
|
|
219
|
+
) {
|
|
220
|
+
return 'uuid'
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// CAPTCHA/puzzle generation (legitimate non-security)
|
|
224
|
+
const captchaPatterns = ['captcha', 'puzzle', 'mathproblem']
|
|
225
|
+
// Also check for 'challenge' but only if not in security context
|
|
226
|
+
if (captchaPatterns.some(p => lower.includes(p))) return 'captcha'
|
|
227
|
+
if (lower.includes('challenge') && !lower.includes('auth')) return 'captcha'
|
|
228
|
+
|
|
229
|
+
// Demo/seed/fixture data
|
|
230
|
+
const demoPatterns = ['seed', 'fixture', 'demo', 'mock', 'fake']
|
|
231
|
+
if (demoPatterns.some(p => lower.includes(p))) return 'demo'
|
|
232
|
+
|
|
233
|
+
// Security-sensitive (check this after id generation to avoid false positives)
|
|
234
|
+
const securityPatterns = [
|
|
235
|
+
'token',
|
|
236
|
+
'secret',
|
|
237
|
+
'key',
|
|
238
|
+
'password',
|
|
239
|
+
'credential',
|
|
240
|
+
'signature',
|
|
241
|
+
]
|
|
242
|
+
// Also match generate/create + security term combinations
|
|
243
|
+
const securityFunctionPattern =
|
|
244
|
+
/^(generate|create|make)(token|secret|key|session|password|credential)/i
|
|
245
|
+
if (
|
|
246
|
+
securityPatterns.some(p => lower.includes(p)) ||
|
|
247
|
+
securityFunctionPattern.test(lower)
|
|
248
|
+
) {
|
|
249
|
+
return 'security'
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return 'unknown'
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Analyze toString() pattern in Math.random() usage
|
|
257
|
+
* Determines intent based on base and truncation length
|
|
258
|
+
*/
|
|
259
|
+
export function analyzeToStringPattern(lineContent: string): {
|
|
260
|
+
hasToString: boolean
|
|
261
|
+
base: number | null
|
|
262
|
+
isTruncated: boolean
|
|
263
|
+
truncationLength: number | null
|
|
264
|
+
intent: 'short-ui-id' | 'business-id' | 'full-token' | 'unknown'
|
|
265
|
+
} {
|
|
266
|
+
const toString36Match = lineContent.match(/Math\.random\(\)\.toString\(36\)/)
|
|
267
|
+
const toString16Match = lineContent.match(/Math\.random\(\)\.toString\(16\)/)
|
|
268
|
+
|
|
269
|
+
if (!toString36Match && !toString16Match) {
|
|
270
|
+
return {
|
|
271
|
+
hasToString: false,
|
|
272
|
+
base: null,
|
|
273
|
+
isTruncated: false,
|
|
274
|
+
truncationLength: null,
|
|
275
|
+
intent: 'unknown',
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const base = toString36Match ? 36 : 16
|
|
280
|
+
|
|
281
|
+
// Check for truncation methods
|
|
282
|
+
const substringMatch = lineContent.match(
|
|
283
|
+
/\.substring\((\d+)(?:,\s*(\d+))?\)/
|
|
284
|
+
)
|
|
285
|
+
const sliceMatch = lineContent.match(/\.slice\((\d+)(?:,\s*(\d+))?\)/)
|
|
286
|
+
const substrMatch = lineContent.match(/\.substr\((\d+)(?:,\s*(\d+))?\)/)
|
|
287
|
+
|
|
288
|
+
const truncMatch = substringMatch || sliceMatch || substrMatch
|
|
289
|
+
|
|
290
|
+
if (!truncMatch) {
|
|
291
|
+
return {
|
|
292
|
+
hasToString: true,
|
|
293
|
+
base,
|
|
294
|
+
isTruncated: false,
|
|
295
|
+
truncationLength: null,
|
|
296
|
+
intent: 'full-token',
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Calculate truncation length
|
|
301
|
+
const start = parseInt(truncMatch[1])
|
|
302
|
+
const end = truncMatch[2] ? parseInt(truncMatch[2]) : null
|
|
303
|
+
|
|
304
|
+
// If no end specified (e.g., .substring(7)), the result is from start to end of string
|
|
305
|
+
// Math.random().toString(36) produces ~11 chars like "0.abc123def"
|
|
306
|
+
// .substring(2) gives ~9 chars, .substring(7) gives ~4 chars
|
|
307
|
+
// Estimate remaining length: ~11 - start
|
|
308
|
+
const estimatedFullLength = 11
|
|
309
|
+
const length = end ? end - start : (start >= 2 ? estimatedFullLength - start : null)
|
|
310
|
+
|
|
311
|
+
// Classify intent by length
|
|
312
|
+
// Short (2-9 chars): UI correlation IDs, React keys
|
|
313
|
+
// Medium (10-15 chars): Business IDs, order numbers
|
|
314
|
+
if (length && length <= 9) {
|
|
315
|
+
return {
|
|
316
|
+
hasToString: true,
|
|
317
|
+
base,
|
|
318
|
+
isTruncated: true,
|
|
319
|
+
truncationLength: length,
|
|
320
|
+
intent: 'short-ui-id',
|
|
321
|
+
}
|
|
322
|
+
} else if (length && length <= 15) {
|
|
323
|
+
return {
|
|
324
|
+
hasToString: true,
|
|
325
|
+
base,
|
|
326
|
+
isTruncated: true,
|
|
327
|
+
truncationLength: length,
|
|
328
|
+
intent: 'business-id',
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
return {
|
|
332
|
+
hasToString: true,
|
|
333
|
+
base,
|
|
334
|
+
isTruncated: true,
|
|
335
|
+
truncationLength: length,
|
|
336
|
+
intent: 'business-id',
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Extract variable name from Math.random() assignment
|
|
343
|
+
* Examples:
|
|
344
|
+
* const token = Math.random() -> "token"
|
|
345
|
+
* const businessId = Math.random().toString(36) -> "businessId"
|
|
346
|
+
* return Math.random() -> null (no variable)
|
|
347
|
+
*/
|
|
348
|
+
export function extractMathRandomVariableName(
|
|
349
|
+
lineContent: string
|
|
350
|
+
): string | null {
|
|
351
|
+
// const/let/var variableName = Math.random...
|
|
352
|
+
const assignmentMatch = lineContent.match(
|
|
353
|
+
/(?:const|let|var)\s+(\w+)\s*=.*Math\.random/
|
|
354
|
+
)
|
|
355
|
+
if (assignmentMatch) return assignmentMatch[1]
|
|
356
|
+
|
|
357
|
+
// object.property = Math.random...
|
|
358
|
+
const propertyMatch = lineContent.match(/(\w+)\s*[:=]\s*Math\.random/)
|
|
359
|
+
if (propertyMatch) return propertyMatch[1]
|
|
360
|
+
|
|
361
|
+
// function parameter default: functionName(param = Math.random())
|
|
362
|
+
const paramMatch = lineContent.match(/(\w+)\s*=\s*Math\.random/)
|
|
363
|
+
if (paramMatch) return paramMatch[1]
|
|
364
|
+
|
|
365
|
+
return null // No variable name found
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Classify variable name security risk based on naming patterns
|
|
370
|
+
*
|
|
371
|
+
* High risk: Security-sensitive names (token, secret, key, etc.)
|
|
372
|
+
* Medium risk: Unclear context
|
|
373
|
+
* Low risk: Non-security names (id, businessId, orderId, etc.)
|
|
374
|
+
*/
|
|
375
|
+
export function classifyVariableNameRisk(
|
|
376
|
+
varName: string | null
|
|
377
|
+
): 'high' | 'medium' | 'low' {
|
|
378
|
+
if (!varName) return 'medium' // Unknown usage, moderate risk
|
|
379
|
+
|
|
380
|
+
const lower = varName.toLowerCase()
|
|
381
|
+
|
|
382
|
+
// High risk: security-sensitive variable names
|
|
383
|
+
// Note: 'key' alone is NOT included - it often means React key, not crypto key
|
|
384
|
+
// Instead, we match specific security key patterns
|
|
385
|
+
const highRiskPatterns = [
|
|
386
|
+
'token',
|
|
387
|
+
'secret',
|
|
388
|
+
'password',
|
|
389
|
+
'credential',
|
|
390
|
+
'signature',
|
|
391
|
+
'salt',
|
|
392
|
+
'nonce',
|
|
393
|
+
'session',
|
|
394
|
+
'csrf',
|
|
395
|
+
'auth',
|
|
396
|
+
'apikey',
|
|
397
|
+
'secretkey',
|
|
398
|
+
'privatekey',
|
|
399
|
+
'encryptionkey',
|
|
400
|
+
'accesstoken',
|
|
401
|
+
'refreshtoken',
|
|
402
|
+
'jwt',
|
|
403
|
+
'bearer',
|
|
404
|
+
'oauth',
|
|
405
|
+
'sessionid',
|
|
406
|
+
]
|
|
407
|
+
if (highRiskPatterns.some(p => lower.includes(p))) {
|
|
408
|
+
return 'high'
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Low risk: clearly non-security contexts
|
|
412
|
+
const lowRiskPatterns = [
|
|
413
|
+
// Business identifiers
|
|
414
|
+
'id',
|
|
415
|
+
'uid',
|
|
416
|
+
'guid',
|
|
417
|
+
'business',
|
|
418
|
+
'order',
|
|
419
|
+
'invoice',
|
|
420
|
+
'customer',
|
|
421
|
+
'user',
|
|
422
|
+
'product',
|
|
423
|
+
'item',
|
|
424
|
+
'transaction',
|
|
425
|
+
'request',
|
|
426
|
+
'reference',
|
|
427
|
+
'tracking',
|
|
428
|
+
'confirmation',
|
|
429
|
+
// Test/demo data
|
|
430
|
+
'test',
|
|
431
|
+
'mock',
|
|
432
|
+
'demo',
|
|
433
|
+
'sample',
|
|
434
|
+
'example',
|
|
435
|
+
'fixture',
|
|
436
|
+
'random',
|
|
437
|
+
'temp',
|
|
438
|
+
'temporary',
|
|
439
|
+
'generated',
|
|
440
|
+
'dummy',
|
|
441
|
+
// UI identifiers (checked after high-risk, so 'apikey' etc. already caught)
|
|
442
|
+
'key',
|
|
443
|
+
'toast',
|
|
444
|
+
'notification',
|
|
445
|
+
'element',
|
|
446
|
+
'component',
|
|
447
|
+
'widget',
|
|
448
|
+
'modal',
|
|
449
|
+
'dialog',
|
|
450
|
+
'popup',
|
|
451
|
+
'unique',
|
|
452
|
+
'react',
|
|
453
|
+
// Non-security randomness usage (backoff/sampling/experiments)
|
|
454
|
+
'jitter',
|
|
455
|
+
'retry',
|
|
456
|
+
'backoff',
|
|
457
|
+
'delay',
|
|
458
|
+
'timeout',
|
|
459
|
+
'latency',
|
|
460
|
+
'sample',
|
|
461
|
+
'sampling',
|
|
462
|
+
'probability',
|
|
463
|
+
'chance',
|
|
464
|
+
'rollout',
|
|
465
|
+
'experiment',
|
|
466
|
+
'abtest',
|
|
467
|
+
'cohort',
|
|
468
|
+
'bucket',
|
|
469
|
+
'variant',
|
|
470
|
+
]
|
|
471
|
+
if (lowRiskPatterns.some(p => lower.includes(p))) {
|
|
472
|
+
return 'low'
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return 'medium' // Unclear context, moderate risk
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Analyze surrounding code context for security signals
|
|
480
|
+
* Returns context type and description for severity classification
|
|
481
|
+
*/
|
|
482
|
+
export function analyzeMathRandomContext(
|
|
483
|
+
content: string,
|
|
484
|
+
filePath: string,
|
|
485
|
+
lineNumber: number,
|
|
486
|
+
lines?: string[]
|
|
487
|
+
): {
|
|
488
|
+
inSecurityContext: boolean
|
|
489
|
+
inTestContext: boolean
|
|
490
|
+
inUIContext: boolean
|
|
491
|
+
inBusinessLogicContext: boolean
|
|
492
|
+
contextDescription: string
|
|
493
|
+
} {
|
|
494
|
+
const _lines = lines ?? content.split('\n')
|
|
495
|
+
const start = Math.max(0, lineNumber - 10)
|
|
496
|
+
const end = Math.min(_lines.length, lineNumber + 5)
|
|
497
|
+
const context = _lines.slice(start, end).join('\n')
|
|
498
|
+
|
|
499
|
+
// Security context indicators (functions, imports, comments)
|
|
500
|
+
const securityPatterns = [
|
|
501
|
+
/\b(generate|create)(Token|Secret|Key|Password|Nonce|Salt|Session|Signature)/i,
|
|
502
|
+
/\b(auth|crypto|encrypt|decrypt|hash|sign)\b/i,
|
|
503
|
+
/function\s+.*(?:token|secret|key|auth|crypto)/i,
|
|
504
|
+
/\bimport.*(?:crypto|jsonwebtoken|bcrypt|argon2|jose)/i,
|
|
505
|
+
/\/\*.*(?:security|authentication|cryptograph|authorization)/i,
|
|
506
|
+
/\/\/.*(?:security|auth|crypto|token|secret)/i,
|
|
507
|
+
]
|
|
508
|
+
const inSecurityContext = securityPatterns.some(p => p.test(context))
|
|
509
|
+
|
|
510
|
+
// Test context
|
|
511
|
+
const testFilePatterns = /\.(test|spec)\.(ts|tsx|js|jsx)$/i
|
|
512
|
+
const testContextPatterns = [
|
|
513
|
+
/\b(describe|it|test|expect|mock|jest|vitest|mocha|chai)\b/i,
|
|
514
|
+
/\b(beforeEach|afterEach|beforeAll|afterAll)\b/i,
|
|
515
|
+
/\b(fixture|stub|spy)\b/i,
|
|
516
|
+
]
|
|
517
|
+
const inTestContext =
|
|
518
|
+
testFilePatterns.test(filePath) ||
|
|
519
|
+
testContextPatterns.some(p => p.test(context))
|
|
520
|
+
|
|
521
|
+
// UI/cosmetic context (reuse existing logic)
|
|
522
|
+
const lineContent = _lines[lineNumber]
|
|
523
|
+
const inUIContext = isCosmeticMathRandom(lineContent, content, lineNumber, _lines)
|
|
524
|
+
|
|
525
|
+
// Business logic context (non-security ID generation)
|
|
526
|
+
// Note: UUID/CAPTCHA patterns excluded - handled by functionIntent classification
|
|
527
|
+
const businessLogicPatterns = [
|
|
528
|
+
/\b(business|order|invoice|customer|product|transaction)Id\b/i,
|
|
529
|
+
/\b(reference|tracking|confirmation)Number\b/i,
|
|
530
|
+
/\b(backoff|retry|jitter|delay|timeout|latency)\b/i,
|
|
531
|
+
/\b(sample|sampling|probability|chance|rollout|experiment|abtest|cohort|bucket|variant)\b/i,
|
|
532
|
+
// Load balancing and selection patterns
|
|
533
|
+
/mode\s*===?\s*['"]random['"]/i, // mode === 'random'
|
|
534
|
+
/\.\w*index\s*%/i, // round-robin patterns
|
|
535
|
+
/keys?\[.*Math\.random/i, // keys[Math.floor(Math.random() * keys.length)]
|
|
536
|
+
/\[\s*Math\.floor\s*\(\s*Math\.random/i, // array[Math.floor(Math.random()...)]
|
|
537
|
+
/shuffle/i, // shuffle functions
|
|
538
|
+
/pickRandom/i, // pickRandom helpers
|
|
539
|
+
/randomElement/i, // randomElement helpers
|
|
540
|
+
]
|
|
541
|
+
const inBusinessLogicContext =
|
|
542
|
+
businessLogicPatterns.some(p => p.test(context)) && !inSecurityContext
|
|
543
|
+
|
|
544
|
+
// Determine context description
|
|
545
|
+
let contextDescription = 'unknown context'
|
|
546
|
+
if (inSecurityContext) {
|
|
547
|
+
contextDescription = 'security-sensitive function'
|
|
548
|
+
} else if (inTestContext) {
|
|
549
|
+
contextDescription = 'test/mock data generation'
|
|
550
|
+
} else if (inUIContext) {
|
|
551
|
+
contextDescription = 'UI/cosmetic usage'
|
|
552
|
+
} else if (inBusinessLogicContext) {
|
|
553
|
+
contextDescription = 'non-security usage'
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return {
|
|
557
|
+
inSecurityContext,
|
|
558
|
+
inTestContext,
|
|
559
|
+
inUIContext,
|
|
560
|
+
inBusinessLogicContext,
|
|
561
|
+
contextDescription,
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
/**
|
|
566
|
+
* Check if file is an animation/motion component based on file name
|
|
567
|
+
* Files with animation-related names typically use Math.random for visual effects
|
|
568
|
+
*/
|
|
569
|
+
export function isAnimationFile(filePath: string): boolean {
|
|
570
|
+
const animationPatterns = [
|
|
571
|
+
/animated[-_]/i, // animated-document-scanner.tsx
|
|
572
|
+
/[-_]animation/i, // document-animation.tsx
|
|
573
|
+
/motion[-_]/i, // motion-component.tsx
|
|
574
|
+
/[-_]motion/i, // scroll-motion.tsx
|
|
575
|
+
/particles?[-_]/i, // particles-background.tsx
|
|
576
|
+
/confetti/i, // confetti.tsx
|
|
577
|
+
/[-_]effect/i, // hover-effect.tsx
|
|
578
|
+
/transition[-_]/i, // transition-wrapper.tsx
|
|
579
|
+
/visual[-_]/i, // visual-effects.tsx
|
|
580
|
+
/canvas[-_]/i, // canvas-animation.tsx
|
|
581
|
+
]
|
|
582
|
+
return animationPatterns.some(p => p.test(filePath))
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Check if Math.random() is used for animation/motion coordinates
|
|
587
|
+
* Common in animation libraries like framer-motion, react-spring, Three.js, etc.
|
|
588
|
+
*/
|
|
589
|
+
export function isAnimationCoordinateUsage(lineContent: string, context: string): boolean {
|
|
590
|
+
// Object property assignments for coordinates
|
|
591
|
+
const coordinatePatterns = [
|
|
592
|
+
/\bx:\s*Math\.random/i, // x: Math.random()
|
|
593
|
+
/\by:\s*Math\.random/i, // y: Math.random()
|
|
594
|
+
/\bz:\s*Math\.random/i, // z: Math.random()
|
|
595
|
+
/\bleft:\s*.*Math\.random/i, // left: Math.random()
|
|
596
|
+
/\btop:\s*.*Math\.random/i, // top: Math.random()
|
|
597
|
+
/\bright:\s*.*Math\.random/i, // right: Math.random()
|
|
598
|
+
/\bbottom:\s*.*Math\.random/i, // bottom: Math.random()
|
|
599
|
+
/\brotation:\s*.*Math\.random/i, // rotation: Math.random()
|
|
600
|
+
/\brotateX:\s*.*Math\.random/i, // rotateX: Math.random()
|
|
601
|
+
/\brotateY:\s*.*Math\.random/i, // rotateY: Math.random()
|
|
602
|
+
/\brotateZ:\s*.*Math\.random/i, // rotateZ: Math.random()
|
|
603
|
+
/\bscaleX?:\s*.*Math\.random/i, // scale/scaleX: Math.random()
|
|
604
|
+
/\bscaleY:\s*.*Math\.random/i, // scaleY: Math.random()
|
|
605
|
+
/\bopacity:\s*.*Math\.random/i, // opacity: Math.random()
|
|
606
|
+
/\bduration:\s*.*Math\.random/i, // duration: Math.random()
|
|
607
|
+
/\bdelay:\s*.*Math\.random/i, // delay: Math.random()
|
|
608
|
+
// 3D/Three.js specific patterns
|
|
609
|
+
/\boffset\s*=.*Math\.random/i, // offset = Math.random()
|
|
610
|
+
/useMemo\s*\(\s*\(\s*\)\s*=>\s*Math\.random/i, // useMemo(() => Math.random(), [])
|
|
611
|
+
/\bphase\s*[:=].*Math\.random/i, // phase: Math.random() or phase = Math.random()
|
|
612
|
+
/\bspeed\s*[:=].*Math\.random/i, // speed: Math.random()
|
|
613
|
+
/\bangle\s*[:=].*Math\.random/i, // angle: Math.random()
|
|
614
|
+
]
|
|
615
|
+
|
|
616
|
+
if (coordinatePatterns.some(p => p.test(lineContent))) {
|
|
617
|
+
return true
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
// Motion/animation library context patterns
|
|
621
|
+
const motionLibraryPatterns = [
|
|
622
|
+
/framer-motion/i,
|
|
623
|
+
/react-spring/i,
|
|
624
|
+
/react-motion/i,
|
|
625
|
+
/gsap/i,
|
|
626
|
+
/animejs/i,
|
|
627
|
+
/popmotion/i,
|
|
628
|
+
/motion\.div/i,
|
|
629
|
+
/useSpring/i,
|
|
630
|
+
/useAnimation/i,
|
|
631
|
+
/animate\s*\(/i,
|
|
632
|
+
/keyframes/i,
|
|
633
|
+
// Three.js and React Three Fiber patterns
|
|
634
|
+
/three/i,
|
|
635
|
+
/useFrame/i,
|
|
636
|
+
/@react-three/i,
|
|
637
|
+
/Canvas/i, // React Three Fiber Canvas
|
|
638
|
+
/mesh/i, // Three.js mesh
|
|
639
|
+
/geometry/i, // Three.js geometry
|
|
640
|
+
/material/i, // Three.js material
|
|
641
|
+
]
|
|
642
|
+
|
|
643
|
+
return motionLibraryPatterns.some(p => p.test(context))
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Check if Math.random() is used in a template placeholder generator context
|
|
648
|
+
* Template systems often use random generators for placeholder values
|
|
649
|
+
*
|
|
650
|
+
* Examples:
|
|
651
|
+
* const templates = { random: () => Math.random().toString() }
|
|
652
|
+
* random_hex: () => Math.random().toString(16)
|
|
653
|
+
* {{random}} placeholder generation
|
|
654
|
+
*/
|
|
655
|
+
export function isTemplatePlaceholderGenerator(
|
|
656
|
+
line: string,
|
|
657
|
+
content: string,
|
|
658
|
+
lineNumber: number,
|
|
659
|
+
lines?: string[]
|
|
660
|
+
): boolean {
|
|
661
|
+
const _lines = lines ?? content.split('\n')
|
|
662
|
+
const contextStart = Math.max(0, lineNumber - 10)
|
|
663
|
+
const contextEnd = Math.min(_lines.length, lineNumber + 5)
|
|
664
|
+
const context = _lines.slice(contextStart, contextEnd).join('\n')
|
|
665
|
+
|
|
666
|
+
const templatePatterns = [
|
|
667
|
+
/\{\{random\w*\}\}/i, // {{random}}, {{random_hex}}, etc.
|
|
668
|
+
/random:\s*\(\s*\)\s*=>\s*Math\.random/i, // random: () => Math.random()
|
|
669
|
+
/random_\w+:\s*\(\s*\)\s*=>/i, // random_int: () => ...
|
|
670
|
+
/placeholder.*random/i, // placeholder context
|
|
671
|
+
/templates?\s*[=:]\s*\{/i, // templates = { or template: {
|
|
672
|
+
/generatePlaceholder/i, // generatePlaceholder function
|
|
673
|
+
/placeholder\s*[:=]/i, // placeholder: or placeholder =
|
|
674
|
+
]
|
|
675
|
+
|
|
676
|
+
return templatePatterns.some(p => p.test(context) || p.test(line))
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Check if Math.random() should be skipped entirely
|
|
681
|
+
* Returns true for seed files, test fixtures, captcha/puzzle, uuid, React keys, jitter/backoff, and pure cosmetic uses
|
|
682
|
+
*/
|
|
683
|
+
export function shouldSkipMathRandom(
|
|
684
|
+
content: string,
|
|
685
|
+
filePath: string,
|
|
686
|
+
lineNumber: number,
|
|
687
|
+
options?: { parsed?: ParsedFile }
|
|
688
|
+
): boolean {
|
|
689
|
+
// Seed/data generation files - skip entirely
|
|
690
|
+
if (isSeedOrDataGenFile(filePath)) {
|
|
691
|
+
return true
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Animation/motion component files - skip entirely
|
|
695
|
+
// These use Math.random for visual effects, not security
|
|
696
|
+
if (isAnimationFile(filePath)) {
|
|
697
|
+
return true
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// Educational/intentional vulnerability files - skip entirely
|
|
701
|
+
// These include OWASP Juice Shop, intentionally-vulnerable examples, etc.
|
|
702
|
+
if (isEducationalVulnerabilityFile(filePath)) {
|
|
703
|
+
return true
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Check for React key pattern - this is a common pattern to force re-renders
|
|
707
|
+
// It's not a security issue, just a way to reset component state
|
|
708
|
+
const lines = options?.parsed?.lines ?? content.split('\n')
|
|
709
|
+
const lineContent = lines[lineNumber] || ''
|
|
710
|
+
if (isReactKeyPattern(lineContent, filePath)) {
|
|
711
|
+
return true
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
// Template placeholder generators - skip entirely
|
|
715
|
+
// These generate placeholder values for templates, not security tokens
|
|
716
|
+
if (isTemplatePlaceholderGenerator(lineContent, content, lineNumber, lines)) {
|
|
717
|
+
return true
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// Jitter/backoff/retry patterns - legitimate non-security use of randomness
|
|
721
|
+
// Used for network resilience, rate limiting, exponential backoff, etc.
|
|
722
|
+
if (isJitterOrBackoffContext(content, lineNumber, lines)) {
|
|
723
|
+
return true
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Test files with test fixture patterns
|
|
727
|
+
if (isTestOrMockFile(filePath)) {
|
|
728
|
+
const line = lines[lineNumber]
|
|
729
|
+
// If in a test file and generating test data, skip
|
|
730
|
+
if (
|
|
731
|
+
/\b(mock|fake|fixture|test)Data/i.test(line) ||
|
|
732
|
+
/\b(it|test|describe)\s*\(/.test(line)
|
|
733
|
+
) {
|
|
734
|
+
return true
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Pure cosmetic usage (CSS values, animations)
|
|
739
|
+
if (isCosmeticMathRandom(lineContent, content, lineNumber, lines)) {
|
|
740
|
+
// Additional check: if this is for animation/style, truly skip
|
|
741
|
+
const pureStylePatterns = [
|
|
742
|
+
/\.style\./,
|
|
743
|
+
/animation/i,
|
|
744
|
+
/transform/i,
|
|
745
|
+
/opacity/i,
|
|
746
|
+
/\brgb/i,
|
|
747
|
+
/\bhsl/i,
|
|
748
|
+
]
|
|
749
|
+
if (pureStylePatterns.some(p => p.test(lineContent))) {
|
|
750
|
+
return true
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Animation coordinate usage (x, y, rotation, etc.)
|
|
755
|
+
// Get surrounding context for animation library detection
|
|
756
|
+
const contextStart = Math.max(0, lineNumber - 15)
|
|
757
|
+
const contextEnd = Math.min(lines.length, lineNumber + 5)
|
|
758
|
+
const animContext = lines.slice(contextStart, contextEnd).join('\n')
|
|
759
|
+
if (isAnimationCoordinateUsage(lineContent, animContext)) {
|
|
760
|
+
return true
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// Check function context for demo/seed/captcha/uuid functions
|
|
764
|
+
const functionName = extractFunctionContext(content, lineNumber)
|
|
765
|
+
const functionIntent = classifyFunctionIntent(functionName)
|
|
766
|
+
|
|
767
|
+
// Skip demo, captcha, and uuid functions entirely - these are legitimate uses
|
|
768
|
+
if (functionIntent === 'demo' || functionIntent === 'captcha' || functionIntent === 'uuid') {
|
|
769
|
+
return true
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Check for fallback pattern: crypto.randomUUID?.() ?? Math.random()
|
|
773
|
+
// When a secure primary method is used with Math.random as fallback,
|
|
774
|
+
// the code is safe (Math.random only runs in environments without crypto API)
|
|
775
|
+
const prevLines = lines.slice(Math.max(0, lineNumber - 2), lineNumber + 1).join(' ')
|
|
776
|
+
const multiLineFallbackPatterns = [
|
|
777
|
+
/crypto\??\.?\s*randomUUID\??\.?\s*\(\s*\)\s*\?\?/i, // crypto.randomUUID() ??
|
|
778
|
+
/globalThis\.crypto\??\.?\s*randomUUID\??\.?\s*\(/i, // globalThis.crypto?.randomUUID?.()
|
|
779
|
+
/window\.crypto\??\.?\s*randomUUID\??\.?\s*\(/i, // window.crypto?.randomUUID?.()
|
|
780
|
+
/\?\.\s*randomUUID\??\.?\s*\(\s*\)\s*\?\?/i, // ?.randomUUID?.() ??
|
|
781
|
+
/crypto\??\.?\s*getRandomValues\s*\(/i, // crypto.getRandomValues()
|
|
782
|
+
/randomUUID\??\.?\s*\(\s*\)\s*\?\?/i, // randomUUID?.() ?? (generic)
|
|
783
|
+
]
|
|
784
|
+
if (multiLineFallbackPatterns.some(p => p.test(prevLines))) {
|
|
785
|
+
return true // Skip - Math.random is only a fallback for missing crypto API
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return false
|
|
789
|
+
}
|