浏览代码

code-modernization: harden writes a patch instead of editing legacy; make map/security guidance language-agnostic

- modernize-harden: never edits legacy/ anymore. Writes findings plus a
  reviewed unified diff to analysis/<system>/security_remediation.patch.
  A second security-auditor pass reviews each hunk (RESOLVES / PARTIAL /
  INTRODUCES-RISK) before presenting. The user reviews and applies the
  patch deliberately, then re-runs to verify. This makes every command
  consistent with the recommended deny Edit(legacy/**) workspace setting,
  so the README's exception note is gone.
- modernize-map: restructure the parse-target list around three stack-
  agnostic principles (dispatcher targets are variables; code-storage
  joins live in config; entry points live in deployment descriptors), with
  COBOL/Java/web/CLI examples on equal footing rather than COBOL-dominant.
  Same protections against false dead-code findings, less stack-specific.
- security-auditor agent: rephrase coverage items in stack-neutral terms
  (record layouts/temp datasets, resource ACLs, deployment scripts/job
  definitions, batch input records) so the checklist reads naturally for
  COBOL, Java EE, .NET, and web targets alike.
- README: drop the harden exception note; describe the patch workflow.
Morgan Lunt 4 月之前
父节点
当前提交
5e4a45001d

+ 3 - 5
plugins/code-modernization/README.md

@@ -10,7 +10,7 @@ Legacy modernization fails most often not because the target technology is wrong
 assess → map → extract-rules → brief → reimagine | transform → harden
 ```
 
-The discovery commands (`assess`, `map`, `extract-rules`) build artifacts under `analysis/<system>/`. The `brief` command synthesizes them into an approval gate. The build commands (`reimagine`, `transform`) write new code under `modernized/`. The `harden` command audits and patches the legacy system. Each step has a dedicated slash command, and specialist agents (legacy analyst, business rules extractor, architecture critic, security auditor, test engineer) are invoked from within those commands — or directly — to keep the work honest.
+The discovery commands (`assess`, `map`, `extract-rules`) build artifacts under `analysis/<system>/`. The `brief` command synthesizes them into an approval gate. The build commands (`reimagine`, `transform`) write new code under `modernized/`. The `harden` command audits the legacy system and produces a reviewable remediation patch. Each step has a dedicated slash command, and specialist agents (legacy analyst, business rules extractor, architecture critic, security auditor, test engineer) are invoked from within those commands — or directly — to keep the work honest.
 
 ## Expected layout
 
@@ -47,9 +47,7 @@ Greenfield rebuild from extracted intent rather than a structural port. Mines a
 Surgical, single-module strangler-fig rewrite. Plans first (HITL gate), then writes characterization tests via `test-engineer`, then an idiomatic target implementation under `modernized/<system>/<module>/`, proves equivalence by running the tests, and produces `TRANSFORMATION_NOTES.md` mapping legacy → modern with deliberate deviations called out. Reviewed by `architecture-critic`.
 
 ### `/modernize-harden <system-dir>`
-Security hardening pass on the **legacy** system: OWASP/CWE scan, dependency CVEs, secrets, injection. Spawns `security-auditor`. Produces `analysis/<system>/SECURITY_FINDINGS.md` ranked Critical / High / Medium / Low, then **patches Critical and High findings directly in `legacy/<system>/`** and re-scans to verify. Useful as a pre-modernization step when the legacy system will keep running in production during the migration.
-
-> **Note:** `/modernize-harden` is the one command that edits `legacy/`. If you adopt the `deny: Edit(legacy/**)` workspace setting below, relax it for this command — or run hardening as a separate workstream against its own checkout.
+Security hardening pass on the **legacy** system: OWASP/CWE scan, dependency CVEs, secrets, injection. Spawns `security-auditor`. Produces `analysis/<system>/SECURITY_FINDINGS.md` ranked Critical / High / Medium / Low and a reviewed `analysis/<system>/security_remediation.patch` with minimal fixes for the Critical/High findings. The patch is reviewed by a second `security-auditor` pass before you see it. **Never edits `legacy/`** — you review and apply the patch yourself when ready, then re-run to verify. Useful as a pre-modernization step when the legacy system will keep running in production during the migration.
 
 ## Agents
 
@@ -89,7 +87,7 @@ This plugin ships commands and agents, but modernization projects benefit from a
 }
 ```
 
-Adjust `legacy/` and `modernized/` to match your actual layout. The key invariants: `Edit` under `legacy/` is denied, and writes are scoped to `analysis/` (for documents) and `modernized/` (for the new code). The exception is `/modernize-harden`, which intentionally patches `legacy/` — see its note above.
+Adjust `legacy/` and `modernized/` to match your actual layout. The key invariants: `Edit` under `legacy/` is denied, and writes are scoped to `analysis/` (for documents) and `modernized/` (for the new code). Every command in this plugin respects this — `/modernize-harden` writes a patch to `analysis/` rather than editing `legacy/` in place.
 
 ## Typical Workflow
 

+ 17 - 16
plugins/code-modernization/agents/security-auditor.md

@@ -11,28 +11,29 @@ engineer can fix.
 
 ## Coverage checklist
 
-Adapt to the target stack — web items don't apply to a batch COBOL system,
-mainframe items don't apply to a SPA. Work through what's relevant:
+Adapt to the target stack — web items don't apply to a batch system,
+terminal/screen items don't apply to a SPA. Work through what's relevant:
 
-- **Injection** (SQL, NoSQL, OS command, LDAP, XPath, template, dynamic
-  DB2 SQL, JCL/PARM injection) — trace every user-controlled input to every sink
+- **Injection** (SQL, NoSQL, OS command, LDAP, XPath, template) — trace every
+  user-controlled input to every sink, including dynamic SQL and shell-outs
 - **Authentication / session** — hardcoded creds, weak session handling,
-  missing auth checks on sensitive routes/transactions
-- **Sensitive data exposure** — secrets in source, weak crypto, PII/PAN/SSN in
-  logs, cleartext data in copybooks/flat files
+  missing auth checks on sensitive routes/transactions/jobs
+- **Sensitive data exposure** — secrets in source, weak crypto, PII in logs,
+  cleartext sensitive data in record layouts, flat files, or temp datasets
 - **Access control** — IDOR, missing ownership checks, privilege escalation;
-  for CICS: missing/permissive RACF transaction & resource definitions,
-  unguarded admin transactions
-- **XSS / CSRF** — unescaped output, missing tokens (web targets only)
-- **Insecure deserialization** — pickle/yaml.load/ObjectInputStream on
-  untrusted data
+  missing/permissive resource ACLs (RACF profiles, IAM policies, file perms);
+  unguarded admin functions
+- **XSS / CSRF** — unescaped output, missing tokens (web targets)
+- **Insecure deserialization** — untrusted data into pickle/yaml.load/
+  `ObjectInputStream` or custom record parsers
 - **Vulnerable dependencies** — run `npm audit` / `pip-audit` /
   read manifests and flag versions with known CVEs
-- **SSRF / path traversal / open redirect** (web targets only)
-- **Input validation** — for CICS/3270: unvalidated BMS field input,
-  missing length/range/format checks before file/DB writes
+- **SSRF / path traversal / open redirect** (web/network targets)
+- **Input validation** — missing length/range/format checks at trust
+  boundaries (form/screen fields, API params, batch input records) before
+  persistence or downstream calls
 - **Security misconfiguration** — debug mode, verbose errors, default creds,
-  hardcoded passwords/userids in JCL, PROCs, or sign-on programs
+  hardcoded credentials in deployment scripts, job definitions, or config
 
 ## Tooling
 

+ 38 - 20
plugins/code-modernization/commands/modernize-harden.md

@@ -1,23 +1,26 @@
 ---
-description: Security vulnerability scan + remediation — OWASP, CVE, secrets, injection
+description: Security vulnerability scan with a reviewable remediation patch — OWASP, CWE, CVE, secrets, injection
 argument-hint: <system-dir>
 ---
 
 Run a **security hardening pass** on `legacy/$1`: find vulnerabilities, rank
-them, and fix the critical ones.
+them, and produce a reviewable patch for the critical ones.
+
+This command never edits `legacy/` — it writes findings and a proposed patch
+to `analysis/$1/`. The user reviews and applies (or not).
 
 ## Scan
 
 Spawn the **security-auditor** subagent:
 
-"Adversarially audit legacy/$1 for security vulnerabilities. Cover:
-OWASP Top 10 (injection, broken auth, XSS, SSRF, etc.), hardcoded secrets,
-vulnerable dependency versions (check package manifests against known CVEs),
-missing input validation, insecure deserialization, path traversal.
-For each finding return: CWE ID, severity (Critical/High/Med/Low), file:line,
-one-sentence exploit scenario, and recommended fix. Also run any available
-SAST tooling (npm audit, pip-audit, OWASP dependency-check) and include
-its raw output."
+"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."
 
 ## Triage
 
@@ -28,19 +31,34 @@ Write `analysis/$1/SECURITY_FINDINGS.md`:
 
 ## Remediate
 
-For each **Critical** and **High** finding, fix it directly in the source.
-Make minimal, targeted changes. After each fix, add a one-line entry under
-"Remediation Log" in SECURITY_FINDINGS.md: finding ID → commit-style summary
-of what changed.
+For each **Critical** and **High** finding, draft a minimal, targeted fix.
+Do **not** edit `legacy/` — write all fixes as a single unified diff to
+`analysis/$1/security_remediation.patch`, with a comment line above each
+hunk citing the finding ID it addresses (`# SEC-001: parameterize the query`).
 
-Show the cumulative diff:
-```bash
-git -C legacy/$1 diff
-```
+Add a **Remediation Log** section to SECURITY_FINDINGS.md mapping each
+finding ID → one-line summary of the proposed fix and the patch hunk that
+implements it.
 
 ## Verify
 
-Re-run the security-auditor against the patched code to confirm the
-Critical/High findings are resolved. Update the scorecard with before/after.
+Spawn the **security-auditor** again to **review the patch** against the
+original code:
+
+"Review analysis/$1/security_remediation.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? 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 if appropriate
+  with `git -C legacy/$1 apply ../../analysis/$1/security_remediation.patch`
+- Re-run `/modernize-harden $1` after applying to confirm resolution
 
 Suggest: `glow -p analysis/$1/SECURITY_FINDINGS.md`

+ 38 - 33
plugins/code-modernization/commands/modernize-map.md

@@ -11,39 +11,44 @@ connect? This is the map an engineer needs before touching anything.
 ## What to produce
 
 Write a one-off analysis script (Python or shell — your choice) that parses
-the source under `legacy/$1` and extracts the four datasets below. Cover
-the parse targets that are real for the stack you're looking at — these are
-the ones LLMs reliably miss:
-
-- **Program/module call graph** — who calls whom.
-  - COBOL/CICS: `CALL '...'` and `EXEC CICS LINK/XCTL PROGRAM(...)`. Most
-    `PROGRAM(...)` targets are **data-names, not literals** — resolve them
-    against working-storage `VALUE` clauses and any menu/route copybooks
-    before declaring an edge unresolvable.
-  - Java: class-level imports/invocations. Node: `require`/`import`.
-- **Data dependency graph** — which programs read/write which data stores.
-  - COBOL batch: `SELECT ... ASSIGN TO <ddname>` joined with JCL `DD`
-    statements (this is the *only* way to attribute file I/O to a program).
-  - COBOL/CICS online: `EXEC CICS READ/WRITE/REWRITE/DELETE/STARTBR/READNEXT/
-    READPREV ... FILE(...)` joined with `DEFINE FILE` in the CSD.
-  - DB2: `EXEC SQL ... END-EXEC` table references — *not* JCL DD; DB2 access
-    is via plan/package binds.
-  - BMS: `SEND MAP`/`RECEIVE MAP` ↔ map source under `bms/` and copybooks
-    under `cpy-bms/` (or wherever the maps live).
-  - Java: JPA/MyBatis entities & tables. Node: model files.
-- **Entry points** — whatever the stack's outermost invokers are. Mainframe:
-  JCL `EXEC PGM=` steps **and** CICS `DEFINE TRANSACTION ... PROGRAM(...)`
-  from the CSD — without the CSD, every online program looks unreachable.
-  Web: HTTP routes. CLI: argv parsing.
-- **Dead-end candidates** — modules with no inbound edges. **Only trust this
-  once the entry-point and call-edge types above are all in the graph**, and
-  suppress the dead claim for any module that could be the target of an
-  unresolved dynamic call. A naive grep-only graph will mark most CICS
-  programs dead.
-
-For COBOL fixed-format, slice columns 8-72 and skip `*` indicator lines
-(column 7) before regex matching, or you'll match sequence numbers and
-commented-out code.
+the source under `legacy/$1` and extracts the four datasets below. Three
+principles apply across stacks; getting them wrong produces a misleading map:
+
+1. **Edges live in two places** — direct calls in source, *and* dispatcher/
+   router calls whose targets are variables (config tables, route maps,
+   dependency injection, dynamic dispatch). Resolve variables against config
+   before declaring an edge unresolvable.
+2. **The code↔storage join is usually external configuration**, not source —
+   job/deployment descriptors map logical names to physical stores.
+3. **Entry points usually live in deployment config**, not source — without
+   parsing it, every top-level module looks unreachable.
+
+Extract:
+
+- **Program/module call graph** — direct calls (`CALL`, method invocations,
+  `import`/`require`) *and* dispatcher calls (`EXEC CICS LINK/XCTL`, DI
+  container wiring, framework routing, reflection/factory). Resolve variable
+  call targets against route tables, copybooks, config, or constant pools.
+- **Data dependency graph** — which modules read/write which data stores,
+  joined through the relevant config: `SELECT…ASSIGN TO` ↔ JCL `DD` (batch
+  COBOL), `EXEC CICS READ/WRITE…FILE()` ↔ CSD `DEFINE FILE` (CICS online),
+  `EXEC SQL` table refs (embedded SQL), ORM annotations/mappings (Java/.NET),
+  model files (Node/Python/Ruby). Include UI/screen bindings (BMS maps, JSPs,
+  templates) — they're dependencies too.
+- **Entry points** — whatever the stack's outermost invoker is, read from
+  where it's defined: JCL `EXEC PGM=` and CICS CSD `DEFINE TRANSACTION`
+  (mainframe), `web.xml`/route annotations/route files (web), `main()`/argv
+  parsing (CLI), queue/scheduler subscriptions (event-driven).
+- **Dead-end candidates** — modules with no inbound edges. **Only meaningful
+  once all the entry-point and call-edge types above are in the graph.**
+  Suppress the dead claim for anything that could be the target of an
+  unresolved dynamic call. A grep-only graph will mark most dispatcher-driven
+  modules (CICS programs, Spring controllers, ORM-bound DAOs) dead when they
+  aren't.
+
+If the source is fixed-column (COBOL columns 8–72, RPG, etc.), slice the
+code area and strip comment lines before regex matching, or you'll match
+sequence numbers and commented-out code.
 
 Save the script as `analysis/$1/extract_topology.py` (or `.sh`) so it can be
 re-run and audited. Have it write a machine-readable