@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,531 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 2: Model Supply Chain Security Detection
|
|
3
|
+
* Detects unsafe model loading patterns that can lead to arbitrary code execution
|
|
4
|
+
*
|
|
5
|
+
* Covers AI Detection Roadmap Phase 2:
|
|
6
|
+
* - Pickle/joblib deserialization RCE
|
|
7
|
+
* - torch.load without weights_only=True
|
|
8
|
+
* - Unverified model sources
|
|
9
|
+
* - Unsafe fine-tuning on user data
|
|
10
|
+
*
|
|
11
|
+
* References:
|
|
12
|
+
* - OWASP LLM05: Supply Chain Vulnerabilities
|
|
13
|
+
* - CWE-502: Deserialization of Untrusted Data
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { Vulnerability, VulnerabilitySeverity, VulnerabilityCategory } from '../types'
|
|
17
|
+
import type { ParsedFile } from '../utils/parsed-file'
|
|
18
|
+
import {
|
|
19
|
+
isComment,
|
|
20
|
+
isTestOrMockFile,
|
|
21
|
+
isScannerOrFixtureFile,
|
|
22
|
+
isExampleDirectory,
|
|
23
|
+
isLibraryCode,
|
|
24
|
+
} from '../utils/context-helpers'
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Context Detection
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if file is in an ML/model context based on path and content
|
|
32
|
+
*/
|
|
33
|
+
function isMLContextFile(filePath: string, content: string): boolean {
|
|
34
|
+
// File path indicators
|
|
35
|
+
const mlPathPatterns = [
|
|
36
|
+
/\/(models?|ml|training|inference|weights|checkpoints)\//i,
|
|
37
|
+
/\/(transformers|huggingface|pytorch|tensorflow|keras)\//i,
|
|
38
|
+
/(train|model|inference|fine[-_]?tune).*\.(py|ts|js)$/i,
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
if (mlPathPatterns.some(p => p.test(filePath))) {
|
|
42
|
+
return true
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Content patterns suggesting ML usage
|
|
46
|
+
const mlContentPatterns = [
|
|
47
|
+
/import\s+(?:torch|tensorflow|keras|transformers|joblib|pickle|dill|cloudpickle|onnx)/i,
|
|
48
|
+
/from\s+(?:torch|tensorflow|keras|transformers|joblib|pickle|dill|cloudpickle|onnx)\s+import/i,
|
|
49
|
+
/\.load_model\s*\(/i,
|
|
50
|
+
/\.from_pretrained\s*\(/i,
|
|
51
|
+
/torch\.load\s*\(/i,
|
|
52
|
+
/torch\.jit\.load\s*\(/i,
|
|
53
|
+
/pickle\.load/i,
|
|
54
|
+
/joblib\.load/i,
|
|
55
|
+
/dill\.load/i,
|
|
56
|
+
/cloudpickle\.load/i,
|
|
57
|
+
/onnx\.load/i,
|
|
58
|
+
/numpy\.load.*allow_pickle/i,
|
|
59
|
+
/np\.load.*allow_pickle/i,
|
|
60
|
+
/yaml\.(?:unsafe_load|full_load|load)/i,
|
|
61
|
+
/Trainer|TrainingArguments/i,
|
|
62
|
+
/model\.save|model\.load/i,
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
return mlContentPatterns.some(p => p.test(content))
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ============================================================================
|
|
69
|
+
// Safe Pattern Detection
|
|
70
|
+
// ============================================================================
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Check if torch.load has weights_only=True (safe mode)
|
|
74
|
+
*/
|
|
75
|
+
function hasTorchWeightsOnly(lineContent: string, surroundingContext: string): boolean {
|
|
76
|
+
const fullContext = lineContent + '\n' + surroundingContext
|
|
77
|
+
return /weights_only\s*=\s*True/i.test(fullContext)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Check if TensorFlow/Keras load has safe_mode=True
|
|
82
|
+
*/
|
|
83
|
+
function hasKerasSafeMode(lineContent: string, surroundingContext: string): boolean {
|
|
84
|
+
const fullContext = lineContent + '\n' + surroundingContext
|
|
85
|
+
return /safe_mode\s*=\s*True/i.test(fullContext)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if model source is from a trusted provider
|
|
90
|
+
*/
|
|
91
|
+
function isTrustedModelSource(lineContent: string): boolean {
|
|
92
|
+
const trustedPatterns = [
|
|
93
|
+
// Official Hugging Face repos
|
|
94
|
+
/huggingface\.co\/(meta-llama|openai|anthropic|google|microsoft|facebook|EleutherAI)/i,
|
|
95
|
+
// Specific trusted model IDs (no http prefix)
|
|
96
|
+
/['"`](meta-llama|openai|anthropic|google|microsoft|facebook)\//i,
|
|
97
|
+
// Local file paths (not URLs)
|
|
98
|
+
/['"`]\.\/|['"`]\.\.\/|['"`]\//,
|
|
99
|
+
// Environment variables for paths
|
|
100
|
+
/os\.environ|process\.env/i,
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
return trustedPatterns.some(p => p.test(lineContent))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Check if integrity verification is present
|
|
108
|
+
*/
|
|
109
|
+
function hasIntegrityCheck(content: string, lineNumber: number): boolean {
|
|
110
|
+
const lines = content.split('\n')
|
|
111
|
+
const contextStart = Math.max(0, lineNumber - 20)
|
|
112
|
+
const contextEnd = Math.min(lines.length, lineNumber + 10)
|
|
113
|
+
const context = lines.slice(contextStart, contextEnd).join('\n')
|
|
114
|
+
|
|
115
|
+
const integrityPatterns = [
|
|
116
|
+
/checksum|sha256|sha512|md5|verify_hash|hash_check/i,
|
|
117
|
+
/verify.*integrity|integrity.*verify/i,
|
|
118
|
+
/revision\s*=\s*['"`][a-f0-9]{40}/i, // Git SHA pinning
|
|
119
|
+
/safetensors/i, // SafeTensors format (safe by design)
|
|
120
|
+
/\.ckpt\.sha256|\.bin\.sha256/i, // Hash files
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
return integrityPatterns.some(p => p.test(context))
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Check if using SafeTensors format (safe by design)
|
|
128
|
+
*/
|
|
129
|
+
function usesSafeTensors(lineContent: string, surroundingContext: string): boolean {
|
|
130
|
+
const fullContext = lineContent + '\n' + surroundingContext
|
|
131
|
+
return /safetensors|\.safetensors|from_safetensors|load_safetensors/i.test(fullContext)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Check if data validation is present for training
|
|
136
|
+
*/
|
|
137
|
+
function hasDataValidation(content: string, lineNumber: number): boolean {
|
|
138
|
+
const lines = content.split('\n')
|
|
139
|
+
const contextStart = Math.max(0, lineNumber - 30)
|
|
140
|
+
const contextEnd = Math.min(lines.length, lineNumber + 10)
|
|
141
|
+
const context = lines.slice(contextStart, contextEnd).join('\n')
|
|
142
|
+
|
|
143
|
+
const validationPatterns = [
|
|
144
|
+
/validate|sanitize|clean|filter/i,
|
|
145
|
+
/schema\.(parse|validate)/i,
|
|
146
|
+
/content_moderation|moderate_content/i,
|
|
147
|
+
/review.*data|data.*review/i,
|
|
148
|
+
/verified.*dataset|trusted.*data/i,
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
return validationPatterns.some(p => p.test(context))
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ============================================================================
|
|
155
|
+
// Pattern Definitions
|
|
156
|
+
// ============================================================================
|
|
157
|
+
|
|
158
|
+
interface ModelSupplyChainPattern {
|
|
159
|
+
name: string
|
|
160
|
+
pattern: RegExp
|
|
161
|
+
category: VulnerabilityCategory
|
|
162
|
+
baseSeverity: VulnerabilitySeverity
|
|
163
|
+
description: string
|
|
164
|
+
suggestedFix: string
|
|
165
|
+
checkSafeMode?: 'torch' | 'keras'
|
|
166
|
+
checkTrustedSource?: boolean
|
|
167
|
+
checkIntegrity?: boolean
|
|
168
|
+
checkDataValidation?: boolean
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const MODEL_SUPPLY_CHAIN_PATTERNS: ModelSupplyChainPattern[] = [
|
|
172
|
+
// ========== Pickle Deserialization (RCE) ==========
|
|
173
|
+
{
|
|
174
|
+
name: 'Pickle deserialization',
|
|
175
|
+
pattern: /pickle\.load\s*\(|pickle\.loads\s*\(/gi,
|
|
176
|
+
category: 'ai_unsafe_model_load',
|
|
177
|
+
baseSeverity: 'critical',
|
|
178
|
+
description: 'pickle.load() executes arbitrary Python code embedded in pickle files. Attackers can craft malicious pickle files that execute code when loaded.',
|
|
179
|
+
suggestedFix: 'Use SafeTensors format for ML models. For other data, use JSON or a safe serialization format. If pickle is unavoidable, only load from trusted, verified sources.',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'Joblib deserialization',
|
|
183
|
+
pattern: /joblib\.load\s*\(/gi,
|
|
184
|
+
category: 'ai_unsafe_model_load',
|
|
185
|
+
baseSeverity: 'critical',
|
|
186
|
+
description: 'joblib.load() uses pickle internally and can execute arbitrary code. This is commonly used for sklearn models but is unsafe for untrusted files.',
|
|
187
|
+
suggestedFix: 'Use ONNX format for sklearn models. Alternatively, use skops.io which provides secure model persistence. Only load from verified sources.',
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// ========== Extended Pickle Variants (RCE) ==========
|
|
191
|
+
{
|
|
192
|
+
name: 'Dill deserialization',
|
|
193
|
+
pattern: /\bdill\.(load|loads|load_session)\s*\(/gi,
|
|
194
|
+
category: 'ai_unsafe_model_load',
|
|
195
|
+
baseSeverity: 'critical',
|
|
196
|
+
description: 'dill can execute arbitrary code during deserialization, similar to pickle but with extended capabilities including serializing lambdas and closures.',
|
|
197
|
+
suggestedFix: 'Use SafeTensors format or JSON serialization instead of dill. If dill is unavoidable, only load from trusted, verified sources.',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: 'Cloudpickle deserialization',
|
|
201
|
+
pattern: /\bcloudpickle\.(load|loads)\s*\(/gi,
|
|
202
|
+
category: 'ai_unsafe_model_load',
|
|
203
|
+
baseSeverity: 'critical',
|
|
204
|
+
description: 'cloudpickle can serialize and execute arbitrary functions, enabling remote code execution during deserialization.',
|
|
205
|
+
suggestedFix: 'Use SafeTensors format or explicitly define functions instead of deserializing them. Avoid cloudpickle for untrusted data.',
|
|
206
|
+
},
|
|
207
|
+
|
|
208
|
+
// ========== YAML Unsafe Loading (RCE) ==========
|
|
209
|
+
{
|
|
210
|
+
name: 'YAML unsafe deserialization',
|
|
211
|
+
pattern: /\byaml\.(unsafe_load|full_load|UnsafeLoader)\s*\(/gi,
|
|
212
|
+
category: 'ai_unsafe_model_load',
|
|
213
|
+
baseSeverity: 'critical',
|
|
214
|
+
description: 'yaml.unsafe_load() and yaml.full_load() can instantiate arbitrary Python objects, enabling remote code execution.',
|
|
215
|
+
suggestedFix: 'Use yaml.safe_load() instead of yaml.unsafe_load() or yaml.full_load(). SafeLoader only loads basic Python types.',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: 'YAML load without explicit Loader',
|
|
219
|
+
pattern: /\byaml\.load\s*\(\s*[^,)]+\s*\)(?!\s*,)/gi,
|
|
220
|
+
category: 'ai_unsafe_model_load',
|
|
221
|
+
baseSeverity: 'medium',
|
|
222
|
+
description: 'yaml.load() without explicit Loader argument may use unsafe loading in older PyYAML versions (< 5.1).',
|
|
223
|
+
suggestedFix: 'Use yaml.safe_load() or explicitly specify Loader=yaml.SafeLoader: yaml.load(file, Loader=yaml.SafeLoader)',
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
// ========== NumPy Pickle Loading (RCE) ==========
|
|
227
|
+
{
|
|
228
|
+
name: 'NumPy pickle loading',
|
|
229
|
+
pattern: /\b(?:np|numpy)\.load\s*\([^)]*allow_pickle\s*=\s*True/gi,
|
|
230
|
+
category: 'ai_unsafe_model_load',
|
|
231
|
+
baseSeverity: 'high',
|
|
232
|
+
description: 'numpy.load() with allow_pickle=True can execute arbitrary code embedded in .npy/.npz files via pickle.',
|
|
233
|
+
suggestedFix: 'Use allow_pickle=False (default in numpy >= 1.16.3) or use SafeTensors format. Only allow_pickle from verified sources.',
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
// ========== TorchScript Loading (RCE) ==========
|
|
237
|
+
{
|
|
238
|
+
name: 'TorchScript model loading',
|
|
239
|
+
pattern: /\btorch\.jit\.load\s*\(/gi,
|
|
240
|
+
category: 'ai_unsafe_model_load',
|
|
241
|
+
baseSeverity: 'high',
|
|
242
|
+
description: 'torch.jit.load() can execute arbitrary code embedded in TorchScript models. TorchScript files can contain custom operators with native code.',
|
|
243
|
+
suggestedFix: 'Use SafeTensors format or verify model source and integrity before loading. Pin to specific model revisions with checksums.',
|
|
244
|
+
},
|
|
245
|
+
|
|
246
|
+
// ========== ONNX Model Loading (Code Execution Risk) ==========
|
|
247
|
+
{
|
|
248
|
+
name: 'ONNX model loading',
|
|
249
|
+
pattern: /\bonnx\.load\s*\(/gi,
|
|
250
|
+
category: 'ai_unsafe_model_load',
|
|
251
|
+
baseSeverity: 'medium',
|
|
252
|
+
description: 'ONNX models with custom operators can execute arbitrary code. Custom ops are loaded as shared libraries.',
|
|
253
|
+
suggestedFix: 'Verify ONNX model source and check for custom operators before loading. Use onnx.checker.check_model() and inspect custom ops.',
|
|
254
|
+
},
|
|
255
|
+
|
|
256
|
+
// ========== PyTorch Loading ==========
|
|
257
|
+
{
|
|
258
|
+
name: 'torch.load without weights_only',
|
|
259
|
+
pattern: /torch\.load\s*\([^)]*\)/gi,
|
|
260
|
+
category: 'ai_unsafe_model_load',
|
|
261
|
+
baseSeverity: 'high',
|
|
262
|
+
description: 'torch.load() without weights_only=True can execute arbitrary code embedded in model files via pickle.',
|
|
263
|
+
suggestedFix: 'Use torch.load(path, weights_only=True) to load only tensor data safely. Or use SafeTensors format: from safetensors.torch import load_file',
|
|
264
|
+
checkSafeMode: 'torch',
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
// ========== TensorFlow/Keras Loading ==========
|
|
268
|
+
{
|
|
269
|
+
name: 'Keras load_model without safe_mode',
|
|
270
|
+
pattern: /(?:keras\.models\.)?load_model\s*\([^)]*\)/gi,
|
|
271
|
+
category: 'ai_unsafe_model_load',
|
|
272
|
+
baseSeverity: 'high',
|
|
273
|
+
description: 'Keras load_model() can execute arbitrary code from Lambda layers or custom objects in model files.',
|
|
274
|
+
suggestedFix: 'Use tf.keras.models.load_model(path, safe_mode=True) in TensorFlow 2.13+. Alternatively, save/load only weights with model.save_weights()/load_weights().',
|
|
275
|
+
checkSafeMode: 'keras',
|
|
276
|
+
},
|
|
277
|
+
|
|
278
|
+
// ========== Unverified Model Sources ==========
|
|
279
|
+
{
|
|
280
|
+
name: 'Model from HTTP URL',
|
|
281
|
+
pattern: /from_pretrained\s*\(\s*['"`]https?:\/\/[^'"`)]+['"`]/gi,
|
|
282
|
+
category: 'ai_unverified_model',
|
|
283
|
+
baseSeverity: 'high',
|
|
284
|
+
description: 'Loading models directly from HTTP URLs bypasses the Hugging Face Hub\'s verification. Attackers could serve malicious models via MITM attacks.',
|
|
285
|
+
suggestedFix: 'Load from Hugging Face Hub by model ID instead of direct URL. Pin to specific revision: from_pretrained("org/model", revision="abc123")',
|
|
286
|
+
checkTrustedSource: true,
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'trust_remote_code=True',
|
|
290
|
+
pattern: /trust_remote_code\s*=\s*True/gi,
|
|
291
|
+
category: 'ai_unsafe_model_load',
|
|
292
|
+
baseSeverity: 'high',
|
|
293
|
+
description: 'trust_remote_code=True allows model repos to execute arbitrary Python code during loading. Malicious repos could compromise your system.',
|
|
294
|
+
suggestedFix: 'Avoid trust_remote_code=True. Use models that don\'t require custom code. If necessary, audit the model repo code before enabling.',
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
name: 'Model download without verification',
|
|
298
|
+
pattern: /(?:urllib|requests|wget|curl).*(?:\.pth|\.pt|\.bin|\.ckpt|\.h5|\.keras|model)/gi,
|
|
299
|
+
category: 'ai_unverified_model',
|
|
300
|
+
baseSeverity: 'medium',
|
|
301
|
+
description: 'Downloading model files without integrity verification. Attackers could serve modified files via compromised mirrors or MITM.',
|
|
302
|
+
suggestedFix: 'Verify downloaded files against known checksums: sha256sum file.pth. Use official download APIs (transformers, torch.hub) which include verification.',
|
|
303
|
+
checkIntegrity: true,
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
// ========== Unsafe Fine-tuning ==========
|
|
307
|
+
{
|
|
308
|
+
name: 'Training on user uploads',
|
|
309
|
+
pattern: /(?:trainer|model)\.(?:train|fit|fine_tune)\s*\([^)]*(?:user_uploads?|user_data|uploaded|untrusted)/gi,
|
|
310
|
+
category: 'ai_unsafe_finetuning',
|
|
311
|
+
baseSeverity: 'high',
|
|
312
|
+
description: 'Training/fine-tuning directly on user-uploaded data without validation enables data poisoning attacks.',
|
|
313
|
+
suggestedFix: 'Validate and sanitize all training data. Implement content moderation. Use separate data pipelines for user content with review steps.',
|
|
314
|
+
checkDataValidation: true,
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: 'Trainer with user data',
|
|
318
|
+
pattern: /Trainer\s*\([^)]*(?:train_dataset|eval_dataset)\s*=\s*(?:user_uploads?|user_data|uploaded_data|untrusted_data)/gi,
|
|
319
|
+
category: 'ai_unsafe_finetuning',
|
|
320
|
+
baseSeverity: 'high',
|
|
321
|
+
description: 'Trainer initialized with user-uploaded data without validation. Data poisoning attacks can manipulate model behavior.',
|
|
322
|
+
suggestedFix: 'Validate and sanitize all training data before passing to Trainer. Implement content moderation and review pipelines.',
|
|
323
|
+
checkDataValidation: true,
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
name: 'Fine-tuning with external dataset',
|
|
327
|
+
pattern: /(?:load_dataset|datasets\.load)\s*\([^)]*(?:path|url|http)/gi,
|
|
328
|
+
category: 'ai_unsafe_finetuning',
|
|
329
|
+
baseSeverity: 'medium',
|
|
330
|
+
description: 'Loading training datasets from external sources without verification. Data could be poisoned.',
|
|
331
|
+
suggestedFix: 'Use verified datasets from trusted sources. Implement data validation pipelines. Consider dataset hashing/signing.',
|
|
332
|
+
checkDataValidation: true,
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: 'Training config from user input',
|
|
336
|
+
pattern: /TrainingArguments\s*\([^)]*(?:request|user|input|body)\./gi,
|
|
337
|
+
category: 'ai_unsafe_finetuning',
|
|
338
|
+
baseSeverity: 'medium',
|
|
339
|
+
description: 'Training arguments derived from user input. Attackers could manipulate training hyperparameters to compromise model behavior.',
|
|
340
|
+
suggestedFix: 'Validate and sanitize all training arguments. Use allowlists for hyperparameter values. Don\'t allow users to specify output paths.',
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
// ========== TensorFlow Specific ==========
|
|
344
|
+
{
|
|
345
|
+
name: 'TensorFlow SavedModel from path',
|
|
346
|
+
pattern: /tf\.saved_model\.load\s*\([^)]*(?:http|ftp|user|upload)/gi,
|
|
347
|
+
category: 'ai_unverified_model',
|
|
348
|
+
baseSeverity: 'high',
|
|
349
|
+
description: 'Loading TensorFlow SavedModel from untrusted path. SavedModels can contain arbitrary ops that execute code.',
|
|
350
|
+
suggestedFix: 'Only load SavedModels from trusted, verified sources. Use TF Serving with model verification for production.',
|
|
351
|
+
checkTrustedSource: true,
|
|
352
|
+
},
|
|
353
|
+
]
|
|
354
|
+
|
|
355
|
+
// ============================================================================
|
|
356
|
+
// Helper Functions
|
|
357
|
+
// ============================================================================
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get surrounding context for analysis
|
|
361
|
+
*/
|
|
362
|
+
function getSurroundingContext(content: string, lineIndex: number, windowSize: number = 15): string {
|
|
363
|
+
const lines = content.split('\n')
|
|
364
|
+
const start = Math.max(0, lineIndex - windowSize)
|
|
365
|
+
const end = Math.min(lines.length, lineIndex + windowSize)
|
|
366
|
+
return lines.slice(start, end).join('\n')
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Calculate severity based on mitigations
|
|
371
|
+
*/
|
|
372
|
+
function calculateSeverity(
|
|
373
|
+
baseSeverity: VulnerabilitySeverity,
|
|
374
|
+
isMitigated: boolean,
|
|
375
|
+
isPartiallyMitigated: boolean,
|
|
376
|
+
isTestFile: boolean,
|
|
377
|
+
isExample: boolean,
|
|
378
|
+
isLibrary: boolean
|
|
379
|
+
): VulnerabilitySeverity {
|
|
380
|
+
if (isTestFile || isExample || isLibrary) {
|
|
381
|
+
return 'info'
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (isMitigated) {
|
|
385
|
+
return 'info'
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (isPartiallyMitigated) {
|
|
389
|
+
if (baseSeverity === 'critical') return 'high'
|
|
390
|
+
if (baseSeverity === 'high') return 'medium'
|
|
391
|
+
if (baseSeverity === 'medium') return 'low'
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return baseSeverity
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// ============================================================================
|
|
398
|
+
// Main Detection Function
|
|
399
|
+
// ============================================================================
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Main detection function for model supply chain security issues
|
|
403
|
+
*/
|
|
404
|
+
export function detectModelSupplyChain(
|
|
405
|
+
content: string,
|
|
406
|
+
filePath: string,
|
|
407
|
+
options?: { parsed?: ParsedFile }
|
|
408
|
+
): Vulnerability[] {
|
|
409
|
+
const vulnerabilities: Vulnerability[] = []
|
|
410
|
+
|
|
411
|
+
// Skip non-applicable files
|
|
412
|
+
if (isScannerOrFixtureFile(filePath)) return vulnerabilities
|
|
413
|
+
|
|
414
|
+
// Only scan files that appear to be in ML context
|
|
415
|
+
if (!isMLContextFile(filePath, content)) {
|
|
416
|
+
return vulnerabilities
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const lines = options?.parsed?.lines ?? content.split('\n')
|
|
420
|
+
const isTestFile = isTestOrMockFile(filePath)
|
|
421
|
+
const isExample = isExampleDirectory(filePath)
|
|
422
|
+
const isLibrary = isLibraryCode(filePath)
|
|
423
|
+
|
|
424
|
+
for (const pattern of MODEL_SUPPLY_CHAIN_PATTERNS) {
|
|
425
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags)
|
|
426
|
+
let match
|
|
427
|
+
|
|
428
|
+
while ((match = regex.exec(content)) !== null) {
|
|
429
|
+
const lineNumber = content.substring(0, match.index).split('\n').length
|
|
430
|
+
const lineContent = lines[lineNumber - 1]?.trim() || ''
|
|
431
|
+
|
|
432
|
+
// Skip comments
|
|
433
|
+
if (isComment(lineContent)) continue
|
|
434
|
+
|
|
435
|
+
const surroundingContext = getSurroundingContext(content, lineNumber - 1)
|
|
436
|
+
|
|
437
|
+
// Check for mitigations
|
|
438
|
+
let isMitigated = false
|
|
439
|
+
let isPartiallyMitigated = false
|
|
440
|
+
let description = pattern.description
|
|
441
|
+
|
|
442
|
+
// Check SafeTensors usage (mitigates most pickle/torch issues)
|
|
443
|
+
if (usesSafeTensors(lineContent, surroundingContext)) {
|
|
444
|
+
isMitigated = true
|
|
445
|
+
description += ' (SafeTensors format detected - safe.)'
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Check torch weights_only
|
|
449
|
+
if (pattern.checkSafeMode === 'torch') {
|
|
450
|
+
if (hasTorchWeightsOnly(lineContent, surroundingContext)) {
|
|
451
|
+
isMitigated = true
|
|
452
|
+
description += ' (weights_only=True detected - safe.)'
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Check keras safe_mode
|
|
457
|
+
if (pattern.checkSafeMode === 'keras') {
|
|
458
|
+
if (hasKerasSafeMode(lineContent, surroundingContext)) {
|
|
459
|
+
isMitigated = true
|
|
460
|
+
description += ' (safe_mode=True detected - safe.)'
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Check trusted source
|
|
465
|
+
if (pattern.checkTrustedSource) {
|
|
466
|
+
if (isTrustedModelSource(lineContent)) {
|
|
467
|
+
isPartiallyMitigated = true
|
|
468
|
+
description += ' (Trusted source detected.)'
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// Check integrity verification
|
|
473
|
+
if (pattern.checkIntegrity) {
|
|
474
|
+
if (hasIntegrityCheck(content, lineNumber)) {
|
|
475
|
+
isMitigated = true
|
|
476
|
+
description += ' (Integrity verification detected.)'
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Check data validation for training
|
|
481
|
+
if (pattern.checkDataValidation) {
|
|
482
|
+
if (hasDataValidation(content, lineNumber)) {
|
|
483
|
+
isPartiallyMitigated = true
|
|
484
|
+
description += ' (Data validation detected nearby.)'
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Skip if fully mitigated
|
|
489
|
+
if (isMitigated) continue
|
|
490
|
+
|
|
491
|
+
// Calculate final severity
|
|
492
|
+
const severity = calculateSeverity(
|
|
493
|
+
pattern.baseSeverity,
|
|
494
|
+
isMitigated,
|
|
495
|
+
isPartiallyMitigated,
|
|
496
|
+
isTestFile,
|
|
497
|
+
isExample,
|
|
498
|
+
isLibrary
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
// Add context notes
|
|
502
|
+
if (isTestFile) {
|
|
503
|
+
description += ' (In test file.)'
|
|
504
|
+
} else if (isExample) {
|
|
505
|
+
description += ' (In example/demo directory.)'
|
|
506
|
+
} else if (isLibrary) {
|
|
507
|
+
description += ' (Library code.)'
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Skip info-level in non-ML focused files to reduce noise
|
|
511
|
+
if (severity === 'info' && !isMLContextFile(filePath, content)) continue
|
|
512
|
+
|
|
513
|
+
vulnerabilities.push({
|
|
514
|
+
id: `model-supply-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
515
|
+
filePath,
|
|
516
|
+
lineNumber,
|
|
517
|
+
lineContent,
|
|
518
|
+
severity,
|
|
519
|
+
category: pattern.category,
|
|
520
|
+
title: pattern.name,
|
|
521
|
+
description,
|
|
522
|
+
suggestedFix: pattern.suggestedFix,
|
|
523
|
+
confidence: severity === 'info' ? 'low' : 'high',
|
|
524
|
+
layer: 2,
|
|
525
|
+
requiresAIValidation: severity !== 'info' && severity !== 'low',
|
|
526
|
+
})
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return vulnerabilities
|
|
531
|
+
}
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Vulnerability, VulnerabilitySeverity } from '../types'
|
|
7
|
+
import type { ParsedFile } from '../utils/parsed-file'
|
|
8
|
+
import { isScannerOrFixtureFile } from '../utils/context-helpers'
|
|
7
9
|
|
|
8
10
|
interface RiskyPackage {
|
|
9
11
|
name: string
|
|
@@ -151,10 +153,15 @@ function isComment(line: string): boolean {
|
|
|
151
153
|
|
|
152
154
|
export function detectRiskyImports(
|
|
153
155
|
content: string,
|
|
154
|
-
filePath: string
|
|
156
|
+
filePath: string,
|
|
157
|
+
options?: { parsed?: ParsedFile }
|
|
155
158
|
): Vulnerability[] {
|
|
156
159
|
const vulnerabilities: Vulnerability[] = []
|
|
157
|
-
|
|
160
|
+
|
|
161
|
+
// Skip scanner/fixture files to avoid self-detection
|
|
162
|
+
if (isScannerOrFixtureFile(filePath)) return vulnerabilities
|
|
163
|
+
|
|
164
|
+
const lines = options?.parsed?.lines ?? content.split('\n')
|
|
158
165
|
|
|
159
166
|
lines.forEach((line, index) => {
|
|
160
167
|
// Skip comment lines
|
package/src/layer2/variables.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { Vulnerability, SensitiveVariablePattern } from '../types'
|
|
7
|
+
import type { ParsedFile } from '../utils/parsed-file'
|
|
8
|
+
import { isScannerOrFixtureFile } from '../utils/context-helpers'
|
|
7
9
|
|
|
8
10
|
// Patterns for sensitive variable names
|
|
9
11
|
export const SENSITIVE_VARIABLE_PATTERNS: SensitiveVariablePattern[] = [
|
|
@@ -119,10 +121,15 @@ function isTypeDefinition(line: string): boolean {
|
|
|
119
121
|
|
|
120
122
|
export function detectSensitiveVariables(
|
|
121
123
|
content: string,
|
|
122
|
-
filePath: string
|
|
124
|
+
filePath: string,
|
|
125
|
+
options?: { parsed?: ParsedFile }
|
|
123
126
|
): Vulnerability[] {
|
|
124
127
|
const vulnerabilities: Vulnerability[] = []
|
|
125
|
-
|
|
128
|
+
|
|
129
|
+
// Skip scanner/fixture files to avoid self-detection
|
|
130
|
+
if (isScannerOrFixtureFile(filePath)) return vulnerabilities
|
|
131
|
+
|
|
132
|
+
const lines = options?.parsed?.lines ?? content.split('\n')
|
|
126
133
|
|
|
127
134
|
lines.forEach((line, index) => {
|
|
128
135
|
// Skip comments
|