vibe-guard 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/LICENSE +21 -0
- package/README.md +200 -0
- package/dist/bin/vibe-guard.d.ts +3 -0
- package/dist/bin/vibe-guard.d.ts.map +1 -0
- package/dist/bin/vibe-guard.js +119 -0
- package/dist/bin/vibe-guard.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +128 -0
- package/dist/index.js.map +1 -0
- package/dist/reporter.d.ts +13 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +116 -0
- package/dist/reporter.js.map +1 -0
- package/dist/rules/directory-traversal.d.ts +14 -0
- package/dist/rules/directory-traversal.d.ts.map +1 -0
- package/dist/rules/directory-traversal.js +142 -0
- package/dist/rules/directory-traversal.js.map +1 -0
- package/dist/rules/exposed-secrets.d.ts +13 -0
- package/dist/rules/exposed-secrets.d.ts.map +1 -0
- package/dist/rules/exposed-secrets.js +137 -0
- package/dist/rules/exposed-secrets.js.map +1 -0
- package/dist/rules/hardcoded-sensitive-data.d.ts +13 -0
- package/dist/rules/hardcoded-sensitive-data.d.ts.map +1 -0
- package/dist/rules/hardcoded-sensitive-data.js +104 -0
- package/dist/rules/hardcoded-sensitive-data.js.map +1 -0
- package/dist/rules/index.d.ts +15 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +54 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/insecure-dependencies.d.ts +16 -0
- package/dist/rules/insecure-dependencies.d.ts.map +1 -0
- package/dist/rules/insecure-dependencies.js +152 -0
- package/dist/rules/insecure-dependencies.js.map +1 -0
- package/dist/rules/insecure-http.d.ts +13 -0
- package/dist/rules/insecure-http.d.ts.map +1 -0
- package/dist/rules/insecure-http.js +91 -0
- package/dist/rules/insecure-http.js.map +1 -0
- package/dist/rules/missing-authentication.d.ts +14 -0
- package/dist/rules/missing-authentication.d.ts.map +1 -0
- package/dist/rules/missing-authentication.js +98 -0
- package/dist/rules/missing-authentication.js.map +1 -0
- package/dist/rules/missing-security-headers.d.ts +16 -0
- package/dist/rules/missing-security-headers.d.ts.map +1 -0
- package/dist/rules/missing-security-headers.js +158 -0
- package/dist/rules/missing-security-headers.js.map +1 -0
- package/dist/rules/open-cors.d.ts +11 -0
- package/dist/rules/open-cors.d.ts.map +1 -0
- package/dist/rules/open-cors.js +61 -0
- package/dist/rules/open-cors.js.map +1 -0
- package/dist/rules/sql-injection.d.ts +12 -0
- package/dist/rules/sql-injection.d.ts.map +1 -0
- package/dist/rules/sql-injection.js +95 -0
- package/dist/rules/sql-injection.js.map +1 -0
- package/dist/rules/unvalidated-input.d.ts +13 -0
- package/dist/rules/unvalidated-input.d.ts.map +1 -0
- package/dist/rules/unvalidated-input.js +128 -0
- package/dist/rules/unvalidated-input.js.map +1 -0
- package/dist/scanner.d.ts +15 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +240 -0
- package/dist/scanner.js.map +1 -0
- package/dist/types/index.d.ts +66 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +38 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenCorsRule = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
class OpenCorsRule extends types_1.BaseRule {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.name = 'open-cors';
|
|
9
|
+
this.description = 'Detects overly permissive CORS configurations';
|
|
10
|
+
this.severity = 'high';
|
|
11
|
+
this.corsPatterns = [
|
|
12
|
+
// Wildcard CORS origins
|
|
13
|
+
{ pattern: /Access-Control-Allow-Origin\s*:\s*['"`]\*['"`]/gi, message: 'Wildcard CORS origin allows any domain' },
|
|
14
|
+
{ pattern: /cors\(\s*\{\s*origin\s*:\s*['"`]\*['"`]/gi, message: 'CORS middleware configured with wildcard origin' },
|
|
15
|
+
{ pattern: /\.header\s*\(\s*['"`]Access-Control-Allow-Origin['"`]\s*,\s*['"`]\*['"`]/gi, message: 'Manual CORS header set to wildcard' },
|
|
16
|
+
// Express CORS middleware with wildcard
|
|
17
|
+
{ pattern: /app\.use\s*\(\s*cors\s*\(\s*\{\s*origin\s*:\s*true/gi, message: 'CORS origin set to true (allows all origins)' },
|
|
18
|
+
{ pattern: /app\.use\s*\(\s*cors\s*\(\s*\)\s*\)/gi, message: 'CORS middleware used without origin restrictions' },
|
|
19
|
+
// Permissive credentials
|
|
20
|
+
{ pattern: /Access-Control-Allow-Credentials\s*:\s*['"`]true['"`]/gi, message: 'CORS credentials enabled - ensure origin is restricted' },
|
|
21
|
+
// Overly broad methods
|
|
22
|
+
{ pattern: /Access-Control-Allow-Methods\s*:\s*['"`]\*['"`]/gi, message: 'CORS allows all HTTP methods' },
|
|
23
|
+
{ pattern: /Access-Control-Allow-Headers\s*:\s*['"`]\*['"`]/gi, message: 'CORS allows all headers' },
|
|
24
|
+
// Framework-specific patterns
|
|
25
|
+
{ pattern: /@CrossOrigin\s*\(\s*origins\s*=\s*['"`]\*['"`]/gi, message: 'Spring @CrossOrigin annotation with wildcard origin' },
|
|
26
|
+
{ pattern: /enable_cors\s*\(\s*origins\s*=\s*\[\s*['"`]\*['"`]/gi, message: 'FastAPI CORS with wildcard origin' }
|
|
27
|
+
];
|
|
28
|
+
this.safePatterns = [
|
|
29
|
+
/localhost/i,
|
|
30
|
+
/127\.0\.0\.1/,
|
|
31
|
+
/\.local/i,
|
|
32
|
+
/development/i,
|
|
33
|
+
/staging/i,
|
|
34
|
+
/test/i
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
check(fileContent) {
|
|
38
|
+
const issues = [];
|
|
39
|
+
for (const { pattern, message } of this.corsPatterns) {
|
|
40
|
+
const matches = this.findMatches(fileContent.content, pattern);
|
|
41
|
+
for (const { line, column, lineContent } of matches) {
|
|
42
|
+
// Skip if it's in a development/test context
|
|
43
|
+
if (this.isDevelopmentContext(fileContent.content, line)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
issues.push(this.createIssue(fileContent.path, line, column, lineContent, `Permissive CORS configuration: ${message}`, `Restrict CORS origins to specific domains. Use specific origins like 'https://yourdomain.com' instead of '*'. Consider using environment variables for different environments.`));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return issues;
|
|
50
|
+
}
|
|
51
|
+
isDevelopmentContext(content, lineNumber) {
|
|
52
|
+
const lines = content.split('\n');
|
|
53
|
+
const contextRange = 5;
|
|
54
|
+
const startLine = Math.max(0, lineNumber - contextRange - 1);
|
|
55
|
+
const endLine = Math.min(lines.length, lineNumber + contextRange);
|
|
56
|
+
const contextLines = lines.slice(startLine, endLine).join('\n');
|
|
57
|
+
return this.safePatterns.some(pattern => pattern.test(contextLines));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.OpenCorsRule = OpenCorsRule;
|
|
61
|
+
//# sourceMappingURL=open-cors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-cors.js","sourceRoot":"","sources":["../../src/rules/open-cors.ts"],"names":[],"mappings":";;;AAAA,oCAAgE;AAEhE,MAAa,YAAa,SAAQ,gBAAQ;IAA1C;;QACW,SAAI,GAAG,WAAW,CAAC;QACnB,gBAAW,GAAG,+CAA+C,CAAC;QAC9D,aAAQ,GAAG,MAAe,CAAC;QAEnB,iBAAY,GAAG;YAC9B,wBAAwB;YACxB,EAAE,OAAO,EAAE,kDAAkD,EAAE,OAAO,EAAE,wCAAwC,EAAE;YAClH,EAAE,OAAO,EAAE,2CAA2C,EAAE,OAAO,EAAE,iDAAiD,EAAE;YACpH,EAAE,OAAO,EAAE,4EAA4E,EAAE,OAAO,EAAE,oCAAoC,EAAE;YAExI,wCAAwC;YACxC,EAAE,OAAO,EAAE,sDAAsD,EAAE,OAAO,EAAE,8CAA8C,EAAE;YAC5H,EAAE,OAAO,EAAE,uCAAuC,EAAE,OAAO,EAAE,kDAAkD,EAAE;YAEjH,yBAAyB;YACzB,EAAE,OAAO,EAAE,yDAAyD,EAAE,OAAO,EAAE,wDAAwD,EAAE;YAEzI,uBAAuB;YACvB,EAAE,OAAO,EAAE,mDAAmD,EAAE,OAAO,EAAE,8BAA8B,EAAE;YACzG,EAAE,OAAO,EAAE,mDAAmD,EAAE,OAAO,EAAE,yBAAyB,EAAE;YAEpG,8BAA8B;YAC9B,EAAE,OAAO,EAAE,kDAAkD,EAAE,OAAO,EAAE,qDAAqD,EAAE;YAC/H,EAAE,OAAO,EAAE,sDAAsD,EAAE,OAAO,EAAE,mCAAmC,EAAE;SAClH,CAAC;QAEe,iBAAY,GAAG;YAC9B,YAAY;YACZ,cAAc;YACd,UAAU;YACV,cAAc;YACd,UAAU;YACV,OAAO;SACR,CAAC;IAuCJ,CAAC;IArCC,KAAK,CAAC,WAAwB;QAC5B,MAAM,MAAM,GAAoB,EAAE,CAAC;QAEnC,KAAK,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACrD,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAE/D,KAAK,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,OAAO,EAAE,CAAC;gBACpD,6CAA6C;gBAC7C,IAAI,IAAI,CAAC,oBAAoB,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;oBACzD,SAAS;gBACX,CAAC;gBAED,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAC1B,WAAW,CAAC,IAAI,EAChB,IAAI,EACJ,MAAM,EACN,WAAW,EACX,kCAAkC,OAAO,EAAE,EAC3C,gLAAgL,CACjL,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,oBAAoB,CAAC,OAAe,EAAE,UAAkB;QAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,YAAY,GAAG,CAAC,CAAC;QAEvB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,YAAY,GAAG,CAAC,CAAC,CAAC;QAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,YAAY,CAAC,CAAC;QAElE,MAAM,YAAY,GAAG,KAAK,CAAC,KAAK,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEhE,OAAO,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IACvE,CAAC;CACF;AAzED,oCAyEC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseRule, FileContent, SecurityIssue } from '../types';
|
|
2
|
+
export declare class SqlInjectionRule extends BaseRule {
|
|
3
|
+
readonly name = "sql-injection";
|
|
4
|
+
readonly description = "Detects potential SQL injection vulnerabilities";
|
|
5
|
+
readonly severity: "high";
|
|
6
|
+
private readonly sqlInjectionPatterns;
|
|
7
|
+
private readonly safePatterns;
|
|
8
|
+
check(fileContent: FileContent): SecurityIssue[];
|
|
9
|
+
private hasSafeParameterization;
|
|
10
|
+
private isCommentOrTest;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=sql-injection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-injection.d.ts","sourceRoot":"","sources":["../../src/rules/sql-injection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEhE,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,WAAW,qDAAqD;IACzE,QAAQ,CAAC,QAAQ,EAAG,MAAM,CAAU;IAEpC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CA+BnC;IAEF,OAAO,CAAC,QAAQ,CAAC,YAAY,CAS3B;IAEF,KAAK,CAAC,WAAW,EAAE,WAAW,GAAG,aAAa,EAAE;IA+BhD,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,eAAe;CA0BxB"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SqlInjectionRule = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
class SqlInjectionRule extends types_1.BaseRule {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.name = 'sql-injection';
|
|
9
|
+
this.description = 'Detects potential SQL injection vulnerabilities';
|
|
10
|
+
this.severity = 'high';
|
|
11
|
+
this.sqlInjectionPatterns = [
|
|
12
|
+
// String concatenation in SQL
|
|
13
|
+
{ pattern: /(?:query|sql|execute)\s*\(\s*['"`][^'"`]*['"`]\s*\+\s*[^'"`\s)]+/gi, type: 'String concatenation in SQL query' },
|
|
14
|
+
{ pattern: /['"`]SELECT\s+[^'"`]*['"`]\s*\+\s*[^'"`\s)]+/gi, type: 'SELECT query with concatenation' },
|
|
15
|
+
{ pattern: /['"`]INSERT\s+[^'"`]*['"`]\s*\+\s*[^'"`\s)]+/gi, type: 'INSERT query with concatenation' },
|
|
16
|
+
{ pattern: /['"`]UPDATE\s+[^'"`]*['"`]\s*\+\s*[^'"`\s)]+/gi, type: 'UPDATE query with concatenation' },
|
|
17
|
+
{ pattern: /['"`]DELETE\s+[^'"`]*['"`]\s*\+\s*[^'"`\s)]+/gi, type: 'DELETE query with concatenation' },
|
|
18
|
+
// Template literals with variables
|
|
19
|
+
{ pattern: /`SELECT\s+[^`]*\$\{[^}]+\}[^`]*`/gi, type: 'Template literal SQL with variables' },
|
|
20
|
+
{ pattern: /`INSERT\s+[^`]*\$\{[^}]+\}[^`]*`/gi, type: 'Template literal INSERT with variables' },
|
|
21
|
+
{ pattern: /`UPDATE\s+[^`]*\$\{[^}]+\}[^`]*`/gi, type: 'Template literal UPDATE with variables' },
|
|
22
|
+
{ pattern: /`DELETE\s+[^`]*\$\{[^}]+\}[^`]*`/gi, type: 'Template literal DELETE with variables' },
|
|
23
|
+
// String formatting in SQL
|
|
24
|
+
{ pattern: /(?:query|sql)\s*=\s*['"`][^'"`]*%s[^'"`]*['"`]\s*%\s*\(/gi, type: 'Python string formatting in SQL' },
|
|
25
|
+
{ pattern: /(?:query|sql)\s*=\s*f['"`][^'"`]*\{[^}]+\}[^'"`]*['"`]/gi, type: 'Python f-string in SQL' },
|
|
26
|
+
{ pattern: /String\.format\s*\(\s*['"`][^'"`]*\{[^}]*\}[^'"`]*['"`]/gi, type: 'Java String.format in SQL' },
|
|
27
|
+
// Direct variable insertion
|
|
28
|
+
{ pattern: /WHERE\s+[^'"`\s]+\s*=\s*['"`]?\s*\+\s*[^'"`\s)]+/gi, type: 'WHERE clause with concatenation' },
|
|
29
|
+
{ pattern: /WHERE\s+[^'"`\s]+\s*=\s*\$\{[^}]+\}/gi, type: 'WHERE clause with template variable' },
|
|
30
|
+
// Framework-specific patterns
|
|
31
|
+
{ pattern: /\.query\s*\(\s*['"`][^'"`]*['"`]\s*\+/gi, type: 'Database query with concatenation' },
|
|
32
|
+
{ pattern: /\.exec\s*\(\s*['"`][^'"`]*['"`]\s*\+/gi, type: 'Database exec with concatenation' },
|
|
33
|
+
{ pattern: /\.raw\s*\(\s*['"`][^'"`]*['"`]\s*\+/gi, type: 'Raw SQL query with concatenation' },
|
|
34
|
+
// ORM unsafe patterns
|
|
35
|
+
{ pattern: /\.where\s*\(\s*['"`][^'"`]*['"`]\s*\+/gi, type: 'ORM where clause with concatenation' },
|
|
36
|
+
{ pattern: /\.whereRaw\s*\(\s*['"`][^'"`]*['"`]\s*\+/gi, type: 'ORM raw where with concatenation' }
|
|
37
|
+
];
|
|
38
|
+
this.safePatterns = [
|
|
39
|
+
// Parameterized queries
|
|
40
|
+
/\?\s*,/g, // Question mark parameters
|
|
41
|
+
/\$\d+/g, // Numbered parameters
|
|
42
|
+
/:\w+/g, // Named parameters
|
|
43
|
+
/prepare/i,
|
|
44
|
+
/bind/i,
|
|
45
|
+
/params/i,
|
|
46
|
+
/placeholder/i
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
check(fileContent) {
|
|
50
|
+
const issues = [];
|
|
51
|
+
for (const { pattern, type } of this.sqlInjectionPatterns) {
|
|
52
|
+
const matches = this.findMatches(fileContent.content, pattern);
|
|
53
|
+
for (const { line, column, lineContent } of matches) {
|
|
54
|
+
// Skip if the line contains safe parameterization patterns
|
|
55
|
+
if (this.hasSafeParameterization(lineContent)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
// Skip if it's in a comment or test file
|
|
59
|
+
if (this.isCommentOrTest(lineContent, fileContent.path)) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
issues.push(this.createIssue(fileContent.path, line, column, lineContent, `Potential SQL injection vulnerability: ${type}`, `Use parameterized queries or prepared statements instead of string concatenation. Replace concatenation with placeholders (?, $1, :param) and pass values as parameters.`));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return issues;
|
|
66
|
+
}
|
|
67
|
+
hasSafeParameterization(line) {
|
|
68
|
+
return this.safePatterns.some(pattern => pattern.test(line));
|
|
69
|
+
}
|
|
70
|
+
isCommentOrTest(line, filePath) {
|
|
71
|
+
// Check if line is a comment
|
|
72
|
+
const commentPatterns = [
|
|
73
|
+
/^\s*\/\//, // JavaScript comment
|
|
74
|
+
/^\s*#/, // Python/Shell comment
|
|
75
|
+
/^\s*--/, // SQL comment
|
|
76
|
+
/^\s*\*/ // Multi-line comment
|
|
77
|
+
];
|
|
78
|
+
if (commentPatterns.some(pattern => pattern.test(line))) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
// Check if it's a test file
|
|
82
|
+
const testPatterns = [
|
|
83
|
+
/test/i,
|
|
84
|
+
/spec/i,
|
|
85
|
+
/\.test\./i,
|
|
86
|
+
/\.spec\./i,
|
|
87
|
+
/__tests__/i,
|
|
88
|
+
/tests\//i,
|
|
89
|
+
/spec\//i
|
|
90
|
+
];
|
|
91
|
+
return testPatterns.some(pattern => pattern.test(filePath));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
exports.SqlInjectionRule = SqlInjectionRule;
|
|
95
|
+
//# sourceMappingURL=sql-injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql-injection.js","sourceRoot":"","sources":["../../src/rules/sql-injection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseRule, FileContent, SecurityIssue } from '../types';
|
|
2
|
+
export declare class UnvalidatedInputRule extends BaseRule {
|
|
3
|
+
readonly name = "unvalidated-input";
|
|
4
|
+
readonly description = "Detects potentially unvalidated user input";
|
|
5
|
+
readonly severity: "medium";
|
|
6
|
+
private readonly inputPatterns;
|
|
7
|
+
private readonly validationPatterns;
|
|
8
|
+
check(fileContent: FileContent): SecurityIssue[];
|
|
9
|
+
private hasValidationNearby;
|
|
10
|
+
private isCommentOrTest;
|
|
11
|
+
private isSimplePropertyAccess;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=unvalidated-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unvalidated-input.d.ts","sourceRoot":"","sources":["../../src/rules/unvalidated-input.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEhE,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,WAAW,gDAAgD;IACpE,QAAQ,CAAC,QAAQ,EAAG,QAAQ,CAAU;IAEtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CA+B5B;IAEF,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAuBjC;IAEF,KAAK,CAAC,WAAW,EAAE,WAAW,GAAG,aAAa,EAAE;IAoChD,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,eAAe;IA0BvB,OAAO,CAAC,sBAAsB;CAe/B"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UnvalidatedInputRule = void 0;
|
|
4
|
+
const types_1 = require("../types");
|
|
5
|
+
class UnvalidatedInputRule extends types_1.BaseRule {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.name = 'unvalidated-input';
|
|
9
|
+
this.description = 'Detects potentially unvalidated user input';
|
|
10
|
+
this.severity = 'medium';
|
|
11
|
+
this.inputPatterns = [
|
|
12
|
+
// Express.js patterns
|
|
13
|
+
{ pattern: /req\.(?:body|query|params)\.[a-zA-Z_][a-zA-Z0-9_]*(?!\s*\.\s*(?:validate|sanitize|escape|trim|length|match|test))/g, type: 'Express request parameter' },
|
|
14
|
+
{ pattern: /req\.(?:body|query|params)(?!\s*\.\s*(?:validate|sanitize|escape))/g, type: 'Express request object' },
|
|
15
|
+
// Direct user input usage
|
|
16
|
+
{ pattern: /(?:eval|exec|system|shell_exec)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.)/gi, type: 'Code execution with user input' },
|
|
17
|
+
{ pattern: /(?:innerHTML|outerHTML)\s*=\s*(?:req\.|request\.|input\.|params\.|query\.)/gi, type: 'DOM manipulation with user input' },
|
|
18
|
+
// File operations with user input
|
|
19
|
+
{ pattern: /(?:readFile|writeFile|unlink|rmdir|mkdir)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.)/gi, type: 'File operation with user input' },
|
|
20
|
+
{ pattern: /(?:open|fopen|file_get_contents)\s*\(\s*(?:\$_GET|\$_POST|\$_REQUEST)/gi, type: 'PHP file operation with user input' },
|
|
21
|
+
// Database operations without validation
|
|
22
|
+
{ pattern: /\.(?:query|exec|execute)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.)(?![^)]*(?:validate|sanitize|escape))/gi, type: 'Database query with unvalidated input' },
|
|
23
|
+
// Command injection patterns
|
|
24
|
+
{ pattern: /(?:spawn|exec|execSync)\s*\(\s*(?:req\.|request\.|input\.|params\.|query\.)/gi, type: 'Command execution with user input' },
|
|
25
|
+
// Python patterns
|
|
26
|
+
{ pattern: /(?:os\.system|subprocess\.call|eval|exec)\s*\(\s*(?:request\.|flask\.request\.)/gi, type: 'Python system call with user input' },
|
|
27
|
+
// PHP patterns
|
|
28
|
+
{ pattern: /(?:system|exec|shell_exec|passthru)\s*\(\s*(?:\$_GET|\$_POST|\$_REQUEST)/gi, type: 'PHP system call with user input' },
|
|
29
|
+
{ pattern: /(?:include|require|include_once|require_once)\s*\(\s*(?:\$_GET|\$_POST|\$_REQUEST)/gi, type: 'PHP file inclusion with user input' },
|
|
30
|
+
// Java patterns
|
|
31
|
+
{ pattern: /Runtime\.getRuntime\(\)\.exec\s*\(\s*(?:request\.getParameter|request\.getAttribute)/gi, type: 'Java runtime execution with user input' },
|
|
32
|
+
// Generic patterns
|
|
33
|
+
{ pattern: /\$\{(?:req\.|request\.|input\.|params\.|query\.)[^}]+\}/g, type: 'Template literal with user input' }
|
|
34
|
+
];
|
|
35
|
+
this.validationPatterns = [
|
|
36
|
+
/validate/i,
|
|
37
|
+
/sanitize/i,
|
|
38
|
+
/escape/i,
|
|
39
|
+
/filter/i,
|
|
40
|
+
/clean/i,
|
|
41
|
+
/trim/i,
|
|
42
|
+
/strip/i,
|
|
43
|
+
/whitelist/i,
|
|
44
|
+
/blacklist/i,
|
|
45
|
+
/check/i,
|
|
46
|
+
/verify/i,
|
|
47
|
+
/isValid/i,
|
|
48
|
+
/typeof/i,
|
|
49
|
+
/instanceof/i,
|
|
50
|
+
/\.length\s*[><=]/,
|
|
51
|
+
/\.match\s*\(/,
|
|
52
|
+
/\.test\s*\(/,
|
|
53
|
+
/parseInt/i,
|
|
54
|
+
/parseFloat/i,
|
|
55
|
+
/Number\(/,
|
|
56
|
+
/String\(/,
|
|
57
|
+
/Boolean\(/
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
check(fileContent) {
|
|
61
|
+
const issues = [];
|
|
62
|
+
for (const { pattern, type } of this.inputPatterns) {
|
|
63
|
+
const matches = this.findMatches(fileContent.content, pattern);
|
|
64
|
+
for (const { line, column, lineContent } of matches) {
|
|
65
|
+
// Skip if validation is present in the same line or nearby lines
|
|
66
|
+
if (this.hasValidationNearby(fileContent.content, line)) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Skip if it's in a comment or test file
|
|
70
|
+
if (this.isCommentOrTest(lineContent, fileContent.path)) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
// Skip if it's just a simple property access for logging or display
|
|
74
|
+
if (this.isSimplePropertyAccess(lineContent)) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
issues.push(this.createIssue(fileContent.path, line, column, lineContent, `Potentially unvalidated user input: ${type}`, `Validate and sanitize user input before use. Consider using validation libraries like Joi, express-validator, or built-in validation methods.`));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return issues;
|
|
81
|
+
}
|
|
82
|
+
hasValidationNearby(content, lineNumber) {
|
|
83
|
+
const lines = content.split('\n');
|
|
84
|
+
const contextRange = 3; // Check 3 lines before and after
|
|
85
|
+
const startLine = Math.max(0, lineNumber - contextRange - 1);
|
|
86
|
+
const endLine = Math.min(lines.length, lineNumber + contextRange);
|
|
87
|
+
const contextLines = lines.slice(startLine, endLine).join('\n');
|
|
88
|
+
return this.validationPatterns.some(pattern => pattern.test(contextLines));
|
|
89
|
+
}
|
|
90
|
+
isCommentOrTest(line, filePath) {
|
|
91
|
+
// Check if line is a comment
|
|
92
|
+
const commentPatterns = [
|
|
93
|
+
/^\s*\/\//, // JavaScript comment
|
|
94
|
+
/^\s*#/, // Python/Shell comment
|
|
95
|
+
/^\s*\*/ // Multi-line comment
|
|
96
|
+
];
|
|
97
|
+
if (commentPatterns.some(pattern => pattern.test(line))) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
// Check if it's a test file
|
|
101
|
+
const testPatterns = [
|
|
102
|
+
/test/i,
|
|
103
|
+
/spec/i,
|
|
104
|
+
/\.test\./i,
|
|
105
|
+
/\.spec\./i,
|
|
106
|
+
/__tests__/i,
|
|
107
|
+
/tests\//i,
|
|
108
|
+
/spec\//i
|
|
109
|
+
];
|
|
110
|
+
return testPatterns.some(pattern => pattern.test(filePath));
|
|
111
|
+
}
|
|
112
|
+
isSimplePropertyAccess(line) {
|
|
113
|
+
// Check if it's just logging, console output, or simple assignment
|
|
114
|
+
const safePatterns = [
|
|
115
|
+
/console\./i,
|
|
116
|
+
/log\(/i,
|
|
117
|
+
/print\(/i,
|
|
118
|
+
/echo\s/i,
|
|
119
|
+
/return\s/i,
|
|
120
|
+
/res\.json\s*\(/i,
|
|
121
|
+
/res\.send\s*\(/i,
|
|
122
|
+
/JSON\.stringify/i
|
|
123
|
+
];
|
|
124
|
+
return safePatterns.some(pattern => pattern.test(line));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.UnvalidatedInputRule = UnvalidatedInputRule;
|
|
128
|
+
//# sourceMappingURL=unvalidated-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"unvalidated-input.js","sourceRoot":"","sources":["../../src/rules/unvalidated-input.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ScanResult, BaseRule } from './types';
|
|
2
|
+
export declare class FileScanner {
|
|
3
|
+
private readonly supportedExtensions;
|
|
4
|
+
private readonly excludePatterns;
|
|
5
|
+
private readonly maxFileSize;
|
|
6
|
+
private readonly binaryExtensions;
|
|
7
|
+
scanDirectory(targetPath: string, rules: BaseRule[]): Promise<ScanResult>;
|
|
8
|
+
scanFile(filePath: string, rules: BaseRule[]): Promise<ScanResult>;
|
|
9
|
+
private findFiles;
|
|
10
|
+
private isSupportedFile;
|
|
11
|
+
private readFile;
|
|
12
|
+
private isBinaryFile;
|
|
13
|
+
private createScanResult;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=scanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAGA,OAAO,EAA8B,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE3E,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAOlC;IAEF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAc9B;IAGF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAmB;IAG/C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAW/B;IAEI,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAgCzE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC;YAuB1D,SAAS;IAqBvB,OAAO,CAAC,eAAe;YAWT,QAAQ;YAkCR,YAAY;IAuC1B,OAAO,CAAC,gBAAgB;CAmBzB"}
|
package/dist/scanner.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FileScanner = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const glob_1 = require("glob");
|
|
40
|
+
class FileScanner {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.supportedExtensions = [
|
|
43
|
+
'.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte',
|
|
44
|
+
'.py', '.php', '.rb', '.go', '.java', '.cs',
|
|
45
|
+
'.cpp', '.c', '.h', '.hpp', '.rs', '.kt',
|
|
46
|
+
'.swift', '.dart', '.scala', '.clj', '.hs',
|
|
47
|
+
'.json', '.yaml', '.yml', '.xml', '.env',
|
|
48
|
+
'.config', '.conf', '.ini', '.toml'
|
|
49
|
+
];
|
|
50
|
+
this.excludePatterns = [
|
|
51
|
+
'**/node_modules/**',
|
|
52
|
+
'**/dist/**',
|
|
53
|
+
'**/build/**',
|
|
54
|
+
'**/.git/**',
|
|
55
|
+
'**/coverage/**',
|
|
56
|
+
'**/*.min.js',
|
|
57
|
+
'**/*.bundle.js',
|
|
58
|
+
'**/vendor/**',
|
|
59
|
+
'**/__pycache__/**',
|
|
60
|
+
'**/*.pyc',
|
|
61
|
+
'**/target/**',
|
|
62
|
+
'**/bin/**',
|
|
63
|
+
'**/obj/**'
|
|
64
|
+
];
|
|
65
|
+
// File size limit: 5MB (prevents performance issues)
|
|
66
|
+
this.maxFileSize = 5 * 1024 * 1024;
|
|
67
|
+
// Binary file extensions to skip
|
|
68
|
+
this.binaryExtensions = [
|
|
69
|
+
'.exe', '.dll', '.so', '.dylib', '.bin', '.dat',
|
|
70
|
+
'.img', '.iso', '.dmg', '.pkg', '.deb', '.rpm',
|
|
71
|
+
'.zip', '.tar', '.gz', '.bz2', '.7z', '.rar',
|
|
72
|
+
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.ico',
|
|
73
|
+
'.mp3', '.mp4', '.avi', '.mov', '.wmv', '.flv',
|
|
74
|
+
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt',
|
|
75
|
+
'.sqlite', '.db', '.mdb', '.accdb',
|
|
76
|
+
'.ttf', '.otf', '.woff', '.woff2', '.eot',
|
|
77
|
+
'.class', '.jar', '.war', '.ear',
|
|
78
|
+
'.o', '.obj', '.lib', '.a'
|
|
79
|
+
];
|
|
80
|
+
}
|
|
81
|
+
async scanDirectory(targetPath, rules) {
|
|
82
|
+
const files = await this.findFiles(targetPath);
|
|
83
|
+
const issues = [];
|
|
84
|
+
let filesScanned = 0;
|
|
85
|
+
let filesSkipped = 0;
|
|
86
|
+
for (const filePath of files) {
|
|
87
|
+
try {
|
|
88
|
+
const fileContent = await this.readFile(filePath);
|
|
89
|
+
if (fileContent) {
|
|
90
|
+
filesScanned++;
|
|
91
|
+
for (const rule of rules) {
|
|
92
|
+
const ruleIssues = rule.check(fileContent);
|
|
93
|
+
issues.push(...ruleIssues);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
filesSkipped++;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
console.warn(`Warning: Could not scan file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
102
|
+
filesSkipped++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (filesSkipped > 0) {
|
|
106
|
+
console.log(`📋 Skipped ${filesSkipped} files (binary, too large, or unreadable)`);
|
|
107
|
+
}
|
|
108
|
+
return this.createScanResult(issues, filesScanned);
|
|
109
|
+
}
|
|
110
|
+
async scanFile(filePath, rules) {
|
|
111
|
+
const issues = [];
|
|
112
|
+
let filesScanned = 0;
|
|
113
|
+
try {
|
|
114
|
+
const fileContent = await this.readFile(filePath);
|
|
115
|
+
if (fileContent) {
|
|
116
|
+
filesScanned = 1;
|
|
117
|
+
for (const rule of rules) {
|
|
118
|
+
const ruleIssues = rule.check(fileContent);
|
|
119
|
+
issues.push(...ruleIssues);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.log(`📋 Skipped file: ${filePath} (binary, too large, or unreadable)`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
throw new Error(`Could not scan file ${filePath}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
128
|
+
}
|
|
129
|
+
return this.createScanResult(issues, filesScanned);
|
|
130
|
+
}
|
|
131
|
+
async findFiles(targetPath) {
|
|
132
|
+
const stats = await fs.promises.stat(targetPath);
|
|
133
|
+
if (stats.isFile()) {
|
|
134
|
+
return [targetPath];
|
|
135
|
+
}
|
|
136
|
+
if (!stats.isDirectory()) {
|
|
137
|
+
throw new Error(`Target path is neither a file nor a directory: ${targetPath}`);
|
|
138
|
+
}
|
|
139
|
+
const pattern = path.join(targetPath, '**/*');
|
|
140
|
+
const allFiles = glob_1.glob.sync(pattern, {
|
|
141
|
+
ignore: this.excludePatterns,
|
|
142
|
+
nodir: true,
|
|
143
|
+
absolute: true
|
|
144
|
+
});
|
|
145
|
+
return allFiles.filter((file) => this.isSupportedFile(file));
|
|
146
|
+
}
|
|
147
|
+
isSupportedFile(filePath) {
|
|
148
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
149
|
+
// Skip binary files
|
|
150
|
+
if (this.binaryExtensions.includes(ext)) {
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
return this.supportedExtensions.includes(ext);
|
|
154
|
+
}
|
|
155
|
+
async readFile(filePath) {
|
|
156
|
+
try {
|
|
157
|
+
// Check file size first
|
|
158
|
+
const stats = await fs.promises.stat(filePath);
|
|
159
|
+
if (stats.size > this.maxFileSize) {
|
|
160
|
+
console.warn(`Skipping large file: ${filePath} (${Math.round(stats.size / 1024 / 1024)}MB > 5MB limit)`);
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
// Check if file is binary by reading first few bytes
|
|
164
|
+
if (await this.isBinaryFile(filePath)) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const content = await fs.promises.readFile(filePath, 'utf-8');
|
|
168
|
+
const lines = content.split('\n');
|
|
169
|
+
return {
|
|
170
|
+
path: filePath,
|
|
171
|
+
content,
|
|
172
|
+
lines
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
// Handle encoding errors (binary files read as text)
|
|
180
|
+
if (error instanceof Error && error.message.includes('invalid')) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async isBinaryFile(filePath) {
|
|
187
|
+
try {
|
|
188
|
+
// Read first 512 bytes to check for binary content
|
|
189
|
+
const fd = await fs.promises.open(filePath, 'r');
|
|
190
|
+
const buffer = Buffer.alloc(512);
|
|
191
|
+
const { bytesRead } = await fd.read(buffer, 0, 512, 0);
|
|
192
|
+
await fd.close();
|
|
193
|
+
if (bytesRead === 0) {
|
|
194
|
+
return false; // Empty file, treat as text
|
|
195
|
+
}
|
|
196
|
+
// Check for null bytes (common in binary files)
|
|
197
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
198
|
+
if (buffer[i] === 0) {
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// Check for high percentage of non-printable characters
|
|
203
|
+
let nonPrintable = 0;
|
|
204
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
205
|
+
const byte = buffer[i];
|
|
206
|
+
if (byte !== undefined) {
|
|
207
|
+
// Allow common text characters: printable ASCII, newlines, tabs
|
|
208
|
+
if (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13) {
|
|
209
|
+
nonPrintable++;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
// If more than 30% non-printable, consider it binary
|
|
214
|
+
return (nonPrintable / bytesRead) > 0.3;
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// If we can't read the file, assume it's not binary
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
createScanResult(issues, filesScanned) {
|
|
222
|
+
const summary = {
|
|
223
|
+
critical: 0,
|
|
224
|
+
high: 0,
|
|
225
|
+
medium: 0,
|
|
226
|
+
low: 0
|
|
227
|
+
};
|
|
228
|
+
issues.forEach(issue => {
|
|
229
|
+
summary[issue.severity]++;
|
|
230
|
+
});
|
|
231
|
+
return {
|
|
232
|
+
issues,
|
|
233
|
+
filesScanned,
|
|
234
|
+
issuesFound: issues.length,
|
|
235
|
+
summary
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
exports.FileScanner = FileScanner;
|
|
240
|
+
//# sourceMappingURL=scanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAC7B,+BAA4B;AAG5B,MAAa,WAAW;IAAxB;QACmB,wBAAmB,GAAG;YACrC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS;YAC/C,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK;YAC3C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK;YACxC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK;YAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;YACxC,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO;SACpC,CAAC;QAEe,oBAAe,GAAG;YACjC,oBAAoB;YACpB,YAAY;YACZ,aAAa;YACb,YAAY;YACZ,gBAAgB;YAChB,aAAa;YACb,gBAAgB;YAChB,cAAc;YACd,mBAAmB;YACnB,UAAU;YACV,cAAc;YACd,WAAW;YACX,WAAW;SACZ,CAAC;QAEF,qDAAqD;QACpC,gBAAW,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;QAE/C,iCAAiC;QAChB,qBAAgB,GAAG;YAClC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM;YAC/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;YAC9C,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;YAC5C,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;YAC/C,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;YAC9C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;YAChD,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ;YAClC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM;YACzC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;YAChC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI;SAC3B,CAAC;IAqLJ,CAAC;IAnLC,KAAK,CAAC,aAAa,CAAC,UAAkB,EAAE,KAAiB;QACvD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBAClD,IAAI,WAAW,EAAE,CAAC;oBAChB,YAAY,EAAE,CAAC;oBAEf,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;wBACzB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;wBAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;oBAC7B,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,YAAY,EAAE,CAAC;gBACjB,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,gCAAgC,QAAQ,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;gBACtH,YAAY,EAAE,CAAC;YACjB,CAAC;QACH,CAAC;QAED,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,cAAc,YAAY,2CAA2C,CAAC,CAAC;QACrF,CAAC;QAED,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrD,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,QAAgB,EAAE,KAAiB;QAChD,MAAM,MAAM,GAAoB,EAAE,CAAC;QACnC,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAClD,IAAI,WAAW,EAAE,CAAC;gBAChB,YAAY,GAAG,CAAC,CAAC;gBAEjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;oBAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,CAAC;gBAC7B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,CAAC,oBAAoB,QAAQ,qCAAqC,CAAC,CAAC;YACjF,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QAClH,CAAC;QAED,OAAO,IAAI,CAAC,gBAAgB,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACrD,CAAC;IAEO,KAAK,CAAC,SAAS,CAAC,UAAkB;QACxC,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAEjD,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,OAAO,CAAC,UAAU,CAAC,CAAC;QACtB,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CAAC,kDAAkD,UAAU,EAAE,CAAC,CAAC;QAClF,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,QAAQ,GAAG,WAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YAClC,MAAM,EAAE,IAAI,CAAC,eAAe;YAC5B,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,IAAI;SACf,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACvE,CAAC;IAEO,eAAe,CAAC,QAAgB;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;QAEjD,oBAAoB;QACpB,IAAI,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxC,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAO,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAChD,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,QAAgB;QACrC,IAAI,CAAC;YACH,wBAAwB;YACxB,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAC/C,IAAI,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClC,OAAO,CAAC,IAAI,CAAC,wBAAwB,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,CAAC;gBACzG,OAAO,IAAI,CAAC;YACd,CAAC;YAED,qDAAqD;YACrD,IAAI,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACtC,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC9D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAElC,OAAO;gBACL,IAAI,EAAE,QAAQ;gBACd,OAAO;gBACP,KAAK;aACN,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBACzE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,qDAAqD;YACrD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAChE,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,YAAY,CAAC,QAAgB;QACzC,IAAI,CAAC;YACH,mDAAmD;YACnD,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;YACvD,MAAM,EAAE,CAAC,KAAK,EAAE,CAAC;YAEjB,IAAI,SAAS,KAAK,CAAC,EAAE,CAAC;gBACpB,OAAO,KAAK,CAAC,CAAC,4BAA4B;YAC5C,CAAC;YAED,gDAAgD;YAChD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;oBACpB,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YAED,wDAAwD;YACxD,IAAI,YAAY,GAAG,CAAC,CAAC;YACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;gBACvB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACvB,gEAAgE;oBAChE,IAAI,IAAI,GAAG,EAAE,IAAI,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;wBAC1D,YAAY,EAAE,CAAC;oBACjB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,qDAAqD;YACrD,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC;QAC1C,CAAC;QAAC,MAAM,CAAC;YACP,oDAAoD;YACpD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,MAAuB,EAAE,YAAoB;QACpE,MAAM,OAAO,GAAG;YACd,QAAQ,EAAE,CAAC;YACX,IAAI,EAAE,CAAC;YACP,MAAM,EAAE,CAAC;YACT,GAAG,EAAE,CAAC;SACP,CAAC;QAEF,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YACrB,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,OAAO;YACL,MAAM;YACN,YAAY;YACZ,WAAW,EAAE,MAAM,CAAC,MAAM;YAC1B,OAAO;SACR,CAAC;IACJ,CAAC;CACF;AA9ND,kCA8NC"}
|