@oculum/scanner 1.0.0
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/formatters/cli-terminal.d.ts +27 -0
- package/dist/formatters/cli-terminal.d.ts.map +1 -0
- package/dist/formatters/cli-terminal.js +412 -0
- package/dist/formatters/cli-terminal.js.map +1 -0
- package/dist/formatters/github-comment.d.ts +41 -0
- package/dist/formatters/github-comment.d.ts.map +1 -0
- package/dist/formatters/github-comment.js +306 -0
- package/dist/formatters/github-comment.js.map +1 -0
- package/dist/formatters/grouping.d.ts +52 -0
- package/dist/formatters/grouping.d.ts.map +1 -0
- package/dist/formatters/grouping.js +152 -0
- package/dist/formatters/grouping.js.map +1 -0
- package/dist/formatters/index.d.ts +9 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +35 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/vscode-diagnostic.d.ts +103 -0
- package/dist/formatters/vscode-diagnostic.d.ts.map +1 -0
- package/dist/formatters/vscode-diagnostic.js +151 -0
- package/dist/formatters/vscode-diagnostic.js.map +1 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +648 -0
- package/dist/index.js.map +1 -0
- package/dist/layer1/comments.d.ts +8 -0
- package/dist/layer1/comments.d.ts.map +1 -0
- package/dist/layer1/comments.js +203 -0
- package/dist/layer1/comments.js.map +1 -0
- package/dist/layer1/config-audit.d.ts +8 -0
- package/dist/layer1/config-audit.d.ts.map +1 -0
- package/dist/layer1/config-audit.js +252 -0
- package/dist/layer1/config-audit.js.map +1 -0
- package/dist/layer1/entropy.d.ts +8 -0
- package/dist/layer1/entropy.d.ts.map +1 -0
- package/dist/layer1/entropy.js +500 -0
- package/dist/layer1/entropy.js.map +1 -0
- package/dist/layer1/file-flags.d.ts +7 -0
- package/dist/layer1/file-flags.d.ts.map +1 -0
- package/dist/layer1/file-flags.js +112 -0
- package/dist/layer1/file-flags.js.map +1 -0
- package/dist/layer1/index.d.ts +36 -0
- package/dist/layer1/index.d.ts.map +1 -0
- package/dist/layer1/index.js +132 -0
- package/dist/layer1/index.js.map +1 -0
- package/dist/layer1/patterns.d.ts +8 -0
- package/dist/layer1/patterns.d.ts.map +1 -0
- package/dist/layer1/patterns.js +482 -0
- package/dist/layer1/patterns.js.map +1 -0
- package/dist/layer1/urls.d.ts +8 -0
- package/dist/layer1/urls.d.ts.map +1 -0
- package/dist/layer1/urls.js +296 -0
- package/dist/layer1/urls.js.map +1 -0
- package/dist/layer1/weak-crypto.d.ts +7 -0
- package/dist/layer1/weak-crypto.d.ts.map +1 -0
- package/dist/layer1/weak-crypto.js +291 -0
- package/dist/layer1/weak-crypto.js.map +1 -0
- package/dist/layer2/ai-agent-tools.d.ts +19 -0
- package/dist/layer2/ai-agent-tools.d.ts.map +1 -0
- package/dist/layer2/ai-agent-tools.js +528 -0
- package/dist/layer2/ai-agent-tools.js.map +1 -0
- package/dist/layer2/ai-endpoint-protection.d.ts +36 -0
- package/dist/layer2/ai-endpoint-protection.d.ts.map +1 -0
- package/dist/layer2/ai-endpoint-protection.js +332 -0
- package/dist/layer2/ai-endpoint-protection.js.map +1 -0
- package/dist/layer2/ai-execution-sinks.d.ts +18 -0
- package/dist/layer2/ai-execution-sinks.d.ts.map +1 -0
- package/dist/layer2/ai-execution-sinks.js +496 -0
- package/dist/layer2/ai-execution-sinks.js.map +1 -0
- package/dist/layer2/ai-fingerprinting.d.ts +7 -0
- package/dist/layer2/ai-fingerprinting.d.ts.map +1 -0
- package/dist/layer2/ai-fingerprinting.js +654 -0
- package/dist/layer2/ai-fingerprinting.js.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts +19 -0
- package/dist/layer2/ai-prompt-hygiene.d.ts.map +1 -0
- package/dist/layer2/ai-prompt-hygiene.js +356 -0
- package/dist/layer2/ai-prompt-hygiene.js.map +1 -0
- package/dist/layer2/ai-rag-safety.d.ts +21 -0
- package/dist/layer2/ai-rag-safety.d.ts.map +1 -0
- package/dist/layer2/ai-rag-safety.js +459 -0
- package/dist/layer2/ai-rag-safety.js.map +1 -0
- package/dist/layer2/ai-schema-validation.d.ts +25 -0
- package/dist/layer2/ai-schema-validation.d.ts.map +1 -0
- package/dist/layer2/ai-schema-validation.js +375 -0
- package/dist/layer2/ai-schema-validation.js.map +1 -0
- package/dist/layer2/auth-antipatterns.d.ts +20 -0
- package/dist/layer2/auth-antipatterns.d.ts.map +1 -0
- package/dist/layer2/auth-antipatterns.js +333 -0
- package/dist/layer2/auth-antipatterns.js.map +1 -0
- package/dist/layer2/byok-patterns.d.ts +12 -0
- package/dist/layer2/byok-patterns.d.ts.map +1 -0
- package/dist/layer2/byok-patterns.js +299 -0
- package/dist/layer2/byok-patterns.js.map +1 -0
- package/dist/layer2/dangerous-functions.d.ts +7 -0
- package/dist/layer2/dangerous-functions.d.ts.map +1 -0
- package/dist/layer2/dangerous-functions.js +1375 -0
- package/dist/layer2/dangerous-functions.js.map +1 -0
- package/dist/layer2/data-exposure.d.ts +16 -0
- package/dist/layer2/data-exposure.d.ts.map +1 -0
- package/dist/layer2/data-exposure.js +279 -0
- package/dist/layer2/data-exposure.js.map +1 -0
- package/dist/layer2/framework-checks.d.ts +7 -0
- package/dist/layer2/framework-checks.d.ts.map +1 -0
- package/dist/layer2/framework-checks.js +388 -0
- package/dist/layer2/framework-checks.js.map +1 -0
- package/dist/layer2/index.d.ts +58 -0
- package/dist/layer2/index.d.ts.map +1 -0
- package/dist/layer2/index.js +380 -0
- package/dist/layer2/index.js.map +1 -0
- package/dist/layer2/logic-gates.d.ts +7 -0
- package/dist/layer2/logic-gates.d.ts.map +1 -0
- package/dist/layer2/logic-gates.js +182 -0
- package/dist/layer2/logic-gates.js.map +1 -0
- package/dist/layer2/risky-imports.d.ts +7 -0
- package/dist/layer2/risky-imports.d.ts.map +1 -0
- package/dist/layer2/risky-imports.js +161 -0
- package/dist/layer2/risky-imports.js.map +1 -0
- package/dist/layer2/variables.d.ts +8 -0
- package/dist/layer2/variables.d.ts.map +1 -0
- package/dist/layer2/variables.js +152 -0
- package/dist/layer2/variables.js.map +1 -0
- package/dist/layer3/anthropic.d.ts +83 -0
- package/dist/layer3/anthropic.d.ts.map +1 -0
- package/dist/layer3/anthropic.js +1745 -0
- package/dist/layer3/anthropic.js.map +1 -0
- package/dist/layer3/index.d.ts +24 -0
- package/dist/layer3/index.d.ts.map +1 -0
- package/dist/layer3/index.js +119 -0
- package/dist/layer3/index.js.map +1 -0
- package/dist/layer3/openai.d.ts +25 -0
- package/dist/layer3/openai.d.ts.map +1 -0
- package/dist/layer3/openai.js +238 -0
- package/dist/layer3/openai.js.map +1 -0
- package/dist/layer3/package-check.d.ts +63 -0
- package/dist/layer3/package-check.d.ts.map +1 -0
- package/dist/layer3/package-check.js +508 -0
- package/dist/layer3/package-check.js.map +1 -0
- package/dist/modes/incremental.d.ts +66 -0
- package/dist/modes/incremental.d.ts.map +1 -0
- package/dist/modes/incremental.js +200 -0
- package/dist/modes/incremental.js.map +1 -0
- package/dist/tiers.d.ts +125 -0
- package/dist/tiers.d.ts.map +1 -0
- package/dist/tiers.js +234 -0
- package/dist/tiers.js.map +1 -0
- package/dist/types.d.ts +175 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +50 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/auth-helper-detector.d.ts +56 -0
- package/dist/utils/auth-helper-detector.d.ts.map +1 -0
- package/dist/utils/auth-helper-detector.js +360 -0
- package/dist/utils/auth-helper-detector.js.map +1 -0
- package/dist/utils/context-helpers.d.ts +96 -0
- package/dist/utils/context-helpers.d.ts.map +1 -0
- package/dist/utils/context-helpers.js +493 -0
- package/dist/utils/context-helpers.js.map +1 -0
- package/dist/utils/diff-detector.d.ts +53 -0
- package/dist/utils/diff-detector.d.ts.map +1 -0
- package/dist/utils/diff-detector.js +104 -0
- package/dist/utils/diff-detector.js.map +1 -0
- package/dist/utils/diff-parser.d.ts +80 -0
- package/dist/utils/diff-parser.d.ts.map +1 -0
- package/dist/utils/diff-parser.js +202 -0
- package/dist/utils/diff-parser.js.map +1 -0
- package/dist/utils/imported-auth-detector.d.ts +37 -0
- package/dist/utils/imported-auth-detector.d.ts.map +1 -0
- package/dist/utils/imported-auth-detector.js +251 -0
- package/dist/utils/imported-auth-detector.js.map +1 -0
- package/dist/utils/middleware-detector.d.ts +55 -0
- package/dist/utils/middleware-detector.d.ts.map +1 -0
- package/dist/utils/middleware-detector.js +260 -0
- package/dist/utils/middleware-detector.js.map +1 -0
- package/dist/utils/oauth-flow-detector.d.ts +41 -0
- package/dist/utils/oauth-flow-detector.d.ts.map +1 -0
- package/dist/utils/oauth-flow-detector.js +202 -0
- package/dist/utils/oauth-flow-detector.js.map +1 -0
- package/dist/utils/path-exclusions.d.ts +55 -0
- package/dist/utils/path-exclusions.d.ts.map +1 -0
- package/dist/utils/path-exclusions.js +222 -0
- package/dist/utils/path-exclusions.js.map +1 -0
- package/dist/utils/project-context-builder.d.ts +119 -0
- package/dist/utils/project-context-builder.d.ts.map +1 -0
- package/dist/utils/project-context-builder.js +534 -0
- package/dist/utils/project-context-builder.js.map +1 -0
- package/dist/utils/registry-clients.d.ts +93 -0
- package/dist/utils/registry-clients.d.ts.map +1 -0
- package/dist/utils/registry-clients.js +273 -0
- package/dist/utils/registry-clients.js.map +1 -0
- package/dist/utils/trpc-analyzer.d.ts +78 -0
- package/dist/utils/trpc-analyzer.d.ts.map +1 -0
- package/dist/utils/trpc-analyzer.js +297 -0
- package/dist/utils/trpc-analyzer.js.map +1 -0
- package/package.json +45 -0
- package/src/__tests__/benchmark/fixtures/false-positives.ts +227 -0
- package/src/__tests__/benchmark/fixtures/index.ts +68 -0
- package/src/__tests__/benchmark/fixtures/layer1/config-audit.ts +364 -0
- package/src/__tests__/benchmark/fixtures/layer1/hardcoded-secrets.ts +173 -0
- package/src/__tests__/benchmark/fixtures/layer1/high-entropy.ts +234 -0
- package/src/__tests__/benchmark/fixtures/layer1/index.ts +31 -0
- package/src/__tests__/benchmark/fixtures/layer1/sensitive-urls.ts +90 -0
- package/src/__tests__/benchmark/fixtures/layer1/weak-crypto.ts +197 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-agent-tools.ts +170 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-endpoint-protection.ts +418 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-execution-sinks.ts +189 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-fingerprinting.ts +316 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-prompt-hygiene.ts +178 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-rag-safety.ts +184 -0
- package/src/__tests__/benchmark/fixtures/layer2/ai-schema-validation.ts +434 -0
- package/src/__tests__/benchmark/fixtures/layer2/auth-antipatterns.ts +159 -0
- package/src/__tests__/benchmark/fixtures/layer2/byok-patterns.ts +112 -0
- package/src/__tests__/benchmark/fixtures/layer2/dangerous-functions.ts +246 -0
- package/src/__tests__/benchmark/fixtures/layer2/data-exposure.ts +168 -0
- package/src/__tests__/benchmark/fixtures/layer2/framework-checks.ts +346 -0
- package/src/__tests__/benchmark/fixtures/layer2/index.ts +67 -0
- package/src/__tests__/benchmark/fixtures/layer2/injection-vulnerabilities.ts +239 -0
- package/src/__tests__/benchmark/fixtures/layer2/logic-gates.ts +246 -0
- package/src/__tests__/benchmark/fixtures/layer2/risky-imports.ts +231 -0
- package/src/__tests__/benchmark/fixtures/layer2/variables.ts +167 -0
- package/src/__tests__/benchmark/index.ts +29 -0
- package/src/__tests__/benchmark/run-benchmark.ts +144 -0
- package/src/__tests__/benchmark/run-depth-validation.ts +206 -0
- package/src/__tests__/benchmark/run-real-world-test.ts +243 -0
- package/src/__tests__/benchmark/security-benchmark-script.ts +1737 -0
- package/src/__tests__/benchmark/tier-integration-script.ts +177 -0
- package/src/__tests__/benchmark/types.ts +144 -0
- package/src/__tests__/benchmark/utils/test-runner.ts +475 -0
- package/src/__tests__/regression/known-false-positives.test.ts +467 -0
- package/src/__tests__/snapshots/__snapshots__/scan-depth.test.ts.snap +178 -0
- package/src/__tests__/snapshots/scan-depth.test.ts +258 -0
- package/src/__tests__/validation/analyze-results.ts +542 -0
- package/src/__tests__/validation/extract-for-triage.ts +146 -0
- package/src/__tests__/validation/fp-deep-analysis.ts +327 -0
- package/src/__tests__/validation/run-validation.ts +364 -0
- package/src/__tests__/validation/triage-template.md +132 -0
- package/src/formatters/cli-terminal.ts +446 -0
- package/src/formatters/github-comment.ts +382 -0
- package/src/formatters/grouping.ts +190 -0
- package/src/formatters/index.ts +47 -0
- package/src/formatters/vscode-diagnostic.ts +243 -0
- package/src/index.ts +823 -0
- package/src/layer1/comments.ts +218 -0
- package/src/layer1/config-audit.ts +289 -0
- package/src/layer1/entropy.ts +583 -0
- package/src/layer1/file-flags.ts +127 -0
- package/src/layer1/index.ts +181 -0
- package/src/layer1/patterns.ts +516 -0
- package/src/layer1/urls.ts +334 -0
- package/src/layer1/weak-crypto.ts +328 -0
- package/src/layer2/ai-agent-tools.ts +601 -0
- package/src/layer2/ai-endpoint-protection.ts +387 -0
- package/src/layer2/ai-execution-sinks.ts +580 -0
- package/src/layer2/ai-fingerprinting.ts +758 -0
- package/src/layer2/ai-prompt-hygiene.ts +411 -0
- package/src/layer2/ai-rag-safety.ts +511 -0
- package/src/layer2/ai-schema-validation.ts +421 -0
- package/src/layer2/auth-antipatterns.ts +394 -0
- package/src/layer2/byok-patterns.ts +336 -0
- package/src/layer2/dangerous-functions.ts +1563 -0
- package/src/layer2/data-exposure.ts +315 -0
- package/src/layer2/framework-checks.ts +433 -0
- package/src/layer2/index.ts +473 -0
- package/src/layer2/logic-gates.ts +206 -0
- package/src/layer2/risky-imports.ts +186 -0
- package/src/layer2/variables.ts +166 -0
- package/src/layer3/anthropic.ts +2030 -0
- package/src/layer3/index.ts +130 -0
- package/src/layer3/package-check.ts +604 -0
- package/src/modes/incremental.ts +293 -0
- package/src/tiers.ts +318 -0
- package/src/types.ts +284 -0
- package/src/utils/auth-helper-detector.ts +443 -0
- package/src/utils/context-helpers.ts +535 -0
- package/src/utils/diff-detector.ts +135 -0
- package/src/utils/diff-parser.ts +272 -0
- package/src/utils/imported-auth-detector.ts +320 -0
- package/src/utils/middleware-detector.ts +333 -0
- package/src/utils/oauth-flow-detector.ts +246 -0
- package/src/utils/path-exclusions.ts +266 -0
- package/src/utils/project-context-builder.ts +707 -0
- package/src/utils/registry-clients.ts +351 -0
- package/src/utils/trpc-analyzer.ts +382 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weak Cryptography Test Fixtures
|
|
3
|
+
* Tests for detecting insecure cryptographic algorithms and practices
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TestGroup } from '../../types'
|
|
7
|
+
|
|
8
|
+
export const weakCryptoTests: TestGroup = {
|
|
9
|
+
name: 'Weak Cryptography',
|
|
10
|
+
tier: 'A',
|
|
11
|
+
layer: 1,
|
|
12
|
+
description: 'Detection of weak hash algorithms, insecure ciphers, and poor cryptographic practices',
|
|
13
|
+
|
|
14
|
+
truePositives: [
|
|
15
|
+
{
|
|
16
|
+
name: 'Weak Crypto - True Positives',
|
|
17
|
+
expectFindings: true,
|
|
18
|
+
expectedCategories: ['weak_crypto'],
|
|
19
|
+
description: 'Weak cryptographic patterns that MUST be detected',
|
|
20
|
+
file: {
|
|
21
|
+
path: 'src/utils/crypto.ts',
|
|
22
|
+
content: `
|
|
23
|
+
import crypto from 'crypto'
|
|
24
|
+
|
|
25
|
+
// MD5 for passwords (Critical)
|
|
26
|
+
function hashPassword(password: string) {
|
|
27
|
+
return crypto.createHash('md5').update(password).digest('hex')
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// SHA1 for security tokens (High)
|
|
31
|
+
function generateToken(data: string) {
|
|
32
|
+
return crypto.createHash('sha1').update(data).digest('hex')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Weak ciphers (High)
|
|
36
|
+
function encryptData(data: string, key: string) {
|
|
37
|
+
const cipher = crypto.createCipher('des', key)
|
|
38
|
+
return cipher.update(data, 'utf8', 'hex') + cipher.final('hex')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ECB mode (High - vulnerable to pattern analysis)
|
|
42
|
+
function encryptECB(data: string, key: Buffer) {
|
|
43
|
+
const cipher = crypto.createCipheriv('aes-128-ecb', key, null)
|
|
44
|
+
return cipher.update(data, 'utf8', 'hex') + cipher.final('hex')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// RC4 (Critical - broken)
|
|
48
|
+
function encryptRC4(data: string, key: string) {
|
|
49
|
+
const cipher = crypto.createCipher('rc4', key)
|
|
50
|
+
return cipher.update(data, 'utf8', 'hex')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Math.random for security tokens (High)
|
|
54
|
+
function generateSecureToken() {
|
|
55
|
+
return Math.random().toString(36).substring(2)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Low PBKDF2 iterations (Medium)
|
|
59
|
+
function deriveKey(password: string, salt: Buffer) {
|
|
60
|
+
return crypto.pbkdf2Sync(password, salt, 100, 32, 'sha256')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Hardcoded IV (High)
|
|
64
|
+
const STATIC_IV = Buffer.from('1234567890123456')
|
|
65
|
+
function encryptWithStaticIV(data: string, key: Buffer) {
|
|
66
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', key, STATIC_IV)
|
|
67
|
+
return cipher.update(data, 'utf8', 'hex') + cipher.final('hex')
|
|
68
|
+
}
|
|
69
|
+
`,
|
|
70
|
+
language: 'typescript',
|
|
71
|
+
size: 1500,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
name: 'Weak Crypto - Insecure Password Hashing',
|
|
76
|
+
expectFindings: true,
|
|
77
|
+
expectedCategories: ['weak_crypto'],
|
|
78
|
+
description: 'Weak password hashing implementations',
|
|
79
|
+
file: {
|
|
80
|
+
path: 'src/auth/password.ts',
|
|
81
|
+
content: `
|
|
82
|
+
import crypto from 'crypto'
|
|
83
|
+
import bcrypt from 'bcrypt'
|
|
84
|
+
|
|
85
|
+
// MD5 password hash (Critical)
|
|
86
|
+
export function hashPasswordMD5(password: string): string {
|
|
87
|
+
return crypto.createHash('md5').update(password).digest('hex')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// SHA1 password hash (High)
|
|
91
|
+
export function hashPasswordSHA1(password: string): string {
|
|
92
|
+
return crypto.createHash('sha1').update(password).digest('hex')
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Bcrypt with low rounds (Medium)
|
|
96
|
+
export async function hashPasswordWeakBcrypt(password: string): Promise<string> {
|
|
97
|
+
return bcrypt.hash(password, 4) // Should be at least 10
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Unsalted hash (High)
|
|
101
|
+
export function hashPasswordUnsalted(password: string): string {
|
|
102
|
+
return crypto.createHash('sha256').update(password).digest('hex')
|
|
103
|
+
}
|
|
104
|
+
`,
|
|
105
|
+
language: 'typescript',
|
|
106
|
+
size: 700,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
|
|
111
|
+
falseNegatives: [
|
|
112
|
+
{
|
|
113
|
+
name: 'Weak Crypto - False Negatives',
|
|
114
|
+
expectFindings: false,
|
|
115
|
+
description: 'Secure cryptographic patterns that should NOT be flagged',
|
|
116
|
+
file: {
|
|
117
|
+
path: 'src/utils/secure-crypto.ts',
|
|
118
|
+
content: `
|
|
119
|
+
import crypto from 'crypto'
|
|
120
|
+
|
|
121
|
+
// MD5 for checksums (not security) - SAFE
|
|
122
|
+
function generateChecksum(data: Buffer) {
|
|
123
|
+
return crypto.createHash('md5').update(data).digest('hex')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// SHA256 for passwords - SAFE
|
|
127
|
+
function hashPasswordSecurely(password: string, salt: string) {
|
|
128
|
+
return crypto.createHash('sha256').update(password + salt).digest('hex')
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// AES-256-GCM - SAFE
|
|
132
|
+
function encryptSecurely(data: string, key: Buffer) {
|
|
133
|
+
const iv = crypto.randomBytes(16)
|
|
134
|
+
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv)
|
|
135
|
+
const encrypted = cipher.update(data, 'utf8', 'hex') + cipher.final('hex')
|
|
136
|
+
const tag = cipher.getAuthTag()
|
|
137
|
+
return { encrypted, iv, tag }
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// crypto.randomBytes for tokens - SAFE
|
|
141
|
+
function generateSecureToken() {
|
|
142
|
+
return crypto.randomBytes(32).toString('hex')
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// High PBKDF2 iterations - SAFE
|
|
146
|
+
function deriveKeySecurely(password: string, salt: Buffer) {
|
|
147
|
+
return crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha256')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Random IV per encryption - SAFE
|
|
151
|
+
function encryptWithRandomIV(data: string, key: Buffer) {
|
|
152
|
+
const iv = crypto.randomBytes(16)
|
|
153
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv)
|
|
154
|
+
return { encrypted: cipher.update(data, 'utf8', 'hex') + cipher.final('hex'), iv }
|
|
155
|
+
}
|
|
156
|
+
`,
|
|
157
|
+
language: 'typescript',
|
|
158
|
+
size: 1200,
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'Weak Crypto - MD5 for Checksums',
|
|
163
|
+
expectFindings: false,
|
|
164
|
+
description: 'MD5 used for non-security checksums should not be flagged',
|
|
165
|
+
file: {
|
|
166
|
+
path: 'src/utils/checksum.ts',
|
|
167
|
+
content: `
|
|
168
|
+
import crypto from 'crypto'
|
|
169
|
+
import fs from 'fs'
|
|
170
|
+
|
|
171
|
+
// MD5 checksum for file integrity (not security) - SAFE
|
|
172
|
+
export function getFileChecksum(filePath: string): string {
|
|
173
|
+
const content = fs.readFileSync(filePath)
|
|
174
|
+
return crypto.createHash('md5').update(content).digest('hex')
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// MD5 for etag generation - SAFE
|
|
178
|
+
export function generateEtag(content: string): string {
|
|
179
|
+
return crypto.createHash('md5').update(content).digest('hex')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// MD5 for content fingerprint - SAFE
|
|
183
|
+
export function contentFingerprint(data: Buffer): string {
|
|
184
|
+
return crypto.createHash('md5').update(data).digest('hex')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// MD5 for cache key - SAFE
|
|
188
|
+
export function cacheKey(input: string): string {
|
|
189
|
+
return 'cache_' + crypto.createHash('md5').update(input).digest('hex')
|
|
190
|
+
}
|
|
191
|
+
`,
|
|
192
|
+
language: 'typescript',
|
|
193
|
+
size: 600,
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Agent Tools Test Fixtures
|
|
3
|
+
* Tests for detecting overly permissive AI agent tool definitions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TestGroup } from '../../types'
|
|
7
|
+
|
|
8
|
+
export const aiAgentToolsTests: TestGroup = {
|
|
9
|
+
name: 'AI Agent Tools',
|
|
10
|
+
tier: 'A',
|
|
11
|
+
layer: 2,
|
|
12
|
+
description: 'Detection of overly permissive AI agent tool definitions that could be abused',
|
|
13
|
+
|
|
14
|
+
truePositives: [
|
|
15
|
+
{
|
|
16
|
+
name: 'AI Agent Tools - True Positives',
|
|
17
|
+
expectFindings: true,
|
|
18
|
+
expectedCategories: ['ai_overpermissive_tool'],
|
|
19
|
+
description: 'Overly permissive agent tool patterns that MUST be detected',
|
|
20
|
+
file: {
|
|
21
|
+
path: 'src/agents/overpermissive-tools.ts',
|
|
22
|
+
content: `
|
|
23
|
+
import { z } from 'zod'
|
|
24
|
+
import { exec } from 'child_process'
|
|
25
|
+
import fs from 'fs'
|
|
26
|
+
|
|
27
|
+
// Over-permissive file system tool - HIGH
|
|
28
|
+
const fileSystemTool = {
|
|
29
|
+
name: 'file_system',
|
|
30
|
+
description: 'Read, write, or delete any file on the system',
|
|
31
|
+
schema: z.object({
|
|
32
|
+
operation: z.enum(['read', 'write', 'delete']),
|
|
33
|
+
path: z.string(), // No path restrictions!
|
|
34
|
+
content: z.string().optional(),
|
|
35
|
+
}),
|
|
36
|
+
execute: async ({ operation, path, content }) => {
|
|
37
|
+
switch (operation) {
|
|
38
|
+
case 'read': return fs.readFileSync(path, 'utf-8')
|
|
39
|
+
case 'write': return fs.writeFileSync(path, content!)
|
|
40
|
+
case 'delete': return fs.unlinkSync(path)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Unrestricted shell execution tool - CRITICAL
|
|
46
|
+
const shellTool = {
|
|
47
|
+
name: 'shell',
|
|
48
|
+
description: 'Execute any shell command',
|
|
49
|
+
schema: z.object({
|
|
50
|
+
command: z.string(), // No command restrictions!
|
|
51
|
+
}),
|
|
52
|
+
execute: async ({ command }) => {
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
exec(command, (err, stdout, stderr) => {
|
|
55
|
+
resolve({ stdout, stderr })
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Network access without restrictions - HIGH
|
|
62
|
+
const networkTool = {
|
|
63
|
+
name: 'http_request',
|
|
64
|
+
description: 'Make HTTP requests to any URL',
|
|
65
|
+
schema: z.object({
|
|
66
|
+
url: z.string(), // No URL restrictions - SSRF risk!
|
|
67
|
+
method: z.string(),
|
|
68
|
+
body: z.any().optional(),
|
|
69
|
+
}),
|
|
70
|
+
execute: async ({ url, method, body }) => {
|
|
71
|
+
return fetch(url, { method, body: JSON.stringify(body) })
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Database access without user context - HIGH
|
|
76
|
+
const databaseTool = {
|
|
77
|
+
name: 'database',
|
|
78
|
+
description: 'Execute SQL queries',
|
|
79
|
+
schema: z.object({
|
|
80
|
+
query: z.string(), // Raw SQL - injection risk!
|
|
81
|
+
}),
|
|
82
|
+
execute: async ({ query }) => {
|
|
83
|
+
return db.query(query) // No user_id scoping!
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Tool without proper authorization - MEDIUM
|
|
88
|
+
const userDataTool = {
|
|
89
|
+
name: 'get_user_data',
|
|
90
|
+
description: 'Get any user data',
|
|
91
|
+
schema: z.object({
|
|
92
|
+
userId: z.string(), // Can access ANY user!
|
|
93
|
+
}),
|
|
94
|
+
execute: async ({ userId }) => {
|
|
95
|
+
// No check that agent's user can access this userId
|
|
96
|
+
return db.user.findUnique({ where: { id: userId } })
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
`,
|
|
100
|
+
language: 'typescript',
|
|
101
|
+
size: 1800,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
|
|
106
|
+
falseNegatives: [
|
|
107
|
+
{
|
|
108
|
+
name: 'AI Agent Tools - False Negatives',
|
|
109
|
+
expectFindings: false,
|
|
110
|
+
description: 'Properly scoped agent tool patterns that should NOT be flagged',
|
|
111
|
+
file: {
|
|
112
|
+
path: 'src/agents/safe-tools.ts',
|
|
113
|
+
content: `
|
|
114
|
+
import { z } from 'zod'
|
|
115
|
+
import fs from 'fs'
|
|
116
|
+
|
|
117
|
+
// File system with path restrictions - SAFE
|
|
118
|
+
const safeFileSystemTool = {
|
|
119
|
+
name: 'file_system',
|
|
120
|
+
description: 'Read files from the allowed directory',
|
|
121
|
+
schema: z.object({
|
|
122
|
+
filename: z.string().regex(/^[a-zA-Z0-9_-]+\\.(txt|json|md)$/),
|
|
123
|
+
}),
|
|
124
|
+
execute: async ({ filename }, { userId }) => {
|
|
125
|
+
// Scoped to user's directory
|
|
126
|
+
const safePath = \`/data/users/\${userId}/\${filename}\`
|
|
127
|
+
// No path traversal possible due to regex
|
|
128
|
+
return fs.readFileSync(safePath, 'utf-8')
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// HTTP with URL allowlist - SAFE
|
|
133
|
+
const safeNetworkTool = {
|
|
134
|
+
name: 'http_request',
|
|
135
|
+
description: 'Make HTTP requests to allowed APIs',
|
|
136
|
+
schema: z.object({
|
|
137
|
+
api: z.enum(['weather', 'news', 'stocks']),
|
|
138
|
+
params: z.record(z.string()),
|
|
139
|
+
}),
|
|
140
|
+
execute: async ({ api, params }) => {
|
|
141
|
+
const allowedUrls = {
|
|
142
|
+
weather: 'https://api.weather.gov',
|
|
143
|
+
news: 'https://newsapi.org',
|
|
144
|
+
stocks: 'https://api.stocks.com',
|
|
145
|
+
}
|
|
146
|
+
const url = new URL(allowedUrls[api])
|
|
147
|
+
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v))
|
|
148
|
+
return fetch(url.toString())
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Database with user scoping - SAFE
|
|
153
|
+
const safeDatabaseTool = {
|
|
154
|
+
name: 'get_my_data',
|
|
155
|
+
description: 'Get data belonging to the current user',
|
|
156
|
+
schema: z.object({
|
|
157
|
+
dataType: z.enum(['orders', 'settings', 'preferences']),
|
|
158
|
+
}),
|
|
159
|
+
execute: async ({ dataType }, { userId }) => {
|
|
160
|
+
// Always scoped to current user
|
|
161
|
+
return db[dataType].findMany({ where: { userId } })
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
`,
|
|
165
|
+
language: 'typescript',
|
|
166
|
+
size: 1400,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
}
|