Imported from valegrajales/agentic-retrieval-example (
.github/skills/atv-security/SKILL.md). Install upstream withnpx skills add valegrajales/agentic-retrieval-example --skill atv-security. Copyright stays with the author.
/atv-security β Unified Security Auditor
Scan your project for security issues across two surfaces:
- Agentic configuration β
.github/,.vscode/configs (33 rules adapted from AgentShield). - Application source code β OWASP Top 10 (2021) static checks + STRIDE threat model.
Heritage: This skill absorbs the former
/csoskill. Old/csotriggers still route here. The on-disk security report file keeps the legacy<!-- cso -->marker block so existing reports stay structurally compatible.
5 config categories: Secrets Β· Permissions Β· Hooks Β· MCP Servers Β· Agents & Skills
Arguments
#$ARGUMENTS
Argument grammar: Two independent axes, parsed in order.
- Mode β if any token in
$ARGUMENTSmatchesfix(case-insensitive), mode =fix. Otherwise mode =report(default). - Scope β examine the remaining tokens (after stripping
fix):- Token
configβ scope =config(config audit only; skip OWASP/STRIDE) - Token
owaspβ scope =owasp(OWASP scan only; skip config + STRIDE) - Token
strideβ scope =stride(STRIDE only; skip config + OWASP) - Token that looks like a file/directory path (contains
/or\or matches an existing path) β scope =<path>(run OWASP/STRIDE narrowed to that path; skip config) - No remaining tokens, or token is
fullβ scope =full(default; run everything available)
- Token
Examples:
/atv-securityβmode=report scope=full/atv-security fixβmode=fix scope=full/atv-security config fixor/atv-security fix configβmode=fix scope=config/atv-security owaspβmode=report scope=owasp/atv-security src/api/βmode=report scope=src/api/
Execution Flow
Phase 1: Discovery β Detect config surfaces + app source stack
Phase 2: Tier 1 Config β Deterministic regex scan of .github/, .vscode/ (skip if scope β {full, config})
Phase 3: Tier 2 Config β LLM-assessed config rules (skip if scope β {full, config})
Phase 4: OWASP Top 10 β Application source code scan (skip if no source OR scope β {full, owasp, <path>})
Phase 5: STRIDE β Threat model the application (skip if no source OR scope β {full, stride, <path>})
Phase 6: Score & Grade β Per-surface grades with N/A semantics
Phase 7: Output β Combined report (config + OWASP + STRIDE)
Phase 8: Persist β Upsert into docs/security/YYYY-MM-DD-security-report.md (both marker blocks)
Phase 9: Fix β Opt-in safe fixes for auto-fixable config rules (only when mode=fix)
Phase 1: Discovery
Use file_search and list_dir to detect both surfaces in parallel.
1a. ATV configuration surfaces
| Surface | File Pattern | Category |
|---|---|---|
| Instructions | .github/copilot-instructions.md |
Agents & Skills |
| MCP Config | .github/copilot-mcp-config.json |
MCP Servers |
| Skills | .github/skills/**/*.md |
Agents & Skills |
| Agents | .github/agents/**/*.agent.md |
Agents & Skills |
| Hooks | .github/hooks/copilot-hooks.json + .github/hooks/scripts/** |
Hooks |
| Setup Steps | .github/copilot-setup-steps.yml |
Hooks |
| VS Code | .vscode/settings.json, .vscode/extensions.json |
Permissions |
Set hasConfig = true if any of the above are found.
1b. Application source stack
| Signal | Stack | Key files to scan |
|---|---|---|
package.json, *.ts, *.js |
Node.js / TypeScript | src/**, routes/**, api/**, pages/** |
requirements.txt, *.py |
Python | app/**, src/**, views/**, api/** |
Gemfile, *.rb |
Ruby / Rails | app/**, config/**, db/** |
go.mod, *.go |
Go | **/*.go |
*.cs, *.csproj |
.NET | **/*.cs, Controllers/** |
pom.xml, *.java |
Java | src/**/*.java |
Set hasSource = true if any application source files are found. If scope is a path, narrow source detection to that path.
Record: list of discovered config files, detected stack, total source files found, entry points identified.
1c. Bail rule
If !hasConfig && !hasSource: report "No ATV configuration or application source detected. Run npx atv-starterkit init to scaffold an agentic environment, or run /atv-security from a project directory." and stop.
If scope=config and !hasConfig: report "Scope config requested but no .github/ directory found." and stop.
If scope β {owasp, stride, <path>} and !hasSource: report "Scope requires application source, but none found." and stop.
Phase 2: Tier 1 Config Scan β Deterministic Detection
Run only when
hasConfig && scope β {full, config}.
Run grep_search with isRegexp: true for each pattern below. For each match, record a finding with the specified fields.
Secrets Rules
| Rule | Pattern | Scope | Severity | Fix |
|---|---|---|---|---|
| SEC-01 | sk-ant-[a-zA-Z0-9]{20,} |
All .github/**, .vscode/** |
π΄ critical | Replace with ${ANTHROPIC_API_KEY} env var reference |
| SEC-02 | sk-proj-[a-zA-Z0-9]{20,} |
All .github/**, .vscode/** |
π΄ critical | Replace with ${OPENAI_API_KEY} env var reference |
| SEC-03 | AKIA[0-9A-Z]{16} |
All .github/**, .vscode/** |
π΄ critical | Replace with ${AWS_ACCESS_KEY_ID} env var reference |
For rules whose regex patterns require alternation, use the entries below instead of markdown table rows so the raw | characters remain valid for isRegexp: true:
- SEC-04
- Pattern:
(?:ghp_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22,}) - Scope: All
.github/**,.vscode/** - Severity: π΄ critical
- Fix: Replace with
${GITHUB_TOKEN}env var reference
- Pattern:
- SEC-05
- Pattern:
(?:Bearer [a-zA-Z0-9_\-\.]{20,}|mongodb(\+srv)?://[^\s]+|postgres(ql)?://[^\s]+|mysql://[^\s]+|redis://[^\s]+) - Scope: All
.github/**,.vscode/** - Severity: π‘ high
- Fix: Replace with
${ENV_VAR}reference appropriate to the service
- Pattern:
MCP Server Rules (grep-detectable)
| Rule | Pattern | Scope | Severity | Fix |
|---|---|---|---|---|
| MCP-02 | "tools"\s*:\s*\["?\*"?\] |
.github/copilot-mcp-config.json |
π‘ high | Scope to specific tools needed: ["tool1", "tool2"] |
| MCP-03 | autoApprove |
.github/copilot-mcp-config.json |
π’ medium | Remove autoApprove or restrict to safe read-only tools |
- MCP-04
- Pattern:
(?:sk-ant-|sk-proj-|AKIA|ghp_|Bearer ) - Scope:
.github/copilot-mcp-config.jsonenv sections - Severity: π΄ critical
- Fix: Use
${input:VAR}or${ENV_VAR}references
- Pattern:
Hook Rules (grep-detectable)
- HOOK-01
- Pattern:
(?:curl.*\$\{|wget.*\$\{|eval.*\$\{) - Scope:
.github/hooks/scripts/** - Severity: π‘ high
- Fix: Validate/sanitize variables before use in network/eval commands
- Pattern:
- HOOK-02
- Pattern:
(?:curl\s+-X\s+POST.*\$|wget\s+--post) - Scope:
.github/hooks/scripts/** - Severity: π΄ critical
- Fix: Remove data exfiltration patterns or restrict to known-safe URLs
- Pattern:
| Rule | Pattern | Scope | Severity | Fix |
|---|---|---|---|---|
| HOOK-03a | 2>/dev/null |
.github/hooks/scripts/** |
π’ medium | Log errors instead of suppressing them silently |
| HOOK-03b | || true$ |
.github/hooks/scripts/** |
π’ medium | Log errors instead of suppressing them silently |
| HOOK-03c | || exit 0$ |
.github/hooks/scripts/** |
π’ medium | Log errors instead of suppressing them silently |
Agent & Skill Rules (grep-detectable)
| Rule | Pattern | Scope | Severity | Fix |
|---|---|---|---|---|
| AGENT-01 | [\u200B\u200C\u200D\uFEFF] |
.github/skills/**, .github/agents/**, .github/copilot-instructions.md |
π΄ critical | Remove zero-width characters β likely hidden instruction injection |
| AGENT-02 | [A-Za-z0-9+/]{80,}={0,2} |
.github/skills/**, .github/agents/** |
π’ medium | Decode and inspect β may contain hidden instructions. Ignore if preceded by sha256:, data:, or http |
Permission Rules (grep-detectable)
| Rule | Pattern | Scope | Severity | Fix |
|---|---|---|---|---|
| PERM-01 | security\.workspace\.trust\.enabled"?\s*:\s*false|chat\.tools\.autoApprove"?\s*:\s*true |
.vscode/settings.json |
π’ medium | Enable workspace trust; disable agent-tool auto-approval |
Execution: For each rule, run grep_search with the pattern and includePattern matching the scope. Record every match as a finding with: rule ID, category, severity, title, file path, matched evidence (truncated to 100 chars), and fix suggestion.
Phase 3: Tier 2 Config Scan β LLM-Assessed Detection
Run only when
hasConfig && scope β {full, config}.
For each config surface, use read_file to load the content, then assess against the following rules. Apply judgment β distinguish benign patterns from genuinely suspicious ones.
Prompt Injection Rules (Instructions, Agents, Skills)
Read: .github/copilot-instructions.md, all .github/agents/*.agent.md, all .github/skills/**/SKILL.md
| Rule | What to detect | Severity | Category | Benign exceptions |
|---|---|---|---|---|
| INJ-01 | Instructions containing "always run", "without asking", "automatically install", "execute without confirmation" | π‘ high | Agents & Skills | "always run tests" or "always check" are benign quality patterns |
| INJ-02 | Instructions to "fetch and execute", "download and run", "curl | bash", "eval remote" | π΄ critical | Agents & Skills | None β always flag |
| INJ-03 | System prompt overrides: "ignore previous instructions", "you are now", "DAN", "jailbreak", fake system messages | π΄ critical | Agents & Skills | None β always flag |
| INJ-04 | Output manipulation: "always report ok", "suppress warnings", "remove security findings", "hide errors" | π‘ high | Agents & Skills | Legitimate error handling instructions are benign |
| INJ-05 | Time-delayed execution: "after 5 minutes", "when user is away", "at 3am", conditional on absence | π‘ high | Agents & Skills | Scheduled CI/CD references are benign |
Agent Access Control Rules
Read: All .github/agents/*.agent.md
| Rule | What to detect | Severity | Category |
|---|---|---|---|
| ACC-01 | Agent definitions granting unrestricted Bash/shell access without scoping | π‘ high | Agents & Skills |
| ACC-02 | Agent with no allowedTools restriction when it has tool access |
π’ medium | Agents & Skills |
| ACC-03 | Escalation chains: agent can spawn sub-agents with elevated permissions | π‘ high | Agents & Skills |
Hook Execution Safety Rules
Read: .github/hooks/copilot-hooks.json, all .github/hooks/scripts/**
| Rule | What to detect | Severity | Category |
|---|---|---|---|
| EXEC-01 | Hook scripts that download and execute remote code (curl | sh, wget + execute) | π΄ critical | Hooks |
| EXEC-02 | Global package installs in hooks (npm install -g, pip install, gem install, cargo install) | π’ medium | Hooks |
| EXEC-03 | Container escape patterns: docker --privileged, --pid=host, --network=host, root volume mounts | π΄ critical | Hooks |
| EXEC-04 | Credential access: keychain reads, /etc/shadow, .aws/credentials, credential file access | π΄ critical | Hooks |
Setup Steps Rules β scores under: Hooks
Read: .github/copilot-setup-steps.yml
| Rule | What to detect | Severity | Category |
|---|---|---|---|
| SETUP-01 | Remote script execution in setup (curl | bash, wget | sh, remote script download + run) | π΄ critical | Hooks |
| SETUP-02 | Privileged operations (sudo without justification, chmod 777, chown root) | π‘ high | Hooks |
MCP β LLM-Assessed Rules
Read: .github/copilot-mcp-config.json
| Rule | What to detect | Severity | Category |
|---|---|---|---|
| MCP-01 | MCP servers using npx -y without version pinning (@package instead of @package@version) β requires parsing JSON structure: check each server's command is "npx" and args array contains "-y" with an unpinned package name (no @semver suffix) |
π‘ high | MCP Servers |
VS Code β LLM-Assessed Rules β scores under: Permissions
Read: .vscode/extensions.json
| Rule | What to detect | Severity | Category |
|---|---|---|---|
| VSCODE-01 | Extension recommendations from untrusted/unknown publishers without justification | π΅ low | Permissions |
Oversized Prompt Rule
Read: All .github/skills/**/SKILL.md, all .github/agents/*.agent.md
| Rule | What to detect | Severity | Category |
|---|---|---|---|
| AGENT-03 | Files with >8,000 characters of effective prose (exclude YAML frontmatter, fenced code blocks, and markdown tables from count) | π’ medium | Agents & Skills |
AGENT-03 exemptions: Skip the following first-party security skill files. They intentionally bundle config + OWASP + STRIDE rule definitions and exceed 8,000 chars by design:
.github/skills/atv-security/SKILL.md
This exemption applies to the file currently performing the scan and any other ATV-bundled security skills with the same purpose. Do not exempt user-authored skills β long custom skills are legitimate findings.
Execution: For each rule, read the relevant files, assess content against criteria, and record findings. Include the specific evidence that triggered the finding (quoted text, line context). Distinguish benign patterns from suspicious ones using the exceptions listed.
Phase 4: OWASP Top 10 (2021) Scan
Run only when
hasSource && scope β {full, owasp, <path>}.
For each category, use grep_search for Tier 1 patterns and read_file + LLM assessment for Tier 2. Scan only application source files, not .github/ configs. If scope is a path, scope all greps and reads to that path.
A01: Broken Access Control
Tier 1 β grep patterns:
| Pattern | What it catches | Severity |
|---|---|---|
role\s*===?\s*["']admin["'] scoped to route/controller files |
Hardcoded role checks instead of RBAC | π’ medium |
req\.user\s*&& without authorization middleware |
Ad-hoc auth checks bypassing middleware | π‘ high |
Tier 2 β LLM assessment:
- Read route/controller files. Check: Are all state-changing endpoints protected by auth middleware?
- Look for: direct object references without ownership validation (e.g.,
/users/:idwithout checkingreq.user.id === id) - Look for: missing authorization on admin/management endpoints
- Severity: π‘ high per unprotected endpoint
A02: Cryptographic Failures
Tier 1 β grep patterns:
| Pattern | What it catches | Severity |
|---|---|---|
| `(?:md5 | sha1 | DES |
http:// in API endpoint URLs (not localhost) |
Unencrypted data in transit | π’ medium |
password.*=.*["'][^$] |
Hardcoded passwords | π΄ critical |
Tier 2 β LLM assessment:
- Check: Are passwords hashed with bcrypt/scrypt/argon2, not MD5/SHA1?
- Check: Is sensitive data (PII, tokens, cards) encrypted at rest?
- Check: Are TLS/HTTPS enforced for external communications?
A03: Injection
Tier 1 β grep patterns:
For A03 alternation patterns, use the entries below instead of markdown table rows so the raw | characters remain valid for isRegexp: true:
- SQL injection via string concatenation β π΄ critical
- Pattern:
query\s*\(\s*["'\x60].*\$\{orquery\s*\(.*\+\s*
- Pattern:
- Code injection β π΄ critical
- Pattern:
(?:eval|exec|Function)\s*\(with variable input
- Pattern:
- XSS via unsafe HTML rendering β π‘ high
- Pattern:
(?:innerHTML\s*=|dangerouslySetInnerHTML|\| safe|\|raw)
- Pattern:
- OS command injection β π΄ critical
- Pattern:
child_process\.(?:exec|spawn).*\$\{orsubprocess\.call.*\+
- Pattern:
- NoSQL injection β π‘ high
- Pattern:
\.find\(\{.*\$or\.aggregate\(\[.*\$in Mongo contexts
- Pattern:
Tier 2 β LLM assessment:
- Read files with database queries. Are all queries parameterized?
- Read template files. Is user input escaped before rendering?
- Check for LDAP injection, XPath injection, header injection patterns
A04: Insecure Design
Tier 2 β LLM assessment only (no grep patterns):
- Check: Is there rate limiting on auth endpoints (login, register, password reset)?
- Check: Are there business logic flaws (e.g., negative quantity in cart, price manipulation)?
- Check: Is there account enumeration via different error messages for valid/invalid usernames?
- Severity: π‘ high per design flaw
A05: Security Misconfiguration
Tier 1 β grep patterns:
- Debug mode enabled β π‘ high
- Pattern:
(?:DEBUG\s*=\s*True|debug:\s*true)
- Pattern:
- Unrestricted CORS β π‘ high
- Pattern:
cors\(\)without origin restriction, ororigin:\s*["']\*["']
- Pattern:
- Django wildcard hosts β π‘ high
- Pattern:
ALLOWED_HOSTS\s*=\s*\[["']\*["']\]
- Pattern:
Tier 2 β LLM assessment:
- Check: Are default credentials changed?
- Check: Are error pages custom (not showing stack traces)?
- Check: Are unnecessary features/endpoints disabled in production config?
- Check: In Node.js/Express apps, are security headers configured appropriately (for example, via
helmetor equivalent middleware)? This is an absence check that requires reading the app setup, not a grep pattern.
A06: Vulnerable and Outdated Components
Tier 1 β grep patterns:
| Pattern | What it catches | Severity |
|---|---|---|
"dependencies" in package.json |
Check for known-vulnerable versions | π’ medium |
Tier 2 β LLM assessment:
- Read
package.json,requirements.txt,Gemfile, orgo.mod - Flag any dependency that hasn't been updated in >1 year (check version patterns)
- Recommend:
npm audit,pip-audit,bundle audit,govulncheck - Severity: π’ medium (recommend tooling, don't duplicate it)
A07: Identification and Authentication Failures
Tier 1 β grep patterns:
- Excessive token lifetime β π‘ high
- Pattern:
jwt\.sign.*expiresIn.*(?:["']30d|["']365d|["']never)
- Pattern:
- Long session duration β π’ medium
- Pattern:
session.*maxAge.*86400000(>24h in ms)
- Pattern:
- Weak bcrypt rounds (<6) β π‘ high
- Pattern:
(?:bcrypt|salt).*rounds.*[1-5][^0-9]
- Pattern:
Tier 2 β LLM assessment:
- Check: Is there brute-force protection (account lockout, progressive delays)?
- Check: Is password complexity enforced?
- Check: Are sessions invalidated on logout/password change?
A08: Software and Data Integrity Failures
Tier 1 β grep patterns:
| Pattern | What it catches | Severity |
|---|---|---|
| `(?:deserialize | unserialize | pickle.load |
Tier 2 β LLM assessment:
- Check: Is CI/CD pipeline protected against tampering?
- Check: Are software updates verified with signatures?
- Check: Do HTML pages include
<script src="https://...">tags that load third-party/CDN scripts without anintegrityattribute (Subresource Integrity)? This requires reading/parsing the tag β a simple grep can't validate absence of an attribute reliably.
A09: Security Logging and Monitoring Failures
Tier 2 β LLM assessment only:
- Check: Are login failures, access denied events, and input validation failures logged?
- Check: Are logs protected against injection (structured logging vs string concat)?
- Check: Is there alerting on suspicious patterns?
- Severity: π’ medium per gap
A10: Server-Side Request Forgery (SSRF)
Tier 1 β grep patterns:
- Potential SSRF if URL is user-controlled β π‘ high
- Pattern:
(?:fetch\s*\(\s*\w+|axios\.\w+\(\s*\w+|requests\.\w+\(\s*\w+)
- Pattern:
- Same pattern in Go/Python β π‘ high
- Pattern:
(?:http\.Get\(\s*\w+|urllib\.request\.urlopen\(\s*\w+)
- Pattern:
Tier 2 β LLM assessment:
- Check: Is user input validated/allowlisted before being used in server-side HTTP requests?
- Check: Are internal network ranges (169.254.x.x, 10.x.x.x, 127.x.x.x) blocked?
Phase 5: STRIDE Threat Model
Run only when
hasSource && scope β {full, stride, <path>}.
Read the application's architecture by examining:
- Entry points (routes, API endpoints, webhooks, event handlers)
- Data flows (database queries, external API calls, file I/O)
- Trust boundaries (auth middleware, API gateways, service boundaries)
- Assets (user data, credentials, tokens, business logic)
Produce a threat matrix:
| Threat | Category | Description | Affected Component | Risk | Mitigation |
|---|---|---|---|---|---|
| Spoofing | Identity | Can an attacker impersonate a user/service? | [auth system, API] | [H/M/L] | [existing or missing control] |
| Tampering | Data integrity | Can data be modified in transit or at rest? | [database, API payload] | [H/M/L] | [existing or missing control] |
| Repudiation | Accountability | Can actions be performed without audit trail? | [logging system] | [H/M/L] | [existing or missing control] |
| Information Disclosure | Confidentiality | Can sensitive data leak? | [error pages, logs, API responses] | [H/M/L] | [existing or missing control] |
| Denial of Service | Availability | Can the service be overwhelmed? | [endpoints without rate limiting] | [H/M/L] | [existing or missing control] |
| Elevation of Privilege | Authorization | Can a user gain unauthorized access? | [role system, admin endpoints] | [H/M/L] | [existing or missing control] |
For each threat:
- Identify whether a mitigation already exists in the codebase
- If missing, provide a concrete recommendation
- Rate risk as High/Medium/Low based on exploitability and impact
Phase 6: Score & Grade
Compute up to three independent grades. Surfaces that were not scanned (because they were absent or out of scope) render as N/A and are excluded from the aggregate.
6a. Config Grade (only if Phase 2/3 ran)
Step 1 β Per-category deductions:
For each category, start at 100 and deduct per finding within that category:
- π΄ critical: β15
- π‘ high: β10
- π’ medium: β5
- π΅ low: β2
- βͺ info: 0
Floor each category at 0 (never go negative).
Category mapping for rules:
- Secrets: SEC-01 through SEC-05
- Permissions: PERM-01, VSCODE-01
- Hooks: HOOK-01 through HOOK-03, EXEC-01 through EXEC-04, SETUP-01, SETUP-02
- MCP Servers: MCP-01 through MCP-04
- Agents & Skills: AGENT-01 through AGENT-03, INJ-01 through INJ-05, ACC-01 through ACC-03
Step 2 β Weighted aggregate:
ConfigScore = SecretsΓ0.20 + PermissionsΓ0.15 + HooksΓ0.25 + MCPΓ0.25 + AgentsΓ0.15
Round to nearest integer. Map to letter grade.
Simplified alternative: If exact arithmetic is difficult, use per-category pass/fail:
- β₯1 critical finding in category β π΄
- β₯1 high finding (no critical) β π‘
- Otherwise β π’
- Overall config status = worst category status, mapped to: π’βA/95, π‘βC/70, π΄βF/40.
6b. OWASP Grade (only if Phase 4 ran)
Start at 100, deduct per OWASP finding:
- π΄ critical: β15
- π‘ high: β10
- π’ medium: β5
- π΅ low: β2
Floor at 0. Map to letter grade.
6c. STRIDE Posture (only if Phase 5 ran)
Count of unmitigated threats:
- 0 unmitigated: π’ Strong posture
- 1β2 unmitigated: π‘ Moderate risk
- 3+ unmitigated: π΄ Weak posture
6d. Letter grade table
| Score | Grade |
|---|---|
| 90β100 | A |
| 80β89 | B |
| 65β79 | C |
| 50β64 | D |
| 0β49 | F |
6e. Aggregate
OverallScore = unweighted mean of the present grades among {ConfigScore, OWASPScore}. STRIDE contributes to OverallScore only as a β5 penalty per unmitigated threat (capped at β20). Surfaces marked N/A are skipped β never scored as 0 or 100.
If only one surface ran, OverallScore = <that surface's score> and the other appears as N/A in the report.
Phase 7: Output
Run regardless of mode. In
mode=fix, this report is rendered before any fix prompts.
Print the following report in chat. Do not modify any files (file modifications happen in Phase 8 persistence and Phase 9 fix).
Severity indicators: π΄ critical, π‘ high, π’ medium, π΅ low, βͺ info
## π‘οΈ ATV Security Report
**Date:** YYYY-MM-DD
**Scope:** [config | owasp | stride | <path> | full]
**Surfaces scanned:** [config: yes/no/N/A] Β· [source: yes/no/N/A β stack: <detected>]
| Metric | Value |
|--------|-------|
| **Overall Grade** | [AβF or N/A] |
| **Config Grade** | [AβF]/[0β100] or N/A β no `.github/` configs |
| **OWASP Grade** | [AβF]/[0β100] or N/A β no application source |
| **STRIDE Posture** | [π’/π‘/π΄] or N/A |
### Config Category Breakdown _(omit if config N/A)_
| Category | Score | Status |
|----------|-------|--------|
| Secrets | [0β100] | [π’/π‘/π΄] |
| Permissions | [0β100] | [π’/π‘/π΄] |
| Hooks | [0β100] | [π’/π‘/π΄] |
| MCP Servers | [0β100] | [π’/π‘/π΄] |
| Agents & Skills | [0β100] | [π’/π‘/π΄] |
### Config Findings _(omit if config N/A)_
#### π΄ Critical
- **[RULE-ID] Title** in `file/path`
Evidence: `<matched text, truncated to 100 chars>`
Fix: <actionable fix suggestion>
#### π‘ High
- ...
#### π’ Medium
- ...
#### π΅ Low
- ...
### OWASP Findings _(omit if OWASP N/A)_
#### π΄ Critical
- **[A03] SQL Injection** in `src/db/queries.ts:42`
Evidence: `db.query("SELECT * FROM users WHERE id = " + userId)`
Fix: Use parameterized query: `db.query("SELECT * FROM users WHERE id = $1", [userId])`
#### π‘ High / π’ Medium / π΅ Low
- ...
### STRIDE Threat Matrix _(omit if STRIDE N/A)_
| Threat | Risk | Status |
|--------|------|--------|
| Spoofing | [H/M/L] | [β
Mitigated / β οΈ Partial / β Unmitigated] |
| Tampering | [H/M/L] | [β
/β οΈ/β] |
| Repudiation | [H/M/L] | [β
/β οΈ/β] |
| Info Disclosure | [H/M/L] | [β
/β οΈ/β] |
| Denial of Service | [H/M/L] | [β
/β οΈ/β] |
| Elevation of Privilege | [H/M/L] | [β
/β οΈ/β] |
### Summary
| Files scanned | Config findings | OWASP findings | STRIDE unmitigated | Auto-fixable |
|---------------|----------------|----------------|--------------------|--------------|
| [N] | [N] | [N] | [N] | [N] |
If zero findings across all run phases: report Grade A, all surfaces π’, and congratulate: "Your project looks secure! No findings detected."
Phase 8: Persist Report (always, after report is rendered and before any Fix Mode prompts)
After printing the report in chat, persist it to disk so it survives the conversation. Persistence happens immediately after Phase 7 β before the user is prompted for Fix Mode (Phase 9). This ensures the on-disk record reflects the un-fixed state of the scan; re-running with fixes applied will produce a new dated section on the next run.
Target file: docs/security/YYYY-MM-DD-security-report.md (today's date, UTC). One shared file per day. The file retains two marker blocks for backwards compatibility with reports written by the legacy /cso skill.
Marker semantics (post-merge):
- The
<!-- atv-security:start --> ... <!-- atv-security:end -->block holds the config audit section. - The
<!-- cso:start --> ... <!-- cso:end -->block holds the OWASP + STRIDE section. The block heading remains## /cso Scanfor backward compatibility, with a one-line subnote indicating it is now generated by/atv-security.
Steps:
- Ensure
docs/security/exists. If not, create it (write the file β the directory is created implicitly). - Compute today's date as
YYYY-MM-DDand the run timestamp as ISO-8601 (e.g.,2026-04-26T14:32:10Z). - Try to
read_filethe target path.- If the file does not exist:
create_filewith this skeleton, then continue at step 4:# Security Report β YYYY-MM-DD <!-- atv-security:start --> ## /atv-security Scan _No scan recorded yet._ <!-- atv-security:end --> <!-- cso:start --> ## /cso Scan _Generated by /atv-security after the /cso skill was folded in._ _No scan recorded yet._ <!-- cso:end --> - If the file exists: continue at step 4. If a marker block is missing on read, append a fresh block of that type to the end of the file (do NOT overwrite).
- If the file does not exist:
- Build the new section content for whichever phases ran:
- Config block (if config was scanned):
## /atv-security Scan β <ISO timestamp> - **Mode:** report | fix - **Scope:** full | config - **Config Grade:** <AβF> Β· **Score:** <0β100>/100 - **Files scanned:** <N> <full config-section markdown from Phase 7> - OWASP/STRIDE block (if OWASP and/or STRIDE was scanned):
## /cso Scan β <ISO timestamp> _Generated by /atv-security (formerly /cso)._ - **Scope:** full | owasp | stride | <path> - **Stack:** <detected stack> - **OWASP Grade:** <AβF or N/A> Β· **Score:** <0β100 or N/A>/100 - **STRIDE Posture:** π’/π‘/π΄ or N/A <OWASP findings + STRIDE matrix from Phase 7>
- Config block (if config was scanned):
- Use
replace_string_in_fileto swap the matching marker block(s). Only update the block(s) corresponding to phases that actually ran β never wipe the other block. - Confirm in chat:
π Report saved to docs/security/YYYY-MM-DD-security-report.md.
Constraints:
- Never delete or modify a marker block whose phase did not run this invocation.
- Always keep both marker pairs intact in the on-disk file.
- If a marker block cannot be found (file was hand-edited and markers stripped), append the full marker block to the end of the file rather than overwriting. Only fall back to a full-skeleton overwrite if the file is unparseable; in that case, write a backup copy first to
docs/security/YYYY-MM-DD-security-report.md.bak.<unix-timestamp>and warn the user that prior content was preserved in the backup.
Phase 9: Fix Mode (opt-in, only when mode=fix)
After persisting the report (Phase 8), apply safe fixes for auto-fixable findings. Fix mode applies only to config findings β OWASP/STRIDE findings are reported but never auto-fixed (changing application source code requires human review).
Auto-fixable rules: SEC-01 through SEC-05 (secretβenv var), MCP-02 (wildcardβscoped tools), MCP-04 (secretβenv var in MCP env).
Safety protocol:
-
Snapshot: Before touching any file, use
read_fileto load its entire content. Hold in context as rollback backup. -
Present fix: Show the user a before/after diff for each proposed fix:
Fix [RULE-ID]: Replace hardcoded secret with env var reference File: .github/copilot-mcp-config.json Before: "ANTHROPIC_API_KEY": "sk-ant-abc123..." After: "ANTHROPIC_API_KEY": "${ANTHROPIC_API_KEY}" Apply? (y/n) -
Confirm: Wait for explicit user confirmation before each fix.
-
Apply: Use
replace_string_in_fileto apply the change. -
Validate: Re-read the file with
read_file. Confirm JSON/YAML parses correctly:- For JSON: check for balanced braces, no trailing commas, valid syntax
- For YAML: check for valid indentation and structure
-
Revert on failure: If validation fails, use
replace_string_in_filewith the saved original content to restore the file. Report the error to the user. -
Summary: After all fixes, report: "Applied N fixes, skipped M. Re-run
/atv-securityto verify."
Constraints:
- Only value replacements within existing keys β never add, remove, or restructure JSON/YAML keys
- Never apply fixes without user confirmation
- Never apply fixes to files that failed parse validation
- Never auto-fix OWASP/STRIDE findings β recommend manual remediation
Finding Structure
Every finding must include these fields:
| Field | Description |
|---|---|
| Rule ID | e.g., SEC-01, MCP-02, INJ-03, A03 (OWASP), STRIDE-S/T/R/I/D/E |
| Surface | Config / OWASP / STRIDE |
| Category | Secrets / Permissions / Hooks / MCP Servers / Agents & Skills (config) β or OWASP A01βA10 β or STRIDE letter |
| Severity | π΄ critical / π‘ high / π’ medium / π΅ low / βͺ info |
| Title | Short descriptive title |
| File | Repo-relative path to the affected file |
| Evidence | Matched text or assessment reason (truncated to 100 chars) |
| Fix | Actionable remediation suggestion |
| Auto-fixable | Yes/No β applies to config Tier 1 secret rules SEC-01βSEC-05 plus MCP-02 and MCP-04. OWASP/STRIDE findings are always No. |
What This Skill Does NOT Do
- Run dynamic application security testing (DAST/penetration testing)
- Scan container images or infrastructure-as-code
- Replace dedicated SAST tools (Semgrep, CodeQL, Snyk) β the OWASP phase is a fast triage layer, not a substitute
- Perform runtime monitoring or sandbox execution
- Replace ce-review's diff-based security persona
- Run Opus 4.6 multi-agent adversarial analysis
- Create CI/CD GitHub Actions or pre-commit hooks
- Modify application source code (OWASP/STRIDE findings are report-only)
- Modify config files without explicit user confirmation (fix mode only)