@oculum/scanner 1.0.12 → 1.0.13
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/detect/ai-code/agent-tools.d.ts +22 -0
- package/dist/detect/ai-code/agent-tools.d.ts.map +1 -0
- package/dist/detect/ai-code/agent-tools.js +1509 -0
- package/dist/detect/ai-code/agent-tools.js.map +1 -0
- package/dist/detect/ai-code/byok-patterns.d.ts +15 -0
- package/dist/detect/ai-code/byok-patterns.d.ts.map +1 -0
- package/dist/detect/ai-code/byok-patterns.js +313 -0
- package/dist/detect/ai-code/byok-patterns.js.map +1 -0
- package/dist/detect/ai-code/endpoint-protection.d.ts +38 -0
- package/dist/detect/ai-code/endpoint-protection.d.ts.map +1 -0
- package/dist/detect/ai-code/endpoint-protection.js +349 -0
- package/dist/detect/ai-code/endpoint-protection.js.map +1 -0
- package/dist/detect/ai-code/execution-sinks.d.ts +21 -0
- package/dist/detect/ai-code/execution-sinks.d.ts.map +1 -0
- package/dist/detect/ai-code/execution-sinks.js +1158 -0
- package/dist/detect/ai-code/execution-sinks.js.map +1 -0
- package/dist/detect/ai-code/fingerprinting.d.ts +10 -0
- package/dist/detect/ai-code/fingerprinting.d.ts.map +1 -0
- package/dist/detect/ai-code/fingerprinting.js +665 -0
- package/dist/detect/ai-code/fingerprinting.js.map +1 -0
- package/dist/detect/ai-code/index.d.ts +12 -0
- package/dist/detect/ai-code/index.d.ts.map +1 -0
- package/dist/detect/ai-code/index.js +26 -0
- package/dist/detect/ai-code/index.js.map +1 -0
- package/dist/detect/ai-code/mcp-security.d.ts +20 -0
- package/dist/detect/ai-code/mcp-security.d.ts.map +1 -0
- package/dist/detect/ai-code/mcp-security.js +880 -0
- package/dist/detect/ai-code/mcp-security.js.map +1 -0
- package/dist/detect/ai-code/model-supply-chain.d.ts +23 -0
- package/dist/detect/ai-code/model-supply-chain.d.ts.map +1 -0
- package/dist/detect/ai-code/model-supply-chain.js +447 -0
- package/dist/detect/ai-code/model-supply-chain.js.map +1 -0
- package/dist/detect/ai-code/package-hallucination.d.ts +22 -0
- package/dist/detect/ai-code/package-hallucination.d.ts.map +1 -0
- package/dist/detect/ai-code/package-hallucination.js +841 -0
- package/dist/detect/ai-code/package-hallucination.js.map +1 -0
- package/dist/detect/ai-code/prompt-hygiene.d.ts +22 -0
- package/dist/detect/ai-code/prompt-hygiene.d.ts.map +1 -0
- package/dist/detect/ai-code/prompt-hygiene.js +1177 -0
- package/dist/detect/ai-code/prompt-hygiene.js.map +1 -0
- package/dist/detect/ai-code/rag-safety.d.ts +24 -0
- package/dist/detect/ai-code/rag-safety.d.ts.map +1 -0
- package/dist/detect/ai-code/rag-safety.js +913 -0
- package/dist/detect/ai-code/rag-safety.js.map +1 -0
- package/dist/detect/ai-code/schema-validation.d.ts +28 -0
- package/dist/detect/ai-code/schema-validation.d.ts.map +1 -0
- package/dist/detect/ai-code/schema-validation.js +378 -0
- package/dist/detect/ai-code/schema-validation.js.map +1 -0
- package/dist/detect/config/agent-skill-injection.d.ts +27 -0
- package/dist/detect/config/agent-skill-injection.d.ts.map +1 -0
- package/dist/detect/config/agent-skill-injection.js +472 -0
- package/dist/detect/config/agent-skill-injection.js.map +1 -0
- package/dist/detect/config/comments.d.ts +11 -0
- package/dist/detect/config/comments.d.ts.map +1 -0
- package/dist/detect/config/comments.js +206 -0
- package/dist/detect/config/comments.js.map +1 -0
- package/dist/detect/config/file-flags.d.ts +10 -0
- package/dist/detect/config/file-flags.d.ts.map +1 -0
- package/dist/detect/config/file-flags.js +124 -0
- package/dist/detect/config/file-flags.js.map +1 -0
- package/dist/detect/config/index.d.ts +7 -0
- package/dist/detect/config/index.d.ts.map +1 -0
- package/dist/detect/config/index.js +17 -0
- package/dist/detect/config/index.js.map +1 -0
- package/dist/detect/config/osv-check.d.ts +75 -0
- package/dist/detect/config/osv-check.d.ts.map +1 -0
- package/dist/detect/config/osv-check.js +309 -0
- package/dist/detect/config/osv-check.js.map +1 -0
- package/dist/detect/config/package-check.d.ts +63 -0
- package/dist/detect/config/package-check.d.ts.map +1 -0
- package/dist/detect/config/package-check.js +509 -0
- package/dist/detect/config/package-check.js.map +1 -0
- package/dist/detect/config/urls.d.ts +11 -0
- package/dist/detect/config/urls.d.ts.map +1 -0
- package/dist/detect/config/urls.js +450 -0
- package/dist/detect/config/urls.js.map +1 -0
- package/dist/detect/index.d.ts +37 -0
- package/dist/detect/index.d.ts.map +1 -0
- package/dist/detect/index.js +77 -0
- package/dist/detect/index.js.map +1 -0
- package/dist/detect/secrets/config-audit.d.ts +11 -0
- package/dist/detect/secrets/config-audit.d.ts.map +1 -0
- package/dist/detect/secrets/config-audit.js +315 -0
- package/dist/detect/secrets/config-audit.js.map +1 -0
- package/dist/detect/secrets/config-mcp-audit.d.ts +23 -0
- package/dist/detect/secrets/config-mcp-audit.d.ts.map +1 -0
- package/dist/detect/secrets/config-mcp-audit.js +243 -0
- package/dist/detect/secrets/config-mcp-audit.js.map +1 -0
- package/dist/detect/secrets/entropy.d.ts +11 -0
- package/dist/detect/secrets/entropy.d.ts.map +1 -0
- package/dist/detect/secrets/entropy.js +751 -0
- package/dist/detect/secrets/entropy.js.map +1 -0
- package/dist/detect/secrets/index.d.ts +36 -0
- package/dist/detect/secrets/index.d.ts.map +1 -0
- package/dist/detect/secrets/index.js +174 -0
- package/dist/detect/secrets/index.js.map +1 -0
- package/dist/detect/secrets/patterns.d.ts +11 -0
- package/dist/detect/secrets/patterns.d.ts.map +1 -0
- package/dist/detect/secrets/patterns.js +518 -0
- package/dist/detect/secrets/patterns.js.map +1 -0
- package/dist/detect/secrets/weak-crypto.d.ts +10 -0
- package/dist/detect/secrets/weak-crypto.d.ts.map +1 -0
- package/dist/detect/secrets/weak-crypto.js +432 -0
- package/dist/detect/secrets/weak-crypto.js.map +1 -0
- package/dist/detect/structural/auth-patterns.d.ts +22 -0
- package/dist/detect/structural/auth-patterns.d.ts.map +1 -0
- package/dist/detect/structural/auth-patterns.js +533 -0
- package/dist/detect/structural/auth-patterns.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/child-process.d.ts +16 -0
- package/dist/detect/structural/dangerous-functions/child-process.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/child-process.js +74 -0
- package/dist/detect/structural/dangerous-functions/child-process.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/dom-xss.d.ts +34 -0
- package/dist/detect/structural/dangerous-functions/dom-xss.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/dom-xss.js +230 -0
- package/dist/detect/structural/dangerous-functions/dom-xss.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/index.d.ts +16 -0
- package/dist/detect/structural/dangerous-functions/index.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/index.js +1193 -0
- package/dist/detect/structural/dangerous-functions/index.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/json-parse.d.ts +31 -0
- package/dist/detect/structural/dangerous-functions/json-parse.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/json-parse.js +326 -0
- package/dist/detect/structural/dangerous-functions/json-parse.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/math-random.d.ts +111 -0
- package/dist/detect/structural/dangerous-functions/math-random.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/math-random.js +684 -0
- package/dist/detect/structural/dangerous-functions/math-random.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/patterns.d.ts +21 -0
- package/dist/detect/structural/dangerous-functions/patterns.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/patterns.js +163 -0
- package/dist/detect/structural/dangerous-functions/patterns.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/request-validation.d.ts +13 -0
- package/dist/detect/structural/dangerous-functions/request-validation.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/request-validation.js +126 -0
- package/dist/detect/structural/dangerous-functions/request-validation.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/control-flow.d.ts +24 -0
- package/dist/detect/structural/dangerous-functions/utils/control-flow.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/control-flow.js +70 -0
- package/dist/detect/structural/dangerous-functions/utils/control-flow.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/helpers.d.ts +31 -0
- package/dist/detect/structural/dangerous-functions/utils/helpers.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/helpers.js +147 -0
- package/dist/detect/structural/dangerous-functions/utils/helpers.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/index.d.ts +9 -0
- package/dist/detect/structural/dangerous-functions/utils/index.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/index.js +23 -0
- package/dist/detect/structural/dangerous-functions/utils/index.js.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/schema-validation.d.ts +22 -0
- package/dist/detect/structural/dangerous-functions/utils/schema-validation.d.ts.map +1 -0
- package/dist/detect/structural/dangerous-functions/utils/schema-validation.js +102 -0
- package/dist/detect/structural/dangerous-functions/utils/schema-validation.js.map +1 -0
- package/dist/detect/structural/data-exposure.d.ts +19 -0
- package/dist/detect/structural/data-exposure.d.ts.map +1 -0
- package/dist/detect/structural/data-exposure.js +262 -0
- package/dist/detect/structural/data-exposure.js.map +1 -0
- package/dist/detect/structural/framework-checks.d.ts +10 -0
- package/dist/detect/structural/framework-checks.d.ts.map +1 -0
- package/dist/detect/structural/framework-checks.js +389 -0
- package/dist/detect/structural/framework-checks.js.map +1 -0
- package/dist/detect/structural/index.d.ts +71 -0
- package/dist/detect/structural/index.d.ts.map +1 -0
- package/dist/detect/structural/index.js +510 -0
- package/dist/detect/structural/index.js.map +1 -0
- package/dist/detect/structural/log-injection.d.ts +18 -0
- package/dist/detect/structural/log-injection.d.ts.map +1 -0
- package/dist/detect/structural/log-injection.js +217 -0
- package/dist/detect/structural/log-injection.js.map +1 -0
- package/dist/detect/structural/logic-gates.d.ts +10 -0
- package/dist/detect/structural/logic-gates.d.ts.map +1 -0
- package/dist/detect/structural/logic-gates.js +227 -0
- package/dist/detect/structural/logic-gates.js.map +1 -0
- package/dist/detect/structural/risky-imports.d.ts +10 -0
- package/dist/detect/structural/risky-imports.d.ts.map +1 -0
- package/dist/detect/structural/risky-imports.js +168 -0
- package/dist/detect/structural/risky-imports.js.map +1 -0
- package/dist/detect/structural/security-headers.d.ts +18 -0
- package/dist/detect/structural/security-headers.d.ts.map +1 -0
- package/dist/detect/structural/security-headers.js +196 -0
- package/dist/detect/structural/security-headers.js.map +1 -0
- package/dist/detect/structural/ssrf-detection.d.ts +18 -0
- package/dist/detect/structural/ssrf-detection.d.ts.map +1 -0
- package/dist/detect/structural/ssrf-detection.js +263 -0
- package/dist/detect/structural/ssrf-detection.js.map +1 -0
- package/dist/detect/structural/variables.d.ts +11 -0
- package/dist/detect/structural/variables.d.ts.map +1 -0
- package/dist/detect/structural/variables.js +159 -0
- package/dist/detect/structural/variables.js.map +1 -0
- package/dist/detect/structural/xxe-detection.d.ts +18 -0
- package/dist/detect/structural/xxe-detection.d.ts.map +1 -0
- package/dist/detect/structural/xxe-detection.js +245 -0
- package/dist/detect/structural/xxe-detection.js.map +1 -0
- package/dist/index.d.ts +17 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -1034
- package/dist/index.js.map +1 -1
- package/dist/layer2/framework-checks.d.ts.map +1 -1
- package/dist/layer2/framework-checks.js +1 -8
- package/dist/layer2/framework-checks.js.map +1 -1
- package/dist/layer2/index.d.ts +4 -0
- package/dist/layer2/index.d.ts.map +1 -1
- package/dist/layer2/index.js +50 -1
- package/dist/layer2/index.js.map +1 -1
- package/dist/layer2/log-injection.d.ts +18 -0
- package/dist/layer2/log-injection.d.ts.map +1 -0
- package/dist/layer2/log-injection.js +214 -0
- package/dist/layer2/log-injection.js.map +1 -0
- package/dist/layer2/security-headers.d.ts +18 -0
- package/dist/layer2/security-headers.d.ts.map +1 -0
- package/dist/layer2/security-headers.js +187 -0
- package/dist/layer2/security-headers.js.map +1 -0
- package/dist/layer2/ssrf-detection.d.ts +18 -0
- package/dist/layer2/ssrf-detection.d.ts.map +1 -0
- package/dist/layer2/ssrf-detection.js +252 -0
- package/dist/layer2/ssrf-detection.js.map +1 -0
- package/dist/layer2/xxe-detection.d.ts +18 -0
- package/dist/layer2/xxe-detection.d.ts.map +1 -0
- package/dist/layer2/xxe-detection.js +242 -0
- package/dist/layer2/xxe-detection.js.map +1 -0
- package/dist/layer3/anthropic/prompts/index.d.ts +1 -1
- package/dist/layer3/anthropic/prompts/index.d.ts.map +1 -1
- package/dist/layer3/anthropic/prompts/index.js +3 -1
- package/dist/layer3/anthropic/prompts/index.js.map +1 -1
- package/dist/layer3/anthropic/prompts/modules/ai-patterns.d.ts +19 -0
- package/dist/layer3/anthropic/prompts/modules/ai-patterns.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/ai-patterns.js +156 -0
- package/dist/layer3/anthropic/prompts/modules/ai-patterns.js.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/auth-access.d.ts +9 -0
- package/dist/layer3/anthropic/prompts/modules/auth-access.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/auth-access.js +25 -0
- package/dist/layer3/anthropic/prompts/modules/auth-access.js.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/common.d.ts +11 -0
- package/dist/layer3/anthropic/prompts/modules/common.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/common.js +152 -0
- package/dist/layer3/anthropic/prompts/modules/common.js.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/index.d.ts +54 -0
- package/dist/layer3/anthropic/prompts/modules/index.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/index.js +185 -0
- package/dist/layer3/anthropic/prompts/modules/index.js.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/owasp-classic.d.ts +8 -0
- package/dist/layer3/anthropic/prompts/modules/owasp-classic.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/owasp-classic.js +84 -0
- package/dist/layer3/anthropic/prompts/modules/owasp-classic.js.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/secrets-crypto.d.ts +8 -0
- package/dist/layer3/anthropic/prompts/modules/secrets-crypto.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/secrets-crypto.js +68 -0
- package/dist/layer3/anthropic/prompts/modules/secrets-crypto.js.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/xss-prompt.d.ts +8 -0
- package/dist/layer3/anthropic/prompts/modules/xss-prompt.d.ts.map +1 -0
- package/dist/layer3/anthropic/prompts/modules/xss-prompt.js +22 -0
- package/dist/layer3/anthropic/prompts/modules/xss-prompt.js.map +1 -0
- package/dist/layer3/anthropic/prompts/validation.d.ts +9 -3
- package/dist/layer3/anthropic/prompts/validation.d.ts.map +1 -1
- package/dist/layer3/anthropic/prompts/validation.js +14 -410
- package/dist/layer3/anthropic/prompts/validation.js.map +1 -1
- package/dist/layer3/anthropic/providers/anthropic.d.ts.map +1 -1
- package/dist/layer3/anthropic/providers/anthropic.js +6 -3
- package/dist/layer3/anthropic/providers/anthropic.js.map +1 -1
- package/dist/layer3/anthropic/providers/openai.d.ts.map +1 -1
- package/dist/layer3/anthropic/providers/openai.js +6 -3
- package/dist/layer3/anthropic/providers/openai.js.map +1 -1
- package/dist/layer3/anthropic/request-builder.d.ts +11 -4
- package/dist/layer3/anthropic/request-builder.d.ts.map +1 -1
- package/dist/layer3/anthropic/request-builder.js +32 -16
- package/dist/layer3/anthropic/request-builder.js.map +1 -1
- package/dist/layer3/anthropic/utils/context-extractor.d.ts +55 -0
- package/dist/layer3/anthropic/utils/context-extractor.d.ts.map +1 -0
- package/dist/layer3/anthropic/utils/context-extractor.js +161 -0
- package/dist/layer3/anthropic/utils/context-extractor.js.map +1 -0
- package/dist/layer3/anthropic/utils/index.d.ts +2 -0
- package/dist/layer3/anthropic/utils/index.d.ts.map +1 -1
- package/dist/layer3/anthropic/utils/index.js +4 -1
- package/dist/layer3/anthropic/utils/index.js.map +1 -1
- package/dist/model/auth-helper-detector.d.ts +56 -0
- package/dist/model/auth-helper-detector.d.ts.map +1 -0
- package/dist/model/auth-helper-detector.js +360 -0
- package/dist/model/auth-helper-detector.js.map +1 -0
- package/dist/model/cross-file-taint.d.ts +40 -0
- package/dist/model/cross-file-taint.d.ts.map +1 -0
- package/dist/model/cross-file-taint.js +290 -0
- package/dist/model/cross-file-taint.js.map +1 -0
- package/dist/model/framework-models/django.d.ts +9 -0
- package/dist/model/framework-models/django.d.ts.map +1 -0
- package/dist/model/framework-models/django.js +82 -0
- package/dist/model/framework-models/django.js.map +1 -0
- package/dist/model/framework-models/express.d.ts +9 -0
- package/dist/model/framework-models/express.d.ts.map +1 -0
- package/dist/model/framework-models/express.js +52 -0
- package/dist/model/framework-models/express.js.map +1 -0
- package/dist/model/framework-models/index.d.ts +20 -0
- package/dist/model/framework-models/index.d.ts.map +1 -0
- package/dist/model/framework-models/index.js +102 -0
- package/dist/model/framework-models/index.js.map +1 -0
- package/dist/model/framework-models/nextjs.d.ts +9 -0
- package/dist/model/framework-models/nextjs.d.ts.map +1 -0
- package/dist/model/framework-models/nextjs.js +71 -0
- package/dist/model/framework-models/nextjs.js.map +1 -0
- package/dist/model/framework-models/prisma.d.ts +10 -0
- package/dist/model/framework-models/prisma.d.ts.map +1 -0
- package/dist/model/framework-models/prisma.js +54 -0
- package/dist/model/framework-models/prisma.js.map +1 -0
- package/dist/model/framework-models/react.d.ts +9 -0
- package/dist/model/framework-models/react.d.ts.map +1 -0
- package/dist/model/framework-models/react.js +67 -0
- package/dist/model/framework-models/react.js.map +1 -0
- package/dist/model/framework-models/sequelize.d.ts +9 -0
- package/dist/model/framework-models/sequelize.d.ts.map +1 -0
- package/dist/model/framework-models/sequelize.js +62 -0
- package/dist/model/framework-models/sequelize.js.map +1 -0
- package/dist/model/framework-models/types.d.ts +43 -0
- package/dist/model/framework-models/types.d.ts.map +1 -0
- package/dist/model/framework-models/types.js +10 -0
- package/dist/model/framework-models/types.js.map +1 -0
- package/dist/model/function-classifier.d.ts +32 -0
- package/dist/model/function-classifier.d.ts.map +1 -0
- package/dist/model/function-classifier.js +143 -0
- package/dist/model/function-classifier.js.map +1 -0
- package/dist/model/import-resolver.d.ts +45 -0
- package/dist/model/import-resolver.d.ts.map +1 -0
- package/dist/model/import-resolver.js +410 -0
- package/dist/model/import-resolver.js.map +1 -0
- package/dist/model/imported-auth-detector.d.ts +38 -0
- package/dist/model/imported-auth-detector.d.ts.map +1 -0
- package/dist/model/imported-auth-detector.js +199 -0
- package/dist/model/imported-auth-detector.js.map +1 -0
- package/dist/model/index.d.ts +63 -0
- package/dist/model/index.d.ts.map +1 -0
- package/dist/model/index.js +272 -0
- package/dist/model/index.js.map +1 -0
- package/dist/model/middleware-detector.d.ts +55 -0
- package/dist/model/middleware-detector.d.ts.map +1 -0
- package/dist/model/middleware-detector.js +382 -0
- package/dist/model/middleware-detector.js.map +1 -0
- package/dist/model/module-graph.d.ts +46 -0
- package/dist/model/module-graph.d.ts.map +1 -0
- package/dist/model/module-graph.js +187 -0
- package/dist/model/module-graph.js.map +1 -0
- package/dist/model/oauth-flow-detector.d.ts +41 -0
- package/dist/model/oauth-flow-detector.d.ts.map +1 -0
- package/dist/model/oauth-flow-detector.js +202 -0
- package/dist/model/oauth-flow-detector.js.map +1 -0
- package/dist/model/project-context.d.ts +119 -0
- package/dist/model/project-context.d.ts.map +1 -0
- package/dist/model/project-context.js +534 -0
- package/dist/model/project-context.js.map +1 -0
- package/dist/model/route-auth-resolver.d.ts +27 -0
- package/dist/model/route-auth-resolver.d.ts.map +1 -0
- package/dist/model/route-auth-resolver.js +182 -0
- package/dist/model/route-auth-resolver.js.map +1 -0
- package/dist/model/route-discovery/express.d.ts +25 -0
- package/dist/model/route-discovery/express.d.ts.map +1 -0
- package/dist/model/route-discovery/express.js +225 -0
- package/dist/model/route-discovery/express.js.map +1 -0
- package/dist/model/route-discovery/index.d.ts +21 -0
- package/dist/model/route-discovery/index.d.ts.map +1 -0
- package/dist/model/route-discovery/index.js +67 -0
- package/dist/model/route-discovery/index.js.map +1 -0
- package/dist/model/route-discovery/nextjs.d.ts +16 -0
- package/dist/model/route-discovery/nextjs.d.ts.map +1 -0
- package/dist/model/route-discovery/nextjs.js +179 -0
- package/dist/model/route-discovery/nextjs.js.map +1 -0
- package/dist/model/route-discovery/python.d.ts +16 -0
- package/dist/model/route-discovery/python.d.ts.map +1 -0
- package/dist/model/route-discovery/python.js +181 -0
- package/dist/model/route-discovery/python.js.map +1 -0
- package/dist/model/route-discovery/types.d.ts +36 -0
- package/dist/model/route-discovery/types.d.ts.map +1 -0
- package/dist/model/route-discovery/types.js +16 -0
- package/dist/model/route-discovery/types.js.map +1 -0
- package/dist/model/route-discovery/utils.d.ts +18 -0
- package/dist/model/route-discovery/utils.d.ts.map +1 -0
- package/dist/model/route-discovery/utils.js +55 -0
- package/dist/model/route-discovery/utils.js.map +1 -0
- package/dist/model/route-hierarchy.d.ts +50 -0
- package/dist/model/route-hierarchy.d.ts.map +1 -0
- package/dist/model/route-hierarchy.js +226 -0
- package/dist/model/route-hierarchy.js.map +1 -0
- package/dist/model/sanitiser-detection.d.ts +27 -0
- package/dist/model/sanitiser-detection.d.ts.map +1 -0
- package/dist/model/sanitiser-detection.js +224 -0
- package/dist/model/sanitiser-detection.js.map +1 -0
- package/dist/model/sink-matcher.d.ts +17 -0
- package/dist/model/sink-matcher.d.ts.map +1 -0
- package/dist/model/sink-matcher.js +141 -0
- package/dist/model/sink-matcher.js.map +1 -0
- package/dist/model/sink-patterns.d.ts +19 -0
- package/dist/model/sink-patterns.d.ts.map +1 -0
- package/dist/model/sink-patterns.js +88 -0
- package/dist/model/sink-patterns.js.map +1 -0
- package/dist/model/source-discovery.d.ts +15 -0
- package/dist/model/source-discovery.d.ts.map +1 -0
- package/dist/model/source-discovery.js +170 -0
- package/dist/model/source-discovery.js.map +1 -0
- package/dist/model/taint-tracker.d.ts +21 -0
- package/dist/model/taint-tracker.d.ts.map +1 -0
- package/dist/model/taint-tracker.js +281 -0
- package/dist/model/taint-tracker.js.map +1 -0
- package/dist/model/taint-types.d.ts +74 -0
- package/dist/model/taint-types.d.ts.map +1 -0
- package/dist/model/taint-types.js +9 -0
- package/dist/model/taint-types.js.map +1 -0
- package/dist/model/trpc-analyzer.d.ts +78 -0
- package/dist/model/trpc-analyzer.d.ts.map +1 -0
- package/dist/model/trpc-analyzer.js +297 -0
- package/dist/model/trpc-analyzer.js.map +1 -0
- package/dist/parse/file-classifier.d.ts +228 -0
- package/dist/parse/file-classifier.d.ts.map +1 -0
- package/dist/parse/file-classifier.js +933 -0
- package/dist/parse/file-classifier.js.map +1 -0
- package/dist/parse/path-exclusions.d.ts +55 -0
- package/dist/parse/path-exclusions.d.ts.map +1 -0
- package/dist/parse/path-exclusions.js +224 -0
- package/dist/parse/path-exclusions.js.map +1 -0
- package/dist/pipeline/config.d.ts +39 -0
- package/dist/pipeline/config.d.ts.map +1 -0
- package/dist/pipeline/config.js +46 -0
- package/dist/pipeline/config.js.map +1 -0
- package/dist/pipeline/index.d.ts +34 -0
- package/dist/pipeline/index.d.ts.map +1 -0
- package/dist/pipeline/index.js +377 -0
- package/dist/pipeline/index.js.map +1 -0
- package/dist/pipeline/modes/incremental.d.ts +66 -0
- package/dist/pipeline/modes/incremental.d.ts.map +1 -0
- package/dist/pipeline/modes/incremental.js +200 -0
- package/dist/pipeline/modes/incremental.js.map +1 -0
- package/dist/postprocess/aggregation.d.ts +14 -0
- package/dist/postprocess/aggregation.d.ts.map +1 -0
- package/dist/postprocess/aggregation.js +63 -0
- package/dist/postprocess/aggregation.js.map +1 -0
- package/dist/postprocess/contradictions.d.ts +18 -0
- package/dist/postprocess/contradictions.d.ts.map +1 -0
- package/dist/postprocess/contradictions.js +99 -0
- package/dist/postprocess/contradictions.js.map +1 -0
- package/dist/postprocess/dedup.d.ts +13 -0
- package/dist/postprocess/dedup.d.ts.map +1 -0
- package/dist/postprocess/dedup.js +58 -0
- package/dist/postprocess/dedup.js.map +1 -0
- package/dist/postprocess/filtering/context-adjustments.d.ts +23 -0
- package/dist/postprocess/filtering/context-adjustments.d.ts.map +1 -0
- package/dist/postprocess/filtering/context-adjustments.js +100 -0
- package/dist/postprocess/filtering/context-adjustments.js.map +1 -0
- package/dist/postprocess/filtering/index.d.ts +3 -0
- package/dist/postprocess/filtering/index.d.ts.map +1 -0
- package/dist/postprocess/filtering/index.js +8 -0
- package/dist/postprocess/filtering/index.js.map +1 -0
- package/dist/postprocess/filtering/pipeline.d.ts +48 -0
- package/dist/postprocess/filtering/pipeline.d.ts.map +1 -0
- package/dist/postprocess/filtering/pipeline.js +76 -0
- package/dist/postprocess/filtering/pipeline.js.map +1 -0
- package/dist/postprocess/index.d.ts +41 -0
- package/dist/postprocess/index.d.ts.map +1 -0
- package/dist/postprocess/index.js +85 -0
- package/dist/postprocess/index.js.map +1 -0
- package/dist/postprocess/suppression/config-loader.d.ts +74 -0
- package/dist/postprocess/suppression/config-loader.d.ts.map +1 -0
- package/dist/postprocess/suppression/config-loader.js +424 -0
- package/dist/postprocess/suppression/config-loader.js.map +1 -0
- package/dist/postprocess/suppression/hash.d.ts +48 -0
- package/dist/postprocess/suppression/hash.d.ts.map +1 -0
- package/dist/postprocess/suppression/hash.js +88 -0
- package/dist/postprocess/suppression/hash.js.map +1 -0
- package/dist/postprocess/suppression/index.d.ts +11 -0
- package/dist/postprocess/suppression/index.d.ts.map +1 -0
- package/dist/postprocess/suppression/index.js +39 -0
- package/dist/postprocess/suppression/index.js.map +1 -0
- package/dist/postprocess/suppression/inline-parser.d.ts +39 -0
- package/dist/postprocess/suppression/inline-parser.d.ts.map +1 -0
- package/dist/postprocess/suppression/inline-parser.js +218 -0
- package/dist/postprocess/suppression/inline-parser.js.map +1 -0
- package/dist/postprocess/suppression/manager.d.ts +94 -0
- package/dist/postprocess/suppression/manager.d.ts.map +1 -0
- package/dist/postprocess/suppression/manager.js +292 -0
- package/dist/postprocess/suppression/manager.js.map +1 -0
- package/dist/postprocess/suppression/types.d.ts +151 -0
- package/dist/postprocess/suppression/types.d.ts.map +1 -0
- package/dist/postprocess/suppression/types.js +28 -0
- package/dist/postprocess/suppression/types.js.map +1 -0
- package/dist/postprocess/validation-cap.d.ts +17 -0
- package/dist/postprocess/validation-cap.d.ts.map +1 -0
- package/dist/postprocess/validation-cap.js +64 -0
- package/dist/postprocess/validation-cap.js.map +1 -0
- package/dist/report/build-result.d.ts +33 -0
- package/dist/report/build-result.d.ts.map +1 -0
- package/dist/report/build-result.js +59 -0
- package/dist/report/build-result.js.map +1 -0
- package/dist/report/enrichment.d.ts +19 -0
- package/dist/report/enrichment.d.ts.map +1 -0
- package/dist/report/enrichment.js +44 -0
- package/dist/report/enrichment.js.map +1 -0
- package/dist/report/formatters/ai-context.d.ts +23 -0
- package/dist/report/formatters/ai-context.d.ts.map +1 -0
- package/dist/report/formatters/ai-context.js +238 -0
- package/dist/report/formatters/ai-context.js.map +1 -0
- package/dist/report/formatters/cli-terminal.d.ts +65 -0
- package/dist/report/formatters/cli-terminal.d.ts.map +1 -0
- package/dist/report/formatters/cli-terminal.js +735 -0
- package/dist/report/formatters/cli-terminal.js.map +1 -0
- package/dist/report/formatters/github-comment.d.ts +41 -0
- package/dist/report/formatters/github-comment.d.ts.map +1 -0
- package/dist/report/formatters/github-comment.js +370 -0
- package/dist/report/formatters/github-comment.js.map +1 -0
- package/dist/report/formatters/grouping.d.ts +52 -0
- package/dist/report/formatters/grouping.d.ts.map +1 -0
- package/dist/report/formatters/grouping.js +152 -0
- package/dist/report/formatters/grouping.js.map +1 -0
- package/dist/report/formatters/ide/claude-code.d.ts +17 -0
- package/dist/report/formatters/ide/claude-code.d.ts.map +1 -0
- package/dist/report/formatters/ide/claude-code.js +94 -0
- package/dist/report/formatters/ide/claude-code.js.map +1 -0
- package/dist/report/formatters/ide/cursor.d.ts +13 -0
- package/dist/report/formatters/ide/cursor.d.ts.map +1 -0
- package/dist/report/formatters/ide/cursor.js +125 -0
- package/dist/report/formatters/ide/cursor.js.map +1 -0
- package/dist/report/formatters/ide/index.d.ts +62 -0
- package/dist/report/formatters/ide/index.d.ts.map +1 -0
- package/dist/report/formatters/ide/index.js +184 -0
- package/dist/report/formatters/ide/index.js.map +1 -0
- package/dist/report/formatters/ide/windsurf.d.ts +13 -0
- package/dist/report/formatters/ide/windsurf.d.ts.map +1 -0
- package/dist/report/formatters/ide/windsurf.js +117 -0
- package/dist/report/formatters/ide/windsurf.js.map +1 -0
- package/dist/report/formatters/index.d.ts +11 -0
- package/dist/report/formatters/index.d.ts.map +1 -0
- package/dist/report/formatters/index.js +54 -0
- package/dist/report/formatters/index.js.map +1 -0
- package/dist/report/formatters/vscode-diagnostic.d.ts +103 -0
- package/dist/report/formatters/vscode-diagnostic.d.ts.map +1 -0
- package/dist/report/formatters/vscode-diagnostic.js +151 -0
- package/dist/report/formatters/vscode-diagnostic.js.map +1 -0
- package/dist/report/summary.d.ts +27 -0
- package/dist/report/summary.d.ts.map +1 -0
- package/dist/report/summary.js +57 -0
- package/dist/report/summary.js.map +1 -0
- package/dist/rules/metadata.d.ts.map +1 -1
- package/dist/rules/metadata.js +66 -0
- package/dist/rules/metadata.js.map +1 -1
- package/dist/score/adjustments.d.ts +22 -0
- package/dist/score/adjustments.d.ts.map +1 -0
- package/dist/score/adjustments.js +373 -0
- package/dist/score/adjustments.js.map +1 -0
- package/dist/score/auto-dismiss.d.ts +28 -0
- package/dist/score/auto-dismiss.d.ts.map +1 -0
- package/dist/score/auto-dismiss.js +200 -0
- package/dist/score/auto-dismiss.js.map +1 -0
- package/dist/score/confidence.d.ts +19 -0
- package/dist/score/confidence.d.ts.map +1 -0
- package/dist/score/confidence.js +52 -0
- package/dist/score/confidence.js.map +1 -0
- package/dist/score/index.d.ts +61 -0
- package/dist/score/index.d.ts.map +1 -0
- package/dist/score/index.js +250 -0
- package/dist/score/index.js.map +1 -0
- package/dist/score/types.d.ts +160 -0
- package/dist/score/types.d.ts.map +1 -0
- package/dist/score/types.js +14 -0
- package/dist/score/types.js.map +1 -0
- package/dist/shared/ai-context/index.d.ts +6 -0
- package/dist/shared/ai-context/index.d.ts.map +1 -0
- package/dist/shared/ai-context/index.js +13 -0
- package/dist/shared/ai-context/index.js.map +1 -0
- package/dist/shared/ai-context/manager.d.ts +67 -0
- package/dist/shared/ai-context/manager.d.ts.map +1 -0
- package/dist/shared/ai-context/manager.js +104 -0
- package/dist/shared/ai-context/manager.js.map +1 -0
- package/dist/shared/baseline/diff.d.ts +32 -0
- package/dist/shared/baseline/diff.d.ts.map +1 -0
- package/dist/shared/baseline/diff.js +119 -0
- package/dist/shared/baseline/diff.js.map +1 -0
- package/dist/shared/baseline/index.d.ts +9 -0
- package/dist/shared/baseline/index.d.ts.map +1 -0
- package/dist/shared/baseline/index.js +19 -0
- package/dist/shared/baseline/index.js.map +1 -0
- package/dist/shared/baseline/manager.d.ts +67 -0
- package/dist/shared/baseline/manager.d.ts.map +1 -0
- package/dist/shared/baseline/manager.js +180 -0
- package/dist/shared/baseline/manager.js.map +1 -0
- package/dist/shared/baseline/types.d.ts +91 -0
- package/dist/shared/baseline/types.d.ts.map +1 -0
- package/dist/shared/baseline/types.js +12 -0
- package/dist/shared/baseline/types.js.map +1 -0
- package/dist/shared/category-filter.d.ts +125 -0
- package/dist/shared/category-filter.d.ts.map +1 -0
- package/dist/shared/category-filter.js +360 -0
- package/dist/shared/category-filter.js.map +1 -0
- package/dist/shared/code-analysis.d.ts +39 -0
- package/dist/shared/code-analysis.d.ts.map +1 -0
- package/dist/shared/code-analysis.js +159 -0
- package/dist/shared/code-analysis.js.map +1 -0
- package/dist/shared/comment-analyzer.d.ts +38 -0
- package/dist/shared/comment-analyzer.d.ts.map +1 -0
- package/dist/shared/comment-analyzer.js +218 -0
- package/dist/shared/comment-analyzer.js.map +1 -0
- package/dist/shared/diff-detector.d.ts +53 -0
- package/dist/shared/diff-detector.d.ts.map +1 -0
- package/dist/shared/diff-detector.js +104 -0
- package/dist/shared/diff-detector.js.map +1 -0
- package/dist/shared/diff-parser.d.ts +80 -0
- package/dist/shared/diff-parser.d.ts.map +1 -0
- package/dist/shared/diff-parser.js +202 -0
- package/dist/shared/diff-parser.js.map +1 -0
- package/dist/shared/environment-context.d.ts +76 -0
- package/dist/shared/environment-context.d.ts.map +1 -0
- package/dist/shared/environment-context.js +271 -0
- package/dist/shared/environment-context.js.map +1 -0
- package/dist/shared/intent-detector.d.ts +66 -0
- package/dist/shared/intent-detector.d.ts.map +1 -0
- package/dist/shared/intent-detector.js +282 -0
- package/dist/shared/intent-detector.js.map +1 -0
- package/dist/shared/parsed-file.d.ts +51 -0
- package/dist/shared/parsed-file.d.ts.map +1 -0
- package/dist/shared/parsed-file.js +95 -0
- package/dist/shared/parsed-file.js.map +1 -0
- package/dist/shared/registry-clients.d.ts +93 -0
- package/dist/shared/registry-clients.d.ts.map +1 -0
- package/dist/shared/registry-clients.js +273 -0
- package/dist/shared/registry-clients.js.map +1 -0
- package/dist/shared/rules/framework-fixes.d.ts +48 -0
- package/dist/shared/rules/framework-fixes.d.ts.map +1 -0
- package/dist/shared/rules/framework-fixes.js +439 -0
- package/dist/shared/rules/framework-fixes.js.map +1 -0
- package/dist/shared/rules/index.d.ts +8 -0
- package/dist/shared/rules/index.d.ts.map +1 -0
- package/dist/shared/rules/index.js +18 -0
- package/dist/shared/rules/index.js.map +1 -0
- package/dist/shared/rules/metadata.d.ts +43 -0
- package/dist/shared/rules/metadata.d.ts.map +1 -0
- package/dist/shared/rules/metadata.js +819 -0
- package/dist/shared/rules/metadata.js.map +1 -0
- package/dist/shared/schema-semantics.d.ts +45 -0
- package/dist/shared/schema-semantics.d.ts.map +1 -0
- package/dist/shared/schema-semantics.js +193 -0
- package/dist/shared/schema-semantics.js.map +1 -0
- package/dist/shared/types.d.ts +337 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +126 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/tiers.d.ts +2 -2
- package/dist/tiers.d.ts.map +1 -1
- package/dist/tiers.js +10 -0
- package/dist/tiers.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/validate/clients.d.ts +44 -0
- package/dist/validate/clients.d.ts.map +1 -0
- package/dist/validate/clients.js +81 -0
- package/dist/validate/clients.js.map +1 -0
- package/dist/validate/index.d.ts +41 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +141 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate/prompts/index.d.ts +8 -0
- package/dist/validate/prompts/index.d.ts.map +1 -0
- package/dist/validate/prompts/index.js +16 -0
- package/dist/validate/prompts/index.js.map +1 -0
- package/dist/validate/prompts/modules/ai-patterns.d.ts +19 -0
- package/dist/validate/prompts/modules/ai-patterns.d.ts.map +1 -0
- package/dist/validate/prompts/modules/ai-patterns.js +156 -0
- package/dist/validate/prompts/modules/ai-patterns.js.map +1 -0
- package/dist/validate/prompts/modules/auth-access.d.ts +9 -0
- package/dist/validate/prompts/modules/auth-access.d.ts.map +1 -0
- package/dist/validate/prompts/modules/auth-access.js +25 -0
- package/dist/validate/prompts/modules/auth-access.js.map +1 -0
- package/dist/validate/prompts/modules/common.d.ts +11 -0
- package/dist/validate/prompts/modules/common.d.ts.map +1 -0
- package/dist/validate/prompts/modules/common.js +186 -0
- package/dist/validate/prompts/modules/common.js.map +1 -0
- package/dist/validate/prompts/modules/index.d.ts +54 -0
- package/dist/validate/prompts/modules/index.d.ts.map +1 -0
- package/dist/validate/prompts/modules/index.js +186 -0
- package/dist/validate/prompts/modules/index.js.map +1 -0
- package/dist/validate/prompts/modules/owasp-classic.d.ts +8 -0
- package/dist/validate/prompts/modules/owasp-classic.d.ts.map +1 -0
- package/dist/validate/prompts/modules/owasp-classic.js +84 -0
- package/dist/validate/prompts/modules/owasp-classic.js.map +1 -0
- package/dist/validate/prompts/modules/secrets-crypto.d.ts +8 -0
- package/dist/validate/prompts/modules/secrets-crypto.d.ts.map +1 -0
- package/dist/validate/prompts/modules/secrets-crypto.js +68 -0
- package/dist/validate/prompts/modules/secrets-crypto.js.map +1 -0
- package/dist/validate/prompts/modules/xss-prompt.d.ts +8 -0
- package/dist/validate/prompts/modules/xss-prompt.d.ts.map +1 -0
- package/dist/validate/prompts/modules/xss-prompt.js +22 -0
- package/dist/validate/prompts/modules/xss-prompt.js.map +1 -0
- package/dist/validate/prompts/semantic-analysis.d.ts +15 -0
- package/dist/validate/prompts/semantic-analysis.d.ts.map +1 -0
- package/dist/validate/prompts/semantic-analysis.js +169 -0
- package/dist/validate/prompts/semantic-analysis.js.map +1 -0
- package/dist/validate/prompts/validation.d.ts +18 -0
- package/dist/validate/prompts/validation.d.ts.map +1 -0
- package/dist/validate/prompts/validation.js +25 -0
- package/dist/validate/prompts/validation.js.map +1 -0
- package/dist/validate/providers/anthropic.d.ts +17 -0
- package/dist/validate/providers/anthropic.d.ts.map +1 -0
- package/dist/validate/providers/anthropic.js +260 -0
- package/dist/validate/providers/anthropic.js.map +1 -0
- package/dist/validate/providers/index.d.ts +8 -0
- package/dist/validate/providers/index.d.ts.map +1 -0
- package/dist/validate/providers/index.js +13 -0
- package/dist/validate/providers/index.js.map +1 -0
- package/dist/validate/providers/openai.d.ts +14 -0
- package/dist/validate/providers/openai.d.ts.map +1 -0
- package/dist/validate/providers/openai.js +336 -0
- package/dist/validate/providers/openai.js.map +1 -0
- package/dist/validate/request-builder.d.ts +61 -0
- package/dist/validate/request-builder.d.ts.map +1 -0
- package/dist/validate/request-builder.js +346 -0
- package/dist/validate/request-builder.js.map +1 -0
- package/dist/validate/types.d.ts +88 -0
- package/dist/validate/types.d.ts.map +1 -0
- package/dist/validate/types.js +38 -0
- package/dist/validate/types.js.map +1 -0
- package/dist/validate/utils/context-extractor.d.ts +55 -0
- package/dist/validate/utils/context-extractor.d.ts.map +1 -0
- package/dist/validate/utils/context-extractor.js +161 -0
- package/dist/validate/utils/context-extractor.js.map +1 -0
- package/dist/validate/utils/index.d.ts +11 -0
- package/dist/validate/utils/index.d.ts.map +1 -0
- package/dist/validate/utils/index.js +27 -0
- package/dist/validate/utils/index.js.map +1 -0
- package/dist/validate/utils/path-helpers.d.ts +21 -0
- package/dist/validate/utils/path-helpers.d.ts.map +1 -0
- package/dist/validate/utils/path-helpers.js +69 -0
- package/dist/validate/utils/path-helpers.js.map +1 -0
- package/dist/validate/utils/response-parser.d.ts +40 -0
- package/dist/validate/utils/response-parser.d.ts.map +1 -0
- package/dist/validate/utils/response-parser.js +286 -0
- package/dist/validate/utils/response-parser.js.map +1 -0
- package/dist/validate/utils/retry.d.ts +15 -0
- package/dist/validate/utils/retry.d.ts.map +1 -0
- package/dist/validate/utils/retry.js +62 -0
- package/dist/validate/utils/retry.js.map +1 -0
- package/package.json +8 -7
- package/src/__tests__/benchmark/fixtures/layer1/agent-skill-injection.ts +204 -0
- package/src/__tests__/benchmark/fixtures/layer1/index.ts +3 -0
- package/src/__tests__/benchmark/fixtures/layer2/index.ts +15 -0
- package/src/__tests__/benchmark/fixtures/layer2/log-injection.ts +147 -0
- package/src/__tests__/benchmark/fixtures/layer2/security-headers.ts +197 -0
- package/src/__tests__/benchmark/fixtures/layer2/ssrf-detection.ts +210 -0
- package/src/__tests__/benchmark/fixtures/layer2/xxe-detection.ts +195 -0
- package/src/__tests__/benchmark/run-depth-validation.ts +3 -3
- package/src/__tests__/benchmark/run-real-world-test.ts +4 -4
- package/src/__tests__/benchmark/types.ts +1 -1
- package/src/__tests__/benchmark/utils/test-runner.ts +3 -3
- package/src/__tests__/category-filter.test.ts +2 -2
- package/src/__tests__/context-engine/cross-file-taint.test.ts +284 -0
- package/src/__tests__/context-engine/framework-models.test.ts +457 -0
- package/src/__tests__/context-engine/function-classifier.test.ts +146 -0
- package/src/__tests__/context-engine/import-resolver.test.ts +328 -0
- package/src/__tests__/context-engine/integration.test.ts +320 -0
- package/src/__tests__/context-engine/module-graph.test.ts +159 -0
- package/src/__tests__/context-engine/route-discovery/auth-resolver.test.ts +353 -0
- package/src/__tests__/context-engine/route-discovery/express.test.ts +150 -0
- package/src/__tests__/context-engine/route-discovery/nextjs.test.ts +138 -0
- package/src/__tests__/context-engine/route-discovery/python.test.ts +95 -0
- package/src/__tests__/context-engine/sanitiser-detection.test.ts +187 -0
- package/src/__tests__/context-engine/sink-matcher.test.ts +251 -0
- package/src/__tests__/context-engine/source-discovery.test.ts +186 -0
- package/src/__tests__/context-engine/taint-tracker.test.ts +182 -0
- package/src/__tests__/regression/agent-skill-benign.test.ts +174 -0
- package/src/__tests__/regression/known-false-positives.test.ts +312 -4
- package/src/__tests__/score/adjustments.test.ts +385 -0
- package/src/__tests__/score/confidence.test.ts +283 -0
- package/src/__tests__/score/framework-scoring.test.ts +275 -0
- package/src/__tests__/score/route-scoring.test.ts +156 -0
- package/src/__tests__/score/scoring-integration.test.ts +165 -0
- package/src/__tests__/score/taint-adjustments.test.ts +244 -0
- package/src/__tests__/snapshots/__snapshots__/anthropic-validation-refactor.test.ts.snap +37 -49
- package/src/__tests__/snapshots/__snapshots__/dangerous-functions-refactor.test.ts.snap +52 -0
- package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +3 -3
- package/src/__tests__/snapshots/anthropic-validation-refactor.test.ts +2 -2
- package/src/__tests__/snapshots/dangerous-functions-refactor.test.ts +1 -1
- package/src/__tests__/snapshots/scan-depth.test.ts +3 -3
- package/src/__tests__/validate/route-annotations.test.ts +138 -0
- package/src/__tests__/validation/analyze-results.ts +1 -1
- package/src/__tests__/validation/extract-for-triage.ts +1 -1
- package/src/__tests__/validation/fp-deep-analysis.ts +1 -1
- package/src/{layer2/ai-agent-tools.ts → detect/ai-code/agent-tools.ts} +23 -3
- package/src/{layer2 → detect/ai-code}/byok-patterns.ts +17 -5
- package/src/{layer2/ai-endpoint-protection.ts → detect/ai-code/endpoint-protection.ts} +8 -4
- package/src/{layer2/ai-execution-sinks.ts → detect/ai-code/execution-sinks.ts} +8 -4
- package/src/{layer2/ai-fingerprinting.ts → detect/ai-code/fingerprinting.ts} +20 -4
- package/src/detect/ai-code/index.ts +11 -0
- package/src/{layer2/ai-mcp-security.ts → detect/ai-code/mcp-security.ts} +7 -3
- package/src/{layer2 → detect/ai-code}/model-supply-chain.ts +7 -3
- package/src/{layer2/ai-package-hallucination.ts → detect/ai-code/package-hallucination.ts} +18 -3
- package/src/{layer2/ai-prompt-hygiene.ts → detect/ai-code/prompt-hygiene.ts} +25 -3
- package/src/{layer2/ai-rag-safety.ts → detect/ai-code/rag-safety.ts} +7 -3
- package/src/{layer2/ai-schema-validation.ts → detect/ai-code/schema-validation.ts} +7 -3
- package/src/detect/config/agent-skill-injection.ts +551 -0
- package/src/{layer1 → detect/config}/comments.ts +6 -2
- package/src/{layer1 → detect/config}/file-flags.ts +9 -3
- package/src/detect/config/index.ts +6 -0
- package/src/{layer3 → detect/config}/osv-check.ts +3 -2
- package/src/{layer3 → detect/config}/package-check.ts +3 -2
- package/src/{layer1 → detect/config}/urls.ts +12 -5
- package/src/detect/index.ts +131 -0
- package/src/{layer1 → detect/secrets}/config-audit.ts +7 -2
- package/src/{layer1 → detect/secrets}/config-mcp-audit.ts +8 -3
- package/src/{layer1 → detect/secrets}/entropy.ts +23 -11
- package/src/{layer1 → detect/secrets}/index.ts +31 -30
- package/src/{layer1 → detect/secrets}/patterns.ts +10 -3
- package/src/{layer1 → detect/secrets}/weak-crypto.ts +7 -2
- package/src/{layer2/auth-antipatterns.ts → detect/structural/auth-patterns.ts} +23 -11
- package/src/{layer2 → detect/structural}/dangerous-functions/dom-xss.ts +1 -1
- package/src/{layer2 → detect/structural}/dangerous-functions/index.ts +47 -24
- package/src/{layer2 → detect/structural}/dangerous-functions/json-parse.ts +10 -2
- package/src/{layer2 → detect/structural}/dangerous-functions/math-random.ts +2 -2
- package/src/{layer2 → detect/structural}/dangerous-functions/patterns.ts +1 -1
- package/src/{layer2 → detect/structural}/dangerous-functions/request-validation.ts +10 -2
- package/src/{layer2 → detect/structural}/dangerous-functions/utils/control-flow.ts +2 -2
- package/src/{layer2 → detect/structural}/data-exposure.ts +11 -3
- package/src/{layer2 → detect/structural}/framework-checks.ts +10 -11
- package/src/{layer2 → detect/structural}/index.ts +80 -77
- package/src/detect/structural/log-injection.ts +254 -0
- package/src/{layer2 → detect/structural}/logic-gates.ts +13 -5
- package/src/{layer2 → detect/structural}/risky-imports.ts +7 -3
- package/src/detect/structural/security-headers.ts +231 -0
- package/src/detect/structural/ssrf-detection.ts +300 -0
- package/src/{layer2 → detect/structural}/variables.ts +7 -3
- package/src/detect/structural/xxe-detection.ts +295 -0
- package/src/index.ts +39 -1291
- package/src/{utils → model}/auth-helper-detector.ts +1 -1
- package/src/model/cross-file-taint.ts +374 -0
- package/src/model/framework-models/django.ts +82 -0
- package/src/model/framework-models/express.ts +54 -0
- package/src/model/framework-models/index.ts +116 -0
- package/src/model/framework-models/nextjs.ts +69 -0
- package/src/model/framework-models/prisma.ts +57 -0
- package/src/model/framework-models/react.ts +63 -0
- package/src/model/framework-models/sequelize.ts +63 -0
- package/src/model/framework-models/types.ts +46 -0
- package/src/model/function-classifier.ts +184 -0
- package/src/model/import-resolver.ts +453 -0
- package/src/{utils → model}/imported-auth-detector.ts +21 -85
- package/src/model/index.ts +353 -0
- package/src/{utils → model}/middleware-detector.ts +156 -17
- package/src/model/module-graph.ts +254 -0
- package/src/{utils → model}/oauth-flow-detector.ts +1 -1
- package/src/{utils/project-context-builder.ts → model/project-context.ts} +1 -1
- package/src/model/route-auth-resolver.ts +216 -0
- package/src/model/route-discovery/express.ts +251 -0
- package/src/model/route-discovery/index.ts +83 -0
- package/src/model/route-discovery/nextjs.ts +216 -0
- package/src/model/route-discovery/python.ts +214 -0
- package/src/model/route-discovery/types.ts +48 -0
- package/src/model/route-discovery/utils.ts +54 -0
- package/src/model/sanitiser-detection.ts +268 -0
- package/src/model/sink-matcher.ts +178 -0
- package/src/model/sink-patterns.ts +109 -0
- package/src/model/source-discovery.ts +209 -0
- package/src/model/taint-tracker.ts +333 -0
- package/src/model/taint-types.ts +149 -0
- package/src/{utils → model}/trpc-analyzer.ts +1 -1
- package/src/{utils/context-helpers.ts → parse/file-classifier.ts} +54 -0
- package/src/{utils → parse}/path-exclusions.ts +1 -1
- package/src/pipeline/config.ts +81 -0
- package/src/pipeline/index.ts +437 -0
- package/src/{modes → pipeline/modes}/incremental.ts +5 -5
- package/src/postprocess/aggregation.ts +74 -0
- package/src/postprocess/contradictions.ts +128 -0
- package/src/postprocess/dedup.ts +62 -0
- package/src/{filtering → postprocess/filtering}/__tests__/pipeline.test.ts +1 -1
- package/src/{filtering → postprocess/filtering}/context-adjustments.ts +2 -2
- package/src/{filtering → postprocess/filtering}/pipeline.ts +2 -2
- package/src/postprocess/index.ts +118 -0
- package/src/{suppression → postprocess/suppression}/config-loader.ts +1 -1
- package/src/{suppression → postprocess/suppression}/hash.ts +1 -1
- package/src/{suppression → postprocess/suppression}/inline-parser.ts +1 -1
- package/src/{suppression → postprocess/suppression}/manager.ts +1 -1
- package/src/{suppression → postprocess/suppression}/types.ts +2 -2
- package/src/postprocess/validation-cap.ts +66 -0
- package/src/report/build-result.ts +94 -0
- package/src/report/enrichment.ts +52 -0
- package/src/{formatters → report/formatters}/ai-context.ts +1 -1
- package/src/{formatters → report/formatters}/cli-terminal.ts +11 -11
- package/src/{formatters → report/formatters}/github-comment.ts +1 -1
- package/src/{formatters → report/formatters}/grouping.ts +8 -8
- package/src/{formatters → report/formatters}/ide/claude-code.ts +1 -1
- package/src/{formatters → report/formatters}/ide/cursor.ts +1 -1
- package/src/{formatters → report/formatters}/ide/windsurf.ts +1 -1
- package/src/{formatters → report/formatters}/vscode-diagnostic.ts +1 -1
- package/src/report/summary.ts +70 -0
- package/src/score/adjustments.ts +387 -0
- package/src/{layer3/anthropic → score}/auto-dismiss.ts +15 -14
- package/src/score/confidence.ts +66 -0
- package/src/score/index.ts +316 -0
- package/src/score/types.ts +187 -0
- package/src/{baseline → shared/baseline}/__tests__/diff.test.ts +2 -2
- package/src/{baseline → shared/baseline}/diff.ts +1 -1
- package/src/{baseline → shared/baseline}/manager.ts +1 -1
- package/src/{category-filter.ts → shared/category-filter.ts} +1 -1
- package/src/{utils → shared}/code-analysis.ts +1 -1
- package/src/{rules → shared/rules}/__tests__/metadata.test.ts +7 -0
- package/src/{rules → shared/rules}/framework-fixes.ts +1 -1
- package/src/{rules → shared/rules}/metadata.ts +94 -0
- package/src/{types.ts → shared/types.ts} +22 -5
- package/src/tiers.ts +18 -1
- package/src/validate/__tests__/context-extractor.test.ts +191 -0
- package/src/validate/__tests__/prompt-assembly.test.ts +233 -0
- package/src/validate/__tests__/request-builder.test.ts +347 -0
- package/src/{layer3/anthropic → validate}/index.ts +8 -7
- package/src/{layer3/anthropic → validate}/prompts/index.ts +2 -0
- package/src/validate/prompts/modules/ai-patterns.ts +153 -0
- package/src/validate/prompts/modules/auth-access.ts +22 -0
- package/src/validate/prompts/modules/common.ts +183 -0
- package/src/validate/prompts/modules/index.ts +204 -0
- package/src/validate/prompts/modules/owasp-classic.ts +81 -0
- package/src/validate/prompts/modules/secrets-crypto.ts +65 -0
- package/src/validate/prompts/modules/xss-prompt.ts +19 -0
- package/src/validate/prompts/validation.ts +20 -0
- package/src/{layer3/anthropic → validate}/providers/anthropic.ts +28 -27
- package/src/validate/providers/index.ts +8 -0
- package/src/{layer3/anthropic → validate}/providers/openai.ts +30 -25
- package/src/validate/request-builder.ts +448 -0
- package/src/{layer3/anthropic → validate}/types.ts +1 -1
- package/src/validate/utils/context-extractor.ts +220 -0
- package/src/{layer3/anthropic → validate}/utils/index.ts +10 -0
- package/src/{layer3/anthropic → validate}/utils/response-parser.ts +2 -1
- package/src/layer3/anthropic/prompts/validation.ts +0 -419
- package/src/layer3/anthropic/providers/index.ts +0 -8
- package/src/layer3/anthropic/request-builder.ts +0 -150
- package/src/layer3/index.ts +0 -168
- /package/src/{layer3 → detect/config}/__tests__/osv-check.test.ts +0 -0
- /package/src/{layer2 → detect/structural}/__tests__/math-random-enhanced.test.ts +0 -0
- /package/src/{layer2 → detect/structural}/dangerous-functions/child-process.ts +0 -0
- /package/src/{layer2 → detect/structural}/dangerous-functions/utils/helpers.ts +0 -0
- /package/src/{layer2 → detect/structural}/dangerous-functions/utils/index.ts +0 -0
- /package/src/{layer2 → detect/structural}/dangerous-functions/utils/schema-validation.ts +0 -0
- /package/src/{utils → model}/route-hierarchy.ts +0 -0
- /package/src/{filtering → postprocess/filtering}/index.ts +0 -0
- /package/src/{suppression → postprocess/suppression}/__tests__/config-loader.test.ts +0 -0
- /package/src/{suppression → postprocess/suppression}/__tests__/hash.test.ts +0 -0
- /package/src/{suppression → postprocess/suppression}/__tests__/inline-parser.test.ts +0 -0
- /package/src/{suppression → postprocess/suppression}/__tests__/manager.test.ts +0 -0
- /package/src/{suppression → postprocess/suppression}/index.ts +0 -0
- /package/src/{formatters → report/formatters}/__tests__/ai-context.test.ts +0 -0
- /package/src/{formatters → report/formatters}/ide/__tests__/ide.test.ts +0 -0
- /package/src/{formatters → report/formatters}/ide/index.ts +0 -0
- /package/src/{formatters → report/formatters}/index.ts +0 -0
- /package/src/{utils → shared}/__tests__/code-analysis.test.ts +0 -0
- /package/src/{utils → shared}/__tests__/parsed-file.test.ts +0 -0
- /package/src/{ai-context → shared/ai-context}/__tests__/manager.test.ts +0 -0
- /package/src/{ai-context → shared/ai-context}/index.ts +0 -0
- /package/src/{ai-context → shared/ai-context}/manager.ts +0 -0
- /package/src/{baseline → shared/baseline}/__tests__/manager.test.ts +0 -0
- /package/src/{baseline → shared/baseline}/index.ts +0 -0
- /package/src/{baseline → shared/baseline}/types.ts +0 -0
- /package/src/{utils → shared}/comment-analyzer.ts +0 -0
- /package/src/{utils → shared}/diff-detector.ts +0 -0
- /package/src/{utils → shared}/diff-parser.ts +0 -0
- /package/src/{utils → shared}/environment-context.ts +0 -0
- /package/src/{utils → shared}/intent-detector.ts +0 -0
- /package/src/{utils → shared}/parsed-file.ts +0 -0
- /package/src/{utils → shared}/registry-clients.ts +0 -0
- /package/src/{rules → shared/rules}/__tests__/framework-fixes.test.ts +0 -0
- /package/src/{rules → shared/rules}/index.ts +0 -0
- /package/src/{utils → shared}/schema-semantics.ts +0 -0
- /package/src/{layer3/anthropic → validate}/clients.ts +0 -0
- /package/src/{layer3/anthropic → validate}/prompts/semantic-analysis.ts +0 -0
- /package/src/{layer3/anthropic → validate}/utils/path-helpers.ts +0 -0
- /package/src/{layer3/anthropic → validate}/utils/retry.ts +0 -0
|
@@ -0,0 +1,1509 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Layer 2: AI Agent Tool Permission Detection
|
|
4
|
+
* Detects overly permissive agent tools and missing authorization checks
|
|
5
|
+
*
|
|
6
|
+
* Covers B4: Agent/tool orchestration logic
|
|
7
|
+
*
|
|
8
|
+
* Issues detected:
|
|
9
|
+
* - Tools with unrestricted file system access
|
|
10
|
+
* - Tools with unrestricted network access
|
|
11
|
+
* - Tools with shell/code execution capability
|
|
12
|
+
* - Tools without user/tenant context verification
|
|
13
|
+
* - Database tools without proper scoping
|
|
14
|
+
*/
|
|
15
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
16
|
+
exports.detectAIAgentTools = detectAIAgentTools;
|
|
17
|
+
const file_classifier_1 = require("../../parse/file-classifier");
|
|
18
|
+
const BASE_CONFIDENCE = 0.50;
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Agent/Tool Context Detection
|
|
21
|
+
// ============================================================================
|
|
22
|
+
/**
|
|
23
|
+
* Check if file contains agent or tool definitions
|
|
24
|
+
*/
|
|
25
|
+
function isAgentOrToolFile(filePath, content) {
|
|
26
|
+
// File path indicators
|
|
27
|
+
const agentPathPatterns = [
|
|
28
|
+
/\/(agents?|tools?|functions?|actions?)\//i,
|
|
29
|
+
/\/(mcp|langchain|llamaindex|autogen)\//i,
|
|
30
|
+
/(agent|tool|function|action).*\.(ts|js|py)$/i,
|
|
31
|
+
];
|
|
32
|
+
if (agentPathPatterns.some(p => p.test(filePath))) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
// Content patterns indicating tool/agent definitions
|
|
36
|
+
const toolDefinitionPatterns = [
|
|
37
|
+
/@tool/i, // Python decorator
|
|
38
|
+
/def\s+\w+_tool\s*\(/i, // Python tool function
|
|
39
|
+
/defineTool\s*\(/i, // JS/TS tool definition
|
|
40
|
+
/createTool\s*\(/i, // Tool creation
|
|
41
|
+
/\.registerTool\s*\(/i, // Tool registration
|
|
42
|
+
/\.addTool\s*\(/i, // Adding tool to agent
|
|
43
|
+
/tools\s*:\s*\[/i, // Tools array
|
|
44
|
+
/FunctionTool|StructuredTool/i, // LangChain tools
|
|
45
|
+
/tool_choice|function_call/i, // OpenAI function calling
|
|
46
|
+
/Tool\s*\(\s*\{/i, // Tool configuration object
|
|
47
|
+
/type:\s*['"`]function['"`]/i, // OpenAI function type
|
|
48
|
+
/mcpServer|McpServer/i, // MCP server
|
|
49
|
+
];
|
|
50
|
+
return toolDefinitionPatterns.some(p => p.test(content));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Find tool definition boundaries (start and end lines)
|
|
54
|
+
*/
|
|
55
|
+
function findToolDefinitionContext(content, lineNumber, windowSize = 30) {
|
|
56
|
+
const lines = content.split('\n');
|
|
57
|
+
const startLine = Math.max(0, lineNumber - windowSize);
|
|
58
|
+
const endLine = Math.min(lines.length, lineNumber + windowSize);
|
|
59
|
+
return {
|
|
60
|
+
context: lines.slice(startLine, endLine).join('\n'),
|
|
61
|
+
startLine,
|
|
62
|
+
endLine,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// Authorization Detection
|
|
67
|
+
// ============================================================================
|
|
68
|
+
/**
|
|
69
|
+
* Check if user context is verified in tool
|
|
70
|
+
*/
|
|
71
|
+
function hasUserContextVerification(context) {
|
|
72
|
+
const userContextPatterns = [
|
|
73
|
+
/user[_.]?id/i,
|
|
74
|
+
/userId/i,
|
|
75
|
+
/currentUser/i,
|
|
76
|
+
/req\.user/i,
|
|
77
|
+
/request\.user/i,
|
|
78
|
+
/session\.user/i,
|
|
79
|
+
/getUser\s*\(/i,
|
|
80
|
+
/getCurrentUser\s*\(/i,
|
|
81
|
+
/authenticatedUser/i,
|
|
82
|
+
/ctx\.user/i,
|
|
83
|
+
/context\.user/i,
|
|
84
|
+
];
|
|
85
|
+
return userContextPatterns.some(p => p.test(context));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Check if tenant/organization context is verified
|
|
89
|
+
*/
|
|
90
|
+
function hasTenantContextVerification(context) {
|
|
91
|
+
const tenantContextPatterns = [
|
|
92
|
+
/tenant[_.]?id/i,
|
|
93
|
+
/tenantId/i,
|
|
94
|
+
/org[_.]?id/i,
|
|
95
|
+
/orgId/i,
|
|
96
|
+
/organization[_.]?id/i,
|
|
97
|
+
/workspace[_.]?id/i,
|
|
98
|
+
/workspaceId/i,
|
|
99
|
+
/team[_.]?id/i,
|
|
100
|
+
/teamId/i,
|
|
101
|
+
/account[_.]?id/i,
|
|
102
|
+
/accountId/i,
|
|
103
|
+
];
|
|
104
|
+
return tenantContextPatterns.some(p => p.test(context));
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Check if this is an MCP file with proper security controls
|
|
108
|
+
* MCP files with sanitization and authorization should be downgraded
|
|
109
|
+
*/
|
|
110
|
+
function isMCPFileWithSafePatterns(content, filePath) {
|
|
111
|
+
// Check if this is an MCP file
|
|
112
|
+
const mcpPatterns = [
|
|
113
|
+
/McpServer/i,
|
|
114
|
+
/@modelcontextprotocol/i,
|
|
115
|
+
/server\.tool\s*\(/i,
|
|
116
|
+
/@server\.tool/i,
|
|
117
|
+
/\/mcp\//i,
|
|
118
|
+
];
|
|
119
|
+
if (!mcpPatterns.some(p => p.test(content) || p.test(filePath))) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// Check for content sanitization patterns
|
|
123
|
+
const sanitizationPatterns = [
|
|
124
|
+
/sanitize|DOMPurify|purify/i,
|
|
125
|
+
/escapeHtml|escape_html/i,
|
|
126
|
+
/validateSchema|schema\.parse|safeParse/i,
|
|
127
|
+
/ALLOWED_TAGS/i,
|
|
128
|
+
/filterHtml|cleanHtml/i,
|
|
129
|
+
];
|
|
130
|
+
// Check for authorization patterns
|
|
131
|
+
const authorizationPatterns = [
|
|
132
|
+
/if\s*\([^)]*ownerId\s*[!=]==?/i,
|
|
133
|
+
/if\s*\([^)]*userId\s*[!=]==?/i,
|
|
134
|
+
/if\s*\([^)]*tenantId\s*[!=]==?/i,
|
|
135
|
+
/throw.*Error.*(?:auth|Forbidden|Unauthorized)/i,
|
|
136
|
+
/Not\s*authorized/i,
|
|
137
|
+
/checkPermission|hasPermission|isAuthorized/i,
|
|
138
|
+
];
|
|
139
|
+
const hasSanitization = sanitizationPatterns.some(p => p.test(content));
|
|
140
|
+
const hasAuthorization = authorizationPatterns.some(p => p.test(content));
|
|
141
|
+
// MCP file with BOTH sanitization AND authorization is safe
|
|
142
|
+
return hasSanitization && hasAuthorization;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Patterns indicating strong/verified restrictions (actual implementation)
|
|
146
|
+
*/
|
|
147
|
+
const STRONG_RESTRICTION_PATTERNS = [
|
|
148
|
+
// Sandboxing libraries and environments
|
|
149
|
+
/\bvm2\b/i,
|
|
150
|
+
/\bisolated-vm\b/i,
|
|
151
|
+
/\bquickjs\b/i,
|
|
152
|
+
/\bsandboxed\b/i,
|
|
153
|
+
/\bRestrictedPython\b/i,
|
|
154
|
+
/\bnsjail\b/i,
|
|
155
|
+
/\bfirejail\b/i,
|
|
156
|
+
/\bgvisor\b/i,
|
|
157
|
+
// Explicit path/resource restrictions with arrays
|
|
158
|
+
/allowed(?:Paths|Files|Dirs|Hosts|Urls|Commands)\s*[=:]\s*\[/i,
|
|
159
|
+
/(?:white|allow)list\s*[=:]\s*\[/i,
|
|
160
|
+
/(?:blocked|denied|forbidden)(?:Paths|Hosts|Commands)\s*[=:]\s*\[/i,
|
|
161
|
+
// Path validation functions
|
|
162
|
+
/validatePath\s*\(/i,
|
|
163
|
+
/isAllowedPath\s*\(/i,
|
|
164
|
+
/checkPathAccess\s*\(/i,
|
|
165
|
+
/resolvePath.*allowed/i,
|
|
166
|
+
/path\.resolve.*includes/i,
|
|
167
|
+
// Sandbox configuration objects
|
|
168
|
+
/sandbox\s*[=:]\s*(?:true|\{)/i,
|
|
169
|
+
/readonly\s*[=:]\s*true/i,
|
|
170
|
+
/readOnly\s*[=:]\s*true/i,
|
|
171
|
+
// Container/isolation patterns
|
|
172
|
+
/\b(?:docker|podman)\s+run\b.*--read-only/i,
|
|
173
|
+
/seccomp/i,
|
|
174
|
+
/capabilities\s*[=:]\s*\[\s*\]/i, // Empty capabilities = restricted
|
|
175
|
+
// Permission checking code
|
|
176
|
+
/if\s*\(\s*!?\s*(?:allowed|permitted|authorized)/i,
|
|
177
|
+
/(?:check|verify|validate)(?:Access|Permission|Capability)\s*\(/i,
|
|
178
|
+
];
|
|
179
|
+
/**
|
|
180
|
+
* Patterns indicating weak/unverified restriction mentions (comments, TODOs)
|
|
181
|
+
*/
|
|
182
|
+
const WEAK_RESTRICTION_PATTERNS = [
|
|
183
|
+
// Comments mentioning restrictions without implementation
|
|
184
|
+
/\/\/.*(?:sandbox|restrict|allowlist|whitelist|todo)/i,
|
|
185
|
+
/\/\*.*(?:sandbox|restrict|allowlist|whitelist|todo).*\*\//i,
|
|
186
|
+
/#.*(?:sandbox|restrict|allowlist|whitelist|todo)/i,
|
|
187
|
+
// TODOs and FIXMEs
|
|
188
|
+
/TODO.*(?:add|implement|enable).*(?:sandbox|restrict|allowlist)/i,
|
|
189
|
+
/FIXME.*(?:sandbox|restrict|security)/i,
|
|
190
|
+
// Variable names without assignment
|
|
191
|
+
/const\s+(?:sandbox|allowlist|whitelist)\s*;/i,
|
|
192
|
+
];
|
|
193
|
+
/**
|
|
194
|
+
* Check if tool has strong/verified access restrictions
|
|
195
|
+
* These are actual implementations, not just mentions
|
|
196
|
+
*/
|
|
197
|
+
function hasStrongRestrictions(context) {
|
|
198
|
+
// Check for strong patterns
|
|
199
|
+
const hasStrong = STRONG_RESTRICTION_PATTERNS.some(p => p.test(context));
|
|
200
|
+
if (!hasStrong)
|
|
201
|
+
return false;
|
|
202
|
+
// Verify it's not just a weak mention
|
|
203
|
+
const isWeak = WEAK_RESTRICTION_PATTERNS.some(p => p.test(context));
|
|
204
|
+
return !isWeak;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Check if tool has any access restrictions/allowlists (including weak mentions)
|
|
208
|
+
*/
|
|
209
|
+
function hasAccessRestrictions(context) {
|
|
210
|
+
const restrictionPatterns = [
|
|
211
|
+
/allowedPaths/i,
|
|
212
|
+
/allowedFiles/i,
|
|
213
|
+
/allowedDirs/i,
|
|
214
|
+
/allowedHosts/i,
|
|
215
|
+
/allowedUrls/i,
|
|
216
|
+
/allowedCommands/i,
|
|
217
|
+
/allowedOperations/i,
|
|
218
|
+
/whitelist/i,
|
|
219
|
+
/allowlist/i,
|
|
220
|
+
/permissions?:/i,
|
|
221
|
+
/capabilities:/i,
|
|
222
|
+
/restrictions?:/i,
|
|
223
|
+
/constraints?:/i,
|
|
224
|
+
/sandbox/i,
|
|
225
|
+
/readonly/i,
|
|
226
|
+
/readOnly/i,
|
|
227
|
+
];
|
|
228
|
+
return restrictionPatterns.some(p => p.test(context));
|
|
229
|
+
}
|
|
230
|
+
const OVERPERMISSIVE_TOOL_PATTERNS = [
|
|
231
|
+
// ========== Filesystem Access Tools ==========
|
|
232
|
+
{
|
|
233
|
+
name: 'Unrestricted file read tool',
|
|
234
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:read|get).*file|(?:fs|filesystem).*(?:read|get)/gi,
|
|
235
|
+
riskType: 'filesystem',
|
|
236
|
+
baseSeverity: 'high',
|
|
237
|
+
description: 'Tool provides file system read access. Without restrictions, agents can access any file the process can read.',
|
|
238
|
+
suggestedFix: 'Add allowedPaths restriction. Example: { allowedPaths: ["/data/user-uploads"] }. Validate paths stay within allowed directories.',
|
|
239
|
+
requiresRestrictions: true,
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
name: 'Unrestricted file write tool',
|
|
243
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:write|create|save).*file|(?:fs|filesystem).*(?:write|create)/gi,
|
|
244
|
+
riskType: 'filesystem',
|
|
245
|
+
baseSeverity: 'high',
|
|
246
|
+
description: 'Tool provides file system write access. Agents could overwrite critical files or create malicious files.',
|
|
247
|
+
suggestedFix: 'Restrict to specific directories. Validate file extensions. Implement size limits. Consider using signed URLs instead of direct file access.',
|
|
248
|
+
requiresRestrictions: true,
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
name: 'File deletion tool',
|
|
252
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:delete|remove).*file|(?:fs|filesystem).*(?:delete|unlink|remove)/gi,
|
|
253
|
+
riskType: 'filesystem',
|
|
254
|
+
baseSeverity: 'high',
|
|
255
|
+
description: 'Tool provides file deletion capability. High risk of data loss if misused.',
|
|
256
|
+
suggestedFix: 'Implement soft-delete instead of hard delete. Require confirmation. Restrict to user-owned files only.',
|
|
257
|
+
requiresRestrictions: true,
|
|
258
|
+
requiresUserContext: true,
|
|
259
|
+
},
|
|
260
|
+
// ========== Network Access Tools ==========
|
|
261
|
+
{
|
|
262
|
+
name: 'Unrestricted HTTP/fetch tool',
|
|
263
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:http|fetch|request|api)|tool.*(?:fetch|request)\s*\(/gi,
|
|
264
|
+
riskType: 'network',
|
|
265
|
+
baseSeverity: 'medium',
|
|
266
|
+
description: 'Tool provides network/HTTP access. Without restrictions, agents could make requests to internal services (SSRF) or exfiltrate data.',
|
|
267
|
+
suggestedFix: 'Add allowedHosts configuration. Block internal/private IP ranges. Implement request signing for sensitive operations.',
|
|
268
|
+
requiresRestrictions: true,
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: 'Web scraping tool',
|
|
272
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:scrape|crawl|browse)/gi,
|
|
273
|
+
riskType: 'network',
|
|
274
|
+
baseSeverity: 'medium',
|
|
275
|
+
description: 'Tool provides web scraping capability. Could be used for SSRF or accessing internal resources.',
|
|
276
|
+
suggestedFix: 'Restrict to allowed domains. Block internal IP ranges. Implement rate limiting.',
|
|
277
|
+
requiresRestrictions: true,
|
|
278
|
+
},
|
|
279
|
+
// ========== Code Execution Tools ==========
|
|
280
|
+
{
|
|
281
|
+
name: 'Code execution tool',
|
|
282
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:execute|run|eval).*(?:code|script)|tool.*(?:eval|exec)\s*\(/gi,
|
|
283
|
+
riskType: 'code_execution',
|
|
284
|
+
baseSeverity: 'critical',
|
|
285
|
+
description: 'Tool provides code execution capability. This is extremely dangerous without sandboxing.',
|
|
286
|
+
suggestedFix: 'Use vm2, isolated-vm, or similar sandboxing. Implement timeout and memory limits. Restrict available APIs/modules.',
|
|
287
|
+
requiresRestrictions: true,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'Python interpreter tool',
|
|
291
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*python.*(?:exec|run|interpret)|PythonREPL|python_repl/gi,
|
|
292
|
+
riskType: 'code_execution',
|
|
293
|
+
baseSeverity: 'critical',
|
|
294
|
+
description: 'Tool provides Python execution capability. Can execute arbitrary system commands.',
|
|
295
|
+
suggestedFix: 'Use RestrictedPython or sandboxed environments. Block dangerous modules (os, subprocess, socket). Implement resource limits.',
|
|
296
|
+
requiresRestrictions: true,
|
|
297
|
+
},
|
|
298
|
+
// ========== Shell/Command Tools ==========
|
|
299
|
+
{
|
|
300
|
+
name: 'Shell command tool',
|
|
301
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:shell|command|terminal|bash)|ShellTool|BashTool/gi,
|
|
302
|
+
riskType: 'shell',
|
|
303
|
+
baseSeverity: 'critical',
|
|
304
|
+
description: 'Tool provides shell command execution. Allows arbitrary system commands.',
|
|
305
|
+
suggestedFix: 'Implement strict command allowlisting. Use parameterized commands (execFile, not exec). Consider removing this capability entirely.',
|
|
306
|
+
requiresRestrictions: true,
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
name: 'System command tool',
|
|
310
|
+
pattern: /(?:@tool|defineTool|createTool)[^)]*(?:system|exec|spawn|subprocess)/gi,
|
|
311
|
+
riskType: 'shell',
|
|
312
|
+
baseSeverity: 'critical',
|
|
313
|
+
description: 'Tool with system command execution capability.',
|
|
314
|
+
suggestedFix: 'Restrict to specific commands via allowlist. Validate all arguments. Log all command executions.',
|
|
315
|
+
requiresRestrictions: true,
|
|
316
|
+
},
|
|
317
|
+
// ========== Database Tools ==========
|
|
318
|
+
{
|
|
319
|
+
name: 'Database query tool',
|
|
320
|
+
pattern: /(?:@tool|defineTool|createTool|Tool\s*\()[^)]*(?:query|sql|database)|tool.*(?:query|execute)\s*\(/gi,
|
|
321
|
+
riskType: 'database',
|
|
322
|
+
baseSeverity: 'high',
|
|
323
|
+
description: 'Tool provides database query access. Without scoping, agents could access any data.',
|
|
324
|
+
suggestedFix: 'Always scope queries to current user/tenant. Use row-level security (RLS). Implement read-only mode for most operations.',
|
|
325
|
+
requiresUserContext: true,
|
|
326
|
+
requiresTenantContext: true,
|
|
327
|
+
},
|
|
328
|
+
{
|
|
329
|
+
name: 'Raw SQL tool',
|
|
330
|
+
pattern: /(?:@tool|defineTool|createTool)[^)]*(?:raw.*sql|execute.*sql)/gi,
|
|
331
|
+
riskType: 'database',
|
|
332
|
+
baseSeverity: 'critical',
|
|
333
|
+
description: 'Tool allows raw SQL execution. High risk of SQL injection and unauthorized data access.',
|
|
334
|
+
suggestedFix: 'Use parameterized queries only. Implement query validation. Consider using an ORM instead of raw SQL.',
|
|
335
|
+
requiresUserContext: true,
|
|
336
|
+
requiresTenantContext: true,
|
|
337
|
+
},
|
|
338
|
+
// ========== M5: MCP Server Tools ==========
|
|
339
|
+
{
|
|
340
|
+
name: 'MCP server tool registration',
|
|
341
|
+
pattern: /(?:McpServer|Server)\s*\([^)]*\).*(?:setRequestHandler|tool|registerTool)|server\.tool\s*\(/gi,
|
|
342
|
+
riskType: 'code_execution',
|
|
343
|
+
baseSeverity: 'high',
|
|
344
|
+
description: 'MCP (Model Context Protocol) server registering tools. Verify tool capabilities are appropriately restricted.',
|
|
345
|
+
suggestedFix: 'Add capability restrictions to MCP server. Implement allowlists for file paths, network hosts, and commands.',
|
|
346
|
+
requiresRestrictions: true,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: 'MCP tool with shell access',
|
|
350
|
+
pattern: /server\.tool\s*\([^)]*(?:name:\s*['"`](?:run|exec|shell|command)[^)]*|(?:exec|spawn|shell)\s*\()/gi,
|
|
351
|
+
riskType: 'shell',
|
|
352
|
+
baseSeverity: 'critical',
|
|
353
|
+
description: 'MCP tool with shell command execution capability. Extremely dangerous without restrictions.',
|
|
354
|
+
suggestedFix: 'Use allowlist of permitted commands. Never allow arbitrary command execution. Consider read-only alternatives.',
|
|
355
|
+
requiresRestrictions: true,
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: 'MCP file system tool',
|
|
359
|
+
pattern: /server\.tool\s*\([^)]*(?:name:\s*['"`](?:read|write|create|delete|list).*(?:file|dir)[^)]*|fs\.|readFile|writeFile)/gi,
|
|
360
|
+
riskType: 'filesystem',
|
|
361
|
+
baseSeverity: 'high',
|
|
362
|
+
description: 'MCP tool with file system access. Agents could access or modify arbitrary files.',
|
|
363
|
+
suggestedFix: 'Restrict to specific directories with allowedPaths. Implement path validation. Consider read-only access.',
|
|
364
|
+
requiresRestrictions: true,
|
|
365
|
+
},
|
|
366
|
+
// ========== M5: Vercel AI SDK Tools ==========
|
|
367
|
+
{
|
|
368
|
+
name: 'Vercel AI SDK tool definition',
|
|
369
|
+
pattern: /tool\s*\(\s*\{[^}]*(?:execute|parameters)/gi,
|
|
370
|
+
riskType: 'code_execution',
|
|
371
|
+
baseSeverity: 'medium',
|
|
372
|
+
description: 'Vercel AI SDK tool definition. Review the execute function for dangerous operations.',
|
|
373
|
+
suggestedFix: 'Validate tool parameters against expected schema. Implement proper access controls within execute function.',
|
|
374
|
+
requiresUserContext: true,
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
name: 'AI SDK tool with dangerous execute',
|
|
378
|
+
pattern: /tool\s*\(\s*\{[^}]*execute\s*:\s*async[^}]*(?:exec|spawn|eval|fs\.|fetch\s*\()[^}]*\}/gi,
|
|
379
|
+
riskType: 'code_execution',
|
|
380
|
+
baseSeverity: 'high',
|
|
381
|
+
description: 'Vercel AI SDK tool with potentially dangerous execute function (shell, eval, fs, or network access).',
|
|
382
|
+
suggestedFix: 'Add validation and restrictions in execute function. Implement allowlists for external operations.',
|
|
383
|
+
requiresRestrictions: true,
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
name: 'StreamableUI tool action',
|
|
387
|
+
pattern: /createStreamableUI.*tool.*\{.*action/gi,
|
|
388
|
+
riskType: 'code_execution',
|
|
389
|
+
baseSeverity: 'medium',
|
|
390
|
+
description: 'Streamable UI tool with server action. Ensure proper authorization before state mutations.',
|
|
391
|
+
suggestedFix: 'Verify user authentication and authorization before executing actions. Validate all inputs.',
|
|
392
|
+
requiresUserContext: true,
|
|
393
|
+
},
|
|
394
|
+
];
|
|
395
|
+
/**
|
|
396
|
+
* Check if agent has proper iteration limits
|
|
397
|
+
*/
|
|
398
|
+
function hasIterationLimits(context) {
|
|
399
|
+
const limitPatterns = [
|
|
400
|
+
/maxIterations\s*[:=]\s*\d{1,2}\b/i, // 1-99
|
|
401
|
+
/max_iterations\s*[:=]\s*\d{1,2}\b/i,
|
|
402
|
+
/iteration_limit\s*[:=]\s*\d{1,2}\b/i,
|
|
403
|
+
/max_steps\s*[:=]\s*\d{1,2}\b/i,
|
|
404
|
+
];
|
|
405
|
+
return limitPatterns.some(p => p.test(context));
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Check if agent has timeout configured
|
|
409
|
+
*/
|
|
410
|
+
function hasTimeoutConfigured(context) {
|
|
411
|
+
const timeoutPatterns = [
|
|
412
|
+
/timeout\s*[:=]\s*[1-9]\d*/i, // Positive number
|
|
413
|
+
/max_execution_time\s*[:=]\s*[1-9]/i,
|
|
414
|
+
/execution_timeout\s*[:=]\s*[1-9]/i,
|
|
415
|
+
];
|
|
416
|
+
return timeoutPatterns.some(p => p.test(context));
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Check if human-in-the-loop is enabled
|
|
420
|
+
*/
|
|
421
|
+
function hasHumanInLoop(context) {
|
|
422
|
+
const humanPatterns = [
|
|
423
|
+
/humanInLoop\s*[:=]\s*true/i,
|
|
424
|
+
/human_in_loop\s*[:=]\s*True/i,
|
|
425
|
+
/requireApproval\s*[:=]\s*true/i,
|
|
426
|
+
/require_approval\s*[:=]\s*True/i,
|
|
427
|
+
/human_input_mode\s*[:=]\s*['"`](?:ALWAYS|TERMINATE)['"`]/i,
|
|
428
|
+
/confirm_before\s*[:=]\s*true/i,
|
|
429
|
+
];
|
|
430
|
+
return humanPatterns.some(p => p.test(context));
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Check if Docker is configured for code execution
|
|
434
|
+
*/
|
|
435
|
+
function hasDockerConfigured(context) {
|
|
436
|
+
const dockerPatterns = [
|
|
437
|
+
/use_docker\s*[:=]\s*True/i,
|
|
438
|
+
/docker\s*[:=]\s*true/i,
|
|
439
|
+
/container\s*[:=]\s*true/i,
|
|
440
|
+
/sandboxed\s*[:=]\s*true/i,
|
|
441
|
+
];
|
|
442
|
+
return dockerPatterns.some(p => p.test(context));
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Check if budget/cost limits are configured
|
|
446
|
+
*/
|
|
447
|
+
function hasBudgetLimits(context) {
|
|
448
|
+
const budgetPatterns = [
|
|
449
|
+
/budgetLimit|budget_limit/i,
|
|
450
|
+
/costLimit|cost_limit/i,
|
|
451
|
+
/max_cost|maxCost/i,
|
|
452
|
+
/spending_limit/i,
|
|
453
|
+
/token_limit|tokenLimit/i,
|
|
454
|
+
];
|
|
455
|
+
return budgetPatterns.some(p => p.test(context));
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Phase 5: LLM Output Flow Patterns
|
|
459
|
+
* Detect when LLM-generated content flows into dangerous operations
|
|
460
|
+
*/
|
|
461
|
+
const LLM_OUTPUT_FLOW_PATTERNS = [
|
|
462
|
+
// ========== LLM Output in Tool Names/Paths ==========
|
|
463
|
+
{
|
|
464
|
+
name: 'LLM output used as tool name',
|
|
465
|
+
pattern: /(?:tools?\[|getTools?\s*\(|callTool\s*\(|invokeTool\s*\(|executeTool\s*\()\s*(?:response|result|output|completion|message|content|llm|ai|model|gpt|claude)\.(?:content|text|tool|toolName|function|name|choice)/gi,
|
|
466
|
+
baseSeverity: 'critical',
|
|
467
|
+
description: 'LLM output used directly as tool name for invocation. An adversarial prompt could cause the agent to call arbitrary tools, bypassing intended restrictions.',
|
|
468
|
+
suggestedFix: 'Validate tool names against a static allowlist: const ALLOWED_TOOLS = [\'read\', \'write\'] as const; if (!ALLOWED_TOOLS.includes(toolName)) throw new Error("Invalid tool")',
|
|
469
|
+
framework: 'generic',
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: 'LLM output in file path',
|
|
473
|
+
pattern: /(?:fs|file|path|fsp)\.(?:readFile|writeFile|unlink|rm|mkdir|readdir|access|stat|copyFile|rename)\s*\(\s*(?:response|result|output|completion|message|content|llm|ai|model)\.(?:path|filePath|file|filename|directory|dir)/gi,
|
|
474
|
+
baseSeverity: 'critical',
|
|
475
|
+
description: 'LLM output used directly as file path. Path traversal or arbitrary file access could occur via prompt injection.',
|
|
476
|
+
suggestedFix: 'Validate paths against allowed directories: if (!path.startsWith(ALLOWED_BASE_DIR)) throw new Error("Invalid path"). Use path.resolve() and verify the result stays within bounds.',
|
|
477
|
+
framework: 'generic',
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
name: 'LLM output in shell command',
|
|
481
|
+
pattern: /(?:exec|spawn|execFile|execSync|spawnSync)\s*\(\s*(?:response|result|output|completion|message|content|llm|ai|model)\.(?:command|cmd|script|code|executable|program)/gi,
|
|
482
|
+
baseSeverity: 'critical',
|
|
483
|
+
description: 'LLM output used directly as shell command. Remote code execution via prompt injection.',
|
|
484
|
+
suggestedFix: 'Never use LLM output in shell commands. If necessary, use a strict allowlist of permitted commands and validate arguments.',
|
|
485
|
+
framework: 'generic',
|
|
486
|
+
},
|
|
487
|
+
{
|
|
488
|
+
name: 'LLM output in URL/endpoint',
|
|
489
|
+
pattern: /(?:fetch|axios|http|request|got)\s*\(\s*(?:response|result|output|completion|message|content|llm|ai|model)\.(?:url|endpoint|href|uri|link|host)/gi,
|
|
490
|
+
baseSeverity: 'high',
|
|
491
|
+
description: 'LLM output used directly as URL or endpoint. SSRF risk via prompt injection.',
|
|
492
|
+
suggestedFix: 'Validate URLs against allowed hosts. Use URL allowlists and block internal IP ranges.',
|
|
493
|
+
framework: 'generic',
|
|
494
|
+
},
|
|
495
|
+
{
|
|
496
|
+
name: 'LLM response destructured into tool call',
|
|
497
|
+
pattern: /(?:const|let|var)\s*\{\s*(?:tool|toolName|function|functionName|action|method)\s*\}\s*=\s*(?:response|result|output|completion|message|llm|ai|model)/gi,
|
|
498
|
+
baseSeverity: 'high',
|
|
499
|
+
description: 'Tool name destructured from LLM response. This pattern suggests dynamic tool selection based on LLM output.',
|
|
500
|
+
suggestedFix: 'Validate extracted tool names against a static allowlist before invocation.',
|
|
501
|
+
framework: 'generic',
|
|
502
|
+
},
|
|
503
|
+
{
|
|
504
|
+
name: 'Dynamic property access with LLM output',
|
|
505
|
+
pattern: /(?:tools|handlers|actions|functions|methods)\s*\[\s*(?:response|result|output|completion|message|content|llm|ai)(?:\.|(?:\s*\[['"`]?(?:tool|name|function|action)))/gi,
|
|
506
|
+
baseSeverity: 'high',
|
|
507
|
+
description: 'Dynamic object property access using LLM output. Could access unintended tools or methods.',
|
|
508
|
+
suggestedFix: 'Use explicit tool dispatch with allowlist validation: if (toolName in SAFE_TOOLS) { SAFE_TOOLS[toolName]() }',
|
|
509
|
+
framework: 'generic',
|
|
510
|
+
},
|
|
511
|
+
];
|
|
512
|
+
/**
|
|
513
|
+
* Phase 5: Tool Permission Accumulation Patterns
|
|
514
|
+
* Detect unbounded tool registration and permission growth
|
|
515
|
+
*/
|
|
516
|
+
const TOOL_ACCUMULATION_PATTERNS = [
|
|
517
|
+
// ========== Unbounded Tool Registration ==========
|
|
518
|
+
{
|
|
519
|
+
name: 'Unbounded tool registration',
|
|
520
|
+
pattern: /(?:agent|tools?|registry)\.(?:registerTool|addTool|push|add|set)\s*\(\s*(?:user|request|req|input|body|data|param)\.(?:tool|function|action|capability)/gi,
|
|
521
|
+
baseSeverity: 'high',
|
|
522
|
+
description: 'Tools registered dynamically from user input without bounds. Users could accumulate unlimited capabilities over time.',
|
|
523
|
+
suggestedFix: 'Use a static allowlist: const ALLOWED_TOOLS = [...] and validate against it. Implement tool count limits.',
|
|
524
|
+
framework: 'generic',
|
|
525
|
+
},
|
|
526
|
+
{
|
|
527
|
+
name: 'Tool array push without limit check',
|
|
528
|
+
pattern: /tools\.push\s*\([^)]+\)(?![\s\S]{0,50}(?:length\s*[<>]|limit|max|ALLOWED|whitelist|allowlist))/gi,
|
|
529
|
+
baseSeverity: 'medium',
|
|
530
|
+
description: 'Tools added to array without checking count limits. Tool list could grow unboundedly.',
|
|
531
|
+
suggestedFix: 'Add limit check: if (tools.length >= MAX_TOOLS) throw new Error("Tool limit reached")',
|
|
532
|
+
framework: 'generic',
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
name: 'Dynamic tool loading from user config',
|
|
536
|
+
pattern: /(?:require|import|loadModule|dynamicImport)\s*\(\s*(?:user|request|req|input|body|config)\.(?:tool|module|plugin|extension)/gi,
|
|
537
|
+
baseSeverity: 'critical',
|
|
538
|
+
description: 'Tool modules loaded dynamically from user-controlled paths. Could load arbitrary code.',
|
|
539
|
+
suggestedFix: 'Use a static module registry. Validate module paths against an allowlist.',
|
|
540
|
+
framework: 'generic',
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
name: 'Permission grant without authorization check',
|
|
544
|
+
pattern: /(?:grant|add|enable)(?:Permission|Capability|Access)\s*\(\s*[^)]*\)(?![\s\S]{0,30}(?:if|auth|permission|role|admin|isAdmin))/gi,
|
|
545
|
+
baseSeverity: 'high',
|
|
546
|
+
description: 'Permissions granted without visible authorization check. Users could escalate their own privileges.',
|
|
547
|
+
suggestedFix: 'Add authorization check: if (!user.hasRole("admin")) throw new Error("Unauthorized")',
|
|
548
|
+
framework: 'generic',
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
name: 'Tool inheritance without restriction',
|
|
552
|
+
pattern: /(?:inherit|extend|merge)(?:Tools|Capabilities|Permissions)\s*\(\s*(?:parent|base|source)\.tools/gi,
|
|
553
|
+
baseSeverity: 'medium',
|
|
554
|
+
description: 'Agent inherits tools from parent without filtering. Could inherit more permissions than intended.',
|
|
555
|
+
suggestedFix: 'Explicitly list inherited tools instead of blanket inheritance. Use allowlist for permitted inherited capabilities.',
|
|
556
|
+
framework: 'generic',
|
|
557
|
+
},
|
|
558
|
+
];
|
|
559
|
+
/**
|
|
560
|
+
* Phase 5: Database Write Scoping Patterns
|
|
561
|
+
* Detect database writes that may lack proper user scoping
|
|
562
|
+
*/
|
|
563
|
+
const DB_WRITE_SCOPING_PATTERNS = [
|
|
564
|
+
// ========== Database Writes Without User Scoping ==========
|
|
565
|
+
{
|
|
566
|
+
name: 'DB insert without userId',
|
|
567
|
+
pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:insert|create|save|add)\s*\(\s*\{(?![^}]*(?:userId|user_id|ownerId|owner_id|createdBy|created_by|authorId|author_id))[^}]*(?:content|data|text|body|message)\s*:/gi,
|
|
568
|
+
baseSeverity: 'high',
|
|
569
|
+
description: 'Database insert with content field but no user ID. AI-generated content may not be properly attributed to user.',
|
|
570
|
+
suggestedFix: 'Add user context: db.insert({ content: aiGenerated, userId: ctx.user.id })',
|
|
571
|
+
framework: 'generic',
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
name: 'DB insert with AI content unscopedp',
|
|
575
|
+
pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:insert|create)\s*\(\s*\{[^}]*:\s*(?:response|result|output|completion|message|ai|llm|model)\.(?:content|text|data|output|result)/gi,
|
|
576
|
+
baseSeverity: 'high',
|
|
577
|
+
description: 'AI-generated content inserted into database. Ensure proper user scoping and content validation.',
|
|
578
|
+
suggestedFix: 'Add user context and validate content: db.insert({ content: validated, userId: ctx.user.id, createdAt: Date.now() })',
|
|
579
|
+
framework: 'generic',
|
|
580
|
+
},
|
|
581
|
+
{
|
|
582
|
+
name: 'Bulk write without tenant filter',
|
|
583
|
+
pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:insertMany|createMany|bulkCreate|bulkInsert)\s*\([^)]*\)(?![\s\S]{0,50}(?:tenantId|tenant_id|orgId|org_id|organizationId))/gi,
|
|
584
|
+
baseSeverity: 'medium',
|
|
585
|
+
description: 'Bulk database write without visible tenant scoping. Multi-tenant data isolation may be at risk.',
|
|
586
|
+
suggestedFix: 'Add tenant filter to all bulk operations: records.map(r => ({ ...r, tenantId: ctx.tenant.id }))',
|
|
587
|
+
framework: 'generic',
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
name: 'Update without ownership check',
|
|
591
|
+
pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:update|updateOne|updateMany)\s*\(\s*\{[^}]*id\s*:/gi,
|
|
592
|
+
baseSeverity: 'medium',
|
|
593
|
+
description: 'Database update by ID without visible ownership verification. Agent could modify other users\' data.',
|
|
594
|
+
suggestedFix: 'Add ownership check: db.update({ where: { id, userId: ctx.user.id }, data: { ... } })',
|
|
595
|
+
framework: 'generic',
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: 'Delete without user scoping',
|
|
599
|
+
pattern: /(?:db|database|prisma|knex|sequelize|mongoose|supabase|drizzle)\.(?:delete|deleteOne|deleteMany|destroy|remove)\s*\(\s*\{[^}]*id\s*:/gi,
|
|
600
|
+
baseSeverity: 'high',
|
|
601
|
+
description: 'Database delete by ID without user scoping. Agent could delete other users\' data.',
|
|
602
|
+
suggestedFix: 'Add user scoping: db.delete({ where: { id, userId: ctx.user.id } })',
|
|
603
|
+
framework: 'generic',
|
|
604
|
+
},
|
|
605
|
+
];
|
|
606
|
+
/**
|
|
607
|
+
* Phase 6 Task 1: Tool Parameter Injection Patterns
|
|
608
|
+
* Detect LLM output flowing to tool parameters (not just tool names)
|
|
609
|
+
*/
|
|
610
|
+
const TOOL_PARAMETER_INJECTION_PATTERNS = [
|
|
611
|
+
// LLM output in tool parameters
|
|
612
|
+
{
|
|
613
|
+
name: 'LLM output in tool parameters',
|
|
614
|
+
pattern: /tool\s*\(\s*\{[^}]*:\s*(response|output|result|content|message|llmOutput|aiResponse|completion)(\.\w+)*\s*[,}]/gi,
|
|
615
|
+
baseSeverity: 'high',
|
|
616
|
+
description: 'Tool parameters derived from unvalidated LLM output can be manipulated via prompt injection. Attackers could modify tool behavior through crafted responses.',
|
|
617
|
+
suggestedFix: 'Validate and sanitize LLM output before passing as tool parameters. Use schema validation (zod, yup) to ensure expected structure.',
|
|
618
|
+
framework: 'generic',
|
|
619
|
+
},
|
|
620
|
+
// Tool args assigned directly from LLM output
|
|
621
|
+
{
|
|
622
|
+
name: 'Tool args from LLM output',
|
|
623
|
+
pattern: /\bargs\s*=\s*(response|output|result|content|message|llmOutput|aiResponse|completion)(\.\w+)*/gi,
|
|
624
|
+
baseSeverity: 'high',
|
|
625
|
+
description: 'Tool arguments assigned directly from LLM output enable parameter injection. Malicious prompts could inject unexpected arguments.',
|
|
626
|
+
suggestedFix: 'Use schema validation (zod, yup) on LLM output before passing to tools: const validatedArgs = toolArgsSchema.parse(llmOutput)',
|
|
627
|
+
framework: 'generic',
|
|
628
|
+
},
|
|
629
|
+
// Spread LLM output into tool call
|
|
630
|
+
{
|
|
631
|
+
name: 'LLM output spread into tool call',
|
|
632
|
+
pattern: /(?:executeTool|callTool|invokeTool|runTool)\s*\([^)]*\.\.\.(?:response|output|result|content|llmOutput|aiResponse)/gi,
|
|
633
|
+
baseSeverity: 'critical',
|
|
634
|
+
description: 'LLM output spread directly into tool invocation. All LLM-provided fields pass through unvalidated.',
|
|
635
|
+
suggestedFix: 'Destructure and validate specific fields: const { field1, field2 } = schema.parse(llmOutput); executeTool({ field1, field2 })',
|
|
636
|
+
framework: 'generic',
|
|
637
|
+
},
|
|
638
|
+
// Dynamic property access for tool params
|
|
639
|
+
{
|
|
640
|
+
name: 'Dynamic tool param from LLM',
|
|
641
|
+
pattern: /toolParams?\s*\[\s*(response|output|result|llmOutput|aiResponse)\./gi,
|
|
642
|
+
baseSeverity: 'high',
|
|
643
|
+
description: 'Tool parameter accessed dynamically from LLM output. Could access unintended parameters.',
|
|
644
|
+
suggestedFix: 'Use explicit parameter extraction with validation: const param = validateParam(llmOutput.expectedField)',
|
|
645
|
+
framework: 'generic',
|
|
646
|
+
},
|
|
647
|
+
// JSON.parse of LLM output for tool params
|
|
648
|
+
{
|
|
649
|
+
name: 'JSON parsed LLM output as tool params',
|
|
650
|
+
pattern: /JSON\.parse\s*\(\s*(response|output|result|content|llmOutput|aiResponse|completion)(?:\.\w+)?\s*\)[^;]*(?:tool|execute|invoke|call)/gi,
|
|
651
|
+
baseSeverity: 'high',
|
|
652
|
+
description: 'LLM output JSON-parsed and used as tool parameters. Parsed structure could contain malicious fields.',
|
|
653
|
+
suggestedFix: 'Validate parsed JSON against expected schema: const params = toolParamsSchema.parse(JSON.parse(llmOutput))',
|
|
654
|
+
framework: 'generic',
|
|
655
|
+
},
|
|
656
|
+
];
|
|
657
|
+
/**
|
|
658
|
+
* Phase 6 Task 2: Tool Error Message Injection Patterns
|
|
659
|
+
* Detect raw error exposure to LLM that could leak system information or enable injection
|
|
660
|
+
*/
|
|
661
|
+
const TOOL_ERROR_INJECTION_PATTERNS = [
|
|
662
|
+
// Raw error message in tool response
|
|
663
|
+
{
|
|
664
|
+
name: 'Raw error in tool response',
|
|
665
|
+
pattern: /catch\s*\([^)]*\)\s*\{[^}]*(return|resolve)\s*\([^)]*error\.(message|stack|toString)/gi,
|
|
666
|
+
baseSeverity: 'medium',
|
|
667
|
+
description: 'Raw error messages returned to LLM could leak system information (paths, credentials, internal state) or be used for prompt injection attacks.',
|
|
668
|
+
suggestedFix: 'Return sanitized, generic error messages to LLM. Log detailed errors server-side: catch (e) { logger.error(e); return { error: "Operation failed" } }',
|
|
669
|
+
framework: 'generic',
|
|
670
|
+
},
|
|
671
|
+
// Error object in tool return
|
|
672
|
+
{
|
|
673
|
+
name: 'Error object in tool return',
|
|
674
|
+
pattern: /return\s*\{[^}]*error\s*:\s*(?:e|err|error)(?:\s*,|\s*\})/gi,
|
|
675
|
+
baseSeverity: 'medium',
|
|
676
|
+
description: 'Error object returned directly to LLM. Full error objects may contain sensitive stack traces or internal details.',
|
|
677
|
+
suggestedFix: 'Return only error message or generic status: return { error: "Failed to process request", code: "OPERATION_FAILED" }',
|
|
678
|
+
framework: 'generic',
|
|
679
|
+
},
|
|
680
|
+
// Stack trace in response
|
|
681
|
+
{
|
|
682
|
+
name: 'Stack trace in tool response',
|
|
683
|
+
pattern: /return\s*\{[^}]*(?:stack|stackTrace|trace)\s*:\s*(?:e|err|error)\./gi,
|
|
684
|
+
baseSeverity: 'high',
|
|
685
|
+
description: 'Stack trace returned to LLM. Stack traces expose internal code paths, file structures, and potentially sensitive data.',
|
|
686
|
+
suggestedFix: 'Never return stack traces to LLM. Log them server-side for debugging: logger.error({ stack: e.stack }); return { error: "Internal error" }',
|
|
687
|
+
framework: 'generic',
|
|
688
|
+
},
|
|
689
|
+
// Exception details in resolve/reject
|
|
690
|
+
{
|
|
691
|
+
name: 'Exception details in promise resolution',
|
|
692
|
+
pattern: /(?:resolve|reject)\s*\(\s*\{[^}]*(?:exception|error|e)\s*:\s*(?:e|err|error)(?:\.message|\.stack)?/gi,
|
|
693
|
+
baseSeverity: 'medium',
|
|
694
|
+
description: 'Exception details passed in promise resolution. Error information flows to LLM context.',
|
|
695
|
+
suggestedFix: 'Sanitize error information before resolving: resolve({ success: false, error: sanitizeError(e) })',
|
|
696
|
+
framework: 'generic',
|
|
697
|
+
},
|
|
698
|
+
// String interpolation with error
|
|
699
|
+
{
|
|
700
|
+
name: 'Error interpolated in response string',
|
|
701
|
+
pattern: /return\s*[`'"].*\$\{(?:e|err|error)(?:\.message|\.stack)?\}.*[`'"]/gi,
|
|
702
|
+
baseSeverity: 'medium',
|
|
703
|
+
description: 'Error details interpolated into response string. Raw error text could contain sensitive information.',
|
|
704
|
+
suggestedFix: 'Use generic error messages: return `Operation failed: ${getGenericErrorMessage(e.code)}`',
|
|
705
|
+
framework: 'generic',
|
|
706
|
+
},
|
|
707
|
+
];
|
|
708
|
+
/**
|
|
709
|
+
* Phase 5: Recursive Agent Patterns
|
|
710
|
+
* Detect unbounded agent recursion and self-spawning patterns
|
|
711
|
+
*/
|
|
712
|
+
const RECURSIVE_AGENT_PATTERNS = [
|
|
713
|
+
// ========== Unbounded Agent Recursion ==========
|
|
714
|
+
{
|
|
715
|
+
name: 'Recursive agent call without depth limit',
|
|
716
|
+
pattern: /(?:async\s+)?function\s+(?:run|execute|process|handle)?Agent\s*\([^)]*\)\s*\{[\s\S]{0,200}(?:run|execute|process|handle)?Agent\s*\((?![^)]*depth|[^)]*level|[^)]*recursion)/gi,
|
|
717
|
+
baseSeverity: 'high',
|
|
718
|
+
description: 'Agent function calls itself without visible depth parameter. Could recurse indefinitely.',
|
|
719
|
+
suggestedFix: 'Add depth limit: async function runAgent(task, depth = 0) { if (depth > MAX_DEPTH) throw new Error("Max depth"); await runAgent(subtask, depth + 1) }',
|
|
720
|
+
framework: 'generic',
|
|
721
|
+
},
|
|
722
|
+
{
|
|
723
|
+
name: 'Agent spawns sub-agent without limit',
|
|
724
|
+
pattern: /(?:spawn|create|launch|start)(?:Agent|Worker|Task)\s*\([^)]*\)(?![\s\S]{0,50}(?:depth|level|count|limit|max|MAX))/gi,
|
|
725
|
+
baseSeverity: 'medium',
|
|
726
|
+
description: 'Sub-agent spawned without visible depth or count limit. Could lead to unbounded agent proliferation.',
|
|
727
|
+
suggestedFix: 'Track agent depth/count: if (agentCount >= MAX_AGENTS || depth > MAX_DEPTH) throw new Error("Agent limit reached")',
|
|
728
|
+
framework: 'generic',
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
name: 'Recursive task processing without bounds',
|
|
732
|
+
pattern: /(?:result|response|output)\.(?:subtasks?|children|next|followUp)\s*\.(?:forEach|map|for)\s*\([^)]*(?:process|run|execute)(?:Task|Agent)/gi,
|
|
733
|
+
baseSeverity: 'high',
|
|
734
|
+
description: 'Tasks processed recursively based on agent output. Agent could generate unlimited subtasks.',
|
|
735
|
+
suggestedFix: 'Limit subtask count: const subtasks = result.subtasks.slice(0, MAX_SUBTASKS). Track total processed tasks.',
|
|
736
|
+
framework: 'generic',
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
name: 'Self-improvement loop without termination',
|
|
740
|
+
pattern: /while\s*\([^)]*(?:improve|optimize|refine|enhance)[^)]*\)\s*\{[\s\S]{0,100}(?:agent|model|llm)/gi,
|
|
741
|
+
baseSeverity: 'high',
|
|
742
|
+
description: 'Agent self-improvement loop without clear termination. Could run indefinitely.',
|
|
743
|
+
suggestedFix: 'Add termination conditions: while (iterations < MAX_ITERATIONS && !satisfactory) { ... iterations++ }',
|
|
744
|
+
framework: 'generic',
|
|
745
|
+
},
|
|
746
|
+
{
|
|
747
|
+
name: 'CrewAI agent delegation without depth',
|
|
748
|
+
pattern: /\.delegate\s*\(\s*[^)]*\)(?![\s\S]{0,30}(?:max_delegation|delegation_limit|depth))/gi,
|
|
749
|
+
baseSeverity: 'medium',
|
|
750
|
+
description: 'CrewAI agent delegation without depth limit. Agents could delegate indefinitely to each other.',
|
|
751
|
+
suggestedFix: 'Set delegation limits in agent config: Agent(..., max_delegation_depth=3)',
|
|
752
|
+
framework: 'crewai',
|
|
753
|
+
},
|
|
754
|
+
{
|
|
755
|
+
name: 'LangGraph recursive edge without limit',
|
|
756
|
+
pattern: /\.add_edge\s*\([^)]*,\s*(?:SAME_NODE|self|current_node)/gi,
|
|
757
|
+
baseSeverity: 'medium',
|
|
758
|
+
description: 'LangGraph edge points back to same node without visible limit. Could create infinite loops.',
|
|
759
|
+
suggestedFix: 'Add iteration tracking and conditional edges with max_iterations check.',
|
|
760
|
+
framework: 'langchain',
|
|
761
|
+
},
|
|
762
|
+
];
|
|
763
|
+
/**
|
|
764
|
+
* Excessive agency patterns for unbounded agent autonomy
|
|
765
|
+
*/
|
|
766
|
+
const EXCESSIVE_AGENCY_PATTERNS = [
|
|
767
|
+
// ========== Generic Unbounded Loops ==========
|
|
768
|
+
{
|
|
769
|
+
name: 'Unbounded agent loop',
|
|
770
|
+
pattern: /while\s*\(\s*(?:true|1|True)\s*\)[\s\S]{0,100}(?:agent|step|run|execute|iterate)/gi,
|
|
771
|
+
baseSeverity: 'high',
|
|
772
|
+
description: 'Agent runs in an unbounded loop without explicit termination condition. This can lead to infinite execution, resource exhaustion, or runaway costs.',
|
|
773
|
+
suggestedFix: 'Add maxIterations limit: while (iterations < maxIterations) { ... }. Consider adding timeout and cost limits.',
|
|
774
|
+
framework: 'generic',
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
name: 'No iteration limit configured',
|
|
778
|
+
pattern: /maxIterations\s*[:=]\s*(?:-1|null|undefined|None|Infinity|float\s*\(\s*['"`]inf)/gi,
|
|
779
|
+
baseSeverity: 'high',
|
|
780
|
+
description: 'Agent configured with no iteration limit. This allows unbounded execution which can consume excessive resources.',
|
|
781
|
+
suggestedFix: 'Set a reasonable iteration limit: maxIterations: 10 (adjust based on your use case).',
|
|
782
|
+
framework: 'generic',
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
name: 'No timeout configured',
|
|
786
|
+
pattern: /timeout\s*[:=]\s*(?:-1|0|null|undefined|None|false|False)/gi,
|
|
787
|
+
baseSeverity: 'medium',
|
|
788
|
+
description: 'Agent timeout is disabled or set to zero. Long-running agents can hang indefinitely.',
|
|
789
|
+
suggestedFix: 'Configure a reasonable timeout: timeout: 300000 (5 minutes). Adjust based on expected execution time.',
|
|
790
|
+
framework: 'generic',
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
name: 'Auto-approve without human oversight',
|
|
794
|
+
pattern: /(?:autoApprove|auto_approve)\s*[:=]\s*(?:true|True)/gi,
|
|
795
|
+
baseSeverity: 'high',
|
|
796
|
+
description: 'Agent auto-approves actions without human review. Combined with destructive capabilities, this is dangerous.',
|
|
797
|
+
suggestedFix: 'Enable human-in-the-loop for sensitive operations: autoApprove: false, or implement approval workflows for destructive actions.',
|
|
798
|
+
framework: 'generic',
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
name: 'Human-in-loop disabled',
|
|
802
|
+
pattern: /(?:humanInLoop|human_in_loop)\s*[:=]\s*(?:false|False)/gi,
|
|
803
|
+
baseSeverity: 'medium',
|
|
804
|
+
description: 'Human oversight is explicitly disabled. The agent can take actions without human review.',
|
|
805
|
+
suggestedFix: 'Enable human-in-the-loop for sensitive operations: humanInLoop: true. Add confirmation prompts for destructive actions.',
|
|
806
|
+
framework: 'generic',
|
|
807
|
+
},
|
|
808
|
+
// ========== CrewAI Specific ==========
|
|
809
|
+
{
|
|
810
|
+
name: 'CrewAI unsafe code execution mode',
|
|
811
|
+
pattern: /code_execution_mode\s*[:=]\s*['"`]unsafe['"`]/gi,
|
|
812
|
+
baseSeverity: 'critical',
|
|
813
|
+
description: 'CrewAI agent configured with unsafe code execution mode. This allows arbitrary code execution without sandboxing.',
|
|
814
|
+
suggestedFix: 'Use safe mode: code_execution_mode="safe". This runs code in a restricted environment.',
|
|
815
|
+
framework: 'crewai',
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
name: 'CrewAI code execution without Docker',
|
|
819
|
+
pattern: /allow_code_execution\s*[:=]\s*True(?![\s\S]{0,50}use_docker\s*[:=]\s*True)/gi,
|
|
820
|
+
baseSeverity: 'high',
|
|
821
|
+
description: 'CrewAI agent allows code execution without Docker containerization. Code runs directly on the host system.',
|
|
822
|
+
suggestedFix: 'Enable Docker for code execution: Agent(..., allow_code_execution=True, code_execution_config={"use_docker": True})',
|
|
823
|
+
framework: 'crewai',
|
|
824
|
+
},
|
|
825
|
+
// ========== AutoGen Specific ==========
|
|
826
|
+
{
|
|
827
|
+
name: 'AutoGen use_docker=False',
|
|
828
|
+
pattern: /(?:code_execution_config|LocalCommandLineCodeExecutor)\s*[\s\S]{0,50}use_docker\s*[:=]\s*False/gi,
|
|
829
|
+
baseSeverity: 'critical',
|
|
830
|
+
description: 'AutoGen code execution configured without Docker. Code executes directly on the host, enabling full system access.',
|
|
831
|
+
suggestedFix: 'Use Docker: code_execution_config={"use_docker": True}. Or use DockerCommandLineCodeExecutor for sandboxed execution.',
|
|
832
|
+
framework: 'autogen',
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
name: 'AutoGen NEVER human input mode',
|
|
836
|
+
pattern: /human_input_mode\s*[:=]\s*['"`]NEVER['"`]/gi,
|
|
837
|
+
baseSeverity: 'high',
|
|
838
|
+
description: 'AutoGen agent configured to never request human input. Agent can execute indefinitely without human oversight.',
|
|
839
|
+
suggestedFix: 'Use "ALWAYS" or "TERMINATE" for human_input_mode. "TERMINATE" allows agent to complete but requests input on termination.',
|
|
840
|
+
framework: 'autogen',
|
|
841
|
+
},
|
|
842
|
+
{
|
|
843
|
+
name: 'AutoGen UserProxyAgent without reply limit',
|
|
844
|
+
pattern: /UserProxyAgent\s*\((?![^)]*max_consecutive_auto_reply)[^)]*\)/gi,
|
|
845
|
+
baseSeverity: 'medium',
|
|
846
|
+
description: 'AutoGen UserProxyAgent without max_consecutive_auto_reply limit. Agent can auto-reply indefinitely.',
|
|
847
|
+
suggestedFix: 'Add reply limit: UserProxyAgent(..., max_consecutive_auto_reply=10). Adjust limit based on expected conversation length.',
|
|
848
|
+
framework: 'autogen',
|
|
849
|
+
},
|
|
850
|
+
// ========== LangChain Specific ==========
|
|
851
|
+
{
|
|
852
|
+
name: 'LangChain AgentExecutor without limits',
|
|
853
|
+
pattern: /AgentExecutor\s*\([^)]*(?!max_iterations)[^)]*\)/gi,
|
|
854
|
+
baseSeverity: 'medium',
|
|
855
|
+
description: 'LangChain AgentExecutor without max_iterations. Agent can loop indefinitely on complex tasks.',
|
|
856
|
+
suggestedFix: 'Set iteration limit: AgentExecutor(..., max_iterations=15). Add early_stopping_method="generate" to gracefully stop.',
|
|
857
|
+
framework: 'langchain',
|
|
858
|
+
},
|
|
859
|
+
// ========== Overpermissioned Agents ==========
|
|
860
|
+
{
|
|
861
|
+
name: 'Agent with excessive tools',
|
|
862
|
+
pattern: /tools\s*[:=]\s*\[(?:[^\]]*,){10,}[^\]]*\]/gi,
|
|
863
|
+
baseSeverity: 'medium',
|
|
864
|
+
description: 'Agent configured with more than 10 tools. Overpermissioned agents have larger attack surface if compromised via prompt injection.',
|
|
865
|
+
suggestedFix: 'Follow principle of least privilege. Split into specialized agents with focused tool sets. Remove unused tools.',
|
|
866
|
+
framework: 'generic',
|
|
867
|
+
},
|
|
868
|
+
];
|
|
869
|
+
/**
|
|
870
|
+
* Patterns for missing authorization in tools
|
|
871
|
+
*/
|
|
872
|
+
const MISSING_AUTH_PATTERNS = [
|
|
873
|
+
{
|
|
874
|
+
name: 'Tool without user context',
|
|
875
|
+
pattern: /(?:@tool|defineTool|createTool|\.registerTool|\.addTool)\s*\([^)]*(?:async\s+)?(?:function|\().*(?:create|update|delete|modify|write|send)/gi,
|
|
876
|
+
riskType: 'database',
|
|
877
|
+
baseSeverity: 'medium',
|
|
878
|
+
description: 'Tool performs write operations but may not verify user context. Actions could be performed as wrong user.',
|
|
879
|
+
suggestedFix: 'Pass userId as required parameter. Verify user owns/can access the resource before modification.',
|
|
880
|
+
requiresUserContext: true,
|
|
881
|
+
},
|
|
882
|
+
];
|
|
883
|
+
// ============================================================================
|
|
884
|
+
// Main Detection Function
|
|
885
|
+
// ============================================================================
|
|
886
|
+
/**
|
|
887
|
+
* Main detection function for AI agent tool permission issues
|
|
888
|
+
*/
|
|
889
|
+
function detectAIAgentTools(content, filePath, options) {
|
|
890
|
+
const vulnerabilities = [];
|
|
891
|
+
// Skip non-applicable files
|
|
892
|
+
if ((0, file_classifier_1.isScannerOrFixtureFile)(filePath))
|
|
893
|
+
return vulnerabilities;
|
|
894
|
+
// Only scan files that appear to have agent/tool definitions
|
|
895
|
+
if (!isAgentOrToolFile(filePath, content)) {
|
|
896
|
+
return vulnerabilities;
|
|
897
|
+
}
|
|
898
|
+
const lines = options?.parsed?.lines ?? content.split('\n');
|
|
899
|
+
const isTestFile = (0, file_classifier_1.isTestOrMockFile)(filePath);
|
|
900
|
+
const isExample = (0, file_classifier_1.isExampleDirectory)(filePath);
|
|
901
|
+
const isLibrary = (0, file_classifier_1.isLibraryCode)(filePath);
|
|
902
|
+
// Scan for overly permissive tool patterns
|
|
903
|
+
for (const pattern of OVERPERMISSIVE_TOOL_PATTERNS) {
|
|
904
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
905
|
+
let match;
|
|
906
|
+
while ((match = regex.exec(content)) !== null) {
|
|
907
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
908
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
909
|
+
// Skip comments
|
|
910
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
911
|
+
continue;
|
|
912
|
+
// Get tool context
|
|
913
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
914
|
+
// Check for mitigations (strong vs weak)
|
|
915
|
+
const hasStrong = hasStrongRestrictions(context);
|
|
916
|
+
const hasWeak = hasAccessRestrictions(context);
|
|
917
|
+
const hasUserContext = hasUserContextVerification(context);
|
|
918
|
+
const hasTenantContext = hasTenantContextVerification(context);
|
|
919
|
+
// Determine if issue is fully mitigated
|
|
920
|
+
let isMitigated = true;
|
|
921
|
+
let hasPartialMitigation = false;
|
|
922
|
+
const missingMitigations = [];
|
|
923
|
+
if (pattern.requiresRestrictions) {
|
|
924
|
+
if (hasStrong) {
|
|
925
|
+
// Strong restrictions = fully mitigated for this requirement
|
|
926
|
+
}
|
|
927
|
+
else if (hasWeak) {
|
|
928
|
+
// Weak restrictions = partial mitigation
|
|
929
|
+
hasPartialMitigation = true;
|
|
930
|
+
missingMitigations.push('verified access restrictions (found mentions but not implementation)');
|
|
931
|
+
isMitigated = false;
|
|
932
|
+
}
|
|
933
|
+
else {
|
|
934
|
+
isMitigated = false;
|
|
935
|
+
missingMitigations.push('access restrictions');
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
if (pattern.requiresUserContext && !hasUserContext) {
|
|
939
|
+
isMitigated = false;
|
|
940
|
+
missingMitigations.push('user context verification');
|
|
941
|
+
}
|
|
942
|
+
if (pattern.requiresTenantContext && !hasTenantContext) {
|
|
943
|
+
isMitigated = false;
|
|
944
|
+
missingMitigations.push('tenant/org context verification');
|
|
945
|
+
}
|
|
946
|
+
// Skip if all required mitigations are present with strong verification
|
|
947
|
+
if (isMitigated)
|
|
948
|
+
continue;
|
|
949
|
+
// Calculate severity
|
|
950
|
+
let severity = pattern.baseSeverity;
|
|
951
|
+
const isMCPSafe = isMCPFileWithSafePatterns(content, filePath);
|
|
952
|
+
if (isTestFile) {
|
|
953
|
+
severity = 'info';
|
|
954
|
+
}
|
|
955
|
+
else if (isExample) {
|
|
956
|
+
// Example/demo code - downgrade to info
|
|
957
|
+
severity = 'info';
|
|
958
|
+
}
|
|
959
|
+
else if (isLibrary) {
|
|
960
|
+
// Library code - tool definitions are intentionally flexible
|
|
961
|
+
// Consumers add restrictions when they use the tools
|
|
962
|
+
severity = 'info';
|
|
963
|
+
}
|
|
964
|
+
else if (isMCPSafe) {
|
|
965
|
+
// MCP file with proper security controls (sanitization + authorization)
|
|
966
|
+
severity = 'info';
|
|
967
|
+
}
|
|
968
|
+
else if (hasPartialMitigation || hasUserContext || hasTenantContext) {
|
|
969
|
+
// Partial mitigation - downgrade
|
|
970
|
+
if (severity === 'critical')
|
|
971
|
+
severity = 'high';
|
|
972
|
+
else if (severity === 'high')
|
|
973
|
+
severity = 'medium';
|
|
974
|
+
}
|
|
975
|
+
// Build description
|
|
976
|
+
let description = pattern.description;
|
|
977
|
+
if (missingMitigations.length > 0) {
|
|
978
|
+
description += ` Missing: ${missingMitigations.join(', ')}.`;
|
|
979
|
+
}
|
|
980
|
+
if (isTestFile) {
|
|
981
|
+
description += ' (In test file.)';
|
|
982
|
+
}
|
|
983
|
+
else if (isExample) {
|
|
984
|
+
description += ' (In example/demo directory - not production code.)';
|
|
985
|
+
}
|
|
986
|
+
else if (isLibrary) {
|
|
987
|
+
description += ' (Library code - tool definitions are generic; consumers add restrictions.)';
|
|
988
|
+
}
|
|
989
|
+
else if (isMCPSafe) {
|
|
990
|
+
description += ' (MCP file with sanitization and authorization controls detected.)';
|
|
991
|
+
}
|
|
992
|
+
vulnerabilities.push({
|
|
993
|
+
id: `ai-tool-${filePath}-${lineNumber}-${pattern.riskType}`,
|
|
994
|
+
filePath,
|
|
995
|
+
lineNumber,
|
|
996
|
+
lineContent,
|
|
997
|
+
severity,
|
|
998
|
+
category: 'ai_overpermissive_tool',
|
|
999
|
+
title: pattern.name,
|
|
1000
|
+
description,
|
|
1001
|
+
suggestedFix: pattern.suggestedFix,
|
|
1002
|
+
confidence: 'medium',
|
|
1003
|
+
layer: 2,
|
|
1004
|
+
source: 'ai_code',
|
|
1005
|
+
requiresAIValidation: true, // Always validate - context dependent
|
|
1006
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
// Scan for missing authorization patterns
|
|
1011
|
+
for (const pattern of MISSING_AUTH_PATTERNS) {
|
|
1012
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1013
|
+
let match;
|
|
1014
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1015
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1016
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1017
|
+
// Skip comments
|
|
1018
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1019
|
+
continue;
|
|
1020
|
+
// Get tool context
|
|
1021
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1022
|
+
// Check if user context is verified
|
|
1023
|
+
const hasUserContext = hasUserContextVerification(context);
|
|
1024
|
+
// Skip if user context is present
|
|
1025
|
+
if (hasUserContext)
|
|
1026
|
+
continue;
|
|
1027
|
+
let severity = pattern.baseSeverity;
|
|
1028
|
+
let description = pattern.description;
|
|
1029
|
+
if (isTestFile) {
|
|
1030
|
+
severity = 'info';
|
|
1031
|
+
description += ' (In test file.)';
|
|
1032
|
+
}
|
|
1033
|
+
vulnerabilities.push({
|
|
1034
|
+
id: `ai-tool-auth-${filePath}-${lineNumber}`,
|
|
1035
|
+
filePath,
|
|
1036
|
+
lineNumber,
|
|
1037
|
+
lineContent,
|
|
1038
|
+
severity,
|
|
1039
|
+
category: 'ai_overpermissive_tool',
|
|
1040
|
+
title: pattern.name,
|
|
1041
|
+
description,
|
|
1042
|
+
suggestedFix: pattern.suggestedFix,
|
|
1043
|
+
confidence: 'low', // Lower confidence - needs context
|
|
1044
|
+
layer: 2,
|
|
1045
|
+
source: 'ai_code',
|
|
1046
|
+
requiresAIValidation: true,
|
|
1047
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
// Scan for excessive agency patterns (Phase 2)
|
|
1052
|
+
for (const pattern of EXCESSIVE_AGENCY_PATTERNS) {
|
|
1053
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1054
|
+
let match;
|
|
1055
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1056
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1057
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1058
|
+
// Skip comments
|
|
1059
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1060
|
+
continue;
|
|
1061
|
+
// Get surrounding context for mitigation checks
|
|
1062
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1063
|
+
// Check for mitigations
|
|
1064
|
+
let isMitigated = false;
|
|
1065
|
+
let isPartiallyMitigated = false;
|
|
1066
|
+
let description = pattern.description;
|
|
1067
|
+
// Check iteration limits
|
|
1068
|
+
if (hasIterationLimits(context)) {
|
|
1069
|
+
isPartiallyMitigated = true;
|
|
1070
|
+
description += ' (Iteration limits detected nearby.)';
|
|
1071
|
+
}
|
|
1072
|
+
// Check timeout configuration
|
|
1073
|
+
if (hasTimeoutConfigured(context)) {
|
|
1074
|
+
isPartiallyMitigated = true;
|
|
1075
|
+
description += ' (Timeout configured.)';
|
|
1076
|
+
}
|
|
1077
|
+
// Check human-in-the-loop
|
|
1078
|
+
if (hasHumanInLoop(context)) {
|
|
1079
|
+
isPartiallyMitigated = true;
|
|
1080
|
+
description += ' (Human-in-loop enabled.)';
|
|
1081
|
+
}
|
|
1082
|
+
// Check Docker for code execution patterns
|
|
1083
|
+
if (pattern.framework === 'crewai' || pattern.framework === 'autogen') {
|
|
1084
|
+
if (hasDockerConfigured(context)) {
|
|
1085
|
+
isMitigated = true;
|
|
1086
|
+
description += ' (Docker containerization detected.)';
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
// Check budget limits
|
|
1090
|
+
if (hasBudgetLimits(context)) {
|
|
1091
|
+
isPartiallyMitigated = true;
|
|
1092
|
+
description += ' (Budget limits configured.)';
|
|
1093
|
+
}
|
|
1094
|
+
// Calculate severity
|
|
1095
|
+
let severity = pattern.baseSeverity;
|
|
1096
|
+
const isMCPSafe = isMCPFileWithSafePatterns(content, filePath);
|
|
1097
|
+
if (isMitigated) {
|
|
1098
|
+
severity = 'info';
|
|
1099
|
+
}
|
|
1100
|
+
else if (isTestFile) {
|
|
1101
|
+
severity = 'info';
|
|
1102
|
+
description += ' (In test file.)';
|
|
1103
|
+
}
|
|
1104
|
+
else if (isExample) {
|
|
1105
|
+
severity = 'info';
|
|
1106
|
+
description += ' (In example/demo directory.)';
|
|
1107
|
+
}
|
|
1108
|
+
else if (isLibrary) {
|
|
1109
|
+
severity = 'info';
|
|
1110
|
+
description += ' (Library code.)';
|
|
1111
|
+
}
|
|
1112
|
+
else if (isPartiallyMitigated) {
|
|
1113
|
+
// Downgrade if partial mitigations present
|
|
1114
|
+
if (severity === 'critical')
|
|
1115
|
+
severity = 'high';
|
|
1116
|
+
else if (severity === 'high')
|
|
1117
|
+
severity = 'medium';
|
|
1118
|
+
else if (severity === 'medium')
|
|
1119
|
+
severity = 'low';
|
|
1120
|
+
}
|
|
1121
|
+
// Skip fully mitigated or info-level in non-agent files
|
|
1122
|
+
if (isMitigated && severity === 'info')
|
|
1123
|
+
continue;
|
|
1124
|
+
vulnerabilities.push({
|
|
1125
|
+
id: `ai-agency-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
1126
|
+
filePath,
|
|
1127
|
+
lineNumber,
|
|
1128
|
+
lineContent,
|
|
1129
|
+
severity,
|
|
1130
|
+
category: 'ai_excessive_agency',
|
|
1131
|
+
title: pattern.name,
|
|
1132
|
+
description,
|
|
1133
|
+
suggestedFix: pattern.suggestedFix,
|
|
1134
|
+
confidence: severity === 'info' ? 'low' : 'medium',
|
|
1135
|
+
layer: 2,
|
|
1136
|
+
source: 'ai_code',
|
|
1137
|
+
requiresAIValidation: severity !== 'info' && severity !== 'low',
|
|
1138
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
// Phase 5: Scan for LLM output flow patterns (Task 1)
|
|
1143
|
+
for (const pattern of LLM_OUTPUT_FLOW_PATTERNS) {
|
|
1144
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1145
|
+
let match;
|
|
1146
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1147
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1148
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1149
|
+
// Skip comments
|
|
1150
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1151
|
+
continue;
|
|
1152
|
+
// Get surrounding context
|
|
1153
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1154
|
+
// Check for validation/allowlist mitigations
|
|
1155
|
+
const hasValidation = /(?:allowlist|whitelist|ALLOWED_|validTools|VALID_TOOLS|allowedTools|validateTool|isValidTool|includes|has)\s*\(/i.test(context);
|
|
1156
|
+
const hasAllowlistCheck = /if\s*\(\s*!?\s*(?:ALLOWED|VALID|SAFE|permitted).*(?:includes|has|indexOf)/i.test(context);
|
|
1157
|
+
let description = pattern.description;
|
|
1158
|
+
let severity = pattern.baseSeverity;
|
|
1159
|
+
if (hasValidation || hasAllowlistCheck) {
|
|
1160
|
+
severity = severity === 'critical' ? 'medium' : 'low';
|
|
1161
|
+
description += ' (Validation/allowlist detected nearby - verify it covers this case.)';
|
|
1162
|
+
}
|
|
1163
|
+
if (isTestFile) {
|
|
1164
|
+
severity = 'info';
|
|
1165
|
+
description += ' (In test file.)';
|
|
1166
|
+
}
|
|
1167
|
+
else if (isExample) {
|
|
1168
|
+
severity = 'info';
|
|
1169
|
+
description += ' (In example/demo directory.)';
|
|
1170
|
+
}
|
|
1171
|
+
else if (isLibrary) {
|
|
1172
|
+
severity = 'info';
|
|
1173
|
+
description += ' (Library code.)';
|
|
1174
|
+
}
|
|
1175
|
+
vulnerabilities.push({
|
|
1176
|
+
id: `ai-llm-flow-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
1177
|
+
filePath,
|
|
1178
|
+
lineNumber,
|
|
1179
|
+
lineContent,
|
|
1180
|
+
severity,
|
|
1181
|
+
category: 'ai_excessive_agency',
|
|
1182
|
+
title: pattern.name,
|
|
1183
|
+
description,
|
|
1184
|
+
suggestedFix: pattern.suggestedFix,
|
|
1185
|
+
confidence: severity === 'critical' ? 'high' : 'medium',
|
|
1186
|
+
layer: 2,
|
|
1187
|
+
source: 'ai_code',
|
|
1188
|
+
requiresAIValidation: severity !== 'info',
|
|
1189
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
// Phase 5: Scan for tool permission accumulation patterns (Task 2)
|
|
1194
|
+
for (const pattern of TOOL_ACCUMULATION_PATTERNS) {
|
|
1195
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1196
|
+
let match;
|
|
1197
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1198
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1199
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1200
|
+
// Skip comments
|
|
1201
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1202
|
+
continue;
|
|
1203
|
+
// Skip UI array building patterns (not actual AI tool registration)
|
|
1204
|
+
if (pattern.name === 'Tool array push without limit check') {
|
|
1205
|
+
// Check if this is in a selector or UI configuration builder
|
|
1206
|
+
const isUIPattern =
|
|
1207
|
+
// In selectors (zustand/redux pattern)
|
|
1208
|
+
/selectors?\.ts$/i.test(filePath) ||
|
|
1209
|
+
// In store configuration
|
|
1210
|
+
/store\/.*\/selectors/i.test(filePath) ||
|
|
1211
|
+
// Building manifest/config arrays
|
|
1212
|
+
/manifest\s*:/i.test(lineContent) ||
|
|
1213
|
+
/identifier\s*:/i.test(lineContent) ||
|
|
1214
|
+
// Map/forEach building UI arrays
|
|
1215
|
+
/\.map\s*\([^)]*=>\s*\{[\s\S]{0,100}tools\.push/i.test(content.substring(Math.max(0, match.index - 200), match.index + 100));
|
|
1216
|
+
if (isUIPattern) {
|
|
1217
|
+
continue; // Skip - this is building a UI configuration array
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
// Get surrounding context
|
|
1221
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1222
|
+
// Check for limits and authorization
|
|
1223
|
+
const hasLimits = /(?:max|limit|MAX_|LIMIT_|\.length\s*[<>])/i.test(context);
|
|
1224
|
+
const hasAuthCheck = /(?:if\s*\(.*(?:auth|permission|role|isAdmin|canRegister)|throw.*(?:Unauthorized|Forbidden))/i.test(context);
|
|
1225
|
+
let description = pattern.description;
|
|
1226
|
+
let severity = pattern.baseSeverity;
|
|
1227
|
+
if (hasLimits) {
|
|
1228
|
+
severity = severity === 'critical' ? 'high' : severity === 'high' ? 'medium' : 'low';
|
|
1229
|
+
description += ' (Limit check detected nearby.)';
|
|
1230
|
+
}
|
|
1231
|
+
if (hasAuthCheck) {
|
|
1232
|
+
severity = severity === 'critical' ? 'high' : severity === 'high' ? 'medium' : 'low';
|
|
1233
|
+
description += ' (Authorization check detected.)';
|
|
1234
|
+
}
|
|
1235
|
+
if (isTestFile) {
|
|
1236
|
+
severity = 'info';
|
|
1237
|
+
description += ' (In test file.)';
|
|
1238
|
+
}
|
|
1239
|
+
else if (isExample) {
|
|
1240
|
+
severity = 'info';
|
|
1241
|
+
description += ' (In example/demo directory.)';
|
|
1242
|
+
}
|
|
1243
|
+
else if (isLibrary) {
|
|
1244
|
+
severity = 'info';
|
|
1245
|
+
description += ' (Library code.)';
|
|
1246
|
+
}
|
|
1247
|
+
vulnerabilities.push({
|
|
1248
|
+
id: `ai-tool-accum-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
1249
|
+
filePath,
|
|
1250
|
+
lineNumber,
|
|
1251
|
+
lineContent,
|
|
1252
|
+
severity,
|
|
1253
|
+
category: 'ai_excessive_agency',
|
|
1254
|
+
title: pattern.name,
|
|
1255
|
+
description,
|
|
1256
|
+
suggestedFix: pattern.suggestedFix,
|
|
1257
|
+
confidence: 'medium',
|
|
1258
|
+
layer: 2,
|
|
1259
|
+
source: 'ai_code',
|
|
1260
|
+
requiresAIValidation: severity !== 'info' && severity !== 'low',
|
|
1261
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1262
|
+
});
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
// Phase 5: Scan for database write scoping patterns (Task 3)
|
|
1266
|
+
for (const pattern of DB_WRITE_SCOPING_PATTERNS) {
|
|
1267
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1268
|
+
let match;
|
|
1269
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1270
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1271
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1272
|
+
// Skip comments
|
|
1273
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1274
|
+
continue;
|
|
1275
|
+
// Get surrounding context
|
|
1276
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1277
|
+
// Check for user/tenant scoping
|
|
1278
|
+
const hasUserScoping = hasUserContextVerification(context);
|
|
1279
|
+
const hasTenantScoping = hasTenantContextVerification(context);
|
|
1280
|
+
// Skip if properly scoped
|
|
1281
|
+
if (hasUserScoping && hasTenantScoping)
|
|
1282
|
+
continue;
|
|
1283
|
+
let description = pattern.description;
|
|
1284
|
+
let severity = pattern.baseSeverity;
|
|
1285
|
+
if (hasUserScoping || hasTenantScoping) {
|
|
1286
|
+
severity = severity === 'high' ? 'medium' : 'low';
|
|
1287
|
+
description += hasUserScoping ? ' (User context detected.)' : ' (Tenant context detected.)';
|
|
1288
|
+
}
|
|
1289
|
+
if (isTestFile) {
|
|
1290
|
+
severity = 'info';
|
|
1291
|
+
description += ' (In test file.)';
|
|
1292
|
+
}
|
|
1293
|
+
else if (isExample) {
|
|
1294
|
+
severity = 'info';
|
|
1295
|
+
description += ' (In example/demo directory.)';
|
|
1296
|
+
}
|
|
1297
|
+
else if (isLibrary) {
|
|
1298
|
+
severity = 'info';
|
|
1299
|
+
description += ' (Library code.)';
|
|
1300
|
+
}
|
|
1301
|
+
vulnerabilities.push({
|
|
1302
|
+
id: `ai-db-scoping-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
1303
|
+
filePath,
|
|
1304
|
+
lineNumber,
|
|
1305
|
+
lineContent,
|
|
1306
|
+
severity,
|
|
1307
|
+
category: 'ai_excessive_agency',
|
|
1308
|
+
title: pattern.name,
|
|
1309
|
+
description,
|
|
1310
|
+
suggestedFix: pattern.suggestedFix,
|
|
1311
|
+
confidence: 'medium',
|
|
1312
|
+
layer: 2,
|
|
1313
|
+
source: 'ai_code',
|
|
1314
|
+
requiresAIValidation: severity !== 'info',
|
|
1315
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
// Phase 5: Scan for recursive agent patterns (Task 4)
|
|
1320
|
+
for (const pattern of RECURSIVE_AGENT_PATTERNS) {
|
|
1321
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1322
|
+
let match;
|
|
1323
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1324
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1325
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1326
|
+
// Skip comments
|
|
1327
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1328
|
+
continue;
|
|
1329
|
+
// Skip CRUD/data operations that are NOT AI agent spawning
|
|
1330
|
+
// These are false positives in apps where "agent" means "chat assistant configuration"
|
|
1331
|
+
if (pattern.name === 'Agent spawns sub-agent without limit') {
|
|
1332
|
+
const crudPatterns = [
|
|
1333
|
+
// Service/SDK method calls - database CRUD for agent configurations
|
|
1334
|
+
/(?:service|Service|sdk|SDK|store|Store|runtime|Runtime)\.(?:create|get|update|delete)Agent/i,
|
|
1335
|
+
/\.agents\.createAgent/i, // sdk.agents.createAgent
|
|
1336
|
+
/agentService\.createAgent/i,
|
|
1337
|
+
/agentState\.createAgent/i,
|
|
1338
|
+
/marketSDK\.agents\.createAgent/i,
|
|
1339
|
+
// React event handlers creating UI entities
|
|
1340
|
+
/onClick\s*=\s*\{\s*\(\s*\)\s*=>\s*createAgent/i,
|
|
1341
|
+
// Store action patterns
|
|
1342
|
+
/await\s+(?:state|store)\w*\.createAgent/i,
|
|
1343
|
+
// Builder/Runtime patterns for UI
|
|
1344
|
+
/agentBuilder(?:Runtime)?\.createAgent/i,
|
|
1345
|
+
/groupAgentBuilderRuntime\.createAgent/i,
|
|
1346
|
+
];
|
|
1347
|
+
if (crudPatterns.some(p => p.test(lineContent))) {
|
|
1348
|
+
continue; // Skip - this is a data CRUD operation, not AI agent spawning
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
// Get surrounding context
|
|
1352
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1353
|
+
// Check for depth/count limits
|
|
1354
|
+
const hasDepthLimit = /(?:depth|level|recursion)\s*[<>]|MAX_DEPTH|maxDepth|max_depth/i.test(context);
|
|
1355
|
+
const hasCountLimit = /(?:count|iterations?)\s*[<>]|MAX_(?:AGENTS|TASKS|ITERATIONS)/i.test(context);
|
|
1356
|
+
let description = pattern.description;
|
|
1357
|
+
let severity = pattern.baseSeverity;
|
|
1358
|
+
if (hasDepthLimit || hasCountLimit) {
|
|
1359
|
+
severity = severity === 'high' ? 'medium' : 'low';
|
|
1360
|
+
description += hasDepthLimit ? ' (Depth limit detected.)' : ' (Count limit detected.)';
|
|
1361
|
+
}
|
|
1362
|
+
// Check for iteration/timeout limits
|
|
1363
|
+
if (hasIterationLimits(context) || hasTimeoutConfigured(context)) {
|
|
1364
|
+
severity = severity === 'high' ? 'medium' : severity === 'medium' ? 'low' : severity;
|
|
1365
|
+
description += ' (Iteration/timeout limits configured.)';
|
|
1366
|
+
}
|
|
1367
|
+
if (isTestFile) {
|
|
1368
|
+
severity = 'info';
|
|
1369
|
+
description += ' (In test file.)';
|
|
1370
|
+
}
|
|
1371
|
+
else if (isExample) {
|
|
1372
|
+
severity = 'info';
|
|
1373
|
+
description += ' (In example/demo directory.)';
|
|
1374
|
+
}
|
|
1375
|
+
else if (isLibrary) {
|
|
1376
|
+
severity = 'info';
|
|
1377
|
+
description += ' (Library code.)';
|
|
1378
|
+
}
|
|
1379
|
+
vulnerabilities.push({
|
|
1380
|
+
id: `ai-recursive-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
1381
|
+
filePath,
|
|
1382
|
+
lineNumber,
|
|
1383
|
+
lineContent,
|
|
1384
|
+
severity,
|
|
1385
|
+
category: 'ai_excessive_agency',
|
|
1386
|
+
title: pattern.name,
|
|
1387
|
+
description,
|
|
1388
|
+
suggestedFix: pattern.suggestedFix,
|
|
1389
|
+
confidence: 'medium',
|
|
1390
|
+
layer: 2,
|
|
1391
|
+
source: 'ai_code',
|
|
1392
|
+
requiresAIValidation: severity !== 'info' && severity !== 'low',
|
|
1393
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1394
|
+
});
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
// Phase 6: Scan for tool parameter injection patterns (Task 1)
|
|
1398
|
+
for (const pattern of TOOL_PARAMETER_INJECTION_PATTERNS) {
|
|
1399
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1400
|
+
let match;
|
|
1401
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1402
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1403
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1404
|
+
// Skip comments
|
|
1405
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1406
|
+
continue;
|
|
1407
|
+
// Get surrounding context
|
|
1408
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1409
|
+
// Check for validation/schema patterns
|
|
1410
|
+
const hasValidation = /(?:zod|yup|joi|schema|validate|safeParse|\.parse\(|validateSchema)/i.test(context);
|
|
1411
|
+
const hasSanitization = /(?:sanitize|clean|escape|filter|strip)/i.test(context);
|
|
1412
|
+
let description = pattern.description;
|
|
1413
|
+
let severity = pattern.baseSeverity;
|
|
1414
|
+
if (hasValidation) {
|
|
1415
|
+
severity = 'low';
|
|
1416
|
+
description += ' (Schema validation detected nearby - verify it covers LLM output.)';
|
|
1417
|
+
}
|
|
1418
|
+
else if (hasSanitization) {
|
|
1419
|
+
severity = severity === 'critical' ? 'high' : severity === 'high' ? 'medium' : 'low';
|
|
1420
|
+
description += ' (Sanitization detected nearby.)';
|
|
1421
|
+
}
|
|
1422
|
+
if (isTestFile) {
|
|
1423
|
+
severity = 'info';
|
|
1424
|
+
description += ' (In test file.)';
|
|
1425
|
+
}
|
|
1426
|
+
else if (isExample) {
|
|
1427
|
+
severity = 'info';
|
|
1428
|
+
description += ' (In example/demo directory.)';
|
|
1429
|
+
}
|
|
1430
|
+
else if (isLibrary) {
|
|
1431
|
+
severity = 'info';
|
|
1432
|
+
description += ' (Library code.)';
|
|
1433
|
+
}
|
|
1434
|
+
vulnerabilities.push({
|
|
1435
|
+
id: `ai-tool-param-injection-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
1436
|
+
filePath,
|
|
1437
|
+
lineNumber,
|
|
1438
|
+
lineContent,
|
|
1439
|
+
severity,
|
|
1440
|
+
category: 'ai_excessive_agency',
|
|
1441
|
+
title: pattern.name,
|
|
1442
|
+
description,
|
|
1443
|
+
suggestedFix: pattern.suggestedFix,
|
|
1444
|
+
confidence: severity === 'critical' ? 'high' : 'medium',
|
|
1445
|
+
layer: 2,
|
|
1446
|
+
source: 'ai_code',
|
|
1447
|
+
requiresAIValidation: severity !== 'info' && severity !== 'low',
|
|
1448
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
// Phase 6: Scan for tool error message injection patterns (Task 2)
|
|
1453
|
+
for (const pattern of TOOL_ERROR_INJECTION_PATTERNS) {
|
|
1454
|
+
const regex = new RegExp(pattern.pattern.source, pattern.pattern.flags);
|
|
1455
|
+
let match;
|
|
1456
|
+
while ((match = regex.exec(content)) !== null) {
|
|
1457
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
1458
|
+
const lineContent = lines[lineNumber - 1]?.trim() || '';
|
|
1459
|
+
// Skip comments
|
|
1460
|
+
if ((0, file_classifier_1.isComment)(lineContent))
|
|
1461
|
+
continue;
|
|
1462
|
+
// Get surrounding context
|
|
1463
|
+
const { context } = findToolDefinitionContext(content, lineNumber);
|
|
1464
|
+
// Check for error sanitization patterns
|
|
1465
|
+
const hasSanitizedError = /(?:sanitizeError|genericError|safeError|errorMessage\s*=\s*['"`])/i.test(context);
|
|
1466
|
+
const hasLogging = /(?:logger|console)\.\w+\s*\([^)]*(?:error|err|e)\)/i.test(context);
|
|
1467
|
+
let description = pattern.description;
|
|
1468
|
+
let severity = pattern.baseSeverity;
|
|
1469
|
+
if (hasSanitizedError) {
|
|
1470
|
+
severity = 'info';
|
|
1471
|
+
description += ' (Error sanitization detected.)';
|
|
1472
|
+
}
|
|
1473
|
+
else if (hasLogging) {
|
|
1474
|
+
severity = severity === 'high' ? 'medium' : 'low';
|
|
1475
|
+
description += ' (Server-side logging detected - verify error is sanitized in response.)';
|
|
1476
|
+
}
|
|
1477
|
+
if (isTestFile) {
|
|
1478
|
+
severity = 'info';
|
|
1479
|
+
description += ' (In test file.)';
|
|
1480
|
+
}
|
|
1481
|
+
else if (isExample) {
|
|
1482
|
+
severity = 'info';
|
|
1483
|
+
description += ' (In example/demo directory.)';
|
|
1484
|
+
}
|
|
1485
|
+
else if (isLibrary) {
|
|
1486
|
+
severity = 'info';
|
|
1487
|
+
description += ' (Library code.)';
|
|
1488
|
+
}
|
|
1489
|
+
vulnerabilities.push({
|
|
1490
|
+
id: `ai-tool-error-injection-${filePath}-${lineNumber}-${pattern.name.replace(/\s+/g, '-')}`,
|
|
1491
|
+
filePath,
|
|
1492
|
+
lineNumber,
|
|
1493
|
+
lineContent,
|
|
1494
|
+
severity,
|
|
1495
|
+
category: 'ai_excessive_agency',
|
|
1496
|
+
title: pattern.name,
|
|
1497
|
+
description,
|
|
1498
|
+
suggestedFix: pattern.suggestedFix,
|
|
1499
|
+
confidence: 'medium',
|
|
1500
|
+
layer: 2,
|
|
1501
|
+
source: 'ai_code',
|
|
1502
|
+
requiresAIValidation: severity !== 'info' && severity !== 'low',
|
|
1503
|
+
baseConfidence: BASE_CONFIDENCE,
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
return vulnerabilities;
|
|
1508
|
+
}
|
|
1509
|
+
//# sourceMappingURL=agent-tools.js.map
|