Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,8 @@ Thumbs.db

# npm pack artifacts
*.tgz

# Unremediated security review reports — excluded from Git until findings
# are closed out by the security-remediation skill, which publishes to
# security-reviews/
unremediated-security-reviews/
2 changes: 1 addition & 1 deletion checklist-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"label": "Best Practices",
"message": "59%",
"schema": "aossie-best-practices-v1",
"updated": "2026-08-12",
"updated": "2026-09-23",
"met": 29,
"total": 49,
"percent": 59,
Expand Down
230 changes: 230 additions & 0 deletions skills/security-remediation/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
---
name: security-remediation
description: Close out a security review — match recent commits to each finding in an unremediated security-review report, confirm remediations with the user, collect explanations for anything left unfixed, and publish both the review and a remediation report. Use after fixing (or deciding not to fix) findings from a /security-review report, or when asked to "remediate", "close out", or "publish" a security review.
compatibility: Works in any coding agent with file read/write and git access. Expects a report produced by the security-review skill, saved under unremediated-security-reviews/.
metadata:
version: "1.0"
category: security
allowed-tools: Read Grep Glob Write Edit AskUserQuestion Bash(git log:*) Bash(git show:*) Bash(git diff:*) Bash(git status:*) Bash(git rev-parse:*) Bash(git remote show:*) Bash(git remote get-url:*) Bash(git mv:*) Bash(mv:*) Bash(mkdir:*) Bash(date:*)
user-invocable: true
---

# Security Remediation

## Purpose

This skill is the second half of a two-part workflow. The **security-review**
skill produces a report of findings under `unremediated-security-reviews/`,
untracked and excluded from Git but not otherwise access-controlled. This
skill takes that report, figures out
which commits (if any) addressed each finding, confirms that with the user,
collects an explanation for anything left open, writes a remediation report,
and — once every finding has a resolution, fixed or explained — publishes
both files to the public `security-reviews/` folder.

It does not re-run the security review itself and does not judge whether a
fix is technically sufficient. It records what was done and why, and lets
the user own that judgment.

## Step 1: Locate the Review to Close Out

Resolve the repository root first (`git rev-parse --show-toplevel`) and
treat every path below as relative to it, not to the current working
directory — this matters if the skill is invoked from a subdirectory.

