@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,346 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework-Specific Checks Test Fixtures
|
|
3
|
+
* Tests for detecting framework-specific security issues
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TestGroup } from '../../types'
|
|
7
|
+
|
|
8
|
+
export const frameworkChecksTests: TestGroup = {
|
|
9
|
+
name: 'Framework-Specific Security',
|
|
10
|
+
tier: 'C',
|
|
11
|
+
layer: 2,
|
|
12
|
+
description: 'Detection of framework-specific security issues (Next.js, Express, React, Django, Flask)',
|
|
13
|
+
|
|
14
|
+
truePositives: [
|
|
15
|
+
{
|
|
16
|
+
name: 'Next.js Security Issues',
|
|
17
|
+
expectFindings: true,
|
|
18
|
+
expectedCategories: ['insecure_config'],
|
|
19
|
+
description: 'Next.js specific security issues that MUST be detected',
|
|
20
|
+
file: {
|
|
21
|
+
path: 'src/app/api/data/route.ts',
|
|
22
|
+
content: `
|
|
23
|
+
import { NextResponse } from 'next/server'
|
|
24
|
+
import { redirect, revalidatePath } from 'next/navigation'
|
|
25
|
+
|
|
26
|
+
// Next.js API route handler without auth check - MEDIUM
|
|
27
|
+
export default async function handler(req: any, res: any) {
|
|
28
|
+
const data = await fetchSensitiveData()
|
|
29
|
+
return res.json(data)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Server action publicly accessible - MEDIUM
|
|
33
|
+
'use server';
|
|
34
|
+
export async function submitForm(data: FormData) {
|
|
35
|
+
await db.insert(data)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// revalidatePath with user input - HIGH
|
|
39
|
+
export async function updateCache(request: Request) {
|
|
40
|
+
const { path } = await request.json()
|
|
41
|
+
revalidatePath(request.url)
|
|
42
|
+
revalidatePath(params.path)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// redirect with user input - HIGH
|
|
46
|
+
export async function handleLogin(searchParams: { redirect: string }) {
|
|
47
|
+
redirect(searchParams.redirect)
|
|
48
|
+
redirect(url.pathname)
|
|
49
|
+
redirect(request.nextUrl.pathname)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// getServerSideProps without auth - LOW
|
|
53
|
+
export async function getServerSideProps(context: any) {
|
|
54
|
+
const data = await fetchData()
|
|
55
|
+
return { props: { data } }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// unstable_cache without tags
|
|
59
|
+
import { unstable_cache } from 'next/cache'
|
|
60
|
+
const getCachedData = unstable_cache(async () => {
|
|
61
|
+
return fetchData()
|
|
62
|
+
})
|
|
63
|
+
`,
|
|
64
|
+
language: 'typescript',
|
|
65
|
+
size: 900,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'Express Security Issues',
|
|
70
|
+
expectFindings: true,
|
|
71
|
+
expectedCategories: ['insecure_config'],
|
|
72
|
+
description: 'Express specific security issues that MUST be detected',
|
|
73
|
+
file: {
|
|
74
|
+
path: 'src/server/app.ts',
|
|
75
|
+
content: `
|
|
76
|
+
import express from 'express'
|
|
77
|
+
import cors from 'cors'
|
|
78
|
+
import bodyParser from 'body-parser'
|
|
79
|
+
|
|
80
|
+
const app = express()
|
|
81
|
+
|
|
82
|
+
// Express without helmet - MEDIUM
|
|
83
|
+
const server = express()
|
|
84
|
+
// No helmet middleware added
|
|
85
|
+
|
|
86
|
+
// CORS allow all origins - HIGH
|
|
87
|
+
app.use(cors({
|
|
88
|
+
origin: '*',
|
|
89
|
+
credentials: true,
|
|
90
|
+
}))
|
|
91
|
+
|
|
92
|
+
// Body parser without limit - LOW
|
|
93
|
+
app.use(bodyParser.json())
|
|
94
|
+
app.use(bodyParser.urlencoded())
|
|
95
|
+
|
|
96
|
+
// Trust proxy without verification - MEDIUM
|
|
97
|
+
app.set('trust proxy', true)
|
|
98
|
+
|
|
99
|
+
// Error handler exposes stack - HIGH
|
|
100
|
+
app.use((err, req, res, next) => {
|
|
101
|
+
res.json({
|
|
102
|
+
error: err.message,
|
|
103
|
+
stack: err.stack, // Stack trace exposed!
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
// Another stack exposure pattern
|
|
108
|
+
app.get('/error', (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
throw new Error('Oops')
|
|
111
|
+
} catch (err) {
|
|
112
|
+
res.send({ stack: err.stack })
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
`,
|
|
116
|
+
language: 'typescript',
|
|
117
|
+
size: 700,
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: 'React Security Issues',
|
|
122
|
+
expectFindings: true,
|
|
123
|
+
expectedCategories: ['insecure_config'],
|
|
124
|
+
description: 'React specific security issues that MUST be detected',
|
|
125
|
+
file: {
|
|
126
|
+
path: 'src/components/Form.tsx',
|
|
127
|
+
content: `
|
|
128
|
+
import { useState, useEffect } from 'react'
|
|
129
|
+
|
|
130
|
+
// useEffect with async (memory leak) - LOW
|
|
131
|
+
function Component() {
|
|
132
|
+
useEffect(async () => {
|
|
133
|
+
const data = await fetchData()
|
|
134
|
+
setData(data)
|
|
135
|
+
}, [])
|
|
136
|
+
|
|
137
|
+
// State with sensitive data - MEDIUM
|
|
138
|
+
const [password, setPassword] = useState('initial-password')
|
|
139
|
+
const [secret, setSecret] = useState('')
|
|
140
|
+
const [token, setToken] = useState('')
|
|
141
|
+
const [apiKey, setApiKey] = useState('')
|
|
142
|
+
const [api_key, setApiKey2] = useState('')
|
|
143
|
+
|
|
144
|
+
// href javascript: XSS - HIGH
|
|
145
|
+
return (
|
|
146
|
+
<div>
|
|
147
|
+
<a href="javascript:alert('xss')">Click me</a>
|
|
148
|
+
<a href='javascript:void(0)'>Another</a>
|
|
149
|
+
</div>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Uncontrolled input with sensitive defaultValue - MEDIUM
|
|
154
|
+
function LoginForm({ savedPassword }) {
|
|
155
|
+
return (
|
|
156
|
+
<input
|
|
157
|
+
type="password"
|
|
158
|
+
defaultValue={password}
|
|
159
|
+
/>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function TokenForm({ savedToken }) {
|
|
164
|
+
return <input defaultValue={token} />
|
|
165
|
+
}
|
|
166
|
+
`,
|
|
167
|
+
language: 'typescript',
|
|
168
|
+
size: 750,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'Django/Flask Security Issues',
|
|
173
|
+
expectFindings: true,
|
|
174
|
+
expectedCategories: ['insecure_config'],
|
|
175
|
+
description: 'Python framework security issues that MUST be detected',
|
|
176
|
+
file: {
|
|
177
|
+
path: 'src/settings.py',
|
|
178
|
+
content: `
|
|
179
|
+
from django.conf import settings
|
|
180
|
+
|
|
181
|
+
# Django DEBUG in production - CRITICAL
|
|
182
|
+
DEBUG = True
|
|
183
|
+
|
|
184
|
+
# ALLOWED_HOSTS wildcard - HIGH
|
|
185
|
+
ALLOWED_HOSTS = ['*']
|
|
186
|
+
|
|
187
|
+
# Django SECRET_KEY hardcoded - CRITICAL
|
|
188
|
+
SECRET_KEY = 'django-insecure-abc123def456ghi789jkl'
|
|
189
|
+
|
|
190
|
+
# ========== Flask patterns ==========
|
|
191
|
+
|
|
192
|
+
from flask import Flask
|
|
193
|
+
|
|
194
|
+
app = Flask(__name__)
|
|
195
|
+
|
|
196
|
+
# Flask debug mode - CRITICAL
|
|
197
|
+
app.run(debug=True, host='0.0.0.0')
|
|
198
|
+
|
|
199
|
+
# Flask secret key hardcoded - CRITICAL
|
|
200
|
+
app.secret_key = 'super-secret-flask-key'
|
|
201
|
+
`,
|
|
202
|
+
language: 'python',
|
|
203
|
+
size: 500,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
|
|
208
|
+
falseNegatives: [
|
|
209
|
+
{
|
|
210
|
+
name: 'Framework Checks - False Negatives',
|
|
211
|
+
expectFindings: false,
|
|
212
|
+
description: 'Safe framework patterns that should NOT be flagged',
|
|
213
|
+
allowedInfoFindings: [
|
|
214
|
+
{
|
|
215
|
+
category: 'insecure_config',
|
|
216
|
+
maxCount: 2,
|
|
217
|
+
reason: 'Some framework patterns may still generate low-severity findings',
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
category: 'ai_pattern',
|
|
221
|
+
maxCount: 2,
|
|
222
|
+
reason: 'Framework patterns may trigger AI detection',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
category: 'data_exposure',
|
|
226
|
+
maxCount: 2,
|
|
227
|
+
reason: 'Error handling patterns may trigger data exposure detection',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
category: 'sensitive_url',
|
|
231
|
+
maxCount: 2,
|
|
232
|
+
reason: 'URLs in CORS config may trigger URL detection',
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
file: {
|
|
236
|
+
path: 'src/config/server-config.ts',
|
|
237
|
+
content: `
|
|
238
|
+
// Server configuration with proper defaults - SAFE
|
|
239
|
+
export const serverConfig = {
|
|
240
|
+
port: process.env.PORT || 3000,
|
|
241
|
+
host: process.env.HOST || 'localhost',
|
|
242
|
+
bodyLimit: '100kb',
|
|
243
|
+
}
|
|
244
|
+
`,
|
|
245
|
+
language: 'typescript',
|
|
246
|
+
size: 150,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
name: 'Safe Python Framework Patterns',
|
|
251
|
+
expectFindings: false,
|
|
252
|
+
description: 'Safe Django/Flask patterns that should NOT be flagged',
|
|
253
|
+
allowedInfoFindings: [
|
|
254
|
+
{
|
|
255
|
+
category: 'insecure_config',
|
|
256
|
+
maxCount: 2,
|
|
257
|
+
reason: 'Environment checks may still generate low-severity findings',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
category: 'sensitive_variable',
|
|
261
|
+
maxCount: 2,
|
|
262
|
+
reason: 'Variable names like SECRET_KEY may trigger sensitive variable detection',
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
file: {
|
|
266
|
+
path: 'src/safe_settings.py',
|
|
267
|
+
content: `
|
|
268
|
+
import os
|
|
269
|
+
from django.conf import settings
|
|
270
|
+
|
|
271
|
+
# DEBUG from environment - SAFE
|
|
272
|
+
DEBUG = os.environ.get('DEBUG', 'False') == 'True'
|
|
273
|
+
|
|
274
|
+
# ALLOWED_HOSTS from environment - SAFE
|
|
275
|
+
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')
|
|
276
|
+
|
|
277
|
+
# SECRET_KEY from environment - SAFE
|
|
278
|
+
SECRET_KEY = os.environ.get('SECRET_KEY')
|
|
279
|
+
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')
|
|
280
|
+
|
|
281
|
+
# ========== Safe Flask patterns ==========
|
|
282
|
+
|
|
283
|
+
from flask import Flask
|
|
284
|
+
|
|
285
|
+
app = Flask(__name__)
|
|
286
|
+
|
|
287
|
+
# Debug from environment - SAFE
|
|
288
|
+
app.run(debug=os.environ.get('FLASK_DEBUG') == 'true')
|
|
289
|
+
|
|
290
|
+
# Secret key from environment - SAFE
|
|
291
|
+
app.secret_key = os.environ.get('SECRET_KEY')
|
|
292
|
+
app.config['SECRET_KEY'] = os.getenv('FLASK_SECRET')
|
|
293
|
+
`,
|
|
294
|
+
language: 'python',
|
|
295
|
+
size: 500,
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'Safe React Patterns',
|
|
300
|
+
expectFindings: false,
|
|
301
|
+
description: 'Safe React patterns that should NOT be flagged',
|
|
302
|
+
allowedInfoFindings: [
|
|
303
|
+
{
|
|
304
|
+
category: 'insecure_config',
|
|
305
|
+
maxCount: 1,
|
|
306
|
+
reason: 'Some patterns may generate info findings',
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
category: 'sensitive_url',
|
|
310
|
+
maxCount: 2,
|
|
311
|
+
reason: 'URLs in href may trigger URL detection',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
category: 'sensitive_variable',
|
|
315
|
+
maxCount: 2,
|
|
316
|
+
reason: 'Variable names like password may trigger sensitive variable detection',
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
file: {
|
|
320
|
+
path: 'src/components/SafeForm.tsx',
|
|
321
|
+
content: `
|
|
322
|
+
import { useState, useEffect } from 'react'
|
|
323
|
+
|
|
324
|
+
// Proper useEffect with async - SAFE
|
|
325
|
+
function Component() {
|
|
326
|
+
useEffect(() => {
|
|
327
|
+
loadData()
|
|
328
|
+
}, [])
|
|
329
|
+
|
|
330
|
+
// Non-sensitive state - SAFE
|
|
331
|
+
const [name, setName] = useState('')
|
|
332
|
+
|
|
333
|
+
return (
|
|
334
|
+
<div>
|
|
335
|
+
<a href="/dashboard">Dashboard</a>
|
|
336
|
+
<button onClick={handleClick}>Click</button>
|
|
337
|
+
</div>
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
`,
|
|
341
|
+
language: 'typescript',
|
|
342
|
+
size: 300,
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layer 2 Test Fixtures Index
|
|
3
|
+
* Exports all Layer 2 (Structural Scan) test fixtures
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { dangerousFunctionsTests } from './dangerous-functions'
|
|
7
|
+
export { byokTests } from './byok-patterns'
|
|
8
|
+
export { aiExecutionSinksTests } from './ai-execution-sinks'
|
|
9
|
+
export { aiAgentToolsTests } from './ai-agent-tools'
|
|
10
|
+
export { authAntipatternsTests } from './auth-antipatterns'
|
|
11
|
+
export { aiPromptHygieneTests } from './ai-prompt-hygiene'
|
|
12
|
+
export { dataExposureTests } from './data-exposure'
|
|
13
|
+
export { injectionTests } from './injection-vulnerabilities'
|
|
14
|
+
// M5: New AI-era detector fixtures
|
|
15
|
+
export { aiRagSafetyTests } from './ai-rag-safety'
|
|
16
|
+
export { aiEndpointProtectionTests } from './ai-endpoint-protection'
|
|
17
|
+
export { aiSchemaValidationTests } from './ai-schema-validation'
|
|
18
|
+
// M6: New Layer 2 fixtures (5 untested detectors)
|
|
19
|
+
export { aiFingerprintingTests } from './ai-fingerprinting'
|
|
20
|
+
export { logicGatesTests } from './logic-gates'
|
|
21
|
+
export { sensitiveVariablesTests } from './variables'
|
|
22
|
+
export { riskyImportsTests } from './risky-imports'
|
|
23
|
+
export { frameworkChecksTests } from './framework-checks'
|
|
24
|
+
|
|
25
|
+
import type { TestGroup } from '../../types'
|
|
26
|
+
import { dangerousFunctionsTests } from './dangerous-functions'
|
|
27
|
+
import { byokTests } from './byok-patterns'
|
|
28
|
+
import { aiExecutionSinksTests } from './ai-execution-sinks'
|
|
29
|
+
import { aiAgentToolsTests } from './ai-agent-tools'
|
|
30
|
+
import { authAntipatternsTests } from './auth-antipatterns'
|
|
31
|
+
import { aiPromptHygieneTests } from './ai-prompt-hygiene'
|
|
32
|
+
import { dataExposureTests } from './data-exposure'
|
|
33
|
+
import { injectionTests } from './injection-vulnerabilities'
|
|
34
|
+
// M5: New AI-era detector fixtures
|
|
35
|
+
import { aiRagSafetyTests } from './ai-rag-safety'
|
|
36
|
+
import { aiEndpointProtectionTests } from './ai-endpoint-protection'
|
|
37
|
+
import { aiSchemaValidationTests } from './ai-schema-validation'
|
|
38
|
+
// M6: New Layer 2 fixtures (5 untested detectors)
|
|
39
|
+
import { aiFingerprintingTests } from './ai-fingerprinting'
|
|
40
|
+
import { logicGatesTests } from './logic-gates'
|
|
41
|
+
import { sensitiveVariablesTests } from './variables'
|
|
42
|
+
import { riskyImportsTests } from './risky-imports'
|
|
43
|
+
import { frameworkChecksTests } from './framework-checks'
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* All Layer 2 test groups
|
|
47
|
+
*/
|
|
48
|
+
export const layer2TestGroups: TestGroup[] = [
|
|
49
|
+
dangerousFunctionsTests,
|
|
50
|
+
byokTests,
|
|
51
|
+
aiExecutionSinksTests,
|
|
52
|
+
aiAgentToolsTests,
|
|
53
|
+
authAntipatternsTests,
|
|
54
|
+
aiPromptHygieneTests,
|
|
55
|
+
dataExposureTests,
|
|
56
|
+
injectionTests,
|
|
57
|
+
// M5: New AI-era detectors
|
|
58
|
+
aiRagSafetyTests,
|
|
59
|
+
aiEndpointProtectionTests,
|
|
60
|
+
aiSchemaValidationTests,
|
|
61
|
+
// M6: New Layer 2 fixtures (5 untested detectors)
|
|
62
|
+
aiFingerprintingTests,
|
|
63
|
+
logicGatesTests,
|
|
64
|
+
sensitiveVariablesTests,
|
|
65
|
+
riskyImportsTests,
|
|
66
|
+
frameworkChecksTests,
|
|
67
|
+
]
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Injection Vulnerabilities Test Fixtures
|
|
3
|
+
* Comprehensive tests for SQL injection, XSS, command injection, and template injection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { TestGroup } from '../../types'
|
|
7
|
+
|
|
8
|
+
export const injectionTests: TestGroup = {
|
|
9
|
+
name: 'Injection Vulnerabilities',
|
|
10
|
+
tier: 'A',
|
|
11
|
+
layer: 2,
|
|
12
|
+
description: 'Comprehensive detection of SQL injection, XSS, command injection, and template injection',
|
|
13
|
+
|
|
14
|
+
truePositives: [
|
|
15
|
+
{
|
|
16
|
+
name: 'SQL Injection - Various Patterns',
|
|
17
|
+
expectFindings: true,
|
|
18
|
+
expectedCategories: ['dangerous_function'],
|
|
19
|
+
description: 'Various SQL injection patterns across different frameworks',
|
|
20
|
+
file: {
|
|
21
|
+
path: 'src/api/sql-injection.ts',
|
|
22
|
+
content: `
|
|
23
|
+
// Basic string concatenation
|
|
24
|
+
export async function getUserByName(name: string) {
|
|
25
|
+
return db.query("SELECT * FROM users WHERE name = '" + name + "'")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Template literal injection
|
|
29
|
+
export async function searchProducts(term: string) {
|
|
30
|
+
return db.query(\`SELECT * FROM products WHERE name LIKE '%\${term}%'\`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ORDER BY injection (often overlooked)
|
|
34
|
+
export async function getSortedUsers(sortColumn: string) {
|
|
35
|
+
return db.query(\`SELECT * FROM users ORDER BY \${sortColumn}\`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// LIMIT/OFFSET injection
|
|
39
|
+
export async function getPaginatedData(limit: string, offset: string) {
|
|
40
|
+
return db.query(\`SELECT * FROM items LIMIT \${limit} OFFSET \${offset}\`)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// IN clause injection
|
|
44
|
+
export async function getUsersByIds(ids: string) {
|
|
45
|
+
return db.query(\`SELECT * FROM users WHERE id IN (\${ids})\`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Table name injection
|
|
49
|
+
export async function getFromTable(tableName: string, id: string) {
|
|
50
|
+
return db.query(\`SELECT * FROM \${tableName} WHERE id = '\${id}'\`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Multi-statement injection
|
|
54
|
+
export async function updateAndSelect(userId: string, newName: string) {
|
|
55
|
+
const query = \`UPDATE users SET name = '\${newName}' WHERE id = '\${userId}'; SELECT * FROM users\`
|
|
56
|
+
return db.query(query)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Stored procedure injection
|
|
60
|
+
export async function callProcedure(procName: string, param: string) {
|
|
61
|
+
return db.query(\`CALL \${procName}('\${param}')\`)
|
|
62
|
+
}
|
|
63
|
+
`,
|
|
64
|
+
language: 'typescript',
|
|
65
|
+
size: 1200,
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'XSS - Various Patterns',
|
|
70
|
+
expectFindings: true,
|
|
71
|
+
expectedCategories: ['dangerous_function'],
|
|
72
|
+
description: 'Various XSS patterns in different contexts',
|
|
73
|
+
file: {
|
|
74
|
+
path: 'src/components/xss-vulnerable.tsx',
|
|
75
|
+
content: `
|
|
76
|
+
import React from 'react'
|
|
77
|
+
|
|
78
|
+
// Direct innerHTML assignment
|
|
79
|
+
export function RenderUserHTML({ html }: { html: string }) {
|
|
80
|
+
const divRef = useRef<HTMLDivElement>(null)
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (divRef.current) {
|
|
83
|
+
divRef.current.innerHTML = html // XSS!
|
|
84
|
+
}
|
|
85
|
+
}, [html])
|
|
86
|
+
return <div ref={divRef} />
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// dangerouslySetInnerHTML without sanitization
|
|
90
|
+
export function UserBio({ bioHtml }: { bioHtml: string }) {
|
|
91
|
+
return <div dangerouslySetInnerHTML={{ __html: bioHtml }} />
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// document.write
|
|
95
|
+
export function InjectScript(content: string) {
|
|
96
|
+
document.write(content) // XSS!
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// createElement with user content
|
|
100
|
+
export function CreateElement(tagName: string, content: string) {
|
|
101
|
+
const el = document.createElement(tagName)
|
|
102
|
+
el.innerHTML = content // XSS!
|
|
103
|
+
document.body.appendChild(el)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// URL-based XSS
|
|
107
|
+
export function RedirectWithMessage(url: string) {
|
|
108
|
+
window.location.href = \`javascript:alert('\${url}')\` // XSS via javascript: URL
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Event handler injection
|
|
112
|
+
export function AddHandler(element: HTMLElement, handler: string) {
|
|
113
|
+
element.setAttribute('onclick', handler) // XSS!
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// SVG injection
|
|
117
|
+
export function RenderSVG({ svgContent }: { svgContent: string }) {
|
|
118
|
+
return <div dangerouslySetInnerHTML={{ __html: svgContent }} />
|
|
119
|
+
}
|
|
120
|
+
`,
|
|
121
|
+
language: 'typescript',
|
|
122
|
+
size: 1100,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'Command Injection - Various Patterns',
|
|
127
|
+
expectFindings: true,
|
|
128
|
+
expectedCategories: ['dangerous_function'],
|
|
129
|
+
description: 'Command injection patterns in Node.js and shell contexts',
|
|
130
|
+
file: {
|
|
131
|
+
path: 'src/utils/command-injection.ts',
|
|
132
|
+
content: `
|
|
133
|
+
import { exec, execSync, spawn } from 'child_process'
|
|
134
|
+
|
|
135
|
+
// Basic exec with user input
|
|
136
|
+
export function runUserCommand(cmd: string) {
|
|
137
|
+
exec(cmd, (err, stdout) => console.log(stdout))
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// exec with template literal
|
|
141
|
+
export function processFile(filename: string) {
|
|
142
|
+
exec(\`cat \${filename}\`)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// execSync with concatenation
|
|
146
|
+
export function getFileInfo(path: string) {
|
|
147
|
+
return execSync('ls -la ' + path)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Shell option enabled
|
|
151
|
+
export function runWithShell(command: string) {
|
|
152
|
+
spawn(command, { shell: true })
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Backtick command substitution vulnerability
|
|
156
|
+
export function generateReport(date: string) {
|
|
157
|
+
exec(\`echo "Report for \${date}" > /tmp/report.txt\`)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Chained commands
|
|
161
|
+
export function chainedCommands(input: string) {
|
|
162
|
+
exec(\`echo \${input} && whoami\`) // User can inject "; rm -rf /"
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Environment variable injection
|
|
166
|
+
export function setEnvAndRun(value: string) {
|
|
167
|
+
exec(\`export VAR="\${value}" && ./script.sh\`)
|
|
168
|
+
}
|
|
169
|
+
`,
|
|
170
|
+
language: 'typescript',
|
|
171
|
+
size: 900,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
],
|
|
175
|
+
|
|
176
|
+
falseNegatives: [
|
|
177
|
+
{
|
|
178
|
+
name: 'Injection - Safe Patterns',
|
|
179
|
+
expectFindings: false,
|
|
180
|
+
description: 'Safe patterns that prevent injection attacks',
|
|
181
|
+
allowedInfoFindings: [
|
|
182
|
+
{
|
|
183
|
+
category: 'dangerous_function',
|
|
184
|
+
maxCount: 5,
|
|
185
|
+
reason: 'SQL with allowlist validation and static commands generate info-level findings',
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
file: {
|
|
189
|
+
path: 'src/api/safe-injection.ts',
|
|
190
|
+
content: `
|
|
191
|
+
import { execFile } from 'child_process'
|
|
192
|
+
import DOMPurify from 'dompurify'
|
|
193
|
+
|
|
194
|
+
// Parameterized SQL - SAFE
|
|
195
|
+
export async function getUserById(id: string) {
|
|
196
|
+
return db.query('SELECT * FROM users WHERE id = $1', [id])
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Query builder with escaping - SAFE
|
|
200
|
+
export async function searchProducts(term: string) {
|
|
201
|
+
return knex('products').where('name', 'ilike', \`%\${term}%\`)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ORM - SAFE
|
|
205
|
+
export async function findUser(email: string) {
|
|
206
|
+
return prisma.user.findUnique({ where: { email } })
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// DOMPurify sanitized HTML - SAFE
|
|
210
|
+
export function SafeHTML({ html }: { html: string }) {
|
|
211
|
+
const clean = DOMPurify.sanitize(html)
|
|
212
|
+
return <div dangerouslySetInnerHTML={{ __html: clean }} />
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// execFile with array args - SAFE
|
|
216
|
+
export function safeExec(filename: string) {
|
|
217
|
+
execFile('ls', ['-la', filename])
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Allowlist validation - SAFE
|
|
221
|
+
export async function getSortedUsers(column: string) {
|
|
222
|
+
const allowed = ['name', 'email', 'created_at']
|
|
223
|
+
if (!allowed.includes(column)) {
|
|
224
|
+
column = 'created_at'
|
|
225
|
+
}
|
|
226
|
+
return db.query(\`SELECT * FROM users ORDER BY \${column}\`)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Static command - SAFE
|
|
230
|
+
export function getVersion() {
|
|
231
|
+
return execSync('node --version')
|
|
232
|
+
}
|
|
233
|
+
`,
|
|
234
|
+
language: 'typescript',
|
|
235
|
+
size: 1000,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
}
|