Repository navigation
RTP R-12 rates unauthorised deployment at residual LOW on 'chain forensically records every decision', yet a recorded rejection can be erased and replaced by an approval that veri… (CR-A10-010) #385
Description
Wave 1 · theme D1-3: Revert, approval and GDPR erasure safety · highest severity High · 1 finding(s) · effort small
Summary
A high-risk model rejected by the reviewer reaches final_model/. The audit record shows a clean approval by the reviewer and verifies under the strictest mode.
Primary finding
CR-A10-010 — RTP R-12 rates unauthorised deployment at residual LOW on 'chain forensically records every decision', yet a recorded rejection can be erased and replaced by an approval that verifies
severity High · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end
What happens. risk_treatment_plan.md:171-180 (TR :172-181) lowers R-12 from inherent Med x High = HIGH to residual Low x Med = LOW. The treatment list is: require_human_approval (the row itself says strict tiers do not enforce it; config.py:1220-1223 defaults to False and the strict gate at config.py:1822-1842 only enforces safety.enabled), staging until forgelm approve, 'human_approval.required/granted/rejected chain forensically records every decision', and trainer != approver left to the IdP. The impact reduction from High to Med depends on that forensic record, and the record is not reliable: (1) forgelm reject preserves the staging dir (_approve.py:661-760). Approve's 'no prior terminal decision' guard reads the log through find_latest_event_for_run and never verifies the chain or HMAC (_approve.py:455-509, called at :539). Once the rejection line is truncated away, which is undetectable (M-A07-002), the decision re-opens and approve promotes the rejected model. (2) The approval line can be written under any identity by anyone holding the shared secret. The approver comes from FORGELM_OPERATOR (_approve.py:151-153) and the key is derived from the secret and run_id (compliance.py:258). This is M5r1-A07-044, and the access_control section 6 jq recipe then reports no violation. (3) Without the secret, approve writes an unsigned line, and the default verify accepts it (M-A07-001). The likelihood 'Low' rests entirely on deployer config and the IdP, and the impact downgrade rests on a detective control that does not detect.
Where.
docs/qms/risk_treatment_plan.md:171-180(R-12 block; Treatment line 177, Residual 178)docs/qms/risk_treatment_plan.md:178docs/qms/risk_treatment_plan-tr.md:179forgelm/cli/subcommands/_approve.py:455-509forgelm/cli/subcommands/_approve.py:539(call to _read_required_event_for_approve)forgelm/cli/subcommands/_approve.py:151-153forgelm/cli/subcommands/_approve.py:661-760forgelm/compliance.py:258forgelm/config.py:1220-1223forgelm/compliance.py:356-363
Evidence.
- r12_reject_erasure.sh. Log after the attack: pipeline.initialized, training.started and human_approval.required (operator gha:Acme/pipelines:training:run-42), then human_approval.granted (operator=approver=alice@acme.example). verify-audit --require-hmac exits 0, and the access_control.md:209-216 jq query returns no rows. r12_nosecret.sh: the approve runs without the secret; the default verify (secret set, no --require-hmac) prints 'OK: 3 entries verified (HMAC validated)' and exits 0; --requir…
Recommended fix. Keep the residual at HIGH or MED with a section 5 acceptance entry, or name the deployer controls that carry it: decisions recorded outside the output dir (ticketing or IdP-signed), the secret withheld from the trainer job, and WORM shipping. Reword 'forensically records every decision' to state the truncation and shared-key limits. Mirror the changes in the TR page.
Acceptance criteria
- A regression test reproduces the failure scenario and asserts the corrected behaviour.
- Documentation listed above is corrected in English and in its Turkish mirror in the same PR.
- Every finding in this issue is resolved or explicitly closed with a reason.
Found by the October 2026 ForgeLM code and documentation review against commit cab2947. Code links point at that commit; line numbers may have moved since. Finding IDs (CR-…, DOC-…, TECH-…) are stable references for this backlog.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsTodo
Activity