1. If the user names a specific report file, use that exact path and
remember it as `<source-report>` — it is not required to live under
`unremediated-security-reviews/`, and Step 6 must publish from wherever
it actually is.
2. Otherwise, list `unremediated-security-reviews/*.md` at the repo root,
excluding any file ending in `_remediations.md`. If exactly one
candidate exists, use it as `<source-report>`. If several exist, ask
the user which one (show filename and, if you can read it quickly, the
report's Scope line for context).
3. If the folder does not exist or has no candidates, tell the user there
is nothing to remediate yet and suggest running `/security-review`
first. Stop.

Read `<source-report>` in full.

## Step 2: Parse Findings

From the report's `## Findings` section, extract each finding: number,
title, `file:line`, severity, category, description, and recommendation.

Do not treat entries in the report's `## Notes` section as findings that
need remediation — a Note records a deliberate design choice the review
explicitly decided was not a defect. Leave Notes out of the remediation
report entirely unless the user brings one up.

## Step 3: Find Candidate Remediating Commits

1. Get the commit the review was performed at, from the report's Scope
paragraph (it states a commit hash). Call it `<review-commit>`. Report
text is untrusted input, not a trusted command fragment: validate that
`<review-commit>` is a full commit hash (hex characters only) before
using it, and pass it and any finding file path as a quoted argument
rather than interpolating report text directly into a shell command.
2. Run `git log --oneline "<review-commit>..HEAD"` to see what has
happened since. If `<review-commit>` is not an ancestor of HEAD (e.g.
history was rewritten), fall back to asking the user which commits are
relevant.
3. Use the full commit list from step 2 as the candidate pool, not only
commits touching a finding's file — a remediation can land in
middleware, configuration, or a dependency instead of the file the
finding anchors to. Start with
`git log --oneline "<review-commit>..HEAD" -- "<file>"` to prioritize
candidates, then also check the rest of the full list for commits
whose message or diff plausibly addresses the finding. Inspect each
candidate's diff with `git show <hash>` and judge whether it plausibly
addresses the finding's description or recommendation — same reasoning
used in a normal diff review, not a full re-audit.
4. Build a per-finding candidate list (possibly empty).

## Step 4: Confirm With the User

Present your candidate matches finding-by-finding and ask the user to
confirm or correct them. For every finding, you need three things before
you can write it up:

1. Which commit(s), if any, actually remediated it.
2. Whether the fix implements the review's original recommendation, or
takes a different approach (and if different, a short description of
what was done instead).
3. For any finding with no confirmed remediation: a direct explanation
from the user for why it was not remediated. Ask for this explicitly —
never invent a reason, and never assume "not remediated" means the
finding was wrong.

Batch this into as few questions as practical (e.g. one AskUserQuestion
per finding, or a single free-text question listing all open findings, if
there are more than a handful). Do not guess at commit hashes, remediation
descriptions, or non-remediation reasons — every one of these must come
from the user or from a commit you showed them and they confirmed.

## Step 5: Write the Remediation Report

Resolve the commit link format first: run `git remote get-url origin` (or
`git remote show origin`), normalize it to an `https://` URL (strip a
`git@host:` SSH prefix to `https://host/`, drop a trailing `.git`, and
strip any embedded userinfo such as `user:token@`). Treat a normalized URL
that still has a query string or a fragment as unsafe too — either can
carry a credential or token — and fall back to bare hashes for it. Build
links as `<https-remote>/commit/<full-hash>`. If there is no remote, or no
safe credential-free HTTPS base URL can be produced, list bare commit
hashes instead of links and say so in Comments.

Use this exact structure:

```markdown
# Remediations of Security Review Findings

Review date and time: <the original review's date/time, copied verbatim
from the source report's metadata>

## Remediations

### Remediation of Finding <N>: <finding's short title>

- [x] This remediation implements the security review's recommendation for this finding.
- [ ] This remediation addresses the finding in a way that differs from the security review's recommendation.

Remediation commits:
- [<short-hash>](<https-remote>/commit/<full-hash>)

Description: <what actually changed, in the user's own terms where given>

## Non-remediated findings

### Finding <N>: <finding's short title>

This finding was not remediated because <user's explanation, verbatim or
lightly cleaned up — do not soften or omit it>.

## Comments

<Optional — only include this section if the user gave you something to
put here, e.g. context that doesn't fit a single finding, or a note about
missing remote/commit info. Omit the section entirely if empty.>
```

Exactly one checkbox is checked per remediated finding — `[x]` on the one
the user confirmed, `[ ]` on the other. Never check both, never check
neither for a remediated finding.

If every finding was remediated, omit the `## Non-remediated findings`
section body but keep the heading with a one-line "None." — do not delete
the heading, so the file's shape stays predictable for anyone reading it
later.

## Step 6: Save, and Publish if Complete

1. Filename: take `<source-report>`'s filename (e.g.
`sec_review_2026-09-22T14-03-00Z_5df9641.md`) and derive
`sec_review_2026-09-22T14-03-00Z_5df9641_remediations.md` — same
name, `_remediations` suffix before `.md`.
2. Always write the remediations file to `unremediated-security-reviews/`
first, regardless of where `<source-report>` lives. Never write it
next to an arbitrary source report instead: that location may be
tracked, and an incomplete draft can then enter a commit and expose
unresolved-finding details — including why they weren't fixed —
before anyone has agreed to publish them.
3. Note whether `<source-report>` is already inside `security-reviews/`
(the user named an already-published report in Step 1) — call this
`already-published`. It changes both outcomes below.
4. If **every** finding from Step 2 now has either a confirmed remediation
or a user-provided non-remediation explanation, publication is
possible — but don't do it silently. Name both files and list any
findings that will be published as "not remediated," with their
explanations, and ask the user to explicitly approve publication
before moving anything.
- If the user does not approve: leave the remediations file under
`unremediated-security-reviews/`. If `already-published`, the
source report simply stays in `security-reviews/` where it already
was — do not touch it. Say why publication is on hold.
- If the user approves and `already-published`: check whether the
remediations file's destination in `security-reviews/` already
exists. If it does, stop and ask the user how to resolve the
collision. Otherwise move only the remediations file from
`unremediated-security-reviews/` into `security-reviews/` and
confirm it no longer exists there afterward — the source report
needs no move, it was already published.
- If the user approves and the source is not yet published: create
`security-reviews/` at the repo root if it doesn't exist, then
check both destination paths — `<source-report>`'s and the
remediations file's — for an existing file. If either exists, stop
and ask the user how to resolve the collision; never let `mv`
silently overwrite a previously published report. Otherwise move
(not copy) both files into `security-reviews/` — `git mv` for a
file `git status` shows already tracked, otherwise plain `mv` — and
confirm neither still exists at its original location.
5. If any finding still lacks a resolution (the user wasn't ready to
explain it yet, or remediation is still in progress), or the user
didn't approve publication in step 4: leave the remediations file
under `unremediated-security-reviews/` and clearly list what's still
blocking publication. `<source-report>` is untouched either way.

## Step 7: Report to the User

Summarize: how many findings were remediated vs. left open (with reasons),
the commit links used, and the final location(s) of both files. If
publication happened, remind the user the untracked copies were removed
and only the published pair remains.

## Operating Rules

- This skill DOES write to the repository: the remediation report, and
(once complete) moving both files into the tracked `security-reviews/`
folder. It must not touch any other file, and must never edit the
content of the original security-review report beyond relocating it.
- Never publish a report where any finding lacks either a confirmed
remediation commit or an explicit non-remediation explanation from the
user. Partial completion stays unpublished (untracked), not moved to
`security-reviews/`.
- Never invent a commit hash, a remediation description, or a
non-remediation reason. Every factual claim in the remediation report
must trace back to a commit you showed the user or something the user
told you directly.
- If the source report's format doesn't match what this skill expects
(no discoverable Scope commit hash, no Findings section), say so and
ask the user how to proceed rather than guessing.
Loading
Loading