@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
package/src/layer1/urls.ts
CHANGED
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Vulnerability } from '../types'
|
|
7
|
+
import type { ParsedFile } from '../utils/parsed-file'
|
|
8
|
+
import { isTestConfigFile, isPythonFile, isInsidePythonDocstring } from '../utils/context-helpers'
|
|
9
|
+
import {
|
|
10
|
+
getEnvironmentContext,
|
|
11
|
+
isInPlaceholderAttribute,
|
|
12
|
+
isDefaultParameterValue,
|
|
13
|
+
getLocalhostSeverity,
|
|
14
|
+
} from '../utils/environment-context'
|
|
15
|
+
import { getRouteProtectionContext } from '../utils/route-hierarchy'
|
|
7
16
|
|
|
8
17
|
// Check if file is documentation/README/example
|
|
9
18
|
function isDocumentationFile(filePath: string): boolean {
|
|
@@ -26,6 +35,50 @@ function isDocumentationFile(filePath: string): boolean {
|
|
|
26
35
|
return docPatterns.some(p => p.test(filePath))
|
|
27
36
|
}
|
|
28
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Check if file path indicates it's inside an authenticated route group
|
|
40
|
+
* Modern frameworks use route groups/layouts that handle auth at the layout level
|
|
41
|
+
*
|
|
42
|
+
* Examples:
|
|
43
|
+
* - Next.js: (authenticated)/dashboard/page.tsx, (admin)/users/page.tsx
|
|
44
|
+
* - Remix: _authenticated+/dashboard.tsx
|
|
45
|
+
* - SvelteKit: (protected)/+page.svelte
|
|
46
|
+
*/
|
|
47
|
+
function isInsideAuthenticatedRouteGroup(filePath: string): boolean {
|
|
48
|
+
const authRoutePatterns = [
|
|
49
|
+
// Next.js App Router - route groups with auth-related names
|
|
50
|
+
/\/\(authenticated\)\//i,
|
|
51
|
+
/\/\(protected\)\//i,
|
|
52
|
+
/\/\(auth\)\//i,
|
|
53
|
+
/\/\(admin\)\//i,
|
|
54
|
+
/\/\(dashboard\)\//i,
|
|
55
|
+
/\/\(private\)\//i,
|
|
56
|
+
/\/\(secure\)\//i,
|
|
57
|
+
/\/\(user\)\//i,
|
|
58
|
+
/\/\(account\)\//i,
|
|
59
|
+
/\/\(settings\)\//i,
|
|
60
|
+
/\/\(app\)\//i, // Common pattern for authenticated app routes
|
|
61
|
+
|
|
62
|
+
// Remix - authenticated route conventions
|
|
63
|
+
/\/_authenticated\+\//i,
|
|
64
|
+
/\/_protected\+\//i,
|
|
65
|
+
/\/_auth\+\//i,
|
|
66
|
+
/\/__authenticated\//i,
|
|
67
|
+
|
|
68
|
+
// Generic patterns that indicate auth-protected sections
|
|
69
|
+
/\/dashboard\//i,
|
|
70
|
+
/\/admin\//i,
|
|
71
|
+
/\/account\//i,
|
|
72
|
+
/\/settings\//i,
|
|
73
|
+
/\/profile\//i,
|
|
74
|
+
/\/my-/i, // /my-account/, /my-orders/
|
|
75
|
+
/\/user\//i,
|
|
76
|
+
/\/users\/\[/i, // /users/[userId]/ - user-specific routes
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
return authRoutePatterns.some(p => p.test(filePath))
|
|
80
|
+
}
|
|
81
|
+
|
|
29
82
|
// Check if file is a config/constants file where localhost is expected
|
|
30
83
|
function isDevConfigFile(filePath: string): boolean {
|
|
31
84
|
const devConfigPatterns = [
|
|
@@ -44,6 +97,37 @@ function isDevConfigFile(filePath: string): boolean {
|
|
|
44
97
|
return devConfigPatterns.some(p => p.test(filePath))
|
|
45
98
|
}
|
|
46
99
|
|
|
100
|
+
// Check if file is a locale/i18n file where URLs are example placeholders
|
|
101
|
+
function isLocaleFile(filePath: string): boolean {
|
|
102
|
+
const localePatterns = [
|
|
103
|
+
/\/locales?\//i, // /locale/ or /locales/
|
|
104
|
+
/\/i18n\//i, // /i18n/
|
|
105
|
+
/\/translations?\//i, // /translation/ or /translations/
|
|
106
|
+
/\/lang\//i, // /lang/
|
|
107
|
+
/\/languages?\//i, // /language/ or /languages/
|
|
108
|
+
/\.\w{2}(-\w{2})?\.json$/i, // .en.json, .zh-CN.json, etc.
|
|
109
|
+
]
|
|
110
|
+
return localePatterns.some(p => p.test(filePath))
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check if line contains URL placeholder/example text
|
|
114
|
+
function isUrlPlaceholderText(line: string): boolean {
|
|
115
|
+
const placeholderPatterns = [
|
|
116
|
+
/placeholder['"]\s*:/i, // "placeholder": "http://..."
|
|
117
|
+
/example['"]\s*:/i, // "example": "http://..."
|
|
118
|
+
/e\.g\./i, // e.g. http://localhost
|
|
119
|
+
/for\s+example/i, // for example http://...
|
|
120
|
+
/such\s+as/i, // such as http://localhost
|
|
121
|
+
/like\s+http/i, // like http://localhost
|
|
122
|
+
/格式.*http/i, // Chinese: format http://...
|
|
123
|
+
/例如.*http/i, // Chinese: for example http://...
|
|
124
|
+
/beispiel.*http/i, // German: example http://...
|
|
125
|
+
/ejemplo.*http/i, // Spanish: example http://...
|
|
126
|
+
/exemple.*http/i, // French: example http://...
|
|
127
|
+
]
|
|
128
|
+
return placeholderPatterns.some(p => p.test(line))
|
|
129
|
+
}
|
|
130
|
+
|
|
47
131
|
// URL patterns that may indicate security issues
|
|
48
132
|
const URL_PATTERNS = [
|
|
49
133
|
// Internal/staging endpoints in production code
|
|
@@ -84,18 +168,20 @@ const URL_PATTERNS = [
|
|
|
84
168
|
description: 'Hardcoded test environment URL found',
|
|
85
169
|
},
|
|
86
170
|
|
|
87
|
-
// Admin/sensitive endpoints - downgraded
|
|
171
|
+
// Admin/sensitive endpoints - heavily downgraded
|
|
172
|
+
// In most modern frameworks, these are protected by middleware or layout auth
|
|
173
|
+
// Flagging them creates noise when they're inside authenticated route groups
|
|
88
174
|
{
|
|
89
175
|
pattern: /['"`]\/admin(?:\/|['"`])/gi,
|
|
90
176
|
name: 'Admin Endpoint',
|
|
91
177
|
severity: 'info' as const,
|
|
92
|
-
description: 'Admin endpoint path found -
|
|
178
|
+
description: 'Admin endpoint path found - typically protected by auth middleware',
|
|
93
179
|
},
|
|
94
180
|
{
|
|
95
181
|
pattern: /['"`]\/api\/admin/gi,
|
|
96
182
|
name: 'Admin API Endpoint',
|
|
97
|
-
severity: '
|
|
98
|
-
description: 'Admin API endpoint found -
|
|
183
|
+
severity: 'info' as const, // Downgraded from low - usually behind middleware
|
|
184
|
+
description: 'Admin API endpoint found - typically protected by auth middleware',
|
|
99
185
|
},
|
|
100
186
|
|
|
101
187
|
// API keys in URLs
|
|
@@ -178,6 +264,33 @@ function isEnvVarReference(lineContent: string): boolean {
|
|
|
178
264
|
lineContent.includes('import.meta.env')
|
|
179
265
|
}
|
|
180
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Check if localhost URL is used as a fallback default value
|
|
269
|
+
* This is a common safe pattern in development:
|
|
270
|
+
* - process.env.API_URL || 'http://localhost:3000'
|
|
271
|
+
* - baseUrl ?? 'http://localhost:3000'
|
|
272
|
+
* - config.url || 'http://localhost'
|
|
273
|
+
* - config?.url ?? 'http://localhost'
|
|
274
|
+
*/
|
|
275
|
+
function isLocalhostFallback(lineContent: string): boolean {
|
|
276
|
+
const fallbackPatterns = [
|
|
277
|
+
// Logical OR fallback: something || 'http://localhost...'
|
|
278
|
+
/\|\|\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
|
|
279
|
+
// Nullish coalescing fallback: something ?? 'http://localhost...'
|
|
280
|
+
/\?\?\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
|
|
281
|
+
// Default parameter: = 'http://localhost...'
|
|
282
|
+
/=\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)[^'"]*['"`]\s*(,|\)|$)/i,
|
|
283
|
+
// Ternary with env check: process.env.X ? ... : 'http://localhost'
|
|
284
|
+
/process\.env\.\w+\s*\?\s*[^:]+\s*:\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
|
|
285
|
+
// Object property default: url: process.env.X || 'http://localhost'
|
|
286
|
+
/:\s*process\.env\.\w+\s*\|\|\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
|
|
287
|
+
// Import.meta.env fallback
|
|
288
|
+
/import\.meta\.env\.\w+\s*\|\|\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
|
|
289
|
+
/import\.meta\.env\.\w+\s*\?\?\s*['"`]https?:\/\/(localhost|127\.0\.0\.1)/i,
|
|
290
|
+
]
|
|
291
|
+
return fallbackPatterns.some(p => p.test(lineContent))
|
|
292
|
+
}
|
|
293
|
+
|
|
181
294
|
// Get URL context for smarter detection
|
|
182
295
|
function getURLContext(lineContent: string, filePath: string): {
|
|
183
296
|
isDevOnly: boolean
|
|
@@ -240,7 +353,8 @@ export function aggregateLocalhostFindings(
|
|
|
240
353
|
|
|
241
354
|
export function detectSensitiveURLs(
|
|
242
355
|
content: string,
|
|
243
|
-
filePath: string
|
|
356
|
+
filePath: string,
|
|
357
|
+
options?: { parsed?: ParsedFile }
|
|
244
358
|
): Vulnerability[] {
|
|
245
359
|
const vulnerabilities: Vulnerability[] = []
|
|
246
360
|
|
|
@@ -249,9 +363,18 @@ export function detectSensitiveURLs(
|
|
|
249
363
|
return vulnerabilities
|
|
250
364
|
}
|
|
251
365
|
|
|
252
|
-
|
|
366
|
+
// Get environment context for smart handling
|
|
367
|
+
const envContext = getEnvironmentContext(filePath)
|
|
368
|
+
|
|
369
|
+
// Get route protection context
|
|
370
|
+
const routeHierarchy = getRouteProtectionContext(filePath)
|
|
371
|
+
|
|
372
|
+
const lines = options?.parsed?.lines ?? content.split('\n')
|
|
253
373
|
const inTestFile = isTestFile(filePath)
|
|
254
374
|
const inDevConfig = isDevConfigFile(filePath)
|
|
375
|
+
const inTestConfig = isTestConfigFile(filePath)
|
|
376
|
+
const isPython = isPythonFile(filePath)
|
|
377
|
+
const inAuthRoute = isInsideAuthenticatedRouteGroup(filePath) || routeHierarchy.isInProtectedHierarchy
|
|
255
378
|
|
|
256
379
|
for (let i = 0; i < lines.length; i++) {
|
|
257
380
|
const line = lines[i]
|
|
@@ -259,6 +382,11 @@ export function detectSensitiveURLs(
|
|
|
259
382
|
// Skip comments
|
|
260
383
|
if (isComment(line)) continue
|
|
261
384
|
|
|
385
|
+
// Skip Python docstrings - they often contain example URLs
|
|
386
|
+
if (isPython && isInsidePythonDocstring(lines, i)) {
|
|
387
|
+
continue
|
|
388
|
+
}
|
|
389
|
+
|
|
262
390
|
// Get context for this line
|
|
263
391
|
const context = getURLContext(line, filePath)
|
|
264
392
|
|
|
@@ -275,14 +403,50 @@ export function detectSensitiveURLs(
|
|
|
275
403
|
|
|
276
404
|
// Special handling for localhost URLs
|
|
277
405
|
if (/localhost|127\.0\.0\.1/i.test(url)) {
|
|
278
|
-
// Skip localhost
|
|
279
|
-
|
|
406
|
+
// Skip localhost as fallback/default values - this is standard practice
|
|
407
|
+
// e.g., process.env.API_URL || 'http://localhost:3000'
|
|
408
|
+
if (isLocalhostFallback(line)) {
|
|
409
|
+
continue
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// Use environment context for smarter handling
|
|
413
|
+
// Skip localhost in environments where it's expected (templates, tests, tooling)
|
|
414
|
+
if (envContext.allowsLocalhostDefaults) {
|
|
415
|
+
continue
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// Skip localhost in test files, dev-only contexts, test config files, or dev config files
|
|
419
|
+
if (context.isTestFile || context.isDevOnly || inDevConfig || inTestConfig) {
|
|
420
|
+
continue
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Skip localhost in placeholder attributes (placeholder="https://localhost...")
|
|
424
|
+
if (isInPlaceholderAttribute(line)) {
|
|
280
425
|
continue
|
|
281
426
|
}
|
|
282
427
|
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
428
|
+
// Skip localhost when used as default parameter value
|
|
429
|
+
if (isDefaultParameterValue(line)) {
|
|
430
|
+
continue
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Skip localhost in locale/i18n files - these are example placeholders for users
|
|
434
|
+
if (isLocaleFile(filePath)) {
|
|
435
|
+
continue
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// Skip localhost in placeholder/example text (e.g., "e.g. http://localhost")
|
|
439
|
+
if (isUrlPlaceholderText(line)) {
|
|
440
|
+
continue
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Use smart severity determination based on context
|
|
444
|
+
const adjustedSeverity = getLocalhostSeverity(filePath, line)
|
|
445
|
+
|
|
446
|
+
// Skip info-level findings in non-production contexts
|
|
447
|
+
if (adjustedSeverity === 'info' && !filePath.includes('.env.production')) {
|
|
448
|
+
continue
|
|
449
|
+
}
|
|
286
450
|
|
|
287
451
|
vulnerabilities.push({
|
|
288
452
|
id: `url-${filePath}-${i + 1}-${name}`,
|
|
@@ -292,19 +456,29 @@ export function detectSensitiveURLs(
|
|
|
292
456
|
severity: adjustedSeverity,
|
|
293
457
|
category: 'sensitive_url',
|
|
294
458
|
title: name,
|
|
295
|
-
description: description + (
|
|
459
|
+
description: description + (adjustedSeverity === 'high' ? ' (in production config!)' : ' (in dev/config file)'),
|
|
296
460
|
suggestedFix: 'Move URLs to environment variables or configuration files. Use process.env.API_URL pattern.',
|
|
297
|
-
confidence:
|
|
461
|
+
confidence: adjustedSeverity === 'high' ? 'high' : 'low',
|
|
298
462
|
layer: 1,
|
|
299
463
|
})
|
|
300
464
|
} else {
|
|
301
465
|
// Normal URL handling (non-localhost)
|
|
302
466
|
// Lower severity for test files - downgrade more aggressively
|
|
303
467
|
let adjustedSeverity = severity
|
|
468
|
+
let contextNote = ''
|
|
469
|
+
|
|
304
470
|
if (inTestFile) {
|
|
305
471
|
if (severity === 'critical') adjustedSeverity = 'high'
|
|
306
472
|
else if (severity === 'high') adjustedSeverity = 'low'
|
|
307
473
|
else adjustedSeverity = 'info'
|
|
474
|
+
contextNote = ' (in test file)'
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Downgrade admin/sensitive endpoint findings inside authenticated route groups
|
|
478
|
+
// These routes are already protected by layout/middleware auth
|
|
479
|
+
if (inAuthRoute && (name === 'Admin Endpoint' || name === 'Admin API Endpoint')) {
|
|
480
|
+
adjustedSeverity = 'info'
|
|
481
|
+
contextNote = ' (inside authenticated route group)'
|
|
308
482
|
}
|
|
309
483
|
|
|
310
484
|
// Non-critical URL findings require AI validation
|
|
@@ -318,7 +492,7 @@ export function detectSensitiveURLs(
|
|
|
318
492
|
severity: adjustedSeverity,
|
|
319
493
|
category: 'sensitive_url',
|
|
320
494
|
title: name,
|
|
321
|
-
description: description +
|
|
495
|
+
description: description + contextNote,
|
|
322
496
|
suggestedFix: 'Move URLs to environment variables or configuration files. Use process.env.API_URL pattern.',
|
|
323
497
|
confidence: inTestFile ? 'low' : 'medium',
|
|
324
498
|
layer: 1,
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Vulnerability } from '../types'
|
|
7
|
+
import type { ParsedFile } from '../utils/parsed-file'
|
|
7
8
|
|
|
8
9
|
// Weak/deprecated cryptographic patterns
|
|
9
10
|
const WEAK_CRYPTO_PATTERNS = [
|
|
@@ -16,13 +17,34 @@ const WEAK_CRYPTO_PATTERNS = [
|
|
|
16
17
|
fix: 'Use SHA-256 or SHA-3 for hashing. For passwords, use bcrypt, scrypt, or Argon2.',
|
|
17
18
|
contextCheck: (line: string) => {
|
|
18
19
|
// Only flag as high if used in security context (passwords, tokens, etc.)
|
|
19
|
-
// Checksum/etag usage is acceptable for non-security purposes
|
|
20
|
-
const securityContexts = [/password/i, /token/i, /secret/i, /credential/i, /auth/i, /session/i, /key/i]
|
|
21
|
-
const checksumContexts = [
|
|
22
|
-
|
|
20
|
+
// Checksum/etag/cache usage is acceptable for non-security purposes
|
|
21
|
+
const securityContexts = [/password/i, /token/i, /secret/i, /credential/i, /auth/i, /session/i, /key/i, /verify.*user/i, /sign/i]
|
|
22
|
+
const checksumContexts = [
|
|
23
|
+
/checksum/i,
|
|
24
|
+
/hash.*file/i,
|
|
25
|
+
/file.*hash/i,
|
|
26
|
+
/etag/i,
|
|
27
|
+
/content.*hash/i,
|
|
28
|
+
/integrity/i,
|
|
29
|
+
/digest/i,
|
|
30
|
+
/fingerprint/i,
|
|
31
|
+
/cache.*key/i,
|
|
32
|
+
/dedup/i,
|
|
33
|
+
/uniqueId/i,
|
|
34
|
+
/contentId/i,
|
|
35
|
+
/gravatar/i, // Gravatar uses MD5 for email hashing
|
|
36
|
+
/avatar/i, // Avatar URLs often use MD5
|
|
37
|
+
/blob/i, // Blob hashing
|
|
38
|
+
/asset/i, // Asset fingerprinting
|
|
39
|
+
/sprite/i, // Sprite sheet hashing
|
|
40
|
+
/bundle/i, // Bundle hashing
|
|
41
|
+
/chunk/i, // Webpack chunk hashing
|
|
42
|
+
/manifest/i, // Manifest hashing
|
|
43
|
+
]
|
|
44
|
+
|
|
23
45
|
const isSecurityContext = securityContexts.some(p => p.test(line))
|
|
24
46
|
const isChecksumContext = checksumContexts.some(p => p.test(line))
|
|
25
|
-
|
|
47
|
+
|
|
26
48
|
// If it's clearly a checksum context, don't flag
|
|
27
49
|
if (isChecksumContext && !isSecurityContext) {
|
|
28
50
|
return false
|
|
@@ -38,14 +60,32 @@ const WEAK_CRYPTO_PATTERNS = [
|
|
|
38
60
|
fix: 'Use createHash(\'sha256\') or createHash(\'sha3-256\') instead.',
|
|
39
61
|
contextCheck: (line: string, _match: RegExpMatchArray, funcName?: string) => {
|
|
40
62
|
// Check function name and line context for checksum indicators
|
|
41
|
-
const checksumIndicators = [
|
|
42
|
-
|
|
43
|
-
|
|
63
|
+
const checksumIndicators = [
|
|
64
|
+
/checksum/i,
|
|
65
|
+
/etag/i,
|
|
66
|
+
/content.*hash/i,
|
|
67
|
+
/file.*hash/i,
|
|
68
|
+
/integrity/i,
|
|
69
|
+
/digest/i,
|
|
70
|
+
/fingerprint/i,
|
|
71
|
+
/verify.*file/i,
|
|
72
|
+
/cache/i,
|
|
73
|
+
/dedup/i,
|
|
74
|
+
/gravatar/i,
|
|
75
|
+
/avatar/i,
|
|
76
|
+
/asset/i,
|
|
77
|
+
/bundle/i,
|
|
78
|
+
/chunk/i,
|
|
79
|
+
/manifest/i,
|
|
80
|
+
/sprite/i,
|
|
81
|
+
]
|
|
82
|
+
const securityIndicators = [/password/i, /token/i, /secret/i, /credential/i, /auth/i, /session/i, /verify.*user/i, /sign/i]
|
|
83
|
+
|
|
44
84
|
const lineAndFunc = funcName ? `${funcName} ${line}` : line
|
|
45
|
-
|
|
85
|
+
|
|
46
86
|
const isChecksum = checksumIndicators.some(p => p.test(lineAndFunc))
|
|
47
87
|
const isSecurity = securityIndicators.some(p => p.test(lineAndFunc))
|
|
48
|
-
|
|
88
|
+
|
|
49
89
|
// Checksum use without security context = acceptable, don't flag
|
|
50
90
|
if (isChecksum && !isSecurity) {
|
|
51
91
|
return false
|
|
@@ -75,6 +115,23 @@ const WEAK_CRYPTO_PATTERNS = [
|
|
|
75
115
|
severity: 'high' as const,
|
|
76
116
|
description: 'DES is obsolete and easily broken. Use AES instead.',
|
|
77
117
|
fix: 'Use AES-256-GCM for symmetric encryption.',
|
|
118
|
+
contextCheck: (line: string) => {
|
|
119
|
+
// DES must appear in crypto-related context, not natural language
|
|
120
|
+
// Common French/Spanish word "des" should not trigger this
|
|
121
|
+
const cryptoContexts = [
|
|
122
|
+
/cipher/i,
|
|
123
|
+
/encrypt/i,
|
|
124
|
+
/decrypt/i,
|
|
125
|
+
/crypto/i,
|
|
126
|
+
/algorithm/i,
|
|
127
|
+
/createCipher/i,
|
|
128
|
+
/\bDES\s*[.(]/i, // DES function call like DES() or DES.encrypt()
|
|
129
|
+
/['"]DES['"]/i, // DES as string literal
|
|
130
|
+
/DES[-_]/i, // DES-CBC, DES_KEY, etc.
|
|
131
|
+
/[-_]DES/i, // 3DES, etc.
|
|
132
|
+
]
|
|
133
|
+
return cryptoContexts.some(ctx => ctx.test(line))
|
|
134
|
+
},
|
|
78
135
|
},
|
|
79
136
|
{
|
|
80
137
|
pattern: /createCipher(?:iv)?\s*\(\s*['"]des['"]/gi,
|
|
@@ -113,6 +170,39 @@ const WEAK_CRYPTO_PATTERNS = [
|
|
|
113
170
|
description: 'Math.random() is not cryptographically secure and should not be used for security purposes',
|
|
114
171
|
fix: 'Use crypto.randomBytes() or crypto.getRandomValues() for cryptographic operations.',
|
|
115
172
|
contextCheck: (line: string) => {
|
|
173
|
+
// Exclude load balancing/array selection patterns (not security-critical)
|
|
174
|
+
// Pattern: Math.random() * array.length or Math.random() * count
|
|
175
|
+
const loadBalancingPatterns = [
|
|
176
|
+
/Math\.random\s*\(\s*\)\s*\*\s*\w+\.length/i, // * array.length
|
|
177
|
+
/Math\.random\s*\(\s*\)\s*\*\s*\w+\.keyLen/i, // * store.keyLen (API key manager)
|
|
178
|
+
/Math\.random\s*\(\s*\)\s*\*\s*\w+\.size/i, // * collection.size
|
|
179
|
+
/Math\.random\s*\(\s*\)\s*\*\s*\d+\s*\)/, // * literal number in expression
|
|
180
|
+
/Math\.floor\s*\(\s*Math\.random\s*\(\s*\)\s*\*\s*\w+\.length/i, // Math.floor(Math.random() * arr.length)
|
|
181
|
+
/Math\.floor\s*\(\s*Math\.random\s*\(\s*\)\s*\*\s*\w+\.keyLen/i, // Math.floor(Math.random() * store.keyLen)
|
|
182
|
+
/\[\s*Math\.floor\s*\(\s*Math\.random/i, // array[Math.floor(Math.random...)]
|
|
183
|
+
/index\s*=\s*Math\.floor\s*\(\s*Math\.random/i, // index = Math.floor(Math.random...)
|
|
184
|
+
/pick|select|choose|shuffle|sample/i, // Load balancing keywords
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
if (loadBalancingPatterns.some(p => p.test(line))) {
|
|
188
|
+
return false // Don't flag - this is load balancing, not security
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Exclude fallback patterns where crypto is the primary method
|
|
192
|
+
// Pattern: crypto.randomUUID() ?? Math.random() or crypto.getRandomValues() || Math.random()
|
|
193
|
+
const fallbackPatterns = [
|
|
194
|
+
/crypto\??\.\s*randomUUID\s*\(\s*\)\s*\?\?/i, // crypto.randomUUID() ??
|
|
195
|
+
/crypto\??\.\s*getRandomValues\s*\([^)]*\)\s*\?\?/i, // crypto.getRandomValues() ??
|
|
196
|
+
/globalThis\.crypto\??\.\s*randomUUID/i, // globalThis.crypto.randomUUID
|
|
197
|
+
/window\.crypto\??\.\s*randomUUID/i, // window.crypto.randomUUID
|
|
198
|
+
/\?\.\s*randomUUID\s*\(\s*\)\s*\?\?/i, // ?.randomUUID() ??
|
|
199
|
+
/randomUUID\s*\(\s*\)\s*\?\?.*Math\.random/i, // randomUUID() ?? ...Math.random
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
if (fallbackPatterns.some(p => p.test(line))) {
|
|
203
|
+
return false // Don't flag - Math.random is only a fallback for missing crypto API
|
|
204
|
+
}
|
|
205
|
+
|
|
116
206
|
// Only flag if it looks like it's being used for security
|
|
117
207
|
const securityContexts = [
|
|
118
208
|
/token/i, /secret/i, /key/i, /password/i, /salt/i,
|
|
@@ -196,6 +286,20 @@ function isComment(lineContent: string): boolean {
|
|
|
196
286
|
)
|
|
197
287
|
}
|
|
198
288
|
|
|
289
|
+
// Check if this is a Python file
|
|
290
|
+
function isPythonFile(filePath: string): boolean {
|
|
291
|
+
return /\.py$/i.test(filePath)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Check if Python's usedforsecurity=False flag is present
|
|
296
|
+
* Python 3.9+ allows explicitly marking hash usage as non-security
|
|
297
|
+
* Example: hashlib.md5(data, usedforsecurity=False)
|
|
298
|
+
*/
|
|
299
|
+
function hasUsedForSecurityFalse(line: string): boolean {
|
|
300
|
+
return /usedforsecurity\s*=\s*False/i.test(line)
|
|
301
|
+
}
|
|
302
|
+
|
|
199
303
|
// Check if line is a pattern definition (regex or string literal in detector code)
|
|
200
304
|
function isPatternDefinition(lineContent: string): boolean {
|
|
201
305
|
const trimmed = lineContent.trim()
|
|
@@ -244,6 +348,22 @@ function isTestOrFixtureFile(filePath: string): boolean {
|
|
|
244
348
|
)
|
|
245
349
|
}
|
|
246
350
|
|
|
351
|
+
// Check if file is a locale/translation file
|
|
352
|
+
function isLocaleFile(filePath: string): boolean {
|
|
353
|
+
const lowerPath = filePath.toLowerCase()
|
|
354
|
+
return (
|
|
355
|
+
lowerPath.includes('/locales/') ||
|
|
356
|
+
lowerPath.includes('/locale/') ||
|
|
357
|
+
lowerPath.includes('/translations/') ||
|
|
358
|
+
lowerPath.includes('/i18n/') ||
|
|
359
|
+
lowerPath.includes('/lang/') ||
|
|
360
|
+
lowerPath.includes('/languages/') ||
|
|
361
|
+
// Common locale file patterns
|
|
362
|
+
/\/(en|fr|de|es|it|pt|ja|ko|zh|ru|ar|nl|pl|tr|vi|th|id|ms|fil|hi|bn|ta|te|ml|kn|gu|mr|pa|ur|sw|am|or|si|ne|km|my|lo|ka|mk|sr|hr|sl|sk|cs|hu|ro|bg|uk|el|he|fa|ps|ku|ug|yi|mn|bo|dz|dv)[-_][a-z]{2,}\.json$/i.test(lowerPath) ||
|
|
363
|
+
/locale.*\.json$/i.test(lowerPath)
|
|
364
|
+
)
|
|
365
|
+
}
|
|
366
|
+
|
|
247
367
|
/**
|
|
248
368
|
* Find the enclosing function name for a given line
|
|
249
369
|
*/
|
|
@@ -267,10 +387,11 @@ function findEnclosingFunctionName(lines: string[], lineIndex: number): string |
|
|
|
267
387
|
|
|
268
388
|
export function detectWeakCrypto(
|
|
269
389
|
content: string,
|
|
270
|
-
filePath: string
|
|
390
|
+
filePath: string,
|
|
391
|
+
options?: { parsed?: ParsedFile }
|
|
271
392
|
): Vulnerability[] {
|
|
272
393
|
const vulnerabilities: Vulnerability[] = []
|
|
273
|
-
const lines = content.split('\n')
|
|
394
|
+
const lines = options?.parsed?.lines ?? content.split('\n')
|
|
274
395
|
|
|
275
396
|
// Skip scanner's own detector files to avoid self-detection
|
|
276
397
|
if (isScannerDetectorFile(filePath)) {
|
|
@@ -282,23 +403,54 @@ export function detectWeakCrypto(
|
|
|
282
403
|
return vulnerabilities
|
|
283
404
|
}
|
|
284
405
|
|
|
406
|
+
// Skip locale/translation files (natural language text, not crypto code)
|
|
407
|
+
if (isLocaleFile(filePath)) {
|
|
408
|
+
return vulnerabilities
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const isPython = isPythonFile(filePath)
|
|
412
|
+
|
|
285
413
|
for (let i = 0; i < lines.length; i++) {
|
|
286
414
|
const line = lines[i]
|
|
287
|
-
|
|
415
|
+
|
|
288
416
|
// Skip comments
|
|
289
417
|
if (isComment(line)) continue
|
|
290
|
-
|
|
418
|
+
|
|
291
419
|
// Skip pattern definitions (detector rule definitions)
|
|
292
420
|
if (isPatternDefinition(line)) continue
|
|
293
421
|
|
|
422
|
+
// Skip Python code with usedforsecurity=False flag
|
|
423
|
+
// Python 3.9+ allows explicitly marking non-security hash usage
|
|
424
|
+
if (isPython && hasUsedForSecurityFalse(line)) {
|
|
425
|
+
continue
|
|
426
|
+
}
|
|
427
|
+
|
|
294
428
|
for (const cryptoPattern of WEAK_CRYPTO_PATTERNS) {
|
|
295
429
|
const { pattern, name, severity, description, fix, contextCheck } = cryptoPattern
|
|
296
|
-
|
|
430
|
+
|
|
297
431
|
// Reset regex state
|
|
298
432
|
const regex = new RegExp(pattern.source, pattern.flags)
|
|
299
433
|
const match = regex.exec(line)
|
|
300
|
-
|
|
434
|
+
|
|
301
435
|
if (match) {
|
|
436
|
+
// Special handling for Math.random - check multi-line fallback patterns
|
|
437
|
+
if (name === 'Math.random() for Security') {
|
|
438
|
+
// Check previous 2 lines for crypto.randomUUID fallback pattern
|
|
439
|
+
// Pattern: crypto?.randomUUID?.() ?? `...${Math.random()}...`
|
|
440
|
+
const prevLines = lines.slice(Math.max(0, i - 2), i + 1).join(' ')
|
|
441
|
+
const multiLineFallbackPatterns = [
|
|
442
|
+
/crypto\??\.?\s*randomUUID\??\s*\(\s*\)\s*\?\?/i, // crypto.randomUUID() ??
|
|
443
|
+
/globalThis\.crypto\??\.?\s*randomUUID\??\.?\s*\(/i, // globalThis.crypto?.randomUUID?.()
|
|
444
|
+
/window\.crypto\??\.?\s*randomUUID\??\.?\s*\(/i, // window.crypto?.randomUUID?.()
|
|
445
|
+
/\?\.\s*randomUUID\??\.?\s*\(\s*\)\s*\?\?/i, // ?.randomUUID?.() ??
|
|
446
|
+
/crypto\??\.?\s*getRandomValues\s*\(/i, // crypto.getRandomValues()
|
|
447
|
+
/randomUUID\??\.?\s*\(\s*\)\s*\?\?/i, // randomUUID?.() ?? (generic)
|
|
448
|
+
]
|
|
449
|
+
if (multiLineFallbackPatterns.some(p => p.test(prevLines))) {
|
|
450
|
+
continue // Skip - Math.random is only a fallback for missing crypto API
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
302
454
|
// If there's a context check, apply it
|
|
303
455
|
// Pass function name for additional context
|
|
304
456
|
const funcName = findEnclosingFunctionName(lines, i)
|