@oculum/scanner 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/baseline/diff.d.ts +32 -0
- package/dist/baseline/diff.d.ts.map +1 -0
- package/dist/baseline/diff.js +119 -0
- package/dist/baseline/diff.js.map +1 -0
- package/dist/baseline/index.d.ts +9 -0
- package/dist/baseline/index.d.ts.map +1 -0
- package/dist/baseline/index.js +19 -0
- package/dist/baseline/index.js.map +1 -0
- package/dist/baseline/manager.d.ts +67 -0
- package/dist/baseline/manager.d.ts.map +1 -0
- package/dist/baseline/manager.js +180 -0
- package/dist/baseline/manager.js.map +1 -0
- package/dist/baseline/types.d.ts +91 -0
- package/dist/baseline/types.d.ts.map +1 -0
- package/dist/baseline/types.js +12 -0
- package/dist/baseline/types.js.map +1 -0
- package/dist/formatters/cli-terminal.d.ts +38 -0
- package/dist/formatters/cli-terminal.d.ts.map +1 -1
- package/dist/formatters/cli-terminal.js +365 -42
- package/dist/formatters/cli-terminal.js.map +1 -1
- package/dist/formatters/github-comment.d.ts +1 -1
- package/dist/formatters/github-comment.d.ts.map +1 -1
- package/dist/formatters/github-comment.js +75 -11
- package/dist/formatters/github-comment.js.map +1 -1
- package/dist/formatters/index.d.ts +1 -1
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +4 -1
- package/dist/formatters/index.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +155 -16
- package/dist/index.js.map +1 -1
- package/dist/layer1/config-audit.d.ts.map +1 -1
- package/dist/layer1/config-audit.js +20 -3
- package/dist/layer1/config-audit.js.map +1 -1
- package/dist/layer1/config-mcp-audit.d.ts +20 -0
- package/dist/layer1/config-mcp-audit.d.ts.map +1 -0
- package/dist/layer1/config-mcp-audit.js +239 -0
- package/dist/layer1/config-mcp-audit.js.map +1 -0
- package/dist/layer1/index.d.ts +1 -0
- package/dist/layer1/index.d.ts.map +1 -1
- package/dist/layer1/index.js +9 -1
- package/dist/layer1/index.js.map +1 -1
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -1
- package/dist/layer2/ai-agent-tools.js +303 -0
- package/dist/layer2/ai-agent-tools.js.map +1 -1
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -1
- package/dist/layer2/ai-endpoint-protection.js +17 -3
- package/dist/layer2/ai-endpoint-protection.js.map +1 -1
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -1
- package/dist/layer2/ai-execution-sinks.js +462 -12
- package/dist/layer2/ai-execution-sinks.js.map +1 -1
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -1
- package/dist/layer2/ai-fingerprinting.js +3 -0
- package/dist/layer2/ai-fingerprinting.js.map +1 -1
- package/dist/layer2/ai-mcp-security.d.ts +17 -0
- package/dist/layer2/ai-mcp-security.d.ts.map +1 -0
- package/dist/layer2/ai-mcp-security.js +679 -0
- package/dist/layer2/ai-mcp-security.js.map +1 -0
- package/dist/layer2/ai-package-hallucination.d.ts +19 -0
- package/dist/layer2/ai-package-hallucination.d.ts.map +1 -0
- package/dist/layer2/ai-package-hallucination.js +696 -0
- package/dist/layer2/ai-package-hallucination.js.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -1
- package/dist/layer2/ai-prompt-hygiene.js +495 -9
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -1
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -1
- package/dist/layer2/ai-rag-safety.js +372 -1
- package/dist/layer2/ai-rag-safety.js.map +1 -1
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -1
- package/dist/layer2/auth-antipatterns.js +4 -0
- package/dist/layer2/auth-antipatterns.js.map +1 -1
- package/dist/layer2/byok-patterns.d.ts.map +1 -1
- package/dist/layer2/byok-patterns.js +3 -0
- package/dist/layer2/byok-patterns.js.map +1 -1
- package/dist/layer2/dangerous-functions/child-process.d.ts +16 -0
- package/dist/layer2/dangerous-functions/child-process.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/child-process.js +74 -0
- package/dist/layer2/dangerous-functions/child-process.js.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts +29 -0
- package/dist/layer2/dangerous-functions/dom-xss.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/dom-xss.js +179 -0
- package/dist/layer2/dangerous-functions/dom-xss.js.map +1 -0
- package/dist/layer2/dangerous-functions/index.d.ts +13 -0
- package/dist/layer2/dangerous-functions/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/index.js +621 -0
- package/dist/layer2/dangerous-functions/index.js.map +1 -0
- package/dist/layer2/dangerous-functions/json-parse.d.ts +31 -0
- package/dist/layer2/dangerous-functions/json-parse.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/json-parse.js +319 -0
- package/dist/layer2/dangerous-functions/json-parse.js.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts +61 -0
- package/dist/layer2/dangerous-functions/math-random.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/math-random.js +459 -0
- package/dist/layer2/dangerous-functions/math-random.js.map +1 -0
- package/dist/layer2/dangerous-functions/patterns.d.ts +21 -0
- package/dist/layer2/dangerous-functions/patterns.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/patterns.js +161 -0
- package/dist/layer2/dangerous-functions/patterns.js.map +1 -0
- package/dist/layer2/dangerous-functions/request-validation.d.ts +13 -0
- package/dist/layer2/dangerous-functions/request-validation.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/request-validation.js +119 -0
- package/dist/layer2/dangerous-functions/request-validation.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts +23 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js +149 -0
- package/dist/layer2/dangerous-functions/utils/control-flow.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/helpers.d.ts +31 -0
- package/dist/layer2/dangerous-functions/utils/helpers.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/helpers.js +124 -0
- package/dist/layer2/dangerous-functions/utils/helpers.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/index.d.ts +9 -0
- package/dist/layer2/dangerous-functions/utils/index.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/index.js +23 -0
- package/dist/layer2/dangerous-functions/utils/index.js.map +1 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts +22 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js +89 -0
- package/dist/layer2/dangerous-functions/utils/schema-validation.js.map +1 -0
- package/dist/layer2/data-exposure.d.ts.map +1 -1
- package/dist/layer2/data-exposure.js +3 -0
- package/dist/layer2/data-exposure.js.map +1 -1
- package/dist/layer2/framework-checks.d.ts.map +1 -1
- package/dist/layer2/framework-checks.js +3 -0
- package/dist/layer2/framework-checks.js.map +1 -1
- package/dist/layer2/index.d.ts +3 -0
- package/dist/layer2/index.d.ts.map +1 -1
- package/dist/layer2/index.js +61 -2
- package/dist/layer2/index.js.map +1 -1
- package/dist/layer2/logic-gates.d.ts.map +1 -1
- package/dist/layer2/logic-gates.js +4 -0
- package/dist/layer2/logic-gates.js.map +1 -1
- package/dist/layer2/model-supply-chain.d.ts +20 -0
- package/dist/layer2/model-supply-chain.d.ts.map +1 -0
- package/dist/layer2/model-supply-chain.js +376 -0
- package/dist/layer2/model-supply-chain.js.map +1 -0
- package/dist/layer2/risky-imports.d.ts.map +1 -1
- package/dist/layer2/risky-imports.js +4 -0
- package/dist/layer2/risky-imports.js.map +1 -1
- package/dist/layer2/variables.d.ts.map +1 -1
- package/dist/layer2/variables.js +4 -0
- package/dist/layer2/variables.js.map +1 -1
- package/dist/layer3/anthropic/auto-dismiss.d.ts +24 -0
- package/dist/layer3/anthropic/auto-dismiss.d.ts.map +1 -0
- package/dist/layer3/anthropic/auto-dismiss.js +188 -0
- package/dist/layer3/anthropic/auto-dismiss.js.map +1 -0
- package/dist/layer3/anthropic/clients.d.ts +44 -0
- package/dist/layer3/anthropic/clients.d.ts.map +1 -0
- package/dist/layer3/anthropic/clients.js +81 -0
- package/dist/layer3/anthropic/clients.js.map +1 -0
- package/dist/layer3/anthropic/index.d.ts +41 -0
- package/dist/layer3/anthropic/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/index.js +141 -0
- package/dist/layer3/anthropic/index.js.map +1 -0
- package/dist/layer3/anthropic/prompts/index.d.ts +8 -0
- package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/index.js +14 -0
- package/dist/layer3/anthropic/prompts/index.js.map +1 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts +15 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.js +169 -0
- package/dist/layer3/anthropic/prompts/semantic-analysis.js.map +1 -0
- package/dist/layer3/anthropic/prompts/validation.d.ts +12 -0
- package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/validation.js +421 -0
- package/dist/layer3/anthropic/prompts/validation.js.map +1 -0
- package/dist/layer3/anthropic/providers/anthropic.d.ts +21 -0
- package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/anthropic.js +266 -0
- package/dist/layer3/anthropic/providers/anthropic.js.map +1 -0
- package/dist/layer3/anthropic/providers/index.d.ts +8 -0
- package/dist/layer3/anthropic/providers/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/index.js +15 -0
- package/dist/layer3/anthropic/providers/index.js.map +1 -0
- package/dist/layer3/anthropic/providers/openai.d.ts +18 -0
- package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -0
- package/dist/layer3/anthropic/providers/openai.js +340 -0
- package/dist/layer3/anthropic/providers/openai.js.map +1 -0
- package/dist/layer3/anthropic/request-builder.d.ts +20 -0
- package/dist/layer3/anthropic/request-builder.d.ts.map +1 -0
- package/dist/layer3/anthropic/request-builder.js +134 -0
- package/dist/layer3/anthropic/request-builder.js.map +1 -0
- package/dist/layer3/anthropic/types.d.ts +88 -0
- package/dist/layer3/anthropic/types.d.ts.map +1 -0
- package/dist/layer3/anthropic/types.js +38 -0
- package/dist/layer3/anthropic/types.js.map +1 -0
- package/dist/layer3/anthropic/utils/index.d.ts +9 -0
- package/dist/layer3/anthropic/utils/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/index.js +24 -0
- package/dist/layer3/anthropic/utils/index.js.map +1 -0
- package/dist/layer3/anthropic/utils/path-helpers.d.ts +21 -0
- package/dist/layer3/anthropic/utils/path-helpers.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/path-helpers.js +69 -0
- package/dist/layer3/anthropic/utils/path-helpers.js.map +1 -0
- package/dist/layer3/anthropic/utils/response-parser.d.ts +40 -0
- package/dist/layer3/anthropic/utils/response-parser.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/response-parser.js +285 -0
- package/dist/layer3/anthropic/utils/response-parser.js.map +1 -0
- package/dist/layer3/anthropic/utils/retry.d.ts +15 -0
- package/dist/layer3/anthropic/utils/retry.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/retry.js +62 -0
- package/dist/layer3/anthropic/utils/retry.js.map +1 -0
- package/dist/layer3/index.d.ts +1 -0
- package/dist/layer3/index.d.ts.map +1 -1
- package/dist/layer3/index.js +16 -6
- package/dist/layer3/index.js.map +1 -1
- package/dist/layer3/osv-check.d.ts +75 -0
- package/dist/layer3/osv-check.d.ts.map +1 -0
- package/dist/layer3/osv-check.js +308 -0
- package/dist/layer3/osv-check.js.map +1 -0
- package/dist/rules/framework-fixes.d.ts +48 -0
- package/dist/rules/framework-fixes.d.ts.map +1 -0
- package/dist/rules/framework-fixes.js +439 -0
- package/dist/rules/framework-fixes.js.map +1 -0
- package/dist/rules/index.d.ts +8 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +18 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/metadata.d.ts +43 -0
- package/dist/rules/metadata.d.ts.map +1 -0
- package/dist/rules/metadata.js +734 -0
- package/dist/rules/metadata.js.map +1 -0
- package/dist/suppression/config-loader.d.ts +74 -0
- package/dist/suppression/config-loader.d.ts.map +1 -0
- package/dist/suppression/config-loader.js +424 -0
- package/dist/suppression/config-loader.js.map +1 -0
- package/dist/suppression/hash.d.ts +48 -0
- package/dist/suppression/hash.d.ts.map +1 -0
- package/dist/suppression/hash.js +88 -0
- package/dist/suppression/hash.js.map +1 -0
- package/dist/suppression/index.d.ts +11 -0
- package/dist/suppression/index.d.ts.map +1 -0
- package/dist/suppression/index.js +39 -0
- package/dist/suppression/index.js.map +1 -0
- package/dist/suppression/inline-parser.d.ts +39 -0
- package/dist/suppression/inline-parser.d.ts.map +1 -0
- package/dist/suppression/inline-parser.js +218 -0
- package/dist/suppression/inline-parser.js.map +1 -0
- package/dist/suppression/manager.d.ts +94 -0
- package/dist/suppression/manager.d.ts.map +1 -0
- package/dist/suppression/manager.js +292 -0
- package/dist/suppression/manager.js.map +1 -0
- package/dist/suppression/types.d.ts +151 -0
- package/dist/suppression/types.d.ts.map +1 -0
- package/dist/suppression/types.js +28 -0
- package/dist/suppression/types.js.map +1 -0
- package/dist/tiers.d.ts +1 -1
- package/dist/tiers.d.ts.map +1 -1
- package/dist/tiers.js +27 -0
- package/dist/tiers.js.map +1 -1
- package/dist/types.d.ts +62 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/context-helpers.d.ts +4 -0
- package/dist/utils/context-helpers.d.ts.map +1 -1
- package/dist/utils/context-helpers.js +13 -9
- package/dist/utils/context-helpers.js.map +1 -1
- package/package.json +4 -2
- package/src/__tests__/benchmark/fixtures/layer1/mcp-config-audit.json +31 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +1489 -82
- package/src/__tests__/benchmark/fixtures/layer2/ai-mcp-security.ts +495 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-package-hallucination.ts +255 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +300 -1
- package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +139 -0
- package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +7 -0
- package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +63 -0
- package/src/__tests__/benchmark/fixtures/layer2/excessive-agency.ts +221 -0
- package/src/__tests__/benchmark/fixtures/layer2/index.ts +18 -0
- package/src/__tests__/benchmark/fixtures/layer2/model-supply-chain.ts +204 -0
- package/src/__tests__/benchmark/fixtures/layer2/phase1-enhancements.ts +157 -0
- package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +758 -0
- package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +503 -0
- package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +321 -0
- package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +439 -0
- package/src/baseline/__tests__/diff.test.ts +261 -0
- package/src/baseline/__tests__/manager.test.ts +225 -0
- package/src/baseline/diff.ts +135 -0
- package/src/baseline/index.ts +29 -0
- package/src/baseline/manager.ts +230 -0
- package/src/baseline/types.ts +97 -0
- package/src/formatters/cli-terminal.ts +444 -41
- package/src/formatters/github-comment.ts +79 -11
- package/src/formatters/index.ts +4 -0
- package/src/index.ts +197 -14
- package/src/layer1/config-audit.ts +24 -3
- package/src/layer1/config-mcp-audit.ts +276 -0
- package/src/layer1/index.ts +16 -6
- package/src/layer2/ai-agent-tools.ts +336 -0
- package/src/layer2/ai-endpoint-protection.ts +16 -3
- package/src/layer2/ai-execution-sinks.ts +516 -12
- package/src/layer2/ai-fingerprinting.ts +5 -1
- package/src/layer2/ai-mcp-security.ts +730 -0
- package/src/layer2/ai-package-hallucination.ts +791 -0
- package/src/layer2/ai-prompt-hygiene.ts +547 -9
- package/src/layer2/ai-rag-safety.ts +382 -3
- package/src/layer2/auth-antipatterns.ts +5 -0
- package/src/layer2/byok-patterns.ts +5 -1
- package/src/layer2/dangerous-functions/child-process.ts +98 -0
- package/src/layer2/dangerous-functions/dom-xss.ts +220 -0
- package/src/layer2/dangerous-functions/index.ts +949 -0
- package/src/layer2/dangerous-functions/json-parse.ts +385 -0
- package/src/layer2/dangerous-functions/math-random.ts +537 -0
- package/src/layer2/dangerous-functions/patterns.ts +174 -0
- package/src/layer2/dangerous-functions/request-validation.ts +145 -0
- package/src/layer2/dangerous-functions/utils/control-flow.ts +162 -0
- package/src/layer2/dangerous-functions/utils/helpers.ts +170 -0
- package/src/layer2/dangerous-functions/utils/index.ts +25 -0
- package/src/layer2/dangerous-functions/utils/schema-validation.ts +91 -0
- package/src/layer2/data-exposure.ts +5 -1
- package/src/layer2/framework-checks.ts +5 -0
- package/src/layer2/index.ts +63 -1
- package/src/layer2/logic-gates.ts +5 -0
- package/src/layer2/model-supply-chain.ts +456 -0
- package/src/layer2/risky-imports.ts +5 -0
- package/src/layer2/variables.ts +5 -0
- package/src/layer3/__tests__/osv-check.test.ts +384 -0
- package/src/layer3/anthropic/auto-dismiss.ts +212 -0
- package/src/layer3/anthropic/clients.ts +84 -0
- package/src/layer3/anthropic/index.ts +170 -0
- package/src/layer3/anthropic/prompts/index.ts +14 -0
- package/src/layer3/anthropic/prompts/semantic-analysis.ts +173 -0
- package/src/layer3/anthropic/prompts/validation.ts +419 -0
- package/src/layer3/anthropic/providers/anthropic.ts +310 -0
- package/src/layer3/anthropic/providers/index.ts +8 -0
- package/src/layer3/anthropic/providers/openai.ts +384 -0
- package/src/layer3/anthropic/request-builder.ts +150 -0
- package/src/layer3/anthropic/types.ts +148 -0
- package/src/layer3/anthropic/utils/index.ts +26 -0
- package/src/layer3/anthropic/utils/path-helpers.ts +68 -0
- package/src/layer3/anthropic/utils/response-parser.ts +322 -0
- package/src/layer3/anthropic/utils/retry.ts +75 -0
- package/src/layer3/index.ts +18 -5
- package/src/layer3/osv-check.ts +420 -0
- package/src/rules/__tests__/framework-fixes.test.ts +689 -0
- package/src/rules/__tests__/metadata.test.ts +218 -0
- package/src/rules/framework-fixes.ts +470 -0
- package/src/rules/index.ts +21 -0
- package/src/rules/metadata.ts +831 -0
- package/src/suppression/__tests__/config-loader.test.ts +382 -0
- package/src/suppression/__tests__/hash.test.ts +166 -0
- package/src/suppression/__tests__/inline-parser.test.ts +212 -0
- package/src/suppression/__tests__/manager.test.ts +415 -0
- package/src/suppression/config-loader.ts +462 -0
- package/src/suppression/hash.ts +95 -0
- package/src/suppression/index.ts +51 -0
- package/src/suppression/inline-parser.ts +273 -0
- package/src/suppression/manager.ts +379 -0
- package/src/suppression/types.ts +174 -0
- package/src/tiers.ts +36 -0
- package/src/types.ts +90 -0
- package/src/utils/context-helpers.ts +13 -9
- package/dist/layer2/dangerous-functions.d.ts +0 -7
- package/dist/layer2/dangerous-functions.d.ts.map +0 -1
- package/dist/layer2/dangerous-functions.js +0 -1701
- package/dist/layer2/dangerous-functions.js.map +0 -1
- package/dist/layer3/anthropic.d.ts +0 -87
- package/dist/layer3/anthropic.d.ts.map +0 -1
- package/dist/layer3/anthropic.js +0 -1948
- package/dist/layer3/anthropic.js.map +0 -1
- package/dist/layer3/openai.d.ts +0 -25
- package/dist/layer3/openai.d.ts.map +0 -1
- package/dist/layer3/openai.js +0 -238
- package/dist/layer3/openai.js.map +0 -1
- package/src/layer2/dangerous-functions.ts +0 -1940
- package/src/layer3/anthropic.ts +0 -2257
|
@@ -86,6 +86,145 @@ export async function chromaUnscopedQuery(query: string) {
|
|
|
86
86
|
})
|
|
87
87
|
return results
|
|
88
88
|
}
|
|
89
|
+
`,
|
|
90
|
+
language: 'typescript',
|
|
91
|
+
size: 1800,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
// AI Detection Roadmap Phase 1: Enhanced RAG Detection
|
|
95
|
+
{
|
|
96
|
+
name: 'RAG Safety - Corpus Poisoning True Positives',
|
|
97
|
+
expectFindings: true,
|
|
98
|
+
expectedCategories: ['ai_rag_corpus_poisoning'],
|
|
99
|
+
description: 'User uploads directly embedded without sanitization',
|
|
100
|
+
file: {
|
|
101
|
+
path: 'src/api/rag/upload-handler.ts',
|
|
102
|
+
content: `
|
|
103
|
+
import { Pinecone } from '@pinecone-database/pinecone'
|
|
104
|
+
import { OpenAI } from 'openai'
|
|
105
|
+
|
|
106
|
+
const pinecone = new Pinecone()
|
|
107
|
+
const openai = new OpenAI()
|
|
108
|
+
|
|
109
|
+
// Dangerous: User upload embedded directly without sanitization
|
|
110
|
+
export async function handleDocumentUpload(req: Request) {
|
|
111
|
+
const { userId, document } = await req.json()
|
|
112
|
+
|
|
113
|
+
// Direct embedding of user content - no sanitization!
|
|
114
|
+
const embedding = await openai.embeddings.create({
|
|
115
|
+
model: 'text-embedding-3-small',
|
|
116
|
+
input: document.content // Raw user content embedded
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const index = pinecone.index('knowledge-base')
|
|
120
|
+
await index.upsert([{
|
|
121
|
+
id: document.id,
|
|
122
|
+
values: embedding.data[0].embedding,
|
|
123
|
+
metadata: { userId, content: document.content }
|
|
124
|
+
}])
|
|
125
|
+
|
|
126
|
+
return { success: true }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Dangerous: External content fetched and embedded without validation
|
|
130
|
+
export async function indexExternalContent(url: string) {
|
|
131
|
+
const response = await fetch(url)
|
|
132
|
+
const content = await response.text()
|
|
133
|
+
|
|
134
|
+
// External content embedded without sanitization
|
|
135
|
+
const embedding = await createEmbedding(content)
|
|
136
|
+
await vectorStore.addDocument({ content, embedding })
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Dangerous: User-provided PDF indexed without scanning
|
|
140
|
+
export async function indexUploadedPdf(pdfBuffer: Buffer, userId: string) {
|
|
141
|
+
const pdfText = await pdfParser.parse(pdfBuffer)
|
|
142
|
+
|
|
143
|
+
// PDF content could contain injection instructions
|
|
144
|
+
await addToCorpus(pdfText, { userId })
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Dangerous: Slack messages indexed without filtering
|
|
148
|
+
export async function indexSlackChannel(channelId: string) {
|
|
149
|
+
const messages = await slack.getMessages(channelId)
|
|
150
|
+
|
|
151
|
+
// User messages embedded - could contain injections
|
|
152
|
+
for (const msg of messages) {
|
|
153
|
+
await embedDocument(msg.text, { author: msg.user })
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
`,
|
|
157
|
+
language: 'typescript',
|
|
158
|
+
size: 1500,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'RAG Safety - PII Leakage True Positives',
|
|
163
|
+
expectFindings: true,
|
|
164
|
+
expectedCategories: ['ai_rag_pii_leakage'],
|
|
165
|
+
description: 'PII fields exposed in RAG retrieval responses',
|
|
166
|
+
file: {
|
|
167
|
+
path: 'src/api/rag/search-handler.ts',
|
|
168
|
+
content: `
|
|
169
|
+
import { VectorStore } from 'langchain/vectorstores'
|
|
170
|
+
|
|
171
|
+
// Dangerous: PII fields in embedded documents
|
|
172
|
+
export async function indexUserProfile(user: User) {
|
|
173
|
+
const doc = {
|
|
174
|
+
content: user.bio,
|
|
175
|
+
metadata: {
|
|
176
|
+
userId: user.id,
|
|
177
|
+
email: user.email, // PII in metadata!
|
|
178
|
+
ssn: user.ssn, // Highly sensitive PII!
|
|
179
|
+
phoneNumber: user.phone, // PII in metadata!
|
|
180
|
+
fullName: user.fullName, // PII in metadata!
|
|
181
|
+
dateOfBirth: user.dob, // PII in metadata!
|
|
182
|
+
address: user.homeAddress, // PII in metadata!
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
await vectorStore.addDocument(doc)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Dangerous: Returning PII in retrieval response
|
|
190
|
+
export async function searchDocuments(query: string) {
|
|
191
|
+
const results = await vectorStore.similaritySearch(query)
|
|
192
|
+
|
|
193
|
+
// Returning full metadata including PII
|
|
194
|
+
return results.map(doc => ({
|
|
195
|
+
content: doc.pageContent,
|
|
196
|
+
email: doc.metadata.email, // PII in response!
|
|
197
|
+
phone: doc.metadata.phone, // PII in response!
|
|
198
|
+
ssn: doc.metadata.ssn, // PII in response!
|
|
199
|
+
author: doc.metadata.fullName, // PII in response!
|
|
200
|
+
}))
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Dangerous: Medical records embedded with patient info
|
|
204
|
+
export async function indexMedicalRecords(records: MedicalRecord[]) {
|
|
205
|
+
for (const record of records) {
|
|
206
|
+
await embedDocument(record.diagnosis, {
|
|
207
|
+
patientName: record.patientName, // PHI!
|
|
208
|
+
patientDob: record.dateOfBirth, // PHI!
|
|
209
|
+
patientSsn: record.ssn, // PHI!
|
|
210
|
+
insuranceId: record.insuranceNumber // PHI!
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Dangerous: Credit card info in indexed data
|
|
216
|
+
export async function indexTransactions(transactions: Transaction[]) {
|
|
217
|
+
for (const tx of transactions) {
|
|
218
|
+
await vectorStore.addDocument({
|
|
219
|
+
content: tx.description,
|
|
220
|
+
metadata: {
|
|
221
|
+
cardNumber: tx.cardLastFour, // PCI data!
|
|
222
|
+
cvv: tx.cvv, // PCI data - CRITICAL!
|
|
223
|
+
accountNumber: tx.accountNum, // Financial PII!
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
}
|
|
89
228
|
`,
|
|
90
229
|
language: 'typescript',
|
|
91
230
|
size: 1800,
|
|
@@ -67,6 +67,13 @@ export async function POST(request: Request) {
|
|
|
67
67
|
name: 'BYOK - False Negatives',
|
|
68
68
|
expectFindings: false,
|
|
69
69
|
description: 'Secure BYOK patterns that should NOT be flagged',
|
|
70
|
+
allowedInfoFindings: [
|
|
71
|
+
{
|
|
72
|
+
category: 'ai_endpoint_unprotected',
|
|
73
|
+
maxCount: 1,
|
|
74
|
+
reason: 'BYOK with auth but missing rate limiting is info-level (user pays for own usage)',
|
|
75
|
+
},
|
|
76
|
+
],
|
|
70
77
|
file: {
|
|
71
78
|
path: 'src/api/ai/byok-transient.ts',
|
|
72
79
|
content: `
|
|
@@ -83,6 +83,69 @@ export async function handleError(error: any) {
|
|
|
83
83
|
size: 1400,
|
|
84
84
|
},
|
|
85
85
|
},
|
|
86
|
+
{
|
|
87
|
+
name: 'Data Exposure - More Response Patterns',
|
|
88
|
+
expectFindings: true,
|
|
89
|
+
expectedCategories: ['data_exposure'],
|
|
90
|
+
description: 'Additional data exposure patterns in responses that MUST be detected',
|
|
91
|
+
file: {
|
|
92
|
+
path: 'src/api/admin/route.ts',
|
|
93
|
+
content: `
|
|
94
|
+
import { NextResponse } from 'next/server'
|
|
95
|
+
|
|
96
|
+
// Full error object in response - HIGH
|
|
97
|
+
export async function fetchResource() {
|
|
98
|
+
try {
|
|
99
|
+
const data = await db.resource.findMany()
|
|
100
|
+
return NextResponse.json(data)
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return NextResponse.json(error) // Full error object exposed!
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Error spread in response - HIGH
|
|
107
|
+
export async function updateResource() {
|
|
108
|
+
try {
|
|
109
|
+
await db.resource.update({ where: { id: 1 }, data: {} })
|
|
110
|
+
} catch (err) {
|
|
111
|
+
return NextResponse.json({ ...err, timestamp: Date.now() }) // Error spread!
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Stack trace in API response - HIGH
|
|
116
|
+
export async function processRequest() {
|
|
117
|
+
try {
|
|
118
|
+
await doSomething()
|
|
119
|
+
} catch (error) {
|
|
120
|
+
return NextResponse.json({
|
|
121
|
+
error: error.stack, // Stack trace exposed!
|
|
122
|
+
details: 'Internal error'
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Detailed error info in response - MEDIUM
|
|
128
|
+
export async function handleWebhook() {
|
|
129
|
+
try {
|
|
130
|
+
await processWebhook()
|
|
131
|
+
} catch (err) {
|
|
132
|
+
return res.status(500).json({
|
|
133
|
+
details: err.details, // Internal details exposed
|
|
134
|
+
internal: err.message
|
|
135
|
+
})
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// User object in response (may contain sensitive fields) - LOW
|
|
140
|
+
export async function getProfile(userId: string) {
|
|
141
|
+
const user = await db.user.findUnique({ where: { id: userId } })
|
|
142
|
+
return NextResponse.json({ user }) // May expose sensitive fields
|
|
143
|
+
}
|
|
144
|
+
`,
|
|
145
|
+
language: 'typescript',
|
|
146
|
+
size: 1100,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
86
149
|
],
|
|
87
150
|
|
|
88
151
|
falseNegatives: [
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Excessive Agency Test Fixtures
|
|
3
|
+
* Tests for detecting unbounded agent autonomy risks
|
|
4
|
+
* AI Detection Roadmap Phase 2
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TestGroup } from '../../types'
|
|
8
|
+
|
|
9
|
+
export const excessiveAgencyTests: TestGroup = {
|
|
10
|
+
name: 'Excessive Agency Detection',
|
|
11
|
+
tier: 'A',
|
|
12
|
+
layer: 2,
|
|
13
|
+
description: 'Detection of unbounded AI agent execution and missing human oversight',
|
|
14
|
+
|
|
15
|
+
truePositives: [
|
|
16
|
+
{
|
|
17
|
+
name: 'Excessive Agency - Unbounded Loops',
|
|
18
|
+
expectFindings: true,
|
|
19
|
+
expectedCategories: ['ai_excessive_agency'],
|
|
20
|
+
description: 'Unbounded agent execution patterns that MUST be detected',
|
|
21
|
+
file: {
|
|
22
|
+
path: 'src/agents/unbounded-agent.ts',
|
|
23
|
+
content: `
|
|
24
|
+
import { Agent } from '@langchain/agents'
|
|
25
|
+
|
|
26
|
+
// Unbounded while loop - HIGH
|
|
27
|
+
async function runAgentForever(agent: Agent) {
|
|
28
|
+
while (true) {
|
|
29
|
+
await agent.step() // No termination condition!
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// No iteration limit - HIGH
|
|
34
|
+
const agentConfig = {
|
|
35
|
+
maxIterations: -1, // Unbounded!
|
|
36
|
+
tools: [searchTool, calculatorTool],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Timeout disabled - MEDIUM
|
|
40
|
+
const runtimeConfig = {
|
|
41
|
+
timeout: 0, // No timeout!
|
|
42
|
+
maxIterations: 10,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Another timeout disabled pattern - MEDIUM
|
|
46
|
+
const config2 = {
|
|
47
|
+
timeout: null, // Disabled
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Auto-approve without oversight - HIGH
|
|
51
|
+
const autoAgent = {
|
|
52
|
+
autoApprove: true, // No human review!
|
|
53
|
+
tools: [deleteFileTool, sendEmailTool],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Human-in-loop disabled - MEDIUM
|
|
57
|
+
const unsupervisedAgent = {
|
|
58
|
+
humanInLoop: false, // Explicitly disabled
|
|
59
|
+
capabilities: ['code_execution', 'file_write'],
|
|
60
|
+
}
|
|
61
|
+
`,
|
|
62
|
+
language: 'typescript',
|
|
63
|
+
size: 700,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'Excessive Agency - CrewAI Unsafe',
|
|
68
|
+
expectFindings: true,
|
|
69
|
+
expectedCategories: ['ai_excessive_agency'],
|
|
70
|
+
description: 'CrewAI unsafe code execution patterns that MUST be detected',
|
|
71
|
+
file: {
|
|
72
|
+
path: 'src/agents/crewai-unsafe.py',
|
|
73
|
+
content: `
|
|
74
|
+
from crewai import Agent, Task, Crew
|
|
75
|
+
|
|
76
|
+
# CrewAI unsafe code execution mode - CRITICAL
|
|
77
|
+
agent = Agent(
|
|
78
|
+
role="Code Executor",
|
|
79
|
+
goal="Execute user code",
|
|
80
|
+
code_execution_mode="unsafe" # CRITICAL: No sandboxing!
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# CrewAI code execution without Docker - HIGH
|
|
84
|
+
dangerous_agent = Agent(
|
|
85
|
+
role="Script Runner",
|
|
86
|
+
allow_code_execution=True, # HIGH: Runs on host without Docker
|
|
87
|
+
code_execution_config={
|
|
88
|
+
"timeout": 60,
|
|
89
|
+
}
|
|
90
|
+
)
|
|
91
|
+
`,
|
|
92
|
+
language: 'python',
|
|
93
|
+
size: 400,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'Excessive Agency - AutoGen Unsafe',
|
|
98
|
+
expectFindings: true,
|
|
99
|
+
expectedCategories: ['ai_excessive_agency'],
|
|
100
|
+
description: 'AutoGen unsafe patterns that MUST be detected',
|
|
101
|
+
file: {
|
|
102
|
+
path: 'src/agents/autogen-unsafe.py',
|
|
103
|
+
content: `
|
|
104
|
+
from autogen import UserProxyAgent, AssistantAgent
|
|
105
|
+
from autogen.code_utils import LocalCommandLineCodeExecutor
|
|
106
|
+
|
|
107
|
+
# AutoGen use_docker=False - CRITICAL
|
|
108
|
+
code_executor = LocalCommandLineCodeExecutor(
|
|
109
|
+
work_dir="workspace",
|
|
110
|
+
use_docker=False # CRITICAL: Executes directly on host!
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Another use_docker=False pattern
|
|
114
|
+
config = {
|
|
115
|
+
"code_execution_config": {
|
|
116
|
+
"use_docker": False, # CRITICAL
|
|
117
|
+
"work_dir": "workspace",
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# AutoGen NEVER human input - HIGH
|
|
122
|
+
proxy = UserProxyAgent(
|
|
123
|
+
name="user_proxy",
|
|
124
|
+
human_input_mode="NEVER", # HIGH: No human oversight
|
|
125
|
+
code_execution_config=config,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# UserProxyAgent without reply limit - MEDIUM
|
|
129
|
+
unlimited_proxy = UserProxyAgent(
|
|
130
|
+
name="unlimited_proxy",
|
|
131
|
+
human_input_mode="TERMINATE",
|
|
132
|
+
# Missing max_consecutive_auto_reply!
|
|
133
|
+
)
|
|
134
|
+
`,
|
|
135
|
+
language: 'python',
|
|
136
|
+
size: 600,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
|
|
141
|
+
falseNegatives: [
|
|
142
|
+
{
|
|
143
|
+
name: 'Excessive Agency - Safe Patterns',
|
|
144
|
+
expectFindings: false,
|
|
145
|
+
description: 'Safe agent patterns with proper limits that should NOT be flagged',
|
|
146
|
+
file: {
|
|
147
|
+
path: 'src/agents/safe-agent.ts',
|
|
148
|
+
content: `
|
|
149
|
+
import { Agent } from '@langchain/agents'
|
|
150
|
+
|
|
151
|
+
// Bounded iteration loop - SAFE
|
|
152
|
+
async function runAgentWithLimits(agent: Agent) {
|
|
153
|
+
const maxIterations = 10
|
|
154
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
155
|
+
const result = await agent.step()
|
|
156
|
+
if (result.done) break
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Proper configuration - SAFE
|
|
161
|
+
const safeConfig = {
|
|
162
|
+
maxIterations: 15, // Bounded
|
|
163
|
+
timeout: 300000, // 5 minute timeout
|
|
164
|
+
humanInLoop: true, // Human oversight
|
|
165
|
+
budgetLimit: 100, // Cost limit
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Human approval required - SAFE
|
|
169
|
+
const reviewedAgent = {
|
|
170
|
+
requireApproval: true,
|
|
171
|
+
tools: [searchTool],
|
|
172
|
+
}
|
|
173
|
+
`,
|
|
174
|
+
language: 'typescript',
|
|
175
|
+
size: 500,
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'Excessive Agency - Safe CrewAI/AutoGen',
|
|
180
|
+
expectFindings: false,
|
|
181
|
+
description: 'Safe CrewAI and AutoGen patterns that should NOT be flagged',
|
|
182
|
+
file: {
|
|
183
|
+
path: 'src/agents/safe-frameworks.py',
|
|
184
|
+
content: `
|
|
185
|
+
from crewai import Agent
|
|
186
|
+
from autogen import UserProxyAgent
|
|
187
|
+
from autogen.code_utils import DockerCommandLineCodeExecutor
|
|
188
|
+
|
|
189
|
+
# CrewAI with safe mode - SAFE
|
|
190
|
+
safe_crewai_agent = Agent(
|
|
191
|
+
role="Safe Executor",
|
|
192
|
+
code_execution_mode="safe" # SAFE: Sandboxed execution
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# AutoGen with Docker - SAFE
|
|
196
|
+
docker_executor = DockerCommandLineCodeExecutor(
|
|
197
|
+
image="python:3.10-slim",
|
|
198
|
+
timeout=60,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
# AutoGen with human input - SAFE
|
|
202
|
+
supervised_proxy = UserProxyAgent(
|
|
203
|
+
name="supervised",
|
|
204
|
+
human_input_mode="ALWAYS", # SAFE: Human oversight
|
|
205
|
+
max_consecutive_auto_reply=5, # SAFE: Limited replies
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Config with Docker enabled - SAFE
|
|
209
|
+
safe_config = {
|
|
210
|
+
"code_execution_config": {
|
|
211
|
+
"use_docker": True, # SAFE
|
|
212
|
+
"timeout": 60,
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
`,
|
|
216
|
+
language: 'python',
|
|
217
|
+
size: 600,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
}
|
|
@@ -21,6 +21,12 @@ export { logicGatesTests } from './logic-gates'
|
|
|
21
21
|
export { sensitiveVariablesTests } from './variables'
|
|
22
22
|
export { riskyImportsTests } from './risky-imports'
|
|
23
23
|
export { frameworkChecksTests } from './framework-checks'
|
|
24
|
+
// AI Detection Roadmap Phase 1
|
|
25
|
+
export { aiPackageHallucinationTests } from './ai-package-hallucination'
|
|
26
|
+
export { aiMcpSecurityTests } from './ai-mcp-security'
|
|
27
|
+
// AI Detection Roadmap Phase 2
|
|
28
|
+
export { modelSupplyChainTests } from './model-supply-chain'
|
|
29
|
+
export { excessiveAgencyTests } from './excessive-agency'
|
|
24
30
|
|
|
25
31
|
import type { TestGroup } from '../../types'
|
|
26
32
|
import { dangerousFunctionsTests } from './dangerous-functions'
|
|
@@ -41,6 +47,12 @@ import { logicGatesTests } from './logic-gates'
|
|
|
41
47
|
import { sensitiveVariablesTests } from './variables'
|
|
42
48
|
import { riskyImportsTests } from './risky-imports'
|
|
43
49
|
import { frameworkChecksTests } from './framework-checks'
|
|
50
|
+
// AI Detection Roadmap Phase 1
|
|
51
|
+
import { aiPackageHallucinationTests } from './ai-package-hallucination'
|
|
52
|
+
import { aiMcpSecurityTests } from './ai-mcp-security'
|
|
53
|
+
// AI Detection Roadmap Phase 2
|
|
54
|
+
import { modelSupplyChainTests } from './model-supply-chain'
|
|
55
|
+
import { excessiveAgencyTests } from './excessive-agency'
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
58
|
* All Layer 2 test groups
|
|
@@ -64,4 +76,10 @@ export const layer2TestGroups: TestGroup[] = [
|
|
|
64
76
|
sensitiveVariablesTests,
|
|
65
77
|
riskyImportsTests,
|
|
66
78
|
frameworkChecksTests,
|
|
79
|
+
// AI Detection Roadmap Phase 1
|
|
80
|
+
aiPackageHallucinationTests,
|
|
81
|
+
aiMcpSecurityTests,
|
|
82
|
+
// AI Detection Roadmap Phase 2
|
|
83
|
+
modelSupplyChainTests,
|
|
84
|
+
excessiveAgencyTests,
|
|
67
85
|
]
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model Supply Chain Security Test Fixtures
|
|
3
|
+
* Tests for detecting unsafe model loading and supply chain risks
|
|
4
|
+
* AI Detection Roadmap Phase 2
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { TestGroup } from '../../types'
|
|
8
|
+
|
|
9
|
+
export const modelSupplyChainTests: TestGroup = {
|
|
10
|
+
name: 'Model Supply Chain Security',
|
|
11
|
+
tier: 'A',
|
|
12
|
+
layer: 2,
|
|
13
|
+
description: 'Detection of unsafe model loading patterns that can lead to RCE',
|
|
14
|
+
|
|
15
|
+
truePositives: [
|
|
16
|
+
{
|
|
17
|
+
name: 'Model Supply Chain - Pickle RCE',
|
|
18
|
+
expectFindings: true,
|
|
19
|
+
expectedCategories: ['ai_unsafe_model_load'],
|
|
20
|
+
description: 'Unsafe pickle/joblib deserialization that MUST be detected',
|
|
21
|
+
file: {
|
|
22
|
+
path: 'src/ml/model_loader.py',
|
|
23
|
+
content: `
|
|
24
|
+
import pickle
|
|
25
|
+
import joblib
|
|
26
|
+
import torch
|
|
27
|
+
|
|
28
|
+
# Pickle deserialization - CRITICAL RCE risk
|
|
29
|
+
def load_user_model(model_path):
|
|
30
|
+
"""Load a model file uploaded by user"""
|
|
31
|
+
with open(model_path, 'rb') as f:
|
|
32
|
+
model = pickle.load(f) # CRITICAL: Arbitrary code execution
|
|
33
|
+
return model
|
|
34
|
+
|
|
35
|
+
# Joblib deserialization - CRITICAL RCE risk
|
|
36
|
+
def load_sklearn_model(model_path):
|
|
37
|
+
"""Load sklearn model from user upload"""
|
|
38
|
+
model = joblib.load(model_path) # CRITICAL: Uses pickle internally
|
|
39
|
+
return model
|
|
40
|
+
|
|
41
|
+
# Torch.load without weights_only - HIGH
|
|
42
|
+
def load_pytorch_model(checkpoint_path):
|
|
43
|
+
"""Load PyTorch checkpoint"""
|
|
44
|
+
checkpoint = torch.load(checkpoint_path) # HIGH: Can execute code
|
|
45
|
+
return checkpoint['model']
|
|
46
|
+
`,
|
|
47
|
+
language: 'python',
|
|
48
|
+
size: 600,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: 'Model Supply Chain - Unverified Sources',
|
|
53
|
+
expectFindings: true,
|
|
54
|
+
expectedCategories: ['ai_unverified_model', 'ai_unsafe_model_load'],
|
|
55
|
+
description: 'Models loaded from untrusted sources that MUST be detected',
|
|
56
|
+
file: {
|
|
57
|
+
path: 'src/ml/download_model.py',
|
|
58
|
+
content: `
|
|
59
|
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
|
60
|
+
import urllib.request
|
|
61
|
+
|
|
62
|
+
# Model from HTTP URL - HIGH (no integrity verification)
|
|
63
|
+
def load_from_url():
|
|
64
|
+
model = AutoModelForCausalLM.from_pretrained("http://malicious-server.com/model")
|
|
65
|
+
return model
|
|
66
|
+
|
|
67
|
+
# trust_remote_code=True - HIGH (executes arbitrary code)
|
|
68
|
+
def load_with_remote_code():
|
|
69
|
+
model = AutoModelForCausalLM.from_pretrained(
|
|
70
|
+
"unknown-org/untrusted-model",
|
|
71
|
+
trust_remote_code=True # HIGH: Executes arbitrary Python
|
|
72
|
+
)
|
|
73
|
+
return model
|
|
74
|
+
|
|
75
|
+
# Model download without checksum - MEDIUM
|
|
76
|
+
def download_raw_model():
|
|
77
|
+
urllib.request.urlretrieve(
|
|
78
|
+
"https://example.com/model.pth", # No checksum verification
|
|
79
|
+
"model.pth"
|
|
80
|
+
)
|
|
81
|
+
`,
|
|
82
|
+
language: 'python',
|
|
83
|
+
size: 500,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'Model Supply Chain - Unsafe Fine-tuning',
|
|
88
|
+
expectFindings: true,
|
|
89
|
+
expectedCategories: ['ai_unsafe_finetuning'],
|
|
90
|
+
description: 'Unsafe fine-tuning on user data that MUST be detected',
|
|
91
|
+
file: {
|
|
92
|
+
path: 'src/ml/finetune.py',
|
|
93
|
+
content: `
|
|
94
|
+
from transformers import Trainer, TrainingArguments
|
|
95
|
+
|
|
96
|
+
# Training on user uploads - HIGH (data poisoning risk)
|
|
97
|
+
def finetune_on_user_data(user_uploads):
|
|
98
|
+
"""Fine-tune model on user-uploaded data"""
|
|
99
|
+
training_args = TrainingArguments(
|
|
100
|
+
output_dir="./results",
|
|
101
|
+
num_train_epochs=3,
|
|
102
|
+
)
|
|
103
|
+
trainer = Trainer(
|
|
104
|
+
model=model,
|
|
105
|
+
args=training_args,
|
|
106
|
+
train_dataset=user_uploads, # HIGH: Unvalidated user data
|
|
107
|
+
)
|
|
108
|
+
trainer.train() # Training on untrusted data
|
|
109
|
+
`,
|
|
110
|
+
language: 'python',
|
|
111
|
+
size: 400,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: 'Model Supply Chain - Unverified Downloads',
|
|
116
|
+
expectFindings: true,
|
|
117
|
+
expectedCategories: ['ai_unverified_model'],
|
|
118
|
+
description: 'Model downloads without integrity verification that MUST be detected',
|
|
119
|
+
file: {
|
|
120
|
+
path: 'src/ml/model_downloader.py',
|
|
121
|
+
content: `
|
|
122
|
+
import requests
|
|
123
|
+
import wget
|
|
124
|
+
from transformers import AutoModelForCausalLM
|
|
125
|
+
|
|
126
|
+
# Direct model download without checksum - MEDIUM
|
|
127
|
+
def download_model_unsafe():
|
|
128
|
+
# Using requests to download model file
|
|
129
|
+
response = requests.get("https://random-server.com/model.pth")
|
|
130
|
+
with open("model.pth", "wb") as f:
|
|
131
|
+
f.write(response.content) # No integrity check!
|
|
132
|
+
|
|
133
|
+
# wget without verification - MEDIUM
|
|
134
|
+
def download_with_wget():
|
|
135
|
+
wget.download("http://untrusted-host.com/model.bin", "model.bin")
|
|
136
|
+
|
|
137
|
+
# Model from HTTP (not HTTPS) - HIGH
|
|
138
|
+
def load_from_http():
|
|
139
|
+
model = AutoModelForCausalLM.from_pretrained("http://insecure-host.com/models/gpt")
|
|
140
|
+
return model
|
|
141
|
+
|
|
142
|
+
# Model from unknown Hugging Face org - MEDIUM (no integrity pin)
|
|
143
|
+
def load_unknown_model():
|
|
144
|
+
# No revision pin - could be modified by repo owner
|
|
145
|
+
model = AutoModelForCausalLM.from_pretrained("random-unknown-user/suspicious-model")
|
|
146
|
+
return model
|
|
147
|
+
`,
|
|
148
|
+
language: 'python',
|
|
149
|
+
size: 700,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
|
|
154
|
+
falseNegatives: [
|
|
155
|
+
{
|
|
156
|
+
name: 'Model Supply Chain - Safe Patterns',
|
|
157
|
+
expectFindings: false,
|
|
158
|
+
description: 'Safe model loading patterns that should NOT be flagged',
|
|
159
|
+
file: {
|
|
160
|
+
path: 'src/ml/safe_model_loader.py',
|
|
161
|
+
content: `
|
|
162
|
+
import torch
|
|
163
|
+
from safetensors.torch import load_file
|
|
164
|
+
from transformers import AutoModelForCausalLM
|
|
165
|
+
import hashlib
|
|
166
|
+
|
|
167
|
+
# SafeTensors - SAFE (no code execution)
|
|
168
|
+
def load_safe_model(model_path):
|
|
169
|
+
"""Load model using SafeTensors format"""
|
|
170
|
+
state_dict = load_file(model_path)
|
|
171
|
+
model.load_state_dict(state_dict)
|
|
172
|
+
return model
|
|
173
|
+
|
|
174
|
+
# torch.load with weights_only - SAFE
|
|
175
|
+
def load_pytorch_safe(checkpoint_path):
|
|
176
|
+
"""Load PyTorch checkpoint safely"""
|
|
177
|
+
checkpoint = torch.load(checkpoint_path, weights_only=True)
|
|
178
|
+
return checkpoint
|
|
179
|
+
|
|
180
|
+
# Model from trusted source with verification - SAFE
|
|
181
|
+
def load_from_huggingface():
|
|
182
|
+
"""Load from official Hugging Face repo with pinned revision"""
|
|
183
|
+
model = AutoModelForCausalLM.from_pretrained(
|
|
184
|
+
"meta-llama/Llama-2-7b",
|
|
185
|
+
revision="a1b2c3d4e5f6" # Pinned to specific commit
|
|
186
|
+
)
|
|
187
|
+
return model
|
|
188
|
+
|
|
189
|
+
# Model with checksum verification - SAFE
|
|
190
|
+
def load_with_verification(model_path, expected_sha256):
|
|
191
|
+
"""Load model after verifying checksum"""
|
|
192
|
+
with open(model_path, 'rb') as f:
|
|
193
|
+
file_hash = hashlib.sha256(f.read()).hexdigest()
|
|
194
|
+
if file_hash != expected_sha256:
|
|
195
|
+
raise ValueError("Checksum mismatch!")
|
|
196
|
+
# Safe to load after verification
|
|
197
|
+
return torch.load(model_path, weights_only=True)
|
|
198
|
+
`,
|
|
199
|
+
language: 'python',
|
|
200
|
+
size: 800,
|
|
201
|
+
},
|
|
202
|
+
},
|
|
203
|
+
],
|
|
204
|
+
}
|