Morgan Lunt 9d49c4b135
code-modernization: close remaining credential-leak paths
A red-team pass found four ways credential values still reached
shareable artifacts after the initial redaction:

- the remediation patch: a diff removing a hardcoded secret carries the
  raw value on its '-' lines by construction. harden now splits output:
  non-credential hunks in the shareable security_remediation.patch,
  credential hunks in a gitignored security_remediation.local.patch
  with comment-only placeholders in the shareable file
- the other four agents had no secret-handling rules. legacy-analyst
  (hardcoded-config evidence in tech-debt findings),
  business-rules-extractor (credentials recorded as rule parameters),
  test-engineer (legacy literals becoming committed test fixtures), and
  architecture-critic (quoted code in notes files) now all mask values
  and cite file:line; assess's tech-debt prompt and ASSESSMENT.md
  masking now cover every section, not just Security Findings
- non-git projects: a .gitignore protects nothing under SVN/Mercurial.
  Both commands now refuse --show-secrets without git and write the
  quarantine file to ~/.modernize/<system>/ outside the project tree
- the patch-apply instruction was wrong in both documented layouts
  (symlinked legacy/ broke relative paths). Patches are now written
  with project-root-relative paths and applied from the project root

Also: --show-secrets is now position-independent in both commands, and
the README documents the full model.
2026-06-09 08:47:34 -07:00

5.6 KiB
Raw Blame History

description argument-hint
Security vulnerability scan with a reviewable remediation patch — OWASP, CWE, CVE, secrets, injection <system-dir> [--show-secrets]

Run a security hardening pass on the legacy system: find vulnerabilities, rank them, and produce a reviewable patch for the critical ones. Parse arguments flag-independently: the system dir (referred to as $1 below) is the first non-flag token in $ARGUMENTS; --show-secrets may appear anywhere.

This command never edits legacy/ — it writes findings and a proposed patch to analysis/$1/. The user reviews and applies (or not).

Step 0 — Secrets quarantine setup

Findings files get shared, committed, and pasted into decks — discovered credential values must never land in them. Before any scanning:

  1. Ensure analysis/.gitignore exists and contains the lines SECRETS.local.md and *.local.patch. Create the file or append the missing lines.
  2. If the project is a git repo, verify with git check-ignore -q analysis/$1/SECRETS.local.md — if that exits non-zero, fix the ignore rule before proceeding. Do not write any findings until this check passes.
  3. If there is no git repo (check for .svn/.hg/CVS too — a .gitignore protects nothing under another VCS): refuse --show-secrets, and write SECRETS.local.md and any .local.patch file to ~/.modernize/$1/ instead of the project tree, telling the user where they went and why.

All secret values in every shareable artifact this command produces are masked (AKIA****, password=****) and cited by file:line. Raw values may appear in exactly two places, both gitignored: the *.local.patch remediation hunks (unavoidably — see Remediate) and, only with --show-secrets, SECRETS.local.md. Never in SECURITY_FINDINGS.md or patch commentary.

Scan

Spawn the security-auditor subagent:

"Adversarially audit legacy/$1 for security vulnerabilities. Cover what's relevant to the stack: injection (SQL/NoSQL/OS command/template), broken auth, sensitive data exposure, access control gaps, insecure deserialization, hardcoded secrets, vulnerable dependency versions, missing input validation, path traversal. For each finding return: CWE ID, severity (Critical/High/Med/Low), file:line, one-sentence exploit scenario, and recommended fix. Run any available SAST tooling (npm audit, pip-audit, OWASP dependency-check) and include its raw output. Mask every discovered credential value per your secret-handling rules — file:line plus a 24 character masked preview, never the value itself."

Triage

Write analysis/$1/SECURITY_FINDINGS.md:

  • Summary scorecard (count by severity, top CWE categories)
  • Findings table sorted by severity
  • Dependency CVE table (package, installed version, CVE, fixed version)

If any hardcoded credentials were found, also write analysis/$1/SECRETS.local.md (the gitignored quarantine file from Step 0): one row per credential — masked preview, file:line, credential type, what it appears to grant access to, production/test guess, and a rotation recommendation. With --show-secrets, append the raw value column here — this file only. SECURITY_FINDINGS.md gets a one-line pointer: "N hardcoded credentials found — inventory in SECRETS.local.md (gitignored; not for sharing)."

Remediate

For each Critical and High finding, draft a minimal, targeted fix. Do not edit legacy/ — write fixes as unified diffs with paths relative to the project root (legacy/$1/...), applied from the project root, with a comment line above each hunk citing the finding ID it addresses (# SEC-001: parameterize the query).

Credential findings split into two files. A diff that removes a hardcoded secret necessarily contains the raw value on its - and context lines — that cannot go in the shareable patch:

  • analysis/$1/security_remediation.patch (shareable) — every non-credential hunk, plus for each credential finding a comment-only placeholder: # SEC-NNN: credential remediation — hunk in security_remediation.local.patch (gitignored; not for sharing).
  • analysis/$1/security_remediation.local.patch (gitignored in Step 0) — the real, applyable hunks for credential findings only.

Add a Remediation Log section to SECURITY_FINDINGS.md mapping each finding ID → one-line summary of the proposed fix and which patch file carries the hunk.

Verify

Spawn the security-auditor again to review both patches against the original code:

"Review analysis/$1/security_remediation.patch and analysis/$1/security_remediation.local.patch against legacy/$1. For each hunk: does it fully remediate the cited finding? Does it introduce new vulnerabilities or change behavior beyond the fix? Confirm no raw credential values appear anywhere in the shareable patch. Return one verdict per hunk: RESOLVES / PARTIAL / INTRODUCES-RISK, with a one-line reason."

Add a Patch Review section to SECURITY_FINDINGS.md with the verdicts. If any hunk is PARTIAL or INTRODUCES-RISK, revise the patch and re-review.

Present

Tell the user the artifacts are ready:

  • analysis/$1/SECURITY_FINDINGS.md — findings, remediation log, patch review
  • analysis/$1/security_remediation.patch — review, then apply from the project root: git apply analysis/$1/security_remediation.patch (if legacy/$1 is a symlink, use git apply --unsafe-paths or apply with patch -p0 from the project root)
  • analysis/$1/security_remediation.local.patch — the credential fixes; apply the same way, and rotate the affected credentials regardless
  • Re-run /modernize-harden $1 after applying to confirm resolution

Suggest: glow -p analysis/$1/SECURITY_FINDINGS.md