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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/bin/vibe-guard.d.ts +3 -0
  4. package/dist/bin/vibe-guard.d.ts.map +1 -0
  5. package/dist/bin/vibe-guard.js +119 -0
  6. package/dist/bin/vibe-guard.js.map +1 -0
  7. package/dist/index.d.ts +19 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +128 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/reporter.d.ts +13 -0
  12. package/dist/reporter.d.ts.map +1 -0
  13. package/dist/reporter.js +116 -0
  14. package/dist/reporter.js.map +1 -0
  15. package/dist/rules/directory-traversal.d.ts +14 -0
  16. package/dist/rules/directory-traversal.d.ts.map +1 -0
  17. package/dist/rules/directory-traversal.js +142 -0
  18. package/dist/rules/directory-traversal.js.map +1 -0
  19. package/dist/rules/exposed-secrets.d.ts +13 -0
  20. package/dist/rules/exposed-secrets.d.ts.map +1 -0
  21. package/dist/rules/exposed-secrets.js +137 -0
  22. package/dist/rules/exposed-secrets.js.map +1 -0
  23. package/dist/rules/hardcoded-sensitive-data.d.ts +13 -0
  24. package/dist/rules/hardcoded-sensitive-data.d.ts.map +1 -0
  25. package/dist/rules/hardcoded-sensitive-data.js +104 -0
  26. package/dist/rules/hardcoded-sensitive-data.js.map +1 -0
  27. package/dist/rules/index.d.ts +15 -0
  28. package/dist/rules/index.d.ts.map +1 -0
  29. package/dist/rules/index.js +54 -0
  30. package/dist/rules/index.js.map +1 -0
  31. package/dist/rules/insecure-dependencies.d.ts +16 -0
  32. package/dist/rules/insecure-dependencies.d.ts.map +1 -0
  33. package/dist/rules/insecure-dependencies.js +152 -0
  34. package/dist/rules/insecure-dependencies.js.map +1 -0
  35. package/dist/rules/insecure-http.d.ts +13 -0
  36. package/dist/rules/insecure-http.d.ts.map +1 -0
  37. package/dist/rules/insecure-http.js +91 -0
  38. package/dist/rules/insecure-http.js.map +1 -0
  39. package/dist/rules/missing-authentication.d.ts +14 -0
  40. package/dist/rules/missing-authentication.d.ts.map +1 -0
  41. package/dist/rules/missing-authentication.js +98 -0
  42. package/dist/rules/missing-authentication.js.map +1 -0
  43. package/dist/rules/missing-security-headers.d.ts +16 -0
  44. package/dist/rules/missing-security-headers.d.ts.map +1 -0
  45. package/dist/rules/missing-security-headers.js +158 -0
  46. package/dist/rules/missing-security-headers.js.map +1 -0
  47. package/dist/rules/open-cors.d.ts +11 -0
  48. package/dist/rules/open-cors.d.ts.map +1 -0
  49. package/dist/rules/open-cors.js +61 -0
  50. package/dist/rules/open-cors.js.map +1 -0
  51. package/dist/rules/sql-injection.d.ts +12 -0
  52. package/dist/rules/sql-injection.d.ts.map +1 -0
  53. package/dist/rules/sql-injection.js +95 -0
  54. package/dist/rules/sql-injection.js.map +1 -0
  55. package/dist/rules/unvalidated-input.d.ts +13 -0
  56. package/dist/rules/unvalidated-input.d.ts.map +1 -0
  57. package/dist/rules/unvalidated-input.js +128 -0
  58. package/dist/rules/unvalidated-input.js.map +1 -0
  59. package/dist/scanner.d.ts +15 -0
  60. package/dist/scanner.d.ts.map +1 -0
  61. package/dist/scanner.js +240 -0
  62. package/dist/scanner.js.map +1 -0
  63. package/dist/types/index.d.ts +66 -0
  64. package/dist/types/index.d.ts.map +1 -0
  65. package/dist/types/index.js +38 -0
  66. package/dist/types/index.js.map +1 -0
  67. 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":";;;AAAA,oCAAgE;AAEhE,MAAa,oBAAqB,SAAQ,gBAAQ;IAAlD;;QACW,SAAI,GAAG,mBAAmB,CAAC;QAC3B,gBAAW,GAAG,4CAA4C,CAAC;QAC3D,aAAQ,GAAG,QAAiB,CAAC;QAErB,kBAAa,GAAG;YAC/B,sBAAsB;YACtB,EAAE,OAAO,EAAE,oHAAoH,EAAE,IAAI,EAAE,2BAA2B,EAAE;YACpK,EAAE,OAAO,EAAE,qEAAqE,EAAE,IAAI,EAAE,wBAAwB,EAAE;YAElH,0BAA0B;YAC1B,EAAE,OAAO,EAAE,uFAAuF,EAAE,IAAI,EAAE,gCAAgC,EAAE;YAC5I,EAAE,OAAO,EAAE,8EAA8E,EAAE,IAAI,EAAE,kCAAkC,EAAE;YAErI,kCAAkC;YAClC,EAAE,OAAO,EAAE,iGAAiG,EAAE,IAAI,EAAE,gCAAgC,EAAE;YACtJ,EAAE,OAAO,EAAE,yEAAyE,EAAE,IAAI,EAAE,oCAAoC,EAAE;YAElI,yCAAyC;YACzC,EAAE,OAAO,EAAE,qHAAqH,EAAE,IAAI,EAAE,uCAAuC,EAAE;YAEjL,6BAA6B;YAC7B,EAAE,OAAO,EAAE,+EAA+E,EAAE,IAAI,EAAE,mCAAmC,EAAE;YAEvI,kBAAkB;YAClB,EAAE,OAAO,EAAE,mFAAmF,EAAE,IAAI,EAAE,oCAAoC,EAAE;YAE5I,eAAe;YACf,EAAE,OAAO,EAAE,4EAA4E,EAAE,IAAI,EAAE,iCAAiC,EAAE;YAClI,EAAE,OAAO,EAAE,sFAAsF,EAAE,IAAI,EAAE,oCAAoC,EAAE;YAE/I,gBAAgB;YAChB,EAAE,OAAO,EAAE,wFAAwF,EAAE,IAAI,EAAE,wCAAwC,EAAE;YAErJ,mBAAmB;YACnB,EAAE,OAAO,EAAE,0DAA0D,EAAE,IAAI,EAAE,kCAAkC,EAAE;SAClH,CAAC;QAEe,uBAAkB,GAAG;YACpC,WAAW;YACX,WAAW;YACX,SAAS;YACT,SAAS;YACT,QAAQ;YACR,OAAO;YACP,QAAQ;YACR,YAAY;YACZ,YAAY;YACZ,QAAQ;YACR,SAAS;YACT,UAAU;YACV,SAAS;YACT,aAAa;YACb,kBAAkB;YAClB,cAAc;YACd,aAAa;YACb,WAAW;YACX,aAAa;YACb,UAAU;YACV,UAAU;YACV,WAAW;SACZ,CAAC;IA2FJ,CAAC;IAzFC,KAAK,CAAC,WAAwB;QAC5B,MAAM,MAAM,GAAoB,EAAE,CAAC;QAEnC,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACnD,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,iEAAiE;gBACjE,IAAI,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;oBACxD,SAAS;gBACX,CAAC;gBAED,yCAAyC;gBACzC,IAAI,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;oBACxD,SAAS;gBACX,CAAC;gBAED,oEAAoE;gBACpE,IAAI,IAAI,CAAC,sBAAsB,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC7C,SAAS;gBACX,CAAC;gBAED,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAC1B,WAAW,CAAC,IAAI,EAChB,IAAI,EACJ,MAAM,EACN,WAAW,EACX,uCAAuC,IAAI,EAAE,EAC7C,+IAA+I,CAChJ,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAEO,mBAAmB,CAAC,OAAe,EAAE,UAAkB;QAC7D,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,YAAY,GAAG,CAAC,CAAC,CAAC,iCAAiC;QAEzD,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,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;IAC7E,CAAC;IAEO,eAAe,CAAC,IAAY,EAAE,QAAgB;QACpD,6BAA6B;QAC7B,MAAM,eAAe,GAAG;YACtB,UAAU,EAAG,qBAAqB;YAClC,OAAO,EAAM,uBAAuB;YACpC,QAAQ,CAAK,qBAAqB;SACnC,CAAC;QAEF,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YACxD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,4BAA4B;QAC5B,MAAM,YAAY,GAAG;YACnB,OAAO;YACP,OAAO;YACP,WAAW;YACX,WAAW;YACX,YAAY;YACZ,UAAU;YACV,SAAS;SACV,CAAC;QAEF,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9D,CAAC;IAEO,sBAAsB,CAAC,IAAY;QACzC,mEAAmE;QACnE,MAAM,YAAY,GAAG;YACnB,YAAY;YACZ,QAAQ;YACR,UAAU;YACV,SAAS;YACT,WAAW;YACX,iBAAiB;YACjB,iBAAiB;YACjB,kBAAkB;SACnB,CAAC;QAEF,OAAO,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;CACF;AAxJD,oDAwJC"}
@@ -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"}
@@ -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":"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"}