agent-security-scanner-mcp 3.3.0 → 3.4.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/README.md +224 -2
- package/analyzer.py +22 -5
- package/cross_file_analyzer.py +216 -0
- package/index.js +104 -4
- package/package.json +10 -3
- package/pattern_matcher.py +1 -0
- package/regex_fallback.py +199 -1
- package/scripts/postinstall.js +25 -0
- package/src/cli/init-hooks.js +164 -0
- package/src/config.js +181 -0
- package/src/context.js +228 -0
- package/src/dedup.js +129 -0
- package/src/fix-patterns.js +66 -17
- package/src/tools/fix-security.js +31 -4
- package/src/tools/scan-diff.js +151 -0
- package/src/tools/scan-project.js +308 -0
- package/src/tools/scan-security.js +33 -5
- package/src/utils.js +76 -7
package/src/utils.js
CHANGED
|
@@ -36,10 +36,14 @@ export function detectLanguage(filePath) {
|
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// Run the Python analyzer
|
|
39
|
-
export function runAnalyzer(filePath) {
|
|
39
|
+
export function runAnalyzer(filePath, engine = 'auto') {
|
|
40
40
|
try {
|
|
41
41
|
const analyzerPath = join(__dirname, '..', 'analyzer.py');
|
|
42
|
-
const
|
|
42
|
+
const args = [analyzerPath, filePath];
|
|
43
|
+
if (engine !== 'auto') {
|
|
44
|
+
args.push('--engine', engine);
|
|
45
|
+
}
|
|
46
|
+
const result = execFileSync('python3', args, {
|
|
43
47
|
encoding: 'utf-8',
|
|
44
48
|
timeout: 30000
|
|
45
49
|
});
|
|
@@ -49,17 +53,62 @@ export function runAnalyzer(filePath) {
|
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
// Validate that a fix produces syntactically reasonable output
|
|
57
|
+
export function validateFix(original, fixed) {
|
|
58
|
+
if (!fixed || fixed === original) return false;
|
|
59
|
+
|
|
60
|
+
// Strip escaped quotes for bracket/quote counting
|
|
61
|
+
const unescaped = fixed.replace(/\\["'`]/g, '');
|
|
62
|
+
|
|
63
|
+
// Check balanced quotes (single pass)
|
|
64
|
+
const singleQ = (unescaped.match(/'/g) || []).length;
|
|
65
|
+
const doubleQ = (unescaped.match(/"/g) || []).length;
|
|
66
|
+
const backtickQ = (unescaped.match(/`/g) || []).length;
|
|
67
|
+
if (singleQ % 2 !== 0 || doubleQ % 2 !== 0 || backtickQ % 2 !== 0) return false;
|
|
68
|
+
|
|
69
|
+
// Check balanced brackets
|
|
70
|
+
const brackets = { '(': 0, '[': 0, '{': 0 };
|
|
71
|
+
const closers = { ')': '(', ']': '[', '}': '{' };
|
|
72
|
+
for (const char of unescaped) {
|
|
73
|
+
if (brackets[char] !== undefined) brackets[char]++;
|
|
74
|
+
if (closers[char]) {
|
|
75
|
+
brackets[closers[char]]--;
|
|
76
|
+
if (brackets[closers[char]] < 0) return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (Object.values(brackets).some(v => v !== 0)) return false;
|
|
80
|
+
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
|
|
52
84
|
// Generate fix suggestion for an issue
|
|
53
85
|
export function generateFix(issue, line, language) {
|
|
54
86
|
const ruleId = issue.ruleId.toLowerCase();
|
|
55
87
|
|
|
56
88
|
for (const [pattern, template] of Object.entries(FIX_TEMPLATES)) {
|
|
57
89
|
if (ruleId.includes(pattern)) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
fixed
|
|
62
|
-
|
|
90
|
+
try {
|
|
91
|
+
const fixed = template.fix(line, language);
|
|
92
|
+
// Validate the fix produces reasonable output
|
|
93
|
+
if (fixed && !validateFix(line, fixed)) {
|
|
94
|
+
return {
|
|
95
|
+
description: template.description + " (manual fix required)",
|
|
96
|
+
original: line,
|
|
97
|
+
fixed: null
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
description: template.description,
|
|
102
|
+
original: line,
|
|
103
|
+
fixed: fixed
|
|
104
|
+
};
|
|
105
|
+
} catch {
|
|
106
|
+
return {
|
|
107
|
+
description: template.description + " (manual fix required)",
|
|
108
|
+
original: line,
|
|
109
|
+
fixed: null
|
|
110
|
+
};
|
|
111
|
+
}
|
|
63
112
|
}
|
|
64
113
|
}
|
|
65
114
|
|
|
@@ -70,6 +119,26 @@ export function generateFix(issue, line, language) {
|
|
|
70
119
|
};
|
|
71
120
|
}
|
|
72
121
|
|
|
122
|
+
// Run cross-file taint analysis
|
|
123
|
+
export function runCrossFileAnalyzer(filePaths) {
|
|
124
|
+
try {
|
|
125
|
+
const analyzerPath = join(__dirname, '..', 'cross_file_analyzer.py');
|
|
126
|
+
if (!existsSync(analyzerPath)) return [];
|
|
127
|
+
const result = execFileSync('python3', [analyzerPath, ...filePaths], {
|
|
128
|
+
encoding: 'utf-8',
|
|
129
|
+
timeout: 120000,
|
|
130
|
+
maxBuffer: 10 * 1024 * 1024
|
|
131
|
+
});
|
|
132
|
+
const parsed = JSON.parse(result);
|
|
133
|
+
// Return only cross-file warnings (per-file findings are handled by scanSecurity)
|
|
134
|
+
return Array.isArray(parsed)
|
|
135
|
+
? parsed.filter(f => f.ruleId === 'cross-file-taint-warning')
|
|
136
|
+
: [];
|
|
137
|
+
} catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
73
142
|
// Convert issues to SARIF 2.1.0 format
|
|
74
143
|
export function toSarif(file_path, language, issues) {
|
|
75
144
|
const severityToLevel = {
|