vulnerability-scan
Skill7 starsChanged 21 days agoMITView on GitHub
Run an offensive security audit (OWASP-based) using Semgrep and produce a read-only vulnerability report. Use before committing code to detect Broken Access Control, Injection (SQL/NoSQL/OS/Template), Frontend Security issues (XSS/CSP/HSTS), SSRF, CSRF, missing server-side validation, authentication-flow and anti-automation gaps, and hardcoded secrets or PII exposure. Triggers on requests like "security scan", "vulnerability check", "audit security", "find vulnerabilities", "/maruda:vulnerability-scan", or when asked for an offensive security review of the codebase. Does NOT modify any code — read-only inspection only.
- Reads credentials
1 other project uses a version of this.
Adopted by
Most recently taken up by
- northraystudio/marudaword for word, first seen Oct 22, 2026
Other versions1 project uses a version of this.
Word for word · 1
- northraystudio/maruda★ 7100% same
Edited · 0
None found.
Wrote their own version · 0
None found.
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository13 other files it gives its agents.
- data-validationSkill
- dependency-auditSkill
- gh-batch-runnerSkill
- gh-issue-drafterSkill
- gh-issue-plannerSkill
- gh-issue-resolverSkill
- progress-dashboardSkill
- report-to-issuesSkill
- rule-proposerSkill
- sakuda-triageSkill
- setupSkill
- software-evaluationSkill
- spec-docSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
15 rules. About 0 are common, 6 are distinctive, and 9 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈6
Distinctive · estimate
This author's own choices. Worth a look.
9
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 40
Run the standard rules plus any custom rules that exist.
- Distinctive: line 75
Do not treat the mere presence of a validator, guard, or middleware as proof of safety, and do not trust a success status code or a …
- Very specific: line 93
Look for string concatenation in DB queries ("SELECT * FROM users WHERE id=" + id)
- Very specific: line 94
Look for ORM raw query escapes (executeRawQuery, .raw(), $queryRawUnsafe)
- Distinctive: line 95
Verify all parameterized queries use bound parameters, not f-strings/template literals
- Very specific: line 106
Look for render(userInput) or template(userInput) patterns in Jinja2, Handlebars, Pebble, Twig
- Very specific: line 132
No token or Origin defense, relying only on the browser's default SameSite behavior → Requires Runtime Verification (the outcome depends on …
- Very specific: line 142
Look for .. bypass potential
- Distinctive: line 164
Default, demo, or seeded credentials in migrations, seed files, or config that ship to production
- Very specific: line 169
All endpoints that fetch user-supplied URLs (fetch(req.body.url), requests.get(url))
- Very specific: line 170
Check for internal network bypass potential (169.254.x.x, 10.x.x.x, localhost, file://)
- Very specific: line 183
No allowlist of hosts and schemes (javascript:, data:) before the value is used.
- Very specific: line 222
JSON summary: only when its status is complete, copy its vulnerabilities critical / high / medium / low into dependencies.
- Distinctive: line 248
Use CVSS v3.1 reasoning. Assign Critical / High / Medium / Low based on:
- Distinctive: line 263
Before writing the report, verify:
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Role: Offensive Security Auditor (Defense-Focused)
You perform systematic, evidence-based security audits with an attacker's mindset and a defender's output. Your findings are grounded in specific file/line citations. Every reported vulnerability has a clear attack path, a justified severity, and a concrete remediation recommendation. This is a read-only inspection — you do NOT modify any code.
Phase 1: Scope & Context
1.1 Pre-Scan Checklist
Before scanning, identify:
- Language & runtime — determines which injection patterns and tooling apply
- Trust boundaries — where does untrusted input enter the system? (HTTP params, headers, file uploads, message queues, webhooks)
- Authentication model — JWT, session cookies, API keys, OAuth?
- Data sensitivity — PII, financial data, credentials, health data?
- Deployment context — public internet? internal only? multi-tenant?
Context determines severity weighting. A missing HttpOnly flag on an internal admin tool is Medium; on a public banking app it is High.
1.2 Scan Strategy: Two Layers
Security audits require both automated scanning and manual review. Automated tools miss logic flaws; manual review misses patterns at scale. Do both.
Layer 1 — Automated (Semgrep)
Run the standard rules plus any custom rules that exist. Custom rules come from two places:
- Project rules —
.semgrep/rules/in the target project, written for this codebase (see/maruda:rule-proposer). - maruda shared rules —
rules/next to thisSKILL.md.setup.shcopies them into.semgrep/maruda/so CI runs them too; when that copy exists, use it instead of this directory so the same rule does not run twice.
Pass a rule directory only when it holds rule files — Semgrep fails (exit 7) on a --config directory with no rules, and with no custom rules the scan must behave exactly as before. A rule file is what Semgrep's loader reads: *.yml / *.yaml, except *.test.yml / *.test.yaml fixtures:
# Same check as the "Run Semgrep" step in maruda's harness/templates/github/security-scan.yml — change both together.
has_rules() { find "$1" -type f \( -name '*.yml' -o -name '*.yaml' \) ! -name '*.test.yml' ! -name '*.test.yaml' 2>/dev/null | grep -q .; }
extra=()
has_rules .semgrep && extra+=(--config .semgrep)
if ! has_rules .semgrep/maruda && has_rules "<this skill's directory>/rules"; then
extra+=(--config "<this skill's directory>/rules")
fi
semgrep --config auto ${extra[@]+"${extra[@]}"} --json --output semgrep-results.json .Rules live next to their semgrep --test fixtures (<name>.yml + <name>.<ext>); .semgrepignore excludes .semgrep/ so the intentionally vulnerable fixtures are not reported as findings. Conventions and a template: docs/semgrep-rules.md in the maruda repository.
Parse results and triage each finding as True Positive or False Positive (see triage guide in Phase 2).
Layer 2 — Manual Review After automated scanning, manually inspect the following high-risk areas that tools routinely miss. Scanners — static and dynamic — miss them because the signal is business meaning, cross-request state, timing, or an input point that exists only in client code, not a single pattern or a single request/response. Phase 2 lists the code-level signals for each:
- Authorization logic (can user A access user B's resources?) — 2.2
- Business logic flows (can a free user access paid features?) and server-side input range/integrity — 2.2
- CSRF and file upload controls — 2.2
- Authentication flows (credential changes, reset, default credentials) — 2.3
- Client-side parameters flowing into external content — 2.5
- Anti-automation: rate limiting, CAPTCHA, race conditions in state-changing operations — 2.7
- Cryptographic implementation choices
- Third-party dependencies are not reviewed here — they are delegated to
dependency-audit(2.8)
Evidence principle — a check that exists is not a check that enforces
Do not treat the mere presence of a validator, guard, or middleware as proof of safety, and do not trust a success status code or a self-reported "validated/handled" flag. Trace the real effect in the code:
- Enforced — the check runs on the path before the write or side effect, and its failing branch rejects: returns a 4xx, throws, or returns early. → not a finding.
- Not enforced — the failing branch only logs, sets a flag, adds a warning, or falls through; or the check runs after the record is persisted or the outbound request is sent; or it guards one route while a sibling route (bulk, import, admin, older API version) writes the same field unchecked. → True Positive; cite both the check and the unguarded write.
- Success with an abnormal side effect — the handler returns 2xx while persisting an out-of-range, unauthorized, or cross-tenant value. → True Positive; a success response is not evidence of safety.
- Undecidable from code — the outcome depends on runtime configuration, browser behavior, timing, or an out-of-band callback (browser
SameSitedefaults, proxy or multi-instance rate limiting, blind SSRF, races). → neither True Positive nor False Positive; record it under Requires Runtime Verification with the code-level signal and the dynamic check that would settle it.
Requires Runtime Verification is not a way to dismiss a finding: when the code alone is conclusive, it is a True Positive.
Phase 2: Vulnerability Checklist
Work through each category. For each, describe: what you searched for, what you found, and whether each finding is a true positive, a false positive, or requires runtime verification.
2.1 Injection (OWASP A03)
SQL Injection
- Look for string concatenation in DB queries (
"SELECT * FROM users WHERE id=" + id) - Look for ORM raw query escapes (
executeRawQuery,.raw(),$queryRawUnsafe) - Verify all parameterized queries use bound parameters, not f-strings/template literals
NoSQL Injection
- MongoDB: user-controlled objects passed directly to
find(),$whereclauses - Redis: user input in
EVALcommands
OS Command Injection
exec(),spawn(),system(),subprocesswith shell=True and user input- Template engines: user-controlled template strings (
eval,render(userInput))
SSTI (Server-Side Template Injection)
- Look for
render(userInput)ortemplate(userInput)patterns in Jinja2, Handlebars, Pebble, Twig
2.2 Broken Access Control (OWASP A01)
IDOR / BOLA
- Resource endpoints that take an ID parameter: does the handler verify the caller owns the resource?
- Pattern:
GET /api/documents/:id— does it checkdocument.userId === req.user.id?
Privilege Escalation / Mass Assignment
- Role checks: are they enforced server-side or only client-side?
- Admin endpoints: are they protected by middleware or just by convention?
- Mass assignment: Do create/update endpoints accept and honor unexpected fields such as
role,isAdmin,userId,ownerId,price, orstatusfrom the request body? Test by adding them. - JWT claims: Does the server trust
roleor similar claims from the JWT without re-fetching the user's actual permissions from the database (or equivalent trusted store)? - Forced browsing: Are administrative routes protected only by client-side routing, or is there a server-side authorization check?
Server-Side Input Validation & Business Logic
- Constraints that exist only in the UI (
min/max/maxlength, select options, disabled or hidden fields, client-side form schemas) with no matching server-side check — compare the form against the handler - Range, enum, length, required, and cross-field consistency (
quantity > 0, a rating within its scale, start ≤ end) validated before the write, and rejected — not just logged (Evidence principle) - Prices, totals, discounts, coupon effects, and membership conditions recalculated server-side from trusted data, never read from the request
- A validation schema applied on create but not on update/patch, or on one route but not on bulk/import routes
CSRF
- Cookie-authenticated, state-changing routes (POST/PUT/PATCH/DELETE): is there an anti-CSRF token check or
Origin/Referervalidation, applied as middleware and deny-by-default? - State changes reachable via
GET— they bypass most CSRF defenses andSameSite=Lax - Session cookie
SameSiteset explicitly;SameSite=Nonerequires a token defense - APIs authenticated only by a bearer header are not CSRF-prone — document as False Positive with that reason
- No token or
Origindefense, relying only on the browser's defaultSameSitebehavior → Requires Runtime Verification (the outcome depends on the browser and the deployment)
File Upload Controls
- Size limit enforced server-side (body parser / multipart limit), not only by the
acceptattribute or a client check - Type decided by content (magic bytes) or a server allowlist, not by the client
Content-Typeor extension alone - Stored filename generated server-side; the user-supplied name is never joined into a path (see Path Traversal)
- Uploads stored outside the web root, or served with
Content-Disposition: attachmentandnosniff; archives and XML parsed with limits (zip slip, XXE)
Path Traversal
- User-supplied filenames used in
fs.readFile(),open(),Path()without sanitization - Look for
..bypass potential
2.3 Cryptographic & Auth Failures (OWASP A02, A07)
Secrets & Credentials
- Hardcoded passwords, API keys, tokens in source code
.envfiles committed to the repo- Secrets in log statements
Weak Cryptography
- MD5 or SHA-1 used for password hashing (must be bcrypt/argon2/scrypt)
- Predictable random:
Math.random()orrandom.random()for security tokens - JWT:
alg: noneaccepted, HS256 with weak secret, RS256 public key confusion
Session Management
- Cookie flags:
HttpOnly,Secure,SameSite=Strict/Lax - Session token entropy (< 128 bits is weak)
- Missing session invalidation on logout
Authentication Flows
- Password, email, or MFA changes require the current password or recent re-authentication — check the handler, not the form
- Password reset tokens: random, single-use, expiring, bound to one user; responses do not reveal whether an account exists
- Default, demo, or seeded credentials in migrations, seed files, or config that ship to production
- Password policy and lockout enforced server-side
2.4 SSRF (OWASP A10)
- All endpoints that fetch user-supplied URLs (
fetch(req.body.url),requests.get(url)) - Check for internal network bypass potential (169.254.x.x, 10.x.x.x, localhost,
file://) - DNS rebinding surface area
- Blind SSRF (the fetched response never reaches the user): the sink is visible in code, but reachability needs an out-of-band callback → Requires Runtime Verification unless an allowlist is conclusively missing
2.5 Frontend Security (XSS / CSP / Headers)
XSS
- DOM sinks:
innerHTML,document.write(),eval(),dangerouslySetInnerHTML - React/Vue: are user inputs ever rendered as raw HTML?
- Reflected input in error messages
Client-Side Input → External Content
- Front-end code that reads
location.search,location.hash, router params, orpostMessagedata and passes it toiframe.src,script.src,fetch()/XHR URLs,href,window.location, or an HTML sink - No allowlist of hosts and schemes (
javascript:,data:) before the value is used;postMessagehandlers that do not checkevent.origin - These input points exist only in client code — no server response reveals them, so black-box crawling cannot even enumerate them. Read the front-end source.
Security Headers
Content-Security-Policy— present and restrictive?Strict-Transport-Security— present withincludeSubDomains?X-Frame-Optionsorframe-ancestorsin CSPX-Content-Type-Options: nosniff
CORS
Access-Control-Allow-Origin: *on authenticated endpoints- Credentialed requests with wildcard origin
2.6 Data Exposure & Logging (OWASP A02, A09)
- PII (email, phone, SSN, address) logged at INFO or DEBUG level
- Passwords, tokens, or secrets in log output
- Stack traces exposed in API error responses (leaks internal paths/versions)
- Overly verbose error messages revealing DB schema or framework details
2.7 Anti-Automation & Race Conditions (OWASP A04, A07)
- Rate limiting on login, registration, password reset, OTP verification, and abuse-prone actions (posting, coupon redemption, sending email/SMS) — present in middleware or config, keyed on something an attacker cannot rotate freely
- CAPTCHA or challenge: the answer is never sent to the client, is verified server-side, and is single-use and bound to the session
- "Once only" invariants (redeem a coupon, vote once, withdraw a balance, claim a reward) implemented as check-then-act across separate queries, without a transaction, unique constraint, atomic conditional update (
UPDATE ... WHERE balance >= :amount), or row lock → TOCTOU race - Whether a limiter holds in production (in-memory store across instances, proxy or CDN in front) and whether a race window is reachable depend on runtime → Requires Runtime Verification when the code alone cannot decide
2.8 Dependency Vulnerabilities (OWASP A06)
Delegate to /maruda:dependency-audit on the same path. It owns the scope, the pinned
scanner, and the "not run" rules — do not run audit tools from this skill. Run it every time;
do not reuse an earlier report — the lockfiles may have changed since, and the audit takes
minutes.
From its output:
- Report: the "Dependency Audit" section links the dependency-audit report and states its status and counts. Dependency upgrades are not listed in this report's Remediation Priority — they are filed from the dependency-audit report, so the same upgrade never becomes two Issues.
- JSON summary: only when its status is
complete, copy itsvulnerabilitiescritical / high / medium / low intodependencies. When it ispartialornot-run, omitdependenciesand say why in the report (name the targets that were not run). Counts from a partial audit would read as a clean result for the targets that were never scanned; the dependency-audit's own JSON keeps them with their status.
Phase 3: Triage — True Positive vs. False Positive
For each Semgrep finding and each manual-review candidate, apply this decision logic before including it in the report:
| Question | If Yes | If No |
| Is the flagged input actually user-controllable? | Continue | Likely FP — document why |
| Does the data flow reach the vulnerable sink without sanitization? | Continue | Likely FP |
| Is the pattern in test code only? | FP — note location | Continue |
| Can the outcome be decided from the code alone? | Continue | Requires Runtime Verification — neither TP nor FP |
| Does existing validation/sanitization fully neutralize the risk — and does its failing branch reject before the write or side effect, rather than only log or flag (Evidence principle)? | FP — document the mitigation and where it rejects | True Positive — cite the check and the unguarded write |
Document every False Positive with the reason. An unexplained FP dismissal is a red flag in any audit.
Phase 4: Severity Scoring
Use CVSS v3.1 reasoning. Assign Critical / High / Medium / Low based on:
| Severity | Criteria | Examples |
| Critical | Unauthenticated RCE, full data breach of all users, authentication bypass | SQLi on login endpoint, OS command injection via public API, hardcoded admin password |
| High | Authenticated RCE, access to other users' sensitive data, privilege escalation to admin | IDOR on payment records, SSRF with internal network access, stored XSS in admin panel |
| Medium | Limited data exposure, requires chaining with other issues, authenticated low-impact | Reflected XSS (requires user interaction), missing HttpOnly, verbose error messages |
| Low | Defense-in-depth gap, no direct exploitability | Missing X-Content-Type-Options, weak (not broken) crypto, informational disclosure |
Severity floor rule: Any finding involving production credentials, authentication bypass, or RCE is minimum High, regardless of other factors.
Phase 5: Quality Gate
Before writing the report, verify:
- Semgrep scan was run on the correct target path
- Every finding has been triaged (True Positive, documented False Positive, or Requires Runtime Verification)
dependency-auditwas run for this scan and its status is stated — any status other thancompleteomitsdependenciesfrom the JSON, with the reason in the report- Manual review covered every Layer 2 area: authorization, business logic and server-side validation, CSRF, file upload, authentication flows, client-side input, and anti-automation
- Every "validated / guarded" conclusion was checked against the Evidence principle — the check rejects before the write, not merely logs or flags
- Items that cannot be decided from code are listed under Requires Runtime Verification, with the dynamic check that settles each, and are not counted in the findings totals
- Every True Positive has a specific
file:linecitation - No severity is assigned without justification
- Remediation recommendations are specific (not "sanitize inputs")
- Output file saved as
docs/security-audit/[target].YYYYMMDD.md - JSON summary file is saved alongside the report:
docs/security-audit/[target].YYYYMMDD.json
JSON Summary Output
In addition to the Markdown report, produce a machine-readable JSON summary for dashboard consumption. Save it as docs/security-audit/[target].YYYYMMDD.json.
{
"schemaVersion": "1.0",
"type": "vulnerability-scan",
"date": "YYYY-MM-DD",
"target": "[target]",
"scope": "[path]",
"stack": "[language/framework]",
"findings": {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0,
"total": 0,
"falsePositives": 0
},
"dependencies": {
"critical": 0,
"high": 0,
"medium": 0,
"low": 0
},
"remediation": {
"p0": 0,
"p1": 0
},
"topFindings": [
{
"id": "V-01",
"title": "short title",
"severity": "Critical | High | Medium | Low",
"location": "file:line"
}
]
}See
examples/progress-dashboard/security-audit/for sample files.
Output Template
# Security Audit: [Target] — YYYY-MM-DD
> Scope: `[path]` | Stack: [language/framework] | Auditor: Claude vulnerability-scan skill
> Read-only inspection — no code was modified.
---
## Executive Summary
[2–3 sentences: overall security posture, highest-severity finding, and immediate action required.]
**Critical/High findings requiring immediate action:** [N]
**Total findings:** [N true positives] | [N false positives discarded]
**Requires runtime verification:** [N] (not counted in the totals — see that section)
---
## Finding Summary
| ID | Title | Severity | Location | Status |
|----|-------|----------|----------|--------|
| V-01 | SQL Injection in user search | Critical | `src/api/users.ts:87` | True Positive |
| V-02 | Missing HttpOnly on session cookie | Medium | `src/middleware/auth.ts:23` | True Positive |
| V-03 | Raw query flag in test fixture | — | `tests/fixtures/db.ts:12` | False Positive |
---
## Findings
### V-01 — SQL Injection in user search
**Severity**: Critical | **Location**: `src/api/users.ts:87` | **Confidence**: High
#### Vulnerable Code
```typescript
// src/api/users.ts:87
const results = await db.query(`SELECT * FROM users WHERE name = '${req.query.name}'`);
```
#### Attack Path
1. Attacker sends `GET /api/users?name=' OR '1'='1`
2. Query becomes `SELECT * FROM users WHERE name = '' OR '1'='1'` — returns all users
3. With `UNION SELECT` the attacker can extract password hashes, email addresses, or other table data
4. **Impact**: Full user table exposure; potential credential theft
#### Risk Assessment
- **Severity**: Critical (unauthenticated data breach of all users)
- **CVSS v3.1 estimate**: AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N (~9.1)
- **Impact Area**: API → Database
- **Confidence**: High — confirmed user-controlled input reaches raw query
#### Recommended Fix
Use parameterized queries. Never interpolate user input into SQL strings:
```typescript
// Secure pattern — parameterized query
const results = await db.query('SELECT * FROM users WHERE name = $1', [req.query.name]);
```
Apply to all DB queries in `src/api/` and `src/services/`. Audit any `.raw()` or `$queryRawUnsafe` ORM calls.
---
### V-02 — Missing HttpOnly on session cookie
**Severity**: Medium | **Location**: `src/middleware/auth.ts:23` | **Confidence**: High
#### Vulnerable Code
```typescript
// src/middleware/auth.ts:23
res.cookie('session', token, { secure: true, sameSite: 'lax' });
// Missing: httpOnly: true
```
#### Attack Path
1. If any XSS vulnerability exists (now or in future), attacker's script can read `document.cookie`
2. Session token exfiltrated to attacker-controlled server
3. **Impact**: Session hijacking — attacker authenticates as victim
#### Risk Assessment
- **Severity**: Medium (requires XSS to chain; defense-in-depth gap)
- **Impact Area**: Frontend → Authentication
- **Confidence**: High
#### Recommended Fix
Add `httpOnly: true` to all authentication cookies:
```typescript
res.cookie('session', token, { secure: true, sameSite: 'lax', httpOnly: true });
```
---
## False Positives
### FP-01 — Raw query in test fixture (`tests/fixtures/db.ts:12`)
**Reason**: This file is only loaded in test environments (`NODE_ENV=test`). The "raw query" is seeding test data with static strings — no user input is involved. Not exploitable.
---
## Requires Runtime Verification
Static review found the code-level signal but cannot decide the outcome (Evidence principle). These are not counted in the findings totals; hand them to dynamic testing (DAST) or manual verification.
| ID | Area | Code-level signal | Dynamic check that settles it |
|----|------|-------------------|-------------------------------|
| RV-01 | CSRF | `src/app.ts:40` — cookie session, no anti-CSRF token or `Origin` check; relies on the browser's default `SameSite` | Submit the state-changing request from another origin in the target browsers; does the change persist? |
| *(or "None")* | | | |
---
## Dependency Audit
Delegated to `dependency-audit` — see [`docs/dependency-audit/[target].YYYYMMDD.md`](../dependency-audit/[target].YYYYMMDD.md).
**Status:** complete | partial ([targets not run]) | not run ([reason])
**Advisories:** Critical N · High N · Medium N · Low N
**Upgrades to schedule:** P0 N · P1 N — file them from the dependency-audit report.
---
## Remediation Priority
| Priority | Finding | Action |
|----------|---------|--------|
| **P0 — Fix before next deployment** | V-01 SQL Injection | Parameterize all DB queries in `src/api/` |
| **P1 — Fix this sprint** | V-02 Missing HttpOnly | Add `httpOnly: true` to session cookie config |Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

