@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 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,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentsolutionsio/security-agent",
3
- "version": "1.0.0",
3
+ "version": "1.0.5",
4
4
  "description": "Specialized security review subagent",
5
5
  "keywords": [
6
6
  "security",
@@ -1,13 +1,22 @@
1
1
  ---
2
2
  name: performing-security-code-review
3
- description: |
4
- Execute this skill enables AI assistant to conduct a security-focused code review using the security-agent plugin. it analyzes code for potential vulnerabilities like sql injection, xss, authentication flaws, and insecure dependencies. AI assistant uses this skill wh... Use when assessing security or running audits. Trigger with phrases like 'security scan', 'audit', or 'vulnerability'.
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
- compatible-with: claude-code, codex, openclaw
10
- tags: [example, security, authentication, audit]
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('utf-8'),
33
+ password.encode("utf-8"),
33
34
  salt=salt,
34
35
  n=2**14, # CPU/memory cost parameter
35
- r=8, # Block size parameter
36
- p=1, # Parallelization parameter
37
- dklen=64 # Desired key length
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('utf-8'),
64
+ password.encode("utf-8"),
64
65
  salt=salt,
65
66
  n=2**14, # CPU/memory cost parameter
66
- r=8, # Block size parameter
67
- p=1, # Parallelization parameter
68
- dklen=64 # Desired key length
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 = input_string.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;").replace("'", "&#39;")
96
+ sanitized_string = (
97
+ input_string.replace("&", "&amp;")
98
+ .replace("<", "&lt;")
99
+ .replace(">", "&gt;")
100
+ .replace('"', "&quot;")
101
+ .replace("'", "&#39;")
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) # Remove invalid characters
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
- import sys
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}") # For demonstration purposes only
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('utf-8')) > buffer_size:
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('utf-8')[:buffer_size] # Truncate to buffer size
126
+ buffer[:] = data.encode("utf-8")[:buffer_size] # Truncate to buffer size
127
127
  else:
128
- buffer[:] = data.encode('utf-8')
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") # Attempt to access this file
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('utf-8')
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
- * Implement a comprehensive security testing strategy.
69
- * Keep all software and dependencies up to date.
70
- * Follow secure coding practices.]
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.
@@ -1,4 +1,3 @@
1
1
  # References
2
2
 
3
3
  Bundled resources for security-agent skill
4
-
@@ -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, List
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['issues'].append(f"Path does not exist: {self.target_path}")
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['total_files'] += 1
41
- self.stats['total_size'] += file_path.stat().st_size
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['file_types'][ext] = self.stats['file_types'].get(ext, 0) + 1
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['issues'].append(f"Large file: {file_path} ({file_path.stat().st_size // 1024 // 1024}MB)")
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['issues'].append(f"Empty file: {file_path}")
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['total_files'] == 0:
58
- self.stats['recommendations'].append("No files found - check target path")
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['file_types']) > 20:
61
- self.stats['recommendations'].append("Many file types detected - consider organizing")
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['total_size'] > 1024 * 1024 * 1024: # 1GB
64
- self.stats['recommendations'].append("Large total size - consider archiving old data")
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(f"ANALYSIS REPORT - security-agent")
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['file_types']:
77
+ if self.stats["file_types"]:
84
78
  report.append("\n📁 TOP FILE TYPES")
85
- sorted_types = sorted(self.stats['file_types'].items(), key=lambda x: x[1], reverse=True)[:5]
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['issues']:
84
+ if self.stats["issues"]:
91
85
  report.append(f"\n⚠️ ISSUES ({len(self.stats['issues'])})")
92
- for issue in self.stats['issues'][:10]:
86
+ for issue in self.stats["issues"][:10]:
93
87
  report.append(f" - {issue}")
94
- if len(self.stats['issues']) > 10:
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['recommendations']:
92
+ if self.stats["recommendations"]:
99
93
  report.append("\n💡 RECOMMENDATIONS")
100
- for rec in self.stats['recommendations']:
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(description="Analyzes code snippets for common vulnerabilities (SQL injection, XSS, etc.) and generates a report.")
108
- parser.add_argument('target', help='Target directory to analyze')
109
- parser.add_argument('--output', '-o', help='Output report file')
110
- parser.add_argument('--json', action='store_true', help='Output as JSON')
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['issues']) == 0 else 1
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())