@intentsolutionsio/security-agent 1.0.0 → 1.0.5
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 +4 -0
- package/agents/security-reviewer.md +31 -0
- package/package.json +1 -1
- package/skills/performing-security-code-review/SKILL.md +15 -5
- package/skills/performing-security-code-review/assets/example_code_secure.py +23 -14
- package/skills/performing-security-code-review/assets/example_code_vulnerable.py +9 -8
- package/skills/performing-security-code-review/assets/report_template.md +5 -4
- package/skills/performing-security-code-review/references/README.md +0 -1
- package/skills/performing-security-code-review/scripts/code_analyzer.py +36 -37
package/README.md
CHANGED
|
@@ -11,11 +11,13 @@ A specialized security review subagent for identifying vulnerabilities and provi
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
The security reviewer agent will automatically activate when you:
|
|
14
|
+
|
|
14
15
|
- Ask Claude to review code for security issues
|
|
15
16
|
- Request a security audit
|
|
16
17
|
- Mention security concerns in your prompt
|
|
17
18
|
|
|
18
19
|
**Manual invocation**:
|
|
20
|
+
|
|
19
21
|
```
|
|
20
22
|
@security-reviewer Please review this authentication code for security vulnerabilities
|
|
21
23
|
```
|
|
@@ -33,6 +35,7 @@ The security reviewer agent will automatically activate when you:
|
|
|
33
35
|
## Output
|
|
34
36
|
|
|
35
37
|
Provides structured security findings with:
|
|
38
|
+
|
|
36
39
|
- Severity ratings
|
|
37
40
|
- Specific code locations
|
|
38
41
|
- Impact assessment
|
|
@@ -42,6 +45,7 @@ Provides structured security findings with:
|
|
|
42
45
|
## Learning Objectives
|
|
43
46
|
|
|
44
47
|
This plugin demonstrates:
|
|
48
|
+
|
|
45
49
|
- Creating specialized subagents
|
|
46
50
|
- Defining agent capabilities
|
|
47
51
|
- Writing agent prompts
|
|
@@ -1,6 +1,35 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: security-reviewer
|
|
3
3
|
description: Security code review specialist
|
|
4
|
+
tools:
|
|
5
|
+
- Read
|
|
6
|
+
- Write
|
|
7
|
+
- Edit
|
|
8
|
+
- Bash
|
|
9
|
+
- Glob
|
|
10
|
+
- Grep
|
|
11
|
+
- WebFetch
|
|
12
|
+
- WebSearch
|
|
13
|
+
- Task
|
|
14
|
+
- TodoWrite
|
|
15
|
+
model: sonnet
|
|
16
|
+
color: yellow
|
|
17
|
+
version: 1.0.0
|
|
18
|
+
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
19
|
+
tags:
|
|
20
|
+
- examples
|
|
21
|
+
- security
|
|
22
|
+
- reviewer
|
|
23
|
+
disallowedTools: []
|
|
24
|
+
skills: []
|
|
25
|
+
background: false
|
|
26
|
+
# ── upgrade levers — uncomment + set when tuning this agent ──
|
|
27
|
+
# effort: high # reasoning depth: low/medium/high/xhigh/max (omit = inherit session)
|
|
28
|
+
# maxTurns: 50 # cap the agentic loop (omit = engine default)
|
|
29
|
+
# memory: project # persistent scope: user/project/local (omit = ephemeral)
|
|
30
|
+
# isolation: worktree # run in an isolated git worktree
|
|
31
|
+
# initialPrompt: "…" # seed the agent's first turn
|
|
32
|
+
# hooks / mcpServers / permissionMode → set at the PLUGIN level, not on a plugin agent
|
|
4
33
|
---
|
|
5
34
|
# Security Reviewer Agent
|
|
6
35
|
|
|
@@ -16,6 +45,7 @@ You are a specialized security code review agent with deep expertise in applicat
|
|
|
16
45
|
## When to Activate
|
|
17
46
|
|
|
18
47
|
You should be invoked when:
|
|
48
|
+
|
|
19
49
|
- Reviewing code for security issues
|
|
20
50
|
- Conducting security audits
|
|
21
51
|
- Analyzing authentication/authorization logic
|
|
@@ -54,6 +84,7 @@ You should be invoked when:
|
|
|
54
84
|
## Output Format
|
|
55
85
|
|
|
56
86
|
For each finding, provide:
|
|
87
|
+
|
|
57
88
|
- **Severity**: Critical/High/Medium/Low
|
|
58
89
|
- **Issue**: Description of the vulnerability
|
|
59
90
|
- **Location**: File and line numbers
|
package/package.json
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: performing-security-code-review
|
|
3
|
-
description:
|
|
4
|
-
|
|
3
|
+
description: 'Execute this skill enables AI assistant to conduct a security-focused
|
|
4
|
+
code review using the security-agent plugin. it analyzes code for potential vulnerabilities
|
|
5
|
+
like sql injection, xss, authentication flaws, and insecure dependencies. AI assistant
|
|
6
|
+
uses this skill wh... Use when assessing security or running audits. Trigger with
|
|
7
|
+
phrases like ''security scan'', ''audit'', or ''vulnerability''.
|
|
8
|
+
|
|
9
|
+
'
|
|
5
10
|
allowed-tools: Read, Write, Edit, Grep, Glob, Bash(cmd:*)
|
|
6
11
|
version: 1.0.0
|
|
7
12
|
author: Jeremy Longshore <jeremy@intentsolutions.io>
|
|
8
13
|
license: MIT
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
tags:
|
|
15
|
+
- example
|
|
16
|
+
- security
|
|
17
|
+
- authentication
|
|
18
|
+
- audit
|
|
19
|
+
compatibility: Designed for Claude Code, also compatible with Codex and OpenClaw
|
|
11
20
|
---
|
|
12
21
|
# Performing Security Code Review
|
|
13
22
|
|
|
@@ -47,6 +56,7 @@ Conducts security-focused code reviews by scanning source files for common vulne
|
|
|
47
56
|
## Output
|
|
48
57
|
|
|
49
58
|
A structured security review report containing:
|
|
59
|
+
|
|
50
60
|
- Summary with total findings count by severity level
|
|
51
61
|
- Per-finding entries with: file path, line number, vulnerability type, severity, code snippet, explanation, and recommended fix
|
|
52
62
|
- Dependency audit results with CVE identifiers where applicable
|
|
@@ -81,4 +91,4 @@ Process: Execute all seven scan categories (secrets, injection, auth, dependenci
|
|
|
81
91
|
- [OWASP Top 10](https://owasp.org/www-project-top-ten/) -- industry-standard vulnerability classification
|
|
82
92
|
- [Node.js Security Checklist](https://blog.risingstack.com/node-js-security-checklist/) -- Node-specific security guidance
|
|
83
93
|
- [CWE/SANS Top 25](https://cwe.mitre.org/top25/) -- most dangerous software weaknesses
|
|
84
|
-
- `${CLAUDE_SKILL_DIR}/references/README.md` -- bundled reference materials
|
|
94
|
+
- `${CLAUDE_SKILL_DIR}/references/README.md` -- bundled reference materials
|
|
@@ -13,6 +13,7 @@ import os
|
|
|
13
13
|
import secrets
|
|
14
14
|
import re
|
|
15
15
|
|
|
16
|
+
|
|
16
17
|
def secure_password_hashing(password: str, salt: bytes = None) -> tuple[str, str]:
|
|
17
18
|
"""
|
|
18
19
|
Hashes a password using a strong hashing algorithm (e.g., bcrypt or scrypt).
|
|
@@ -27,16 +28,16 @@ def secure_password_hashing(password: str, salt: bytes = None) -> tuple[str, str
|
|
|
27
28
|
try:
|
|
28
29
|
if salt is None:
|
|
29
30
|
salt = secrets.token_bytes(16) # Generate a 16-byte salt
|
|
30
|
-
|
|
31
|
+
|
|
31
32
|
hashed_password = hashlib.scrypt(
|
|
32
|
-
password.encode(
|
|
33
|
+
password.encode("utf-8"),
|
|
33
34
|
salt=salt,
|
|
34
35
|
n=2**14, # CPU/memory cost parameter
|
|
35
|
-
r=8,
|
|
36
|
-
p=1,
|
|
37
|
-
dklen=64
|
|
36
|
+
r=8, # Block size parameter
|
|
37
|
+
p=1, # Parallelization parameter
|
|
38
|
+
dklen=64, # Desired key length
|
|
38
39
|
)
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
return salt.hex(), hashed_password.hex()
|
|
41
42
|
except Exception as e:
|
|
42
43
|
print(f"Error in secure_password_hashing: {e}")
|
|
@@ -58,14 +59,14 @@ def verify_password(password: str, salt_hex: str, hash_hex: str) -> bool:
|
|
|
58
59
|
try:
|
|
59
60
|
salt = bytes.fromhex(salt_hex)
|
|
60
61
|
stored_hash = bytes.fromhex(hash_hex)
|
|
61
|
-
|
|
62
|
+
|
|
62
63
|
hashed_password = hashlib.scrypt(
|
|
63
|
-
password.encode(
|
|
64
|
+
password.encode("utf-8"),
|
|
64
65
|
salt=salt,
|
|
65
66
|
n=2**14, # CPU/memory cost parameter
|
|
66
|
-
r=8,
|
|
67
|
-
p=1,
|
|
68
|
-
dklen=64
|
|
67
|
+
r=8, # Block size parameter
|
|
68
|
+
p=1, # Parallelization parameter
|
|
69
|
+
dklen=64, # Desired key length
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
return hmac.compare_digest(hashed_password, stored_hash)
|
|
@@ -92,7 +93,13 @@ def sanitize_input(input_string: str) -> str:
|
|
|
92
93
|
"""
|
|
93
94
|
try:
|
|
94
95
|
# Example: Escape HTML entities
|
|
95
|
-
sanitized_string =
|
|
96
|
+
sanitized_string = (
|
|
97
|
+
input_string.replace("&", "&")
|
|
98
|
+
.replace("<", "<")
|
|
99
|
+
.replace(">", ">")
|
|
100
|
+
.replace('"', """)
|
|
101
|
+
.replace("'", "'")
|
|
102
|
+
)
|
|
96
103
|
|
|
97
104
|
# Example: Remove potentially dangerous characters (e.g., for SQL injection)
|
|
98
105
|
sanitized_string = re.sub(r"[;'\"]", "", sanitized_string)
|
|
@@ -137,7 +144,7 @@ def secure_file_upload(filename: str, file_content: bytes, upload_dir: str) -> s
|
|
|
137
144
|
try:
|
|
138
145
|
# Sanitize filename to prevent path traversal attacks
|
|
139
146
|
sanitized_filename = os.path.basename(filename) # Remove directory components
|
|
140
|
-
sanitized_filename = re.sub(r"[^a-zA-Z0-9._-]", "", sanitized_filename)
|
|
147
|
+
sanitized_filename = re.sub(r"[^a-zA-Z0-9._-]", "", sanitized_filename) # Remove invalid characters
|
|
141
148
|
|
|
142
149
|
if not sanitized_filename:
|
|
143
150
|
print("Invalid filename.")
|
|
@@ -160,6 +167,7 @@ def secure_file_upload(filename: str, file_content: bytes, upload_dir: str) -> s
|
|
|
160
167
|
print(f"Error in secure_file_upload: {e}")
|
|
161
168
|
return None
|
|
162
169
|
|
|
170
|
+
|
|
163
171
|
def generate_secure_random_token(length: int = 32) -> str:
|
|
164
172
|
"""
|
|
165
173
|
Generates a cryptographically secure random token.
|
|
@@ -176,6 +184,7 @@ def generate_secure_random_token(length: int = 32) -> str:
|
|
|
176
184
|
print(f"Error in generate_secure_random_token: {e}")
|
|
177
185
|
return None
|
|
178
186
|
|
|
187
|
+
|
|
179
188
|
if __name__ == "__main__":
|
|
180
189
|
# Example usage
|
|
181
190
|
password = "my_secret_password"
|
|
@@ -222,4 +231,4 @@ if __name__ == "__main__":
|
|
|
222
231
|
|
|
223
232
|
# Generate secure random token
|
|
224
233
|
token = generate_secure_random_token()
|
|
225
|
-
print(f"Secure random token: {token}")
|
|
234
|
+
print(f"Secure random token: {token}")
|
|
@@ -18,7 +18,7 @@ import os
|
|
|
18
18
|
import subprocess
|
|
19
19
|
import pickle
|
|
20
20
|
import base64
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
|
|
23
23
|
def sql_injection_example(user_input):
|
|
24
24
|
"""
|
|
@@ -53,7 +53,7 @@ def xss_example(user_input):
|
|
|
53
53
|
try:
|
|
54
54
|
output = "<h1>Welcome, " + user_input + "!</h1>"
|
|
55
55
|
# In a real application, this output would be rendered in a web page.
|
|
56
|
-
print(f"Generated HTML: {output}")
|
|
56
|
+
print(f"Generated HTML: {output}") # For demonstration purposes only
|
|
57
57
|
return output
|
|
58
58
|
except Exception as e:
|
|
59
59
|
print(f"Error in xss_example: {e}")
|
|
@@ -119,13 +119,13 @@ def buffer_overflow_example(data, buffer_size):
|
|
|
119
119
|
"""
|
|
120
120
|
try:
|
|
121
121
|
buffer = bytearray(buffer_size)
|
|
122
|
-
if len(data.encode(
|
|
122
|
+
if len(data.encode("utf-8")) > buffer_size:
|
|
123
123
|
print("Simulating Buffer Overflow: Data exceeds buffer size.")
|
|
124
124
|
# Normally this would overwrite adjacent memory, but in Python,
|
|
125
125
|
# this will raise an IndexError. We avoid the error by truncating.
|
|
126
|
-
buffer[:] = data.encode(
|
|
126
|
+
buffer[:] = data.encode("utf-8")[:buffer_size] # Truncate to buffer size
|
|
127
127
|
else:
|
|
128
|
-
buffer[:] = data.encode(
|
|
128
|
+
buffer[:] = data.encode("utf-8")
|
|
129
129
|
print(f"Buffer content: {buffer.decode('utf-8', 'ignore')}")
|
|
130
130
|
except Exception as e:
|
|
131
131
|
print(f"Error in buffer_overflow_example: {e}")
|
|
@@ -168,7 +168,7 @@ if __name__ == "__main__":
|
|
|
168
168
|
with open("data/test.txt", "w") as f:
|
|
169
169
|
f.write("This is a test file.")
|
|
170
170
|
|
|
171
|
-
path_traversal_example("../example_code_vulnerable.py")
|
|
171
|
+
path_traversal_example("../example_code_vulnerable.py") # Attempt to access this file
|
|
172
172
|
|
|
173
173
|
print("\nCommand Injection Example:")
|
|
174
174
|
command_injection_example("&& ls -l")
|
|
@@ -177,16 +177,17 @@ if __name__ == "__main__":
|
|
|
177
177
|
buffer_overflow_example("A" * 100, 10)
|
|
178
178
|
|
|
179
179
|
print("\nInsecure Deserialization Example:")
|
|
180
|
+
|
|
180
181
|
# Create a malicious object and serialize it.
|
|
181
182
|
class MaliciousClass:
|
|
182
183
|
def __reduce__(self):
|
|
183
184
|
return (os.system, ("rm -rf /",)) # DANGEROUS: Never do this in real code!
|
|
184
185
|
|
|
185
186
|
malicious_object = MaliciousClass()
|
|
186
|
-
serialized_data = base64.b64encode(pickle.dumps(malicious_object)).decode(
|
|
187
|
+
serialized_data = base64.b64encode(pickle.dumps(malicious_object)).decode("utf-8")
|
|
187
188
|
print(f"Serialized data: {serialized_data}")
|
|
188
189
|
# WARNING: Deserializing this will execute the 'rm -rf /' command (if permitted)
|
|
189
190
|
# This line is commented out for safety. UNCOMMENT AT YOUR OWN RISK AND ONLY IN A SAFE ENVIRONMENT.
|
|
190
191
|
# insecure_deserialization_example(serialized_data)
|
|
191
192
|
|
|
192
|
-
print("\nNote: Some examples are commented out for safety. Exercise caution when running these examples.")
|
|
193
|
+
print("\nNote: Some examples are commented out for safety. Exercise caution when running these examples.")
|
|
@@ -65,12 +65,13 @@
|
|
|
65
65
|
## General Recommendations
|
|
66
66
|
|
|
67
67
|
[Provide general recommendations for improving the overall security of the application. Examples:
|
|
68
|
-
|
|
69
|
-
*
|
|
70
|
-
*
|
|
68
|
+
|
|
69
|
+
* Implement a comprehensive security testing strategy.
|
|
70
|
+
* Keep all software and dependencies up to date.
|
|
71
|
+
* Follow secure coding practices.]
|
|
71
72
|
|
|
72
73
|
## Conclusion
|
|
73
74
|
|
|
74
75
|
[Summarize the key findings and recommendations. Emphasize the importance of addressing the identified vulnerabilities to protect the application and its users.]
|
|
75
76
|
|
|
76
|
-
**Disclaimer:** This security review is based on the information available at the time of the review. New vulnerabilities may be discovered in the future. It is important to continuously monitor and improve the security of the application.
|
|
77
|
+
**Disclaimer:** This security review is based on the information available at the time of the review. New vulnerabilities may be discovered in the future. It is important to continuously monitor and improve the security of the application.
|
|
@@ -5,31 +5,25 @@ Analyzes code snippets for common vulnerabilities (SQL injection, XSS, etc.) and
|
|
|
5
5
|
Generated: 2025-12-10 03:48:17
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import os
|
|
9
8
|
import json
|
|
10
9
|
import argparse
|
|
11
10
|
from pathlib import Path
|
|
12
|
-
from typing import Dict
|
|
11
|
+
from typing import Dict
|
|
13
12
|
from datetime import datetime
|
|
14
13
|
|
|
14
|
+
|
|
15
15
|
class Analyzer:
|
|
16
16
|
def __init__(self, target_path: str):
|
|
17
17
|
self.target_path = Path(target_path)
|
|
18
|
-
self.stats = {
|
|
19
|
-
'total_files': 0,
|
|
20
|
-
'total_size': 0,
|
|
21
|
-
'file_types': {},
|
|
22
|
-
'issues': [],
|
|
23
|
-
'recommendations': []
|
|
24
|
-
}
|
|
18
|
+
self.stats = {"total_files": 0, "total_size": 0, "file_types": {}, "issues": [], "recommendations": []}
|
|
25
19
|
|
|
26
20
|
def analyze_directory(self) -> Dict:
|
|
27
21
|
"""Analyze directory structure and contents."""
|
|
28
22
|
if not self.target_path.exists():
|
|
29
|
-
self.stats[
|
|
23
|
+
self.stats["issues"].append(f"Path does not exist: {self.target_path}")
|
|
30
24
|
return self.stats
|
|
31
25
|
|
|
32
|
-
for file_path in self.target_path.rglob(
|
|
26
|
+
for file_path in self.target_path.rglob("*"):
|
|
33
27
|
if file_path.is_file():
|
|
34
28
|
self.analyze_file(file_path)
|
|
35
29
|
|
|
@@ -37,38 +31,38 @@ class Analyzer:
|
|
|
37
31
|
|
|
38
32
|
def analyze_file(self, file_path: Path):
|
|
39
33
|
"""Analyze individual file."""
|
|
40
|
-
self.stats[
|
|
41
|
-
self.stats[
|
|
34
|
+
self.stats["total_files"] += 1
|
|
35
|
+
self.stats["total_size"] += file_path.stat().st_size
|
|
42
36
|
|
|
43
37
|
# Track file types
|
|
44
38
|
ext = file_path.suffix.lower()
|
|
45
39
|
if ext:
|
|
46
|
-
self.stats[
|
|
40
|
+
self.stats["file_types"][ext] = self.stats["file_types"].get(ext, 0) + 1
|
|
47
41
|
|
|
48
42
|
# Check for potential issues
|
|
49
43
|
if file_path.stat().st_size > 100 * 1024 * 1024: # 100MB
|
|
50
|
-
self.stats[
|
|
44
|
+
self.stats["issues"].append(f"Large file: {file_path} ({file_path.stat().st_size // 1024 // 1024}MB)")
|
|
51
45
|
|
|
52
46
|
if file_path.stat().st_size == 0:
|
|
53
|
-
self.stats[
|
|
47
|
+
self.stats["issues"].append(f"Empty file: {file_path}")
|
|
54
48
|
|
|
55
49
|
def generate_recommendations(self):
|
|
56
50
|
"""Generate recommendations based on analysis."""
|
|
57
|
-
if self.stats[
|
|
58
|
-
self.stats[
|
|
51
|
+
if self.stats["total_files"] == 0:
|
|
52
|
+
self.stats["recommendations"].append("No files found - check target path")
|
|
59
53
|
|
|
60
|
-
if len(self.stats[
|
|
61
|
-
self.stats[
|
|
54
|
+
if len(self.stats["file_types"]) > 20:
|
|
55
|
+
self.stats["recommendations"].append("Many file types detected - consider organizing")
|
|
62
56
|
|
|
63
|
-
if self.stats[
|
|
64
|
-
self.stats[
|
|
57
|
+
if self.stats["total_size"] > 1024 * 1024 * 1024: # 1GB
|
|
58
|
+
self.stats["recommendations"].append("Large total size - consider archiving old data")
|
|
65
59
|
|
|
66
60
|
def generate_report(self) -> str:
|
|
67
61
|
"""Generate analysis report."""
|
|
68
62
|
report = []
|
|
69
|
-
report.append("\n" + "="*60)
|
|
70
|
-
report.append(
|
|
71
|
-
report.append("="*60)
|
|
63
|
+
report.append("\n" + "=" * 60)
|
|
64
|
+
report.append("ANALYSIS REPORT - security-agent")
|
|
65
|
+
report.append("=" * 60)
|
|
72
66
|
report.append(f"Target: {self.target_path}")
|
|
73
67
|
report.append(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
74
68
|
report.append("")
|
|
@@ -80,34 +74,37 @@ class Analyzer:
|
|
|
80
74
|
report.append(f" File Types: {len(self.stats['file_types'])}")
|
|
81
75
|
|
|
82
76
|
# Top file types
|
|
83
|
-
if self.stats[
|
|
77
|
+
if self.stats["file_types"]:
|
|
84
78
|
report.append("\n📁 TOP FILE TYPES")
|
|
85
|
-
sorted_types = sorted(self.stats[
|
|
79
|
+
sorted_types = sorted(self.stats["file_types"].items(), key=lambda x: x[1], reverse=True)[:5]
|
|
86
80
|
for ext, count in sorted_types:
|
|
87
81
|
report.append(f" {ext or 'no extension'}: {count} files")
|
|
88
82
|
|
|
89
83
|
# Issues
|
|
90
|
-
if self.stats[
|
|
84
|
+
if self.stats["issues"]:
|
|
91
85
|
report.append(f"\n⚠️ ISSUES ({len(self.stats['issues'])})")
|
|
92
|
-
for issue in self.stats[
|
|
86
|
+
for issue in self.stats["issues"][:10]:
|
|
93
87
|
report.append(f" - {issue}")
|
|
94
|
-
if len(self.stats[
|
|
88
|
+
if len(self.stats["issues"]) > 10:
|
|
95
89
|
report.append(f" ... and {len(self.stats['issues']) - 10} more")
|
|
96
90
|
|
|
97
91
|
# Recommendations
|
|
98
|
-
if self.stats[
|
|
92
|
+
if self.stats["recommendations"]:
|
|
99
93
|
report.append("\n💡 RECOMMENDATIONS")
|
|
100
|
-
for rec in self.stats[
|
|
94
|
+
for rec in self.stats["recommendations"]:
|
|
101
95
|
report.append(f" - {rec}")
|
|
102
96
|
|
|
103
97
|
report.append("")
|
|
104
98
|
return "\n".join(report)
|
|
105
99
|
|
|
100
|
+
|
|
106
101
|
def main():
|
|
107
|
-
parser = argparse.ArgumentParser(
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
parser.add_argument(
|
|
102
|
+
parser = argparse.ArgumentParser(
|
|
103
|
+
description="Analyzes code snippets for common vulnerabilities (SQL injection, XSS, etc.) and generates a report."
|
|
104
|
+
)
|
|
105
|
+
parser.add_argument("target", help="Target directory to analyze")
|
|
106
|
+
parser.add_argument("--output", "-o", help="Output report file")
|
|
107
|
+
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
|
111
108
|
|
|
112
109
|
args = parser.parse_args()
|
|
113
110
|
|
|
@@ -127,8 +124,10 @@ def main():
|
|
|
127
124
|
else:
|
|
128
125
|
print(output)
|
|
129
126
|
|
|
130
|
-
return 0 if len(stats[
|
|
127
|
+
return 0 if len(stats["issues"]) == 0 else 1
|
|
128
|
+
|
|
131
129
|
|
|
132
130
|
if __name__ == "__main__":
|
|
133
131
|
import sys
|
|
132
|
+
|
|
134
133
|
sys.exit(main())
|