@nikhil_telkar/codeshield 0.0.0-stage → 0.1.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/LICENSE +22 -0
- package/README.md +289 -2
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +237 -0
- package/dist/cli.js.map +1 -0
- package/dist/engine/fileWalker.d.ts +15 -0
- package/dist/engine/fileWalker.d.ts.map +1 -0
- package/dist/engine/fileWalker.js +170 -0
- package/dist/engine/fileWalker.js.map +1 -0
- package/dist/engine/index.d.ts +4 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +4 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/projectDetector.d.ts +10 -0
- package/dist/engine/projectDetector.d.ts.map +1 -0
- package/dist/engine/projectDetector.js +44 -0
- package/dist/engine/projectDetector.js.map +1 -0
- package/dist/engine/runner.d.ts +8 -0
- package/dist/engine/runner.d.ts.map +1 -0
- package/dist/engine/runner.js +87 -0
- package/dist/engine/runner.js.map +1 -0
- package/dist/fix/fixer.d.ts +17 -0
- package/dist/fix/fixer.d.ts.map +1 -0
- package/dist/fix/fixer.js +190 -0
- package/dist/fix/fixer.js.map +1 -0
- package/dist/fix/history.d.ts +23 -0
- package/dist/fix/history.d.ts.map +1 -0
- package/dist/fix/history.js +107 -0
- package/dist/fix/history.js.map +1 -0
- package/dist/fix/index.d.ts +3 -0
- package/dist/fix/index.d.ts.map +1 -0
- package/dist/fix/index.js +3 -0
- package/dist/fix/index.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/report/colors.d.ts +21 -0
- package/dist/report/colors.d.ts.map +1 -0
- package/dist/report/colors.js +70 -0
- package/dist/report/colors.js.map +1 -0
- package/dist/report/index.d.ts +15 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +39 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/sarif.d.ts +44 -0
- package/dist/report/sarif.d.ts.map +1 -0
- package/dist/report/sarif.js +69 -0
- package/dist/report/sarif.js.map +1 -0
- package/dist/report/score.d.ts +14 -0
- package/dist/report/score.d.ts.map +1 -0
- package/dist/report/score.js +62 -0
- package/dist/report/score.js.map +1 -0
- package/dist/report/terminal.d.ts +10 -0
- package/dist/report/terminal.d.ts.map +1 -0
- package/dist/report/terminal.js +77 -0
- package/dist/report/terminal.js.map +1 -0
- package/dist/scanners/code.d.ts +22 -0
- package/dist/scanners/code.d.ts.map +1 -0
- package/dist/scanners/code.js +319 -0
- package/dist/scanners/code.js.map +1 -0
- package/dist/scanners/dependencies.d.ts +10 -0
- package/dist/scanners/dependencies.d.ts.map +1 -0
- package/dist/scanners/dependencies.js +240 -0
- package/dist/scanners/dependencies.js.map +1 -0
- package/dist/scanners/env.d.ts +7 -0
- package/dist/scanners/env.d.ts.map +1 -0
- package/dist/scanners/env.js +195 -0
- package/dist/scanners/env.js.map +1 -0
- package/dist/scanners/helper.d.ts +27 -0
- package/dist/scanners/helper.d.ts.map +1 -0
- package/dist/scanners/helper.js +50 -0
- package/dist/scanners/helper.js.map +1 -0
- package/dist/scanners/index.d.ts +6 -0
- package/dist/scanners/index.d.ts.map +1 -0
- package/dist/scanners/index.js +6 -0
- package/dist/scanners/index.js.map +1 -0
- package/dist/scanners/secrets.d.ts +19 -0
- package/dist/scanners/secrets.d.ts.map +1 -0
- package/dist/scanners/secrets.js +213 -0
- package/dist/scanners/secrets.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/constants.d.ts +9 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +54 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/levenshtein.d.ts +2 -0
- package/dist/utils/levenshtein.d.ts.map +1 -0
- package/dist/utils/levenshtein.js +35 -0
- package/dist/utils/levenshtein.js.map +1 -0
- package/dist/utils/registry.d.ts +18 -0
- package/dist/utils/registry.d.ts.map +1 -0
- package/dist/utils/registry.js +106 -0
- package/dist/utils/registry.js.map +1 -0
- package/dist/utils/sanitize.d.ts +3 -0
- package/dist/utils/sanitize.d.ts.map +1 -0
- package/dist/utils/sanitize.js +10 -0
- package/dist/utils/sanitize.js.map +1 -0
- package/package.json +56 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../../src/report/terminal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Finding, ScanContext, ScannedFile, Scanner, Severity } from '../types.js';
|
|
2
|
+
export declare const CODE_EXTENSIONS: Set<string>;
|
|
3
|
+
export declare function isCodeFile(filePath: string): boolean;
|
|
4
|
+
export interface RuleMatch {
|
|
5
|
+
line: number;
|
|
6
|
+
snippet?: string;
|
|
7
|
+
customExplanation?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface CodeRule {
|
|
10
|
+
id: string;
|
|
11
|
+
severity: Severity;
|
|
12
|
+
title: string;
|
|
13
|
+
cwe: string;
|
|
14
|
+
plainExplanation: string;
|
|
15
|
+
fix: string;
|
|
16
|
+
aiPromptTemplate: string;
|
|
17
|
+
check: (file: ScannedFile, context: ScanContext) => RuleMatch[];
|
|
18
|
+
}
|
|
19
|
+
export declare const CODE_RULES: readonly CodeRule[];
|
|
20
|
+
export declare function scanCode(context: ScanContext): Finding[];
|
|
21
|
+
export declare const codeScanner: Scanner;
|
|
22
|
+
//# sourceMappingURL=code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../src/scanners/code.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAGnF,eAAO,MAAM,eAAe,aAA0D,CAAC;AAEvF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGpD;AAoBD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,KAAK,SAAS,EAAE,CAAC;CACjE;AA0ED,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAuOzC,CAAC;AAEF,wBAAgB,QAAQ,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,EAAE,CAmCxD;AAED,eAAO,MAAM,WAAW,EAAE,OAGzB,CAAC"}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import * as path from 'node:path';
|
|
2
|
+
import { sanitize } from '../utils/sanitize.js';
|
|
3
|
+
export const CODE_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs']);
|
|
4
|
+
export function isCodeFile(filePath) {
|
|
5
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
6
|
+
return CODE_EXTENSIONS.has(ext);
|
|
7
|
+
}
|
|
8
|
+
function isCommentLine(line) {
|
|
9
|
+
const trimmed = line.trim();
|
|
10
|
+
return (trimmed.startsWith('//') ||
|
|
11
|
+
trimmed.startsWith('*') ||
|
|
12
|
+
trimmed.startsWith('/*') ||
|
|
13
|
+
trimmed.startsWith('<!--'));
|
|
14
|
+
}
|
|
15
|
+
function isLineIgnored(line) {
|
|
16
|
+
return /codeshield-ignore|@codeshield-ignore|codeshield-disable/i.test(line);
|
|
17
|
+
}
|
|
18
|
+
function isIgnoredFile(content) {
|
|
19
|
+
return content.includes('codeshield-ignore-file');
|
|
20
|
+
}
|
|
21
|
+
function matchLineRegex(file, matcher) {
|
|
22
|
+
const matches = [];
|
|
23
|
+
for (let i = 0; i < file.lines.length; i++) {
|
|
24
|
+
const line = file.lines[i] ?? '';
|
|
25
|
+
if (isCommentLine(line) || isLineIgnored(line))
|
|
26
|
+
continue;
|
|
27
|
+
if (matcher(line, i + 1)) {
|
|
28
|
+
matches.push({ line: i + 1, snippet: line.trim() });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return matches;
|
|
32
|
+
}
|
|
33
|
+
const EVAL_RE = /\b(?:eval\s*\(|new\s+Function\s*\()/;
|
|
34
|
+
const SQL_KEYWORD_RE = /\b(?:SELECT|INSERT\s+INTO|UPDATE|DELETE\s+FROM)\b/i;
|
|
35
|
+
function isSqlConcatenation(line) {
|
|
36
|
+
const hasJsConcat = /['"`]\s*\+|\+\s*['"`]/.test(line);
|
|
37
|
+
const hasTemplateVar = /\$\{[^}]+\}/.test(line) && line.includes('`');
|
|
38
|
+
if (SQL_KEYWORD_RE.test(line)) {
|
|
39
|
+
if (hasTemplateVar)
|
|
40
|
+
return true;
|
|
41
|
+
if (hasJsConcat)
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
if (/\.(?:query|raw|execute)\s*\(/.test(line)) {
|
|
45
|
+
if (hasTemplateVar || hasJsConcat)
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
const NOSQL_INJECTION_RE = /\b(?:\w+\.)?(?:find|findOne|findOneAndUpdate|findOneAndDelete|findById|deleteMany|deleteOne|updateOne|updateMany|countDocuments)\s*\(\s*(?:req\.body|req\.query|req\.params)\s*(?:,|\))/;
|
|
51
|
+
const EXEC_CALL_RE = /(?:\b(?:child_process|cp)\.exec\s*\(|(?<!\.)\bexec\s*\()/;
|
|
52
|
+
const STATIC_STRING_EXEC_RE = /(?:\b(?:child_process|cp)\.exec|(?<!\.)\bexec)\s*\(\s*(?:'[^']*'|"[^"]*")\s*[,)]/;
|
|
53
|
+
const MATH_RANDOM_RE = /Math\.random\s*\(\)/;
|
|
54
|
+
const SENSITIVE_TOKEN_RE = /(?:token|pass(?:word|wd)?|secret|key|salt|nonce|session|otp|auth|pin|csrf)/i;
|
|
55
|
+
const WEAK_HASH_RE = /(?:createHash\s*\(\s*['"](?:md5|sha1)['"]\s*\)|crypto\.createHash\(\s*['"](?:md5|sha1)['"]\s*\))/i;
|
|
56
|
+
const PWD_CONTEXT_RE = /(?:pass(?:word|wd)?|pwd|secret|auth|credential|token|user)/i;
|
|
57
|
+
function isDangerousHtmlInjection(line) {
|
|
58
|
+
if (/dangerouslySetInnerHTML\s*=/.test(line)) {
|
|
59
|
+
if (!/dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*['"`][^'"`${}]*['"`]\s*\}\s*\}/.test(line)) {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (/\binnerHTML\s*=/.test(line)) {
|
|
64
|
+
const parts = line.split(/innerHTML\s*=\s*/);
|
|
65
|
+
const rhs = parts[1]?.trim() ?? '';
|
|
66
|
+
const cleanRhs = rhs.replace(/;$/, '').trim();
|
|
67
|
+
if (/^'(?:[^'\\]|\\.)*'$/.test(cleanRhs) && !cleanRhs.includes('+'))
|
|
68
|
+
return false;
|
|
69
|
+
if (/^"(?:[^"\\]|\\.)*"$/.test(cleanRhs) && !cleanRhs.includes('+'))
|
|
70
|
+
return false;
|
|
71
|
+
if (/^`[^`]*`$/.test(cleanRhs) && !cleanRhs.includes('${') && !cleanRhs.includes('+'))
|
|
72
|
+
return false;
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
const CORS_WILDCARD_RE = /cors\s*\(\s*\{[^}]*origin\s*:\s*(?:['"]\*['"]|true)/;
|
|
78
|
+
const CORS_HEADER_WILDCARD_RE = /res\.(?:setHeader|header)\s*\(\s*['"]Access-Control-Allow-Origin['"]\s*,\s*['"]\*['"]\s*\)/;
|
|
79
|
+
const COOKIE_CALL_RE = /res\.cookie\s*\(/;
|
|
80
|
+
const JWT_SIGN_RE = /jwt\.sign\s*\(/;
|
|
81
|
+
const STACK_IN_RES_RE = /res\.(?:status\(\d+\)\.)?(?:send|json)\s*\([^)]*(?:err|error)\.stack\b/;
|
|
82
|
+
const SUPABASE_SERVICE_ROLE_RE = /\b(?:SUPABASE_SERVICE_ROLE_KEY|service_role)\b/;
|
|
83
|
+
export const CODE_RULES = [
|
|
84
|
+
{
|
|
85
|
+
id: 'COD-001',
|
|
86
|
+
severity: 'critical',
|
|
87
|
+
title: 'Dynamic Code Execution via eval() or new Function()',
|
|
88
|
+
cwe: 'CWE-95',
|
|
89
|
+
plainExplanation: 'Using `eval()` or `new Function()` executes arbitrary strings as JavaScript code. ' +
|
|
90
|
+
'If any untrusted input reaches these functions, attackers can execute arbitrary code ' +
|
|
91
|
+
'on your server or in your users’ browsers (Remote Code Execution / XSS).',
|
|
92
|
+
fix: 'Parse structured data using `JSON.parse()`, or replace dynamic code evaluation with standard functions and object lookups.',
|
|
93
|
+
aiPromptTemplate: 'Remove `eval()` or `new Function()` on line {LINE}. Replace it with safe data parsing such as `JSON.parse()` or static lookup maps.',
|
|
94
|
+
check: (file) => matchLineRegex(file, (line) => EVAL_RE.test(line)),
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'COD-002',
|
|
98
|
+
severity: 'critical',
|
|
99
|
+
title: 'SQL Query Built via String Concatenation (SQL Injection)',
|
|
100
|
+
cwe: 'CWE-89',
|
|
101
|
+
plainExplanation: 'Constructing SQL queries using template literals (${...}) or string concatenation (+) allows ' +
|
|
102
|
+
'attackers to inject malicious SQL clauses (SQL Injection). This can bypass authentication, ' +
|
|
103
|
+
'leak confidential records, or destroy database tables.',
|
|
104
|
+
fix: 'Use parameterized queries or prepared statements (e.g., `db.query("SELECT * FROM users WHERE id = $1", [id])`) or a type-safe ORM/query builder.',
|
|
105
|
+
aiPromptTemplate: 'Refactor the SQL query on line {LINE} to use parameterized queries with placeholders ($1, ?, or named parameters) instead of string concatenation.',
|
|
106
|
+
check: (file) => matchLineRegex(file, (line) => isSqlConcatenation(line)),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'COD-003',
|
|
110
|
+
severity: 'high',
|
|
111
|
+
title: 'Unsanitized Request Body in Database Query (NoSQL Injection)',
|
|
112
|
+
cwe: 'CWE-943',
|
|
113
|
+
plainExplanation: 'Passing `req.body`, `req.query`, or `req.params` directly to database query methods (e.g. `find(req.body)`) ' +
|
|
114
|
+
'allows attackers to pass objects containing NoSQL operators like `{ "$gt": "" }` to bypass password checks or extract all records.',
|
|
115
|
+
fix: 'Extract and validate individual primitive fields explicitly (e.g., `{ email: String(req.body.email) }`) or validate payloads with Zod.',
|
|
116
|
+
aiPromptTemplate: 'Refactor the database query on line {LINE} to pass only explicitly validated primitive fields instead of the whole `req.body` object.',
|
|
117
|
+
check: (file) => matchLineRegex(file, (line) => NOSQL_INJECTION_RE.test(line)),
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'COD-004',
|
|
121
|
+
severity: 'critical',
|
|
122
|
+
title: 'Command Injection Risk in child_process.exec()',
|
|
123
|
+
cwe: 'CWE-78',
|
|
124
|
+
plainExplanation: 'Calling `child_process.exec()` with variables or string concatenation passes input directly to an OS shell. ' +
|
|
125
|
+
'If arguments contain shell metacharacters (`;`, `|`, `&&`, `$()`), attackers can execute arbitrary OS commands.',
|
|
126
|
+
fix: 'Use `child_process.execFile()` or `spawn()` with an array of arguments, and ensure `{ shell: false }`.',
|
|
127
|
+
aiPromptTemplate: 'Replace `child_process.exec()` on line {LINE} with `execFile()` or `spawn()`, passing arguments as an array instead of string concatenation.',
|
|
128
|
+
check: (file) => matchLineRegex(file, (line) => {
|
|
129
|
+
if (!EXEC_CALL_RE.test(line))
|
|
130
|
+
return false;
|
|
131
|
+
if (STATIC_STRING_EXEC_RE.test(line) && !line.includes('+') && !line.includes('${')) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
}),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'COD-005',
|
|
139
|
+
severity: 'high',
|
|
140
|
+
title: 'Insecure PRNG (Math.random) Used for Security-Sensitive Value',
|
|
141
|
+
cwe: 'CWE-338',
|
|
142
|
+
plainExplanation: '`Math.random()` is not cryptographically secure; its sequence can be predicted by attackers. ' +
|
|
143
|
+
'Using it for tokens, session IDs, OTPs, or passwords allows attackers to guess generated values.',
|
|
144
|
+
fix: 'Use Node.js `crypto.randomBytes(32).toString("hex")` or `crypto.randomUUID()` for cryptographically secure randomness.',
|
|
145
|
+
aiPromptTemplate: 'Replace `Math.random()` on line {LINE} with `crypto.randomBytes()` or `crypto.randomUUID()` from `node:crypto`.',
|
|
146
|
+
check: (file) => matchLineRegex(file, (line) => {
|
|
147
|
+
if (!MATH_RANDOM_RE.test(line))
|
|
148
|
+
return false;
|
|
149
|
+
return SENSITIVE_TOKEN_RE.test(line);
|
|
150
|
+
}),
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
id: 'COD-006',
|
|
154
|
+
severity: 'high',
|
|
155
|
+
title: 'Weak Hash Algorithm (MD5/SHA1) Used for Credentials',
|
|
156
|
+
cwe: 'CWE-327',
|
|
157
|
+
plainExplanation: 'MD5 and SHA-1 are cryptographically broken algorithms susceptible to collisions and fast brute-force/rainbow table attacks. ' +
|
|
158
|
+
'They must never be used for storing or hashing passwords.',
|
|
159
|
+
fix: 'Use an adaptive, salted password hashing algorithm such as `bcrypt`, `argon2`, or `scrypt`.',
|
|
160
|
+
aiPromptTemplate: 'Replace MD5/SHA-1 on line {LINE} with a dedicated password hashing library such as `bcrypt` or `argon2`.',
|
|
161
|
+
check: (file) => matchLineRegex(file, (line) => {
|
|
162
|
+
if (!WEAK_HASH_RE.test(line))
|
|
163
|
+
return false;
|
|
164
|
+
return PWD_CONTEXT_RE.test(line);
|
|
165
|
+
}),
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'COD-007',
|
|
169
|
+
severity: 'high',
|
|
170
|
+
title: 'Unescaped HTML Injection via innerHTML or dangerouslySetInnerHTML',
|
|
171
|
+
cwe: 'CWE-79',
|
|
172
|
+
plainExplanation: 'Rendering dynamic content through `dangerouslySetInnerHTML` or `element.innerHTML` bypasses framework XSS sanitization. ' +
|
|
173
|
+
'If user-controlled data is injected, attackers can execute malicious scripts in victims’ browsers (Cross-Site Scripting).',
|
|
174
|
+
fix: 'Use standard text rendering (`{text}` in JSX or `element.textContent`), or sanitize HTML with DOMPurify before inserting.',
|
|
175
|
+
aiPromptTemplate: 'Refactor line {LINE} to use safe text interpolation or sanitize HTML using `DOMPurify.sanitize()` before setting innerHTML.',
|
|
176
|
+
check: (file) => matchLineRegex(file, (line) => isDangerousHtmlInjection(line)),
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
id: 'COD-008',
|
|
180
|
+
severity: 'medium',
|
|
181
|
+
title: 'Overly Permissive CORS Policy (Wildcard Origin)',
|
|
182
|
+
cwe: 'CWE-942',
|
|
183
|
+
plainExplanation: 'Configuring CORS with `origin: "*"` or `Access-Control-Allow-Origin: *` allows any website on the internet ' +
|
|
184
|
+
'to make requests to your API. If your API handles authentication cookies or private data, malicious sites can read responses.',
|
|
185
|
+
fix: 'Specify an explicit array of approved frontend domains or validate the request Origin header against an allowlist.',
|
|
186
|
+
aiPromptTemplate: 'Update the CORS configuration on line {LINE} to specify allowed origin domains explicitly rather than using wildcard "*".',
|
|
187
|
+
check: (file) => matchLineRegex(file, (line) => {
|
|
188
|
+
if (CORS_WILDCARD_RE.test(line))
|
|
189
|
+
return true;
|
|
190
|
+
if (CORS_HEADER_WILDCARD_RE.test(line))
|
|
191
|
+
return true;
|
|
192
|
+
return false;
|
|
193
|
+
}),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
id: 'COD-009',
|
|
197
|
+
severity: 'medium',
|
|
198
|
+
title: 'Cookie Set Without httpOnly or secure Flags',
|
|
199
|
+
cwe: 'CWE-1004',
|
|
200
|
+
plainExplanation: 'Cookies set without `httpOnly: true` can be accessed by JavaScript, exposing session credentials to Cross-Site Scripting (XSS) theft. ' +
|
|
201
|
+
'Missing `secure: true` allows cookies to be transmitted over unencrypted HTTP.',
|
|
202
|
+
fix: 'Configure cookies with `{ httpOnly: true, secure: true, sameSite: "lax" }`.',
|
|
203
|
+
aiPromptTemplate: 'Add `{ httpOnly: true, secure: true, sameSite: "lax" }` to the cookie options on line {LINE}.',
|
|
204
|
+
check: (file) => matchLineRegex(file, (line) => {
|
|
205
|
+
if (!COOKIE_CALL_RE.test(line))
|
|
206
|
+
return false;
|
|
207
|
+
const hasHttpOnly = /httpOnly\s*:\s*true/.test(line);
|
|
208
|
+
const hasSecure = /secure\s*:\s*true/.test(line);
|
|
209
|
+
return !hasHttpOnly || !hasSecure;
|
|
210
|
+
}),
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'COD-010',
|
|
214
|
+
severity: 'medium',
|
|
215
|
+
title: 'JWT Token Signed Without Expiration (expiresIn)',
|
|
216
|
+
cwe: 'CWE-613',
|
|
217
|
+
plainExplanation: 'JSON Web Tokens created without an `expiresIn` option remain valid indefinitely. ' +
|
|
218
|
+
'If a token is ever intercepted or leaked, attackers maintain permanent access unless you build a token revocation system.',
|
|
219
|
+
fix: 'Specify an expiration window in the `jwt.sign()` options, e.g. `{ expiresIn: "1h" }` or `{ expiresIn: "15m" }`.',
|
|
220
|
+
aiPromptTemplate: 'Add an expiration option to `jwt.sign()` on line {LINE}, such as `{ expiresIn: "1h" }`.',
|
|
221
|
+
check: (file) => matchLineRegex(file, (line) => {
|
|
222
|
+
if (!JWT_SIGN_RE.test(line))
|
|
223
|
+
return false;
|
|
224
|
+
return !/expiresIn\s*:/.test(line);
|
|
225
|
+
}),
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
id: 'COD-011',
|
|
229
|
+
severity: 'medium',
|
|
230
|
+
title: 'Missing Security Headers Middleware (Helmet) in Express App',
|
|
231
|
+
cwe: 'CWE-693',
|
|
232
|
+
plainExplanation: 'This Express application does not use `helmet` to set secure HTTP headers. ' +
|
|
233
|
+
'Helmet protects your app against MIME-type sniffing, clickjacking, XSS framing, and information disclosure.',
|
|
234
|
+
fix: 'Install helmet (`npm install helmet`) and add `app.use(helmet())` near the top of your Express middleware stack.',
|
|
235
|
+
aiPromptTemplate: 'Import `helmet` from "helmet" and add `app.use(helmet())` right after initializing the Express application.',
|
|
236
|
+
check: (file) => {
|
|
237
|
+
const content = file.content;
|
|
238
|
+
const isExpressApp = /\bexpress\s*\(\s*\)/.test(content) &&
|
|
239
|
+
/\b\.listen\s*\(/.test(content);
|
|
240
|
+
if (!isExpressApp)
|
|
241
|
+
return [];
|
|
242
|
+
const hasHelmet = /\bhelmet\b/.test(content) ||
|
|
243
|
+
/\bapp\.use\s*\(\s*helmet/.test(content);
|
|
244
|
+
if (!hasHelmet) {
|
|
245
|
+
return [{ line: 1, customExplanation: 'Express application initialized without helmet() security headers middleware.' }];
|
|
246
|
+
}
|
|
247
|
+
return [];
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
id: 'COD-012',
|
|
252
|
+
severity: 'high',
|
|
253
|
+
title: 'Server Stack Trace Exposed in HTTP Response',
|
|
254
|
+
cwe: 'CWE-209',
|
|
255
|
+
plainExplanation: 'Returning `err.stack` or raw error objects in HTTP responses leaks internal code paths, database queries, ' +
|
|
256
|
+
'library versions, and architecture details to potential attackers.',
|
|
257
|
+
fix: 'Log the stack trace on the server with `console.error(err)` and return a generic error message (e.g. `{ error: "Internal Server Error" }`) to the client.',
|
|
258
|
+
aiPromptTemplate: 'Remove `err.stack` from the HTTP response on line {LINE}. Log it server-side using `console.error(err)` and return a generic error message to clients.',
|
|
259
|
+
check: (file) => matchLineRegex(file, (line) => STACK_IN_RES_RE.test(line)),
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: 'COD-013',
|
|
263
|
+
severity: 'critical',
|
|
264
|
+
title: 'Supabase service_role Admin Key Used in Client-Side Code',
|
|
265
|
+
cwe: 'CWE-798',
|
|
266
|
+
plainExplanation: 'The Supabase `service_role` key bypasses all Row Level Security (RLS) policies and grants complete admin access to your database. ' +
|
|
267
|
+
'Referencing it in client components or browser bundles leaks your entire database to any web visitor.',
|
|
268
|
+
fix: 'Use the public `anon` key (`NEXT_PUBLIC_SUPABASE_ANON_KEY`) in client code, and restrict `service_role` strictly to backend API routes.',
|
|
269
|
+
aiPromptTemplate: 'Remove the `service_role` key from client code on line {LINE}. Use the public `anon` key instead and enforce Row Level Security in Supabase.',
|
|
270
|
+
check: (file) => {
|
|
271
|
+
const isExplicitClient = file.content.includes("'use client'") || file.content.includes('"use client"');
|
|
272
|
+
const ext = path.extname(file.relativePath).toLowerCase();
|
|
273
|
+
const isReactComponent = (ext === '.jsx' || ext === '.tsx') && !file.relativePath.includes('/api/') && !file.relativePath.includes('/server/');
|
|
274
|
+
if (!isExplicitClient && !isReactComponent)
|
|
275
|
+
return [];
|
|
276
|
+
return matchLineRegex(file, (line) => SUPABASE_SERVICE_ROLE_RE.test(line));
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
];
|
|
280
|
+
export function scanCode(context) {
|
|
281
|
+
const findings = [];
|
|
282
|
+
for (const file of context.files) {
|
|
283
|
+
if (!isCodeFile(file.relativePath))
|
|
284
|
+
continue;
|
|
285
|
+
if (isIgnoredFile(file.content))
|
|
286
|
+
continue;
|
|
287
|
+
for (const rule of CODE_RULES) {
|
|
288
|
+
try {
|
|
289
|
+
const matches = rule.check(file, context);
|
|
290
|
+
for (const match of matches) {
|
|
291
|
+
const lineNum = match.line;
|
|
292
|
+
const snippet = match.snippet ? sanitize(match.snippet) : '';
|
|
293
|
+
const explanation = match.customExplanation ?? rule.plainExplanation;
|
|
294
|
+
findings.push({
|
|
295
|
+
ruleId: rule.id,
|
|
296
|
+
severity: rule.severity,
|
|
297
|
+
title: rule.title,
|
|
298
|
+
cwe: rule.cwe,
|
|
299
|
+
plainExplanation: explanation,
|
|
300
|
+
file: file.relativePath,
|
|
301
|
+
line: lineNum,
|
|
302
|
+
fix: rule.fix,
|
|
303
|
+
aiPrompt: rule.aiPromptTemplate.replace('{LINE}', String(lineNum)) + (snippet ? ` Code: \`${snippet}\`` : ''),
|
|
304
|
+
...(snippet ? { snippet } : {}),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
// rule failure should not crash scanner
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return findings;
|
|
314
|
+
}
|
|
315
|
+
export const codeScanner = {
|
|
316
|
+
name: 'code',
|
|
317
|
+
scan: scanCode,
|
|
318
|
+
};
|
|
319
|
+
//# sourceMappingURL=code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/scanners/code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Finding, ScanContext, Scanner } from '../types.js';
|
|
2
|
+
import { type RegistryFetchFn } from '../utils/registry.js';
|
|
3
|
+
export declare function isValidNpmName(name: string): boolean;
|
|
4
|
+
export interface DepScanOptions {
|
|
5
|
+
offline?: boolean;
|
|
6
|
+
fetchFn?: RegistryFetchFn;
|
|
7
|
+
}
|
|
8
|
+
export declare function scanDependencies(context: ScanContext, options?: DepScanOptions): Promise<Finding[]>;
|
|
9
|
+
export declare const depScanner: Scanner;
|
|
10
|
+
//# sourceMappingURL=dependencies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencies.d.ts","sourceRoot":"","sources":["../../src/scanners/dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAM3E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGpD;AAkED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,eAAe,CAAC;CAC3B;AA2GD,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,OAAO,EAAE,CAAC,CAyFpB;AAED,eAAO,MAAM,UAAU,EAAE,OAKxB,CAAC"}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import { fetchRegistry } from '../utils/registry.js';
|
|
2
|
+
import { levenshtein } from '../utils/levenshtein.js';
|
|
3
|
+
const NPM_NAME_RE = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
4
|
+
export function isValidNpmName(name) {
|
|
5
|
+
if (name.length === 0 || name.length > 214)
|
|
6
|
+
return false;
|
|
7
|
+
return NPM_NAME_RE.test(name);
|
|
8
|
+
}
|
|
9
|
+
const POPULAR_PACKAGES = [
|
|
10
|
+
'react', 'react-dom', 'react-router', 'react-router-dom',
|
|
11
|
+
'next', 'nuxt', 'vue', 'angular', 'svelte',
|
|
12
|
+
'express', 'fastify', 'koa', 'hapi',
|
|
13
|
+
'lodash', 'underscore', 'ramda',
|
|
14
|
+
'axios', 'node-fetch', 'got', 'superagent',
|
|
15
|
+
'typescript', 'ts-node', 'tsx',
|
|
16
|
+
'webpack', 'vite', 'rollup', 'esbuild', 'parcel',
|
|
17
|
+
'jest', 'vitest', 'mocha', 'jasmine', 'ava', 'tap',
|
|
18
|
+
'eslint', 'prettier', 'husky', 'lint-staged',
|
|
19
|
+
'dotenv', 'cors', 'helmet', 'morgan', 'body-parser',
|
|
20
|
+
'mongoose', 'sequelize', 'prisma', 'typeorm', 'knex',
|
|
21
|
+
'socket.io', 'ws', 'jsonwebtoken', 'bcrypt', 'bcryptjs',
|
|
22
|
+
'uuid', 'dayjs', 'moment', 'date-fns',
|
|
23
|
+
'chalk', 'colors', 'ora', 'inquirer',
|
|
24
|
+
'zod', 'yup', 'joi',
|
|
25
|
+
'sharp', 'multer', 'formidable',
|
|
26
|
+
];
|
|
27
|
+
function findTyposquat(name) {
|
|
28
|
+
const bare = name.includes('/') ? (name.split('/')[1] ?? name) : name;
|
|
29
|
+
for (const popular of POPULAR_PACKAGES) {
|
|
30
|
+
if (levenshtein(bare, popular) === 1) {
|
|
31
|
+
return popular;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
class Semaphore {
|
|
37
|
+
limit;
|
|
38
|
+
running = 0;
|
|
39
|
+
queue = [];
|
|
40
|
+
constructor(limit) {
|
|
41
|
+
this.limit = limit;
|
|
42
|
+
}
|
|
43
|
+
async run(fn) {
|
|
44
|
+
await this.acquire();
|
|
45
|
+
try {
|
|
46
|
+
return await fn();
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
this.release();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
acquire() {
|
|
53
|
+
if (this.running < this.limit) {
|
|
54
|
+
this.running++;
|
|
55
|
+
return Promise.resolve();
|
|
56
|
+
}
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
this.queue.push(resolve);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
release() {
|
|
62
|
+
const next = this.queue.shift();
|
|
63
|
+
if (next) {
|
|
64
|
+
next();
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.running--;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function findingHallucinated(pkgName, pkgFile) {
|
|
72
|
+
return {
|
|
73
|
+
ruleId: 'DEP-001',
|
|
74
|
+
severity: 'critical',
|
|
75
|
+
title: `Package "${pkgName}" does not exist on npm`,
|
|
76
|
+
plainExplanation: `The package "${pkgName}" was not found on the npm registry. ` +
|
|
77
|
+
'AI assistants sometimes invent package names that do not exist — ' +
|
|
78
|
+
'called "package hallucination". Installing a non-existent package may ' +
|
|
79
|
+
'succeed if a malicious actor has since published one with that name.',
|
|
80
|
+
file: pkgFile,
|
|
81
|
+
line: 1,
|
|
82
|
+
fix: `Search for "${pkgName}" on npmjs.com to find the correct package name, or remove the dependency.`,
|
|
83
|
+
aiPrompt: `The package "${pkgName}" does not exist on npm. ` +
|
|
84
|
+
'Please find the correct package name or suggest an alternative that actually exists.',
|
|
85
|
+
cwe: 'CWE-829',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function findingNewPackage(pkgName, pkgFile, createdAt) {
|
|
89
|
+
return {
|
|
90
|
+
ruleId: 'DEP-002',
|
|
91
|
+
severity: 'high',
|
|
92
|
+
title: `Package "${pkgName}" was first published less than 30 days ago`,
|
|
93
|
+
plainExplanation: `"${pkgName}" appeared on npm only recently (${createdAt}). ` +
|
|
94
|
+
'Very new packages have not been reviewed by the community and could be ' +
|
|
95
|
+
'malicious or abandoned. Confirm this is the package you intended.',
|
|
96
|
+
file: pkgFile,
|
|
97
|
+
line: 1,
|
|
98
|
+
fix: `Verify "${pkgName}" on npmjs.com. Prefer well-established packages with a track record.`,
|
|
99
|
+
aiPrompt: `The package "${pkgName}" was published less than 30 days ago. ` +
|
|
100
|
+
'Is there a more established alternative? If this package is intentional, explain why.',
|
|
101
|
+
cwe: 'CWE-1104',
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function findingInstallScript(pkgName, pkgFile, scriptName) {
|
|
105
|
+
return {
|
|
106
|
+
ruleId: 'DEP-003',
|
|
107
|
+
severity: 'high',
|
|
108
|
+
title: `Package "${pkgName}" runs a "${scriptName}" script on install`,
|
|
109
|
+
plainExplanation: `"${pkgName}" defines a \`${scriptName}\` script that runs automatically when you do \`npm install\`. ` +
|
|
110
|
+
'This is a common vector for supply-chain attacks: malicious code can ' +
|
|
111
|
+
'exfiltrate secrets, install backdoors, or modify your files at install time.',
|
|
112
|
+
file: pkgFile,
|
|
113
|
+
line: 1,
|
|
114
|
+
fix: `Audit the source of "${pkgName}" before installing, or use \`npm install --ignore-scripts\` and verify manually.`,
|
|
115
|
+
aiPrompt: `The package "${pkgName}" has an \`${scriptName}\` script. ` +
|
|
116
|
+
'Is this script necessary? What does it do, and is there a safer alternative?',
|
|
117
|
+
cwe: 'CWE-506',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function findingTyposquat(pkgName, pkgFile, similarTo) {
|
|
121
|
+
return {
|
|
122
|
+
ruleId: 'DEP-004',
|
|
123
|
+
severity: 'high',
|
|
124
|
+
title: `Package "${pkgName}" looks like a typosquat of "${similarTo}"`,
|
|
125
|
+
plainExplanation: `"${pkgName}" is one character away from the popular package "${similarTo}". ` +
|
|
126
|
+
'Typosquatting is an attack where a malicious package is published with a name ' +
|
|
127
|
+
'nearly identical to a trusted one, hoping developers will mistype it.',
|
|
128
|
+
file: pkgFile,
|
|
129
|
+
line: 1,
|
|
130
|
+
fix: `Double-check whether you meant "${similarTo}". If not, verify "${pkgName}" is legitimate on npmjs.com.`,
|
|
131
|
+
aiPrompt: `The package "${pkgName}" is one character away from "${similarTo}". ` +
|
|
132
|
+
`Did you mean "${similarTo}"? If "${pkgName}" is intentional, please confirm it is a known package.`,
|
|
133
|
+
cwe: 'CWE-829',
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
function findingCouldNotCheck(pkgName, pkgFile, reason) {
|
|
137
|
+
return {
|
|
138
|
+
ruleId: 'DEP-005',
|
|
139
|
+
severity: 'info',
|
|
140
|
+
title: `Could not check package "${pkgName}" against npm registry`,
|
|
141
|
+
plainExplanation: `CodeShield tried to look up "${pkgName}" on the npm registry but failed: ${reason}. ` +
|
|
142
|
+
'The package has NOT been verified as safe — please check it manually.',
|
|
143
|
+
file: pkgFile,
|
|
144
|
+
line: 1,
|
|
145
|
+
fix: `Manually verify "${pkgName}" at https://www.npmjs.com/package/${pkgName}`,
|
|
146
|
+
aiPrompt: `The npm registry check for "${pkgName}" failed (${reason}). ` +
|
|
147
|
+
'Please manually confirm this package is legitimate.',
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function extractDependencies(pkgJson) {
|
|
151
|
+
const deps = [];
|
|
152
|
+
for (const field of ['dependencies', 'devDependencies', 'peerDependencies']) {
|
|
153
|
+
const section = pkgJson[field];
|
|
154
|
+
if (section !== null && typeof section === 'object') {
|
|
155
|
+
deps.push(...Object.keys(section));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return [...new Set(deps)];
|
|
159
|
+
}
|
|
160
|
+
export async function scanDependencies(context, options = {}) {
|
|
161
|
+
const findings = [];
|
|
162
|
+
const pkgFile = context.files.find((f) => f.relativePath === 'package.json' || f.relativePath === './package.json');
|
|
163
|
+
if (!pkgFile)
|
|
164
|
+
return findings;
|
|
165
|
+
let pkgJson;
|
|
166
|
+
try {
|
|
167
|
+
pkgJson = JSON.parse(pkgFile.content);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return findings;
|
|
171
|
+
}
|
|
172
|
+
const depNames = extractDependencies(pkgJson);
|
|
173
|
+
if (depNames.length === 0)
|
|
174
|
+
return findings;
|
|
175
|
+
const pkgFilePath = pkgFile.relativePath;
|
|
176
|
+
if (options.offline) {
|
|
177
|
+
for (const name of depNames) {
|
|
178
|
+
if (!isValidNpmName(name))
|
|
179
|
+
continue;
|
|
180
|
+
const typosquat = findTyposquat(name);
|
|
181
|
+
if (typosquat)
|
|
182
|
+
findings.push(findingTyposquat(name, pkgFilePath, typosquat));
|
|
183
|
+
}
|
|
184
|
+
return findings;
|
|
185
|
+
}
|
|
186
|
+
const sem = new Semaphore(5);
|
|
187
|
+
const thirtyDaysMs = 30 * 24 * 60 * 60 * 1000;
|
|
188
|
+
const tasks = depNames.map((name) => sem.run(async () => {
|
|
189
|
+
const local = [];
|
|
190
|
+
const typosquat = findTyposquat(name);
|
|
191
|
+
if (typosquat)
|
|
192
|
+
local.push(findingTyposquat(name, pkgFilePath, typosquat));
|
|
193
|
+
if (!isValidNpmName(name)) {
|
|
194
|
+
return local;
|
|
195
|
+
}
|
|
196
|
+
let result;
|
|
197
|
+
try {
|
|
198
|
+
result = await fetchRegistry(name, 5_000, options.fetchFn);
|
|
199
|
+
}
|
|
200
|
+
catch (err) {
|
|
201
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
202
|
+
local.push(findingCouldNotCheck(name, pkgFilePath, reason));
|
|
203
|
+
return local;
|
|
204
|
+
}
|
|
205
|
+
if (result.status === 'not-found') {
|
|
206
|
+
local.push(findingHallucinated(name, pkgFilePath));
|
|
207
|
+
return local;
|
|
208
|
+
}
|
|
209
|
+
if (result.status === 'error') {
|
|
210
|
+
local.push(findingCouldNotCheck(name, pkgFilePath, result.reason));
|
|
211
|
+
return local;
|
|
212
|
+
}
|
|
213
|
+
const { data } = result;
|
|
214
|
+
if (data.created) {
|
|
215
|
+
const createdMs = Date.parse(data.created);
|
|
216
|
+
if (!isNaN(createdMs) && Date.now() - createdMs < thirtyDaysMs) {
|
|
217
|
+
local.push(findingNewPackage(name, pkgFilePath, data.created));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
for (const scriptName of ['install', 'postinstall', 'preinstall']) {
|
|
221
|
+
if (scriptName in data.latestScripts) {
|
|
222
|
+
local.push(findingInstallScript(name, pkgFilePath, scriptName));
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return local;
|
|
227
|
+
}));
|
|
228
|
+
const allResults = await Promise.all(tasks);
|
|
229
|
+
for (const result of allResults) {
|
|
230
|
+
findings.push(...result);
|
|
231
|
+
}
|
|
232
|
+
return findings;
|
|
233
|
+
}
|
|
234
|
+
export const depScanner = {
|
|
235
|
+
name: 'dependencies',
|
|
236
|
+
scan: (_context) => {
|
|
237
|
+
return [];
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
//# sourceMappingURL=dependencies.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dependencies.js","sourceRoot":"","sources":["../../src/scanners/dependencies.ts"],"names":[],"mappings":"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"}
|