# Security Scanner

> This skill should be used when "check for security issues", "security review", "scan for vulnerabilities", "find security bugs", "audit this code for security", "check for SQL injection", "check for XSS", "find hardcoded secrets", "security audit".

- Skill: `kwokyc/security-scanner` (Agent Skill)
- Install (CLI): `npx skillmds@latest add kwokyc/security-scanner`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kwokyc/security-scanner/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Data & Analytics
- Author: KwokYC (https://skillmd.com/u/kwokyc)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/kwokyc/security-scanner

---


# Security Scanner

Scan code for common security vulnerabilities. Follow OWASP Top 10 checklist. Provide specific fixes, not vague warnings.

## Scan Categories

### 1. Injection Attacks

#### SQL Injection
```python
# ❌ CRITICAL: String interpolation in SQL
query = f"SELECT * FROM users WHERE id = {user_id}"
query = "SELECT * FROM users WHERE id = " + user_id
query = "SELECT * FROM users WHERE id = '%s'" % user_id

# ✅ Parameterized query
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
```

```javascript
// ❌ CRITICAL: Template literal in SQL
const query = `SELECT * FROM users WHERE id = ${userId}`;

// ✅ Parameterized
const query = "SELECT * FROM users WHERE id = $1";
const result = await db.query(query, [userId]);
```

#### Command Injection
```python
# ❌ CRITICAL: User input in shell command
os.system(f"convert {filename} output.png")
subprocess.call(f"grep {pattern} {file}", shell=True)

# ✅ Use argument list, no shell
subprocess.run(["convert", filename, "output.png"])
subprocess.run(["grep", pattern, file])
```

#### NoSQL Injection
```javascript
// ❌ CRITICAL: User object passed directly to query
db.collection("users").find({ id: req.body.id });

// ✅ Validate and sanitize
const id = sanitize(req.body.id);
db.collection("users").find({ id: new ObjectId(id) });
```

### 2. Cross-Site Scripting (XSS)

```javascript
// ❌ CRITICAL: Unescaped user input in HTML
element.innerHTML = userInput;
document.write(userContent);
dangerouslySetInnerHTML={{ __html: userData }}

// ✅ Escape or use textContent
element.textContent = userInput;
// React: JSX auto-escapes
<div>{userInput}</div>
```

```python
# ❌ CRITICAL: Unescaped output in template
return f"<div>{user_input}</div>"

# ✅ Use template engine with auto-escaping
return render_template("page.html", content=user_input)
```

### 3. Authentication & Authorization

```markdown
Check for:
- [ ] Missing authentication on endpoints
- [ ] Missing authorization checks (IDOR)
- [ ] JWT stored in localStorage (XSS risk)
- [ ] JWT not validated (signature, expiry, issuer)
- [ ] Password stored in plain text
- [ ] Weak password requirements
- [ ] No rate limiting on login
- [ ] Session tokens in URLs
```

```python
# ❌ Missing auth check
@app.route("/api/users/<user_id>")
def get_user(user_id):
    return User.query.get(user_id)  # Anyone can access any user!

# ✅ Check authorization
@app.route("/api/users/<user_id>")
@login_required
def get_user(user_id):
    if current_user.id != user_id and not current_user.is_admin:
        abort(403)
    return User.query.get(user_id)
```

### 4. Sensitive Data Exposure

```python
# ❌ CRITICAL: Hardcoded secrets
API_KEY = "sk-abc123def456"
DATABASE_URL = "postgres://admin:password@localhost/db"
SECRET_KEY = "my-super-secret-key"

# ✅ Environment variables
API_KEY = os.environ["API_KEY"]
DATABASE_URL = os.environ["DATABASE_URL"]
SECRET_KEY = os.environ["SECRET_KEY"]
```

```markdown
Scan for patterns:
- API keys: sk-[a-zA-Z0-9]{20,}, AIza[a-zA-Z0-9]{35}
- AWS keys: AKIA[a-zA-Z0-9]{16}
- Private keys: -----BEGIN (RSA |EC )?PRIVATE KEY-----
- Tokens: ghp_[a-zA-Z0-9]{36}, ghs_[a-zA-Z0-9]{36}
- Passwords in URLs: https?://[^:]+:[^@]+@
- Generic secrets: password\s*=\s*["'][^"']+["']
```

### 5. Insecure Deserialization

```python
# ❌ CRITICAL: Unsafe deserialization
import pickle
data = pickle.loads(user_input)  # Remote code execution!

import yaml
data = yaml.load(user_input)  # Default loader is unsafe

# ✅ Safe alternatives
import json
data = json.loads(user_input)

import yaml
data = yaml.safe_load(user_input)
```

### 6. Security Misconfiguration

```markdown
Check for:
- [ ] Debug mode enabled in production
- [ ] CORS set to allow all origins (*)
- [ ] Missing security headers (CSP, HSTS, X-Frame-Options)
- [ ] Verbose error messages exposing internals
- [ ] Default credentials not changed
```

```python
# ❌ Debug in production
app.run(debug=True)

# ❌ Wide-open CORS
CORS(app, origins="*")

# ✅ Environment-aware
app.run(debug=os.environ.get("FLASK_ENV") == "development")

# ✅ Specific origins
CORS(app, origins=["https://myapp.com"])
```

### 7. Path Traversal

```python
# ❌ CRITICAL: Unsanitized file path
@app.route("/files/<path:filename>")
def serve_file(filename):
    return send_file(f"/uploads/{filename}")
# Attack: GET /files/../../etc/passwd

# ✅ Validate and sanitize
@app.route("/files/<path:filename>")
def serve_file(filename):
    safe_path = os.path.normpath(filename)
    if safe_path.startswith(".."):
        abort(400)
    return send_file(os.path.join("/uploads", safe_path))
```

## Severity Levels

```
🔴 CRITICAL: Remote code execution, SQL injection, auth bypass
   → Fix immediately, do not merge without fix

🟡 HIGH: XSS, CSRF, sensitive data exposure, missing auth
   → Fix before merge

🟠 MEDIUM: Missing input validation, weak crypto, verbose errors
   → Fix in this PR if possible

🟢 LOW: Missing security headers, info disclosure
   → Create follow-up issue
```

## Scan Output Format

When scanning code, output findings in this format:

```
## Security Scan Results

### 🔴 CRITICAL
1. **[SQL Injection]** src/api/users.ts:42
   - Raw string interpolation in SQL query
   - Fix: Use parameterized query with $1 placeholder

### 🟡 HIGH
2. **[Missing Auth]** src/api/admin.ts:15
   - Admin endpoint has no authentication check
   - Fix: Add @require_auth decorator

### Summary
- Critical: 1 | High: 2 | Medium: 3 | Low: 1
- Files scanned: 12
- Recommendation: Do not merge until 1 CRITICAL is resolved
```

## Quick Check Commands

```bash
# Scan for hardcoded secrets
grep -rn "password\s*=" --include="*.{py,js,ts,go,java}" .
grep -rn "api_key\s*=" --include="*.{py,js,ts}" .
grep -rn "BEGIN PRIVATE KEY" .

# Check for eval/dangerous functions
grep -rn "eval(" --include="*.{js,ts}" .
grep -rn "pickle.loads" --include="*.py" .
grep -rn "shell=True" --include="*.py" .
grep -rn "innerHTML" --include="*.{js,ts,tsx}" .

# Check for SQL concatenation
grep -rn "f\"SELECT\|f\"INSERT\|f\"UPDATE\|f\"DELETE" --include="*.py" .
grep -rn "\`SELECT\|\`INSERT" --include="*.{js,ts}" .
```

