agent-security-scanner-mcp 1.0.0 → 1.0.2
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 +208 -35
- package/index.js +573 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# agent-security-scanner-mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A powerful MCP (Model Context Protocol) server for real-time security vulnerability scanning. Integrates with Claude Desktop and Claude Code to automatically detect and fix security issues as you code.
|
|
4
|
+
|
|
5
|
+
**165 Semgrep-aligned security rules | 105 auto-fix templates | 100% fix coverage**
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Real-time scanning** - Detect vulnerabilities instantly as you write code
|
|
10
|
+
- **Auto-fix suggestions** - Get actionable fixes for every security issue
|
|
11
|
+
- **Multi-language support** - JavaScript, TypeScript, Python, Java, Go, Dockerfile
|
|
12
|
+
- **Semgrep-compatible** - Rules aligned with Semgrep registry format
|
|
13
|
+
- **CWE & OWASP mapped** - Every rule includes CWE and OWASP references
|
|
4
14
|
|
|
5
15
|
## Installation
|
|
6
16
|
|
|
@@ -17,7 +27,7 @@ npx agent-security-scanner-mcp
|
|
|
17
27
|
## Requirements
|
|
18
28
|
|
|
19
29
|
- Node.js >= 18.0.0
|
|
20
|
-
- Python 3.x (for the analyzer)
|
|
30
|
+
- Python 3.x (for the analyzer engine)
|
|
21
31
|
|
|
22
32
|
## Configuration
|
|
23
33
|
|
|
@@ -36,9 +46,13 @@ Add to your `claude_desktop_config.json`:
|
|
|
36
46
|
}
|
|
37
47
|
```
|
|
38
48
|
|
|
49
|
+
**Config file locations:**
|
|
50
|
+
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
51
|
+
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
52
|
+
|
|
39
53
|
### Claude Code
|
|
40
54
|
|
|
41
|
-
Add to your MCP settings:
|
|
55
|
+
Add to your MCP settings (`~/.claude/settings.json`):
|
|
42
56
|
|
|
43
57
|
```json
|
|
44
58
|
{
|
|
@@ -57,50 +71,209 @@ Add to your MCP settings:
|
|
|
57
71
|
|
|
58
72
|
Scan a file for security vulnerabilities and return issues with suggested fixes.
|
|
59
73
|
|
|
60
|
-
|
|
61
|
-
|
|
74
|
+
```
|
|
75
|
+
Parameters:
|
|
76
|
+
file_path (string): Absolute path to the file to scan
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
- List of security issues
|
|
80
|
+
- Severity level (ERROR, WARNING, INFO)
|
|
81
|
+
- CWE and OWASP references
|
|
82
|
+
- Line numbers and code context
|
|
83
|
+
- Suggested fixes
|
|
84
|
+
```
|
|
62
85
|
|
|
63
|
-
**
|
|
86
|
+
**Example output:**
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"file": "/path/to/file.js",
|
|
90
|
+
"language": "javascript",
|
|
91
|
+
"issues_count": 3,
|
|
92
|
+
"issues": [
|
|
93
|
+
{
|
|
94
|
+
"ruleId": "javascript.lang.security.audit.sql-injection",
|
|
95
|
+
"message": "SQL Injection detected. Use parameterized queries.",
|
|
96
|
+
"line": 15,
|
|
97
|
+
"severity": "error",
|
|
98
|
+
"metadata": {
|
|
99
|
+
"cwe": "CWE-89",
|
|
100
|
+
"owasp": "A03:2021 - Injection"
|
|
101
|
+
},
|
|
102
|
+
"suggested_fix": {
|
|
103
|
+
"description": "Use parameterized queries instead of string concatenation",
|
|
104
|
+
"original": "db.query(\"SELECT * FROM users WHERE id = \" + userId)",
|
|
105
|
+
"fixed": "db.query(\"SELECT * FROM users WHERE id = ?\", [userId])"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
64
111
|
|
|
65
112
|
### `fix_security`
|
|
66
113
|
|
|
67
|
-
|
|
114
|
+
Automatically fix all security issues in a file.
|
|
68
115
|
|
|
69
|
-
|
|
70
|
-
|
|
116
|
+
```
|
|
117
|
+
Parameters:
|
|
118
|
+
file_path (string): Absolute path to the file to fix
|
|
71
119
|
|
|
72
|
-
|
|
120
|
+
Returns:
|
|
121
|
+
- Number of fixes applied
|
|
122
|
+
- Details of each fix
|
|
123
|
+
- Fixed file content
|
|
124
|
+
```
|
|
73
125
|
|
|
74
126
|
### `list_security_rules`
|
|
75
127
|
|
|
76
|
-
List all available
|
|
77
|
-
|
|
78
|
-
##
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
|
83
|
-
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
128
|
+
List all 105 available auto-fix templates.
|
|
129
|
+
|
|
130
|
+
## Security Rules (165 total)
|
|
131
|
+
|
|
132
|
+
### By Language
|
|
133
|
+
|
|
134
|
+
| Language | Rules | Categories |
|
|
135
|
+
|----------|-------|------------|
|
|
136
|
+
| JavaScript/TypeScript | 31 | XSS, injection, secrets, crypto |
|
|
137
|
+
| Python | 36 | Injection, deserialization, crypto, XXE |
|
|
138
|
+
| Java | 27 | Injection, XXE, crypto, deserialization |
|
|
139
|
+
| Go | 22 | Injection, crypto, race conditions |
|
|
140
|
+
| Dockerfile | 18 | Secrets, permissions, best practices |
|
|
141
|
+
| Generic (Secrets) | 31 | API keys, tokens, passwords |
|
|
142
|
+
|
|
143
|
+
### By Category
|
|
144
|
+
|
|
145
|
+
| Category | Rules | Auto-Fix |
|
|
146
|
+
|----------|-------|----------|
|
|
147
|
+
| **Injection (SQL, Command, XSS)** | 35 | Yes |
|
|
148
|
+
| **Hardcoded Secrets** | 45 | Yes |
|
|
149
|
+
| **Weak Cryptography** | 18 | Yes |
|
|
150
|
+
| **Insecure Deserialization** | 12 | Yes |
|
|
151
|
+
| **Path Traversal** | 6 | Yes |
|
|
152
|
+
| **SSRF** | 6 | Yes |
|
|
153
|
+
| **XXE** | 6 | Yes |
|
|
154
|
+
| **SSL/TLS Issues** | 8 | Yes |
|
|
155
|
+
| **CSRF** | 4 | Yes |
|
|
156
|
+
| **JWT Vulnerabilities** | 6 | Yes |
|
|
157
|
+
| **Dockerfile Security** | 18 | Yes |
|
|
158
|
+
| **Other** | 11 | Yes |
|
|
159
|
+
|
|
160
|
+
## Auto-Fix Templates (105 total)
|
|
161
|
+
|
|
162
|
+
Every detected vulnerability includes an automatic fix suggestion:
|
|
163
|
+
|
|
164
|
+
| Vulnerability | Fix Strategy |
|
|
165
|
+
|--------------|--------------|
|
|
166
|
+
| SQL Injection | Parameterized queries with placeholders |
|
|
167
|
+
| XSS (innerHTML) | Replace with `textContent` or DOMPurify |
|
|
168
|
+
| Command Injection | Use `execFile()` / `spawn()` with `shell: false` |
|
|
169
|
+
| Hardcoded Secrets | Environment variables (`process.env` / `os.environ`) |
|
|
170
|
+
| Weak Crypto (MD5/SHA1) | Replace with SHA-256 |
|
|
171
|
+
| Insecure Deserialization | Use `json.load()` or `yaml.safe_load()` |
|
|
172
|
+
| SSL verify=False | Set `verify=True` |
|
|
173
|
+
| Path Traversal | Use `path.basename()` / `os.path.basename()` |
|
|
174
|
+
| Eval/Exec | Remove or use safer alternatives |
|
|
175
|
+
| CORS Wildcard | Specify allowed origins |
|
|
176
|
+
|
|
177
|
+
## Example Usage
|
|
178
|
+
|
|
179
|
+
### Scanning a file
|
|
180
|
+
|
|
181
|
+
Ask Claude: *"Scan my app.js file for security issues"*
|
|
182
|
+
|
|
183
|
+
Claude will use `scan_security` and return:
|
|
184
|
+
- All vulnerabilities found
|
|
185
|
+
- Severity levels
|
|
186
|
+
- CWE/OWASP references
|
|
187
|
+
- Suggested fixes for each issue
|
|
188
|
+
|
|
189
|
+
### Auto-fixing issues
|
|
190
|
+
|
|
191
|
+
Ask Claude: *"Fix all security issues in app.js"*
|
|
192
|
+
|
|
193
|
+
Claude will use `fix_security` to:
|
|
194
|
+
- Apply all available auto-fixes
|
|
195
|
+
- Return the secured code
|
|
196
|
+
- List all changes made
|
|
197
|
+
|
|
198
|
+
## Supported Vulnerabilities
|
|
199
|
+
|
|
200
|
+
### Injection
|
|
201
|
+
- SQL Injection (multiple databases)
|
|
202
|
+
- NoSQL Injection (MongoDB)
|
|
203
|
+
- Command Injection (exec, spawn, subprocess)
|
|
204
|
+
- XSS (innerHTML, document.write, React dangerouslySetInnerHTML)
|
|
205
|
+
- LDAP Injection
|
|
206
|
+
- XPath Injection
|
|
207
|
+
- Template Injection (Jinja2, SpEL)
|
|
208
|
+
|
|
209
|
+
### Secrets & Credentials
|
|
210
|
+
- AWS Access Keys & Secret Keys
|
|
211
|
+
- GitHub Tokens (PAT, OAuth, App)
|
|
212
|
+
- Stripe API Keys
|
|
213
|
+
- OpenAI API Keys
|
|
214
|
+
- Slack Tokens & Webhooks
|
|
215
|
+
- Database URLs & Passwords
|
|
216
|
+
- Private Keys (RSA, SSH)
|
|
217
|
+
- JWT Secrets
|
|
218
|
+
- 25+ more token types
|
|
219
|
+
|
|
220
|
+
### Cryptography
|
|
221
|
+
- Weak Hashing (MD5, SHA1)
|
|
222
|
+
- Weak Ciphers (DES, RC4)
|
|
223
|
+
- ECB Mode Usage
|
|
224
|
+
- Insecure Random
|
|
225
|
+
- Weak RSA Key Size
|
|
226
|
+
- Weak TLS Versions
|
|
227
|
+
|
|
228
|
+
### Deserialization
|
|
229
|
+
- Python pickle/marshal/shelve
|
|
230
|
+
- YAML unsafe load
|
|
231
|
+
- Java ObjectInputStream
|
|
232
|
+
- Node serialize
|
|
233
|
+
- Go gob decode
|
|
234
|
+
|
|
235
|
+
### Network & SSL
|
|
236
|
+
- SSL Verification Disabled
|
|
237
|
+
- Certificate Validation Bypass
|
|
238
|
+
- SSRF Vulnerabilities
|
|
239
|
+
- Open Redirects
|
|
240
|
+
- CORS Misconfiguration
|
|
241
|
+
|
|
242
|
+
### Other
|
|
243
|
+
- Path Traversal
|
|
244
|
+
- XXE (XML External Entities)
|
|
245
|
+
- CSRF Disabled
|
|
246
|
+
- Debug Mode Enabled
|
|
247
|
+
- Prototype Pollution
|
|
248
|
+
- ReDoS (Regex DoS)
|
|
249
|
+
- Race Conditions
|
|
250
|
+
|
|
251
|
+
## Contributing
|
|
252
|
+
|
|
253
|
+
Contributions welcome! Please see our [GitHub repository](https://github.com/sinewaveai/agent-security-layer-fork).
|
|
254
|
+
|
|
255
|
+
### Adding New Rules
|
|
256
|
+
|
|
257
|
+
Rules are defined in YAML format in the `rules/` directory:
|
|
258
|
+
|
|
259
|
+
```yaml
|
|
260
|
+
- id: language.category.rule-name
|
|
261
|
+
languages: [javascript]
|
|
262
|
+
severity: ERROR
|
|
263
|
+
message: "Description of the vulnerability"
|
|
264
|
+
patterns:
|
|
265
|
+
- "regex_pattern"
|
|
266
|
+
metadata:
|
|
267
|
+
cwe: "CWE-XXX"
|
|
268
|
+
owasp: "Category"
|
|
269
|
+
```
|
|
99
270
|
|
|
100
271
|
## License
|
|
101
272
|
|
|
102
273
|
MIT
|
|
103
274
|
|
|
104
|
-
##
|
|
275
|
+
## Links
|
|
105
276
|
|
|
106
|
-
https://
|
|
277
|
+
- **npm:** https://www.npmjs.com/package/agent-security-scanner-mcp
|
|
278
|
+
- **GitHub:** https://github.com/sinewaveai/agent-security-layer-fork
|
|
279
|
+
- **Issues:** https://github.com/sinewaveai/agent-security-layer-fork/issues
|
package/index.js
CHANGED
|
@@ -10,29 +10,180 @@ import { fileURLToPath } from "url";
|
|
|
10
10
|
|
|
11
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
|
|
13
|
-
// Security fix templates
|
|
13
|
+
// Security fix templates - comprehensive coverage for 165+ rules
|
|
14
14
|
const FIX_TEMPLATES = {
|
|
15
|
+
// ===========================================
|
|
16
|
+
// SQL INJECTION
|
|
17
|
+
// ===========================================
|
|
15
18
|
"sql-injection": {
|
|
16
19
|
description: "Use parameterized queries instead of string concatenation",
|
|
17
20
|
fix: (line) => line.replace(/["']([^"']*)\s*["']\s*\+\s*(\w+)/, '"$1?", [$2]')
|
|
18
21
|
},
|
|
22
|
+
"nosql-injection": {
|
|
23
|
+
description: "Sanitize MongoDB query inputs",
|
|
24
|
+
fix: (line) => line.replace(/\{\s*(\w+)\s*:\s*(\w+)\s*\}/, '{ $1: sanitize($2) }')
|
|
25
|
+
},
|
|
26
|
+
"raw-query": {
|
|
27
|
+
description: "Use parameterized queries instead of raw SQL",
|
|
28
|
+
fix: (line) => line.replace(/\.query\s*\(\s*["'`]/, '.query("SELECT * FROM table WHERE id = ?", [')
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
// ===========================================
|
|
32
|
+
// XSS (Cross-Site Scripting)
|
|
33
|
+
// ===========================================
|
|
19
34
|
"innerhtml": {
|
|
20
35
|
description: "Use textContent or DOMPurify.sanitize()",
|
|
21
36
|
fix: (line) => line.replace(/\.innerHTML\s*=/, '.textContent =')
|
|
22
37
|
},
|
|
38
|
+
"outerhtml": {
|
|
39
|
+
description: "Use textContent or DOMPurify.sanitize()",
|
|
40
|
+
fix: (line) => line.replace(/\.outerHTML\s*=/, '.textContent =')
|
|
41
|
+
},
|
|
42
|
+
"document-write": {
|
|
43
|
+
description: "Use DOM methods instead of document.write()",
|
|
44
|
+
fix: (line) => line.replace(/document\.write(ln)?\s*\(/, 'document.body.appendChild(document.createTextNode(')
|
|
45
|
+
},
|
|
46
|
+
"insertadjacenthtml": {
|
|
47
|
+
description: "Use insertAdjacentText or sanitize input",
|
|
48
|
+
fix: (line) => line.replace(/\.insertAdjacentHTML\s*\(/, '.insertAdjacentText(')
|
|
49
|
+
},
|
|
50
|
+
"dangerouslysetinnerhtml": {
|
|
51
|
+
description: "Sanitize content with DOMPurify before using dangerouslySetInnerHTML",
|
|
52
|
+
fix: (line) => line.replace(/dangerouslySetInnerHTML\s*=\s*\{\s*\{\s*__html\s*:\s*(\w+)/, 'dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize($1)')
|
|
53
|
+
},
|
|
54
|
+
"xss-response-writer": {
|
|
55
|
+
description: "Escape HTML output before writing to response",
|
|
56
|
+
fix: (line) => line.replace(/\.Write\s*\(\s*(\w+)/, '.Write(html.EscapeString($1)')
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
// ===========================================
|
|
60
|
+
// COMMAND INJECTION
|
|
61
|
+
// ===========================================
|
|
23
62
|
"child-process-exec": {
|
|
24
63
|
description: "Use execFile() or spawn() with shell: false",
|
|
25
64
|
fix: (line) => line.replace(/\bexec\s*\(/, 'execFile(')
|
|
26
65
|
},
|
|
66
|
+
"spawn-shell": {
|
|
67
|
+
description: "Use spawn with shell: false",
|
|
68
|
+
fix: (line) => line.replace(/shell\s*:\s*true/i, 'shell: false')
|
|
69
|
+
},
|
|
70
|
+
"dangerous-subprocess": {
|
|
71
|
+
description: "Use subprocess.run with list arguments",
|
|
72
|
+
fix: (line) => line.replace(/subprocess\.(call|run|Popen)\s*\(\s*["'](.+?)["']\s*,\s*shell\s*=\s*True/, 'subprocess.$1(["$2".split()], shell=False')
|
|
73
|
+
},
|
|
74
|
+
"dangerous-system-call": {
|
|
75
|
+
description: "Use subprocess.run instead of os.system",
|
|
76
|
+
fix: (line) => line.replace(/os\.system\s*\(/, 'subprocess.run([')
|
|
77
|
+
},
|
|
78
|
+
"command-injection-exec": {
|
|
79
|
+
description: "Use exec.Command with separate arguments",
|
|
80
|
+
fix: (line) => line.replace(/exec\.Command\s*\(\s*["'](\w+)\s+/, 'exec.Command("$1", ')
|
|
81
|
+
},
|
|
82
|
+
"runtime-exec": {
|
|
83
|
+
description: "Use ProcessBuilder with separate arguments",
|
|
84
|
+
fix: (line) => line.replace(/Runtime\.getRuntime\(\)\.exec\s*\(/, 'new ProcessBuilder(')
|
|
85
|
+
},
|
|
86
|
+
"process-builder": {
|
|
87
|
+
description: "Validate and sanitize command arguments",
|
|
88
|
+
fix: (line) => line.replace(/new ProcessBuilder\s*\(\s*(.+?)\s*\)/, 'new ProcessBuilder(validateArgs($1))')
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// ===========================================
|
|
92
|
+
// HARDCODED SECRETS & CREDENTIALS
|
|
93
|
+
// ===========================================
|
|
27
94
|
"hardcoded": {
|
|
28
95
|
description: "Use environment variables",
|
|
29
96
|
fix: (line, lang) => {
|
|
30
97
|
if (lang === 'python') {
|
|
31
98
|
return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("SECRET")');
|
|
32
99
|
}
|
|
33
|
-
return line.replace(
|
|
100
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.SECRET');
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
"api-key": {
|
|
104
|
+
description: "Use environment variables for API keys",
|
|
105
|
+
fix: (line, lang) => {
|
|
106
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("API_KEY")');
|
|
107
|
+
if (lang === 'go') return line.replace(/=\s*["'][^"']+["']/, '= os.Getenv("API_KEY")');
|
|
108
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.API_KEY');
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"password": {
|
|
112
|
+
description: "Use environment variables for passwords",
|
|
113
|
+
fix: (line, lang) => {
|
|
114
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("PASSWORD")');
|
|
115
|
+
if (lang === 'go') return line.replace(/=\s*["'][^"']+["']/, '= os.Getenv("PASSWORD")');
|
|
116
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.PASSWORD');
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"secret-key": {
|
|
120
|
+
description: "Use environment variables for secret keys",
|
|
121
|
+
fix: (line, lang) => {
|
|
122
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("SECRET_KEY")');
|
|
123
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.SECRET_KEY');
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"aws-access": {
|
|
127
|
+
description: "Use AWS credentials from environment or IAM roles",
|
|
128
|
+
fix: (line) => line.replace(/=\s*["']AKIA[^"']+["']/, '= os.environ.get("AWS_ACCESS_KEY_ID")')
|
|
129
|
+
},
|
|
130
|
+
"aws-secret": {
|
|
131
|
+
description: "Use AWS credentials from environment or IAM roles",
|
|
132
|
+
fix: (line) => line.replace(/=\s*["'][^"']{40}["']/, '= os.environ.get("AWS_SECRET_ACCESS_KEY")')
|
|
133
|
+
},
|
|
134
|
+
"stripe": {
|
|
135
|
+
description: "Use environment variables for Stripe keys",
|
|
136
|
+
fix: (line, lang) => {
|
|
137
|
+
if (lang === 'python') return line.replace(/=\s*["']sk_(live|test)_[^"']+["']/, '= os.environ.get("STRIPE_SECRET_KEY")');
|
|
138
|
+
return line.replace(/=\s*["']sk_(live|test)_[^"']+["']/, '= process.env.STRIPE_SECRET_KEY');
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
"github": {
|
|
142
|
+
description: "Use environment variables for GitHub tokens",
|
|
143
|
+
fix: (line, lang) => {
|
|
144
|
+
if (lang === 'python') return line.replace(/=\s*["'](ghp_|github_pat_)[^"']+["']/, '= os.environ.get("GITHUB_TOKEN")');
|
|
145
|
+
return line.replace(/=\s*["'](ghp_|github_pat_)[^"']+["']/, '= process.env.GITHUB_TOKEN');
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"openai": {
|
|
149
|
+
description: "Use environment variables for OpenAI keys",
|
|
150
|
+
fix: (line, lang) => {
|
|
151
|
+
if (lang === 'python') return line.replace(/=\s*["']sk-[^"']+["']/, '= os.environ.get("OPENAI_API_KEY")');
|
|
152
|
+
return line.replace(/=\s*["']sk-[^"']+["']/, '= process.env.OPENAI_API_KEY');
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"slack": {
|
|
156
|
+
description: "Use environment variables for Slack tokens",
|
|
157
|
+
fix: (line, lang) => {
|
|
158
|
+
if (lang === 'python') return line.replace(/=\s*["']xox[baprs]-[^"']+["']/, '= os.environ.get("SLACK_TOKEN")');
|
|
159
|
+
return line.replace(/=\s*["']xox[baprs]-[^"']+["']/, '= process.env.SLACK_TOKEN');
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"jwt-token": {
|
|
163
|
+
description: "Use environment variables for JWT secrets",
|
|
164
|
+
fix: (line, lang) => {
|
|
165
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("JWT_SECRET")');
|
|
166
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.JWT_SECRET');
|
|
34
167
|
}
|
|
35
168
|
},
|
|
169
|
+
"private-key": {
|
|
170
|
+
description: "Load private keys from secure file or vault",
|
|
171
|
+
fix: (line, lang) => {
|
|
172
|
+
if (lang === 'python') return line.replace(/=\s*["']-----BEGIN[^"']+["']/, '= load_key_from_file(os.environ.get("PRIVATE_KEY_PATH"))');
|
|
173
|
+
return line.replace(/=\s*["']-----BEGIN[^"']+["']/, '= fs.readFileSync(process.env.PRIVATE_KEY_PATH)');
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
"database-url": {
|
|
177
|
+
description: "Use environment variables for database URLs",
|
|
178
|
+
fix: (line, lang) => {
|
|
179
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("DATABASE_URL")');
|
|
180
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.DATABASE_URL');
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
|
|
184
|
+
// ===========================================
|
|
185
|
+
// WEAK CRYPTOGRAPHY
|
|
186
|
+
// ===========================================
|
|
36
187
|
"md5": {
|
|
37
188
|
description: "Use SHA-256 or stronger",
|
|
38
189
|
fix: (line) => line.replace(/md5/gi, 'sha256')
|
|
@@ -41,17 +192,434 @@ const FIX_TEMPLATES = {
|
|
|
41
192
|
description: "Use SHA-256 or stronger",
|
|
42
193
|
fix: (line) => line.replace(/sha1/gi, 'sha256')
|
|
43
194
|
},
|
|
195
|
+
"des": {
|
|
196
|
+
description: "Use AES instead of DES",
|
|
197
|
+
fix: (line) => line.replace(/DES/g, 'AES').replace(/des/g, 'aes')
|
|
198
|
+
},
|
|
199
|
+
"ecb-mode": {
|
|
200
|
+
description: "Use CBC or GCM mode instead of ECB",
|
|
201
|
+
fix: (line) => line.replace(/ECB/g, 'GCM').replace(/ecb/g, 'gcm')
|
|
202
|
+
},
|
|
203
|
+
"weak-cipher": {
|
|
204
|
+
description: "Use AES-256-GCM or ChaCha20-Poly1305",
|
|
205
|
+
fix: (line) => line.replace(/(DES|RC4|Blowfish)/gi, 'AES')
|
|
206
|
+
},
|
|
207
|
+
"insecure-random": {
|
|
208
|
+
description: "Use cryptographically secure random",
|
|
209
|
+
fix: (line, lang) => {
|
|
210
|
+
if (lang === 'python') return line.replace(/random\.(random|randint|choice|randrange)\s*\(/, 'secrets.token_hex(');
|
|
211
|
+
if (lang === 'go') return line.replace(/math\/rand/, 'crypto/rand');
|
|
212
|
+
if (lang === 'java') return line.replace(/new Random\(\)/, 'SecureRandom.getInstanceStrong()');
|
|
213
|
+
return line.replace(/Math\.random\s*\(\)/, 'crypto.randomUUID()');
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"weak-rsa": {
|
|
217
|
+
description: "Use RSA key size of 2048 bits or more",
|
|
218
|
+
fix: (line) => line.replace(/\b(512|1024)\b/, '2048')
|
|
219
|
+
},
|
|
220
|
+
"weak-tls": {
|
|
221
|
+
description: "Use TLS 1.2 or higher",
|
|
222
|
+
fix: (line) => line.replace(/TLS1[01]|SSLv[23]/gi, 'TLS12')
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
// ===========================================
|
|
226
|
+
// INSECURE DESERIALIZATION
|
|
227
|
+
// ===========================================
|
|
44
228
|
"pickle": {
|
|
45
229
|
description: "Use JSON instead of pickle",
|
|
46
|
-
fix: (line) => line.replace(/pickle\.(load|loads)/, 'json.$1')
|
|
230
|
+
fix: (line) => line.replace(/pickle\.(load|loads)\s*\(/, 'json.$1(')
|
|
47
231
|
},
|
|
48
|
-
"yaml
|
|
232
|
+
"yaml-load": {
|
|
49
233
|
description: "Use yaml.safe_load()",
|
|
50
234
|
fix: (line) => line.replace(/yaml\.load\s*\(/, 'yaml.safe_load(')
|
|
51
235
|
},
|
|
52
|
-
"
|
|
236
|
+
"marshal": {
|
|
237
|
+
description: "Use JSON instead of marshal",
|
|
238
|
+
fix: (line) => line.replace(/marshal\.(load|loads)\s*\(/, 'json.$1(')
|
|
239
|
+
},
|
|
240
|
+
"shelve": {
|
|
241
|
+
description: "Use JSON or SQLite instead of shelve",
|
|
242
|
+
fix: (line) => line.replace(/shelve\.open\s*\(/, 'json.load(open(')
|
|
243
|
+
},
|
|
244
|
+
"node-serialize": {
|
|
245
|
+
description: "Use JSON.parse instead of node-serialize",
|
|
246
|
+
fix: (line) => line.replace(/serialize\.unserialize\s*\(/, 'JSON.parse(')
|
|
247
|
+
},
|
|
248
|
+
"object-inputstream": {
|
|
249
|
+
description: "Use JSON or validated deserialization",
|
|
250
|
+
fix: (line) => line.replace(/new ObjectInputStream\s*\(/, 'new JsonReader(')
|
|
251
|
+
},
|
|
252
|
+
"xstream": {
|
|
253
|
+
description: "Configure XStream security or use JSON",
|
|
254
|
+
fix: (line) => line.replace(/xstream\.fromXML\s*\(/, 'new ObjectMapper().readValue(')
|
|
255
|
+
},
|
|
256
|
+
"gob-decode": {
|
|
257
|
+
description: "Use JSON instead of gob for untrusted data",
|
|
258
|
+
fix: (line) => line.replace(/gob\.NewDecoder/, 'json.NewDecoder')
|
|
259
|
+
},
|
|
260
|
+
|
|
261
|
+
// ===========================================
|
|
262
|
+
// SSL/TLS ISSUES
|
|
263
|
+
// ===========================================
|
|
264
|
+
"verify": {
|
|
53
265
|
description: "Enable SSL verification",
|
|
54
266
|
fix: (line) => line.replace(/verify\s*=\s*False/i, 'verify=True')
|
|
267
|
+
},
|
|
268
|
+
"insecure-skip-verify": {
|
|
269
|
+
description: "Enable certificate verification",
|
|
270
|
+
fix: (line) => line.replace(/InsecureSkipVerify\s*:\s*true/, 'InsecureSkipVerify: false')
|
|
271
|
+
},
|
|
272
|
+
"reject-unauthorized": {
|
|
273
|
+
description: "Enable certificate verification",
|
|
274
|
+
fix: (line) => line.replace(/rejectUnauthorized\s*:\s*false/, 'rejectUnauthorized: true')
|
|
275
|
+
},
|
|
276
|
+
"trust-all": {
|
|
277
|
+
description: "Remove trust-all certificate configuration",
|
|
278
|
+
fix: (line) => '// TODO: Remove trust-all certificates - ' + line
|
|
279
|
+
},
|
|
280
|
+
"ssl-verify-disabled": {
|
|
281
|
+
description: "Enable SSL verification",
|
|
282
|
+
fix: (line) => line.replace(/verify\s*=\s*False/, 'verify=True')
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
// ===========================================
|
|
286
|
+
// PATH TRAVERSAL
|
|
287
|
+
// ===========================================
|
|
288
|
+
"path-traversal": {
|
|
289
|
+
description: "Sanitize file paths and use basename",
|
|
290
|
+
fix: (line, lang) => {
|
|
291
|
+
if (lang === 'python') return line.replace(/open\s*\(\s*(\w+)/, 'open(os.path.basename($1)');
|
|
292
|
+
if (lang === 'go') return line.replace(/os\.Open\s*\(\s*(\w+)/, 'os.Open(filepath.Base($1)');
|
|
293
|
+
if (lang === 'java') return line.replace(/new File\s*\(\s*(\w+)/, 'new File(new File($1).getName()');
|
|
294
|
+
return line.replace(/readFileSync\s*\(\s*(\w+)/, 'readFileSync(path.basename($1)');
|
|
295
|
+
}
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// ===========================================
|
|
299
|
+
// SSRF (Server-Side Request Forgery)
|
|
300
|
+
// ===========================================
|
|
301
|
+
"ssrf": {
|
|
302
|
+
description: "Validate and whitelist URLs before making requests",
|
|
303
|
+
fix: (line) => line.replace(/(axios|fetch|requests|http)\.(get|post|request)\s*\(\s*(\w+)/, '$1.$2(validateUrl($3)')
|
|
304
|
+
},
|
|
305
|
+
|
|
306
|
+
// ===========================================
|
|
307
|
+
// EVAL AND CODE INJECTION
|
|
308
|
+
// ===========================================
|
|
309
|
+
"eval": {
|
|
310
|
+
description: "Avoid eval() - use safer alternatives",
|
|
311
|
+
fix: (line) => '// SECURITY: Remove eval() - ' + line
|
|
312
|
+
},
|
|
313
|
+
"exec-detected": {
|
|
314
|
+
description: "Avoid exec() - use safer alternatives",
|
|
315
|
+
fix: (line) => '# SECURITY: Remove exec() - ' + line
|
|
316
|
+
},
|
|
317
|
+
"compile-detected": {
|
|
318
|
+
description: "Avoid compile() with untrusted input",
|
|
319
|
+
fix: (line) => '# SECURITY: Review compile() usage - ' + line
|
|
320
|
+
},
|
|
321
|
+
"function-constructor": {
|
|
322
|
+
description: "Avoid Function constructor - use safer alternatives",
|
|
323
|
+
fix: (line) => '// SECURITY: Remove Function() constructor - ' + line
|
|
324
|
+
},
|
|
325
|
+
"settimeout-string": {
|
|
326
|
+
description: "Use function reference instead of string",
|
|
327
|
+
fix: (line) => line.replace(/setTimeout\s*\(\s*["'](.+?)["']/, 'setTimeout(() => { $1 }')
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
// ===========================================
|
|
331
|
+
// OPEN REDIRECT
|
|
332
|
+
// ===========================================
|
|
333
|
+
"open-redirect": {
|
|
334
|
+
description: "Validate redirect URLs against whitelist",
|
|
335
|
+
fix: (line) => line.replace(/redirect\s*\(\s*(\w+)/, 'redirect(validateRedirectUrl($1)')
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
// ===========================================
|
|
339
|
+
// CORS
|
|
340
|
+
// ===========================================
|
|
341
|
+
"cors-wildcard": {
|
|
342
|
+
description: "Specify allowed origins instead of wildcard",
|
|
343
|
+
fix: (line) => line.replace(/['"]\*['"]/, '"https://yourdomain.com"')
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
// ===========================================
|
|
347
|
+
// CSRF
|
|
348
|
+
// ===========================================
|
|
349
|
+
"csrf": {
|
|
350
|
+
description: "Enable CSRF protection",
|
|
351
|
+
fix: (line) => line.replace(/csrf\s*:\s*false/i, 'csrf: true').replace(/@csrf_exempt/, '# @csrf_exempt // TODO: Add CSRF protection')
|
|
352
|
+
},
|
|
353
|
+
|
|
354
|
+
// ===========================================
|
|
355
|
+
// DEBUG MODE
|
|
356
|
+
// ===========================================
|
|
357
|
+
"debug": {
|
|
358
|
+
description: "Disable debug mode in production",
|
|
359
|
+
fix: (line) => line.replace(/debug\s*=\s*True/i, 'debug=os.environ.get("DEBUG", "False").lower() == "true"')
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
// ===========================================
|
|
363
|
+
// JWT ISSUES
|
|
364
|
+
// ===========================================
|
|
365
|
+
"jwt-none": {
|
|
366
|
+
description: "Specify a secure algorithm for JWT",
|
|
367
|
+
fix: (line) => line.replace(/algorithm\s*[=:]\s*["']none["']/i, 'algorithm: "HS256"')
|
|
368
|
+
},
|
|
369
|
+
"jwt-decode-without-verify": {
|
|
370
|
+
description: "Enable JWT signature verification",
|
|
371
|
+
fix: (line) => line.replace(/verify\s*=\s*False/, 'verify=True')
|
|
372
|
+
},
|
|
373
|
+
|
|
374
|
+
// ===========================================
|
|
375
|
+
// XXE (XML External Entities)
|
|
376
|
+
// ===========================================
|
|
377
|
+
"xxe": {
|
|
378
|
+
description: "Disable external entities in XML parser",
|
|
379
|
+
fix: (line, lang) => {
|
|
380
|
+
if (lang === 'python') return line.replace(/etree\.parse\s*\(/, 'etree.parse(parser=etree.XMLParser(resolve_entities=False), ');
|
|
381
|
+
if (lang === 'java') return '// TODO: Disable external entities - ' + line;
|
|
382
|
+
return line;
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
"lxml": {
|
|
386
|
+
description: "Disable external entities in lxml",
|
|
387
|
+
fix: (line) => line.replace(/etree\.(parse|fromstring)\s*\(/, 'etree.$1(parser=etree.XMLParser(resolve_entities=False, no_network=True), ')
|
|
388
|
+
},
|
|
389
|
+
|
|
390
|
+
// ===========================================
|
|
391
|
+
// LDAP INJECTION
|
|
392
|
+
// ===========================================
|
|
393
|
+
"ldap-injection": {
|
|
394
|
+
description: "Escape LDAP special characters in user input",
|
|
395
|
+
fix: (line) => line.replace(/filter\s*=\s*["']([^"']*)\s*["']\s*\+\s*(\w+)/, 'filter = "$1" + escapeLdap($2)')
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
// ===========================================
|
|
399
|
+
// XPATH INJECTION
|
|
400
|
+
// ===========================================
|
|
401
|
+
"xpath-injection": {
|
|
402
|
+
description: "Use parameterized XPath queries",
|
|
403
|
+
fix: (line) => line.replace(/xpath\s*\(\s*["']([^"']*)\s*["']\s*\+\s*(\w+)/, 'xpath("$1?", [$2]')
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
// ===========================================
|
|
407
|
+
// TEMPLATE INJECTION
|
|
408
|
+
// ===========================================
|
|
409
|
+
"template-injection": {
|
|
410
|
+
description: "Avoid user input in template strings",
|
|
411
|
+
fix: (line) => '// TODO: Sanitize template input - ' + line
|
|
412
|
+
},
|
|
413
|
+
"jinja2-autoescape": {
|
|
414
|
+
description: "Enable autoescape in Jinja2 templates",
|
|
415
|
+
fix: (line) => line.replace(/autoescape\s*=\s*False/, 'autoescape=True')
|
|
416
|
+
},
|
|
417
|
+
|
|
418
|
+
// ===========================================
|
|
419
|
+
// LOGGING SENSITIVE DATA
|
|
420
|
+
// ===========================================
|
|
421
|
+
"logging-sensitive": {
|
|
422
|
+
description: "Remove sensitive data from logs",
|
|
423
|
+
fix: (line) => line.replace(/(password|secret|token|key|credential)/gi, '[REDACTED]')
|
|
424
|
+
},
|
|
425
|
+
|
|
426
|
+
// ===========================================
|
|
427
|
+
// REGEX DOS
|
|
428
|
+
// ===========================================
|
|
429
|
+
"regex-dos": {
|
|
430
|
+
description: "Use regex with timeout or simplified pattern",
|
|
431
|
+
fix: (line) => '// TODO: Review regex for ReDoS - ' + line
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
// ===========================================
|
|
435
|
+
// PROTOTYPE POLLUTION
|
|
436
|
+
// ===========================================
|
|
437
|
+
"prototype-pollution": {
|
|
438
|
+
description: "Validate object keys before assignment",
|
|
439
|
+
fix: (line) => line.replace(/(\w+)\[(\w+)\]\s*=/, 'if (!["__proto__", "constructor", "prototype"].includes($2)) $1[$2] =')
|
|
440
|
+
},
|
|
441
|
+
|
|
442
|
+
// ===========================================
|
|
443
|
+
// DOCKERFILE
|
|
444
|
+
// ===========================================
|
|
445
|
+
"latest-tag": {
|
|
446
|
+
description: "Use specific version tags instead of latest",
|
|
447
|
+
fix: (line) => line.replace(/:latest/, ':1.0.0 # TODO: specify exact version')
|
|
448
|
+
},
|
|
449
|
+
"run-as-root": {
|
|
450
|
+
description: "Add USER directive to run as non-root",
|
|
451
|
+
fix: (line) => line + '\nUSER nonroot'
|
|
452
|
+
},
|
|
453
|
+
"add-instead-of-copy": {
|
|
454
|
+
description: "Use COPY instead of ADD for local files",
|
|
455
|
+
fix: (line) => line.replace(/^ADD\s+/, 'COPY ')
|
|
456
|
+
},
|
|
457
|
+
"curl-pipe-bash": {
|
|
458
|
+
description: "Download and verify scripts before execution",
|
|
459
|
+
fix: (line) => '# TODO: Download, verify checksum, then execute - ' + line
|
|
460
|
+
},
|
|
461
|
+
"secret-in-env": {
|
|
462
|
+
description: "Use Docker secrets or build args with --secret",
|
|
463
|
+
fix: (line) => line.replace(/ENV\s+(\w*(?:PASSWORD|SECRET|KEY|TOKEN)\w*)\s*=\s*(\S+)/, '# Use --secret instead: ENV $1=$2')
|
|
464
|
+
},
|
|
465
|
+
"secret-in-arg": {
|
|
466
|
+
description: "Use Docker secrets instead of ARG for secrets",
|
|
467
|
+
fix: (line) => line.replace(/ARG\s+(\w*(?:PASSWORD|SECRET|KEY|TOKEN)\w*)/, '# Use --secret instead: ARG $1')
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
// ===========================================
|
|
471
|
+
// HELMET / SECURITY HEADERS
|
|
472
|
+
// ===========================================
|
|
473
|
+
"helmet-missing": {
|
|
474
|
+
description: "Add helmet middleware for security headers",
|
|
475
|
+
fix: (line) => 'app.use(helmet()); // Add security headers\n' + line
|
|
476
|
+
},
|
|
477
|
+
|
|
478
|
+
// ===========================================
|
|
479
|
+
// SPEL INJECTION
|
|
480
|
+
// ===========================================
|
|
481
|
+
"spel-injection": {
|
|
482
|
+
description: "Avoid user input in SpEL expressions",
|
|
483
|
+
fix: (line) => '// TODO: Sanitize SpEL input - ' + line
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
// ===========================================
|
|
487
|
+
// ADDITIONAL DOCKERFILE FIXES
|
|
488
|
+
// ===========================================
|
|
489
|
+
"apt-get-no-version": {
|
|
490
|
+
description: "Pin package versions in apt-get install",
|
|
491
|
+
fix: (line) => line.replace(/apt-get install\s+(\w+)/, 'apt-get install $1=VERSION # TODO: specify version')
|
|
492
|
+
},
|
|
493
|
+
"pip-no-version": {
|
|
494
|
+
description: "Pin package versions in pip install",
|
|
495
|
+
fix: (line) => line.replace(/pip install\s+(\w+)/, 'pip install $1==VERSION # TODO: specify version')
|
|
496
|
+
},
|
|
497
|
+
"npm-install-unsafe": {
|
|
498
|
+
description: "Use npm ci for reproducible builds",
|
|
499
|
+
fix: (line) => line.replace(/npm install/, 'npm ci')
|
|
500
|
+
},
|
|
501
|
+
"missing-healthcheck": {
|
|
502
|
+
description: "Add HEALTHCHECK instruction",
|
|
503
|
+
fix: (line) => line + '\nHEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost/ || exit 1'
|
|
504
|
+
},
|
|
505
|
+
"expose-ssh": {
|
|
506
|
+
description: "Avoid exposing SSH port in containers",
|
|
507
|
+
fix: (line) => '# SECURITY: Avoid SSH in containers - ' + line
|
|
508
|
+
},
|
|
509
|
+
"chmod-dangerous": {
|
|
510
|
+
description: "Use least privilege permissions",
|
|
511
|
+
fix: (line) => line.replace(/chmod\s+(777|666|755)/, 'chmod 644 # TODO: use least privilege')
|
|
512
|
+
},
|
|
513
|
+
"apt-no-clean": {
|
|
514
|
+
description: "Clean apt cache to reduce image size",
|
|
515
|
+
fix: (line) => line.replace(/apt-get install/, 'apt-get install -y && apt-get clean && rm -rf /var/lib/apt/lists/* #')
|
|
516
|
+
},
|
|
517
|
+
"curl-insecure": {
|
|
518
|
+
description: "Remove insecure flag from curl",
|
|
519
|
+
fix: (line) => line.replace(/curl\s+(-k|--insecure)/, 'curl')
|
|
520
|
+
},
|
|
521
|
+
"wget-no-check": {
|
|
522
|
+
description: "Enable certificate checking in wget",
|
|
523
|
+
fix: (line) => line.replace(/wget\s+--no-check-certificate/, 'wget')
|
|
524
|
+
},
|
|
525
|
+
"run-shell-form": {
|
|
526
|
+
description: "Use exec form for RUN commands",
|
|
527
|
+
fix: (line) => line.replace(/RUN\s+(.+)$/, 'RUN ["/bin/sh", "-c", "$1"]')
|
|
528
|
+
},
|
|
529
|
+
"sudo-in-dockerfile": {
|
|
530
|
+
description: "Avoid sudo in Dockerfile - use USER directive",
|
|
531
|
+
fix: (line) => line.replace(/sudo\s+/, '')
|
|
532
|
+
},
|
|
533
|
+
"workdir-absolute": {
|
|
534
|
+
description: "Use absolute paths in WORKDIR",
|
|
535
|
+
fix: (line) => line.replace(/WORKDIR\s+([^/])/, 'WORKDIR /$1')
|
|
536
|
+
},
|
|
537
|
+
|
|
538
|
+
// ===========================================
|
|
539
|
+
// ADDITIONAL TOKEN/SECRET TYPES
|
|
540
|
+
// ===========================================
|
|
541
|
+
"gcp": {
|
|
542
|
+
description: "Use environment variables for GCP credentials",
|
|
543
|
+
fix: (line, lang) => {
|
|
544
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("GOOGLE_APPLICATION_CREDENTIALS")');
|
|
545
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.GOOGLE_APPLICATION_CREDENTIALS');
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
"azure": {
|
|
549
|
+
description: "Use environment variables for Azure credentials",
|
|
550
|
+
fix: (line, lang) => {
|
|
551
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("AZURE_STORAGE_KEY")');
|
|
552
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.AZURE_STORAGE_KEY');
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
"npm-token": {
|
|
556
|
+
description: "Use environment variables for npm tokens",
|
|
557
|
+
fix: (line, lang) => {
|
|
558
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("NPM_TOKEN")');
|
|
559
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.NPM_TOKEN');
|
|
560
|
+
}
|
|
561
|
+
},
|
|
562
|
+
"pypi": {
|
|
563
|
+
description: "Use environment variables for PyPI tokens",
|
|
564
|
+
fix: (line) => line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("PYPI_TOKEN")')
|
|
565
|
+
},
|
|
566
|
+
"discord": {
|
|
567
|
+
description: "Use environment variables for Discord tokens",
|
|
568
|
+
fix: (line, lang) => {
|
|
569
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("DISCORD_TOKEN")');
|
|
570
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.DISCORD_TOKEN');
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
"shopify": {
|
|
574
|
+
description: "Use environment variables for Shopify tokens",
|
|
575
|
+
fix: (line, lang) => {
|
|
576
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("SHOPIFY_TOKEN")');
|
|
577
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.SHOPIFY_TOKEN');
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
"facebook": {
|
|
581
|
+
description: "Use environment variables for Facebook tokens",
|
|
582
|
+
fix: (line, lang) => {
|
|
583
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("FACEBOOK_TOKEN")');
|
|
584
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.FACEBOOK_TOKEN');
|
|
585
|
+
}
|
|
586
|
+
},
|
|
587
|
+
"twitter": {
|
|
588
|
+
description: "Use environment variables for Twitter tokens",
|
|
589
|
+
fix: (line, lang) => {
|
|
590
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("TWITTER_BEARER_TOKEN")');
|
|
591
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.TWITTER_BEARER_TOKEN');
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
"gitlab": {
|
|
595
|
+
description: "Use environment variables for GitLab tokens",
|
|
596
|
+
fix: (line, lang) => {
|
|
597
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("GITLAB_TOKEN")');
|
|
598
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.GITLAB_TOKEN');
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
"bitbucket": {
|
|
602
|
+
description: "Use environment variables for Bitbucket tokens",
|
|
603
|
+
fix: (line, lang) => {
|
|
604
|
+
if (lang === 'python') return line.replace(/=\s*["'][^"']+["']/, '= os.environ.get("BITBUCKET_TOKEN")');
|
|
605
|
+
return line.replace(/=\s*["'][^"']+["']/, '= process.env.BITBUCKET_TOKEN');
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
|
|
609
|
+
// ===========================================
|
|
610
|
+
// ADDITIONAL SECURITY FIXES
|
|
611
|
+
// ===========================================
|
|
612
|
+
"race-condition": {
|
|
613
|
+
description: "Use mutex or sync primitives for shared state",
|
|
614
|
+
fix: (line) => '// TODO: Add mutex protection - ' + line
|
|
615
|
+
},
|
|
616
|
+
"gin-bind": {
|
|
617
|
+
description: "Use explicit binding in Gin handlers",
|
|
618
|
+
fix: (line) => line.replace(/ShouldBind\s*\(/, 'ShouldBindJSON(')
|
|
619
|
+
},
|
|
620
|
+
"permit-all": {
|
|
621
|
+
description: "Review permitAll() and restrict access",
|
|
622
|
+
fix: (line) => '// SECURITY: Review permitAll() - ' + line
|
|
55
623
|
}
|
|
56
624
|
};
|
|
57
625
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-security-scanner-mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "MCP server for security vulnerability scanning - detects SQL injection, XSS, command injection, hardcoded secrets, and more",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|