Repository navigation
reverse-pii answers an Art. 15 request with exit 0 and no audit event when operator identity is unresolvable, even with an explicit --audit-dir (CR-A10-013) #386
Description
Wave 1 · theme D1-3: Revert, approval and GDPR erasure safety · highest severity High · 3 finding(s) · effort small
Summary
FORGELM_OPERATOR is unset and getpass.getuser() raises KeyError (stubbed). forgelm reverse-pii --query ada0@example.com --output-dir noop --audit-dir noop/audit --output-format json noop/train.jsonl exits 0 with success:true and the match snippet. noop/audit/ stays empty. The same condition makes purge exit 1.
Primary finding
CR-A10-013 — reverse-pii answers an Art. 15 request with exit 0 and no audit event when operator identity is unresolvable, even with an explicit --audit-dir
severity Medium · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN TRUST-14 — docs/roadmap/phase-16-trust-surface-hardening.md:574-575,989-990 (S9 open; unit id inferred)
What happens. _maybe_audit_logger (_reverse_pii.py:545-558) catches the ConfigError AuditLogger raises when FORGELM_OPERATOR is unset and getpass fails, logs a WARNING and returns None regardless of whether --audit-dir was explicit; _scan_with_audit then skips both audit emits (:655, :689) and the dispatcher prints a success envelope with raw matches. Module header, docs and purge (which exits 1 on the same ConfigError, _purge.py:736-739) all promise fail-closed behaviour.
Where.
forgelm/cli/subcommands/_reverse_pii.py:550-558forgelm/cli/subcommands/_reverse_pii.py:689(confirmed)forgelm/compliance.py:192-229forgelm/cli/subcommands/_reverse_pii.py:550forgelm/cli/subcommands/_reverse_pii.py:558forgelm/cli/subcommands/_reverse_pii.py:47-50forgelm/cli/subcommands/_reverse_pii.py:48forgelm/cli/subcommands/_purge.py:736-739forgelm/cli/subcommands/_reverse_pii.py:27-32forgelm/cli/subcommands/_reverse_pii.py:48-51
Evidence.
- logs/Q10a_rpii_noop_explicit_auditdir.{out,err}, logs/Q10b_purge_noop.out, noop_stub.py in outputs
- _reverse_pii.py:550-558
except ConfigError as exc: logger.warning(... 'access request will run but the regulatory chain will NOT record it' ...) return None - r1/h09.py sets FORGELM_OPERATOR to an empty value and patches getpass.getuser to raise KeyError (the arbitrary-UID container case), then runs
reverse-pii --audit-dir r1/audit --output-format json. Result: WARNING 'AuditLogger init refused ... access request will run but the regulatory chain will NOT record it', EXITCODE 0, success envelope with match_count 1 and a raw snippet, and AUDIT_DIR_CONTENTS [].
Recommended fix. Treat ConfigError like every other AuditLogger failure: exit 1 or 2 before scanning, at least when --audit-dir is explicit, matching purge. Otherwise document the exception.
Other findings with the same root cause
These close with the same fix; update them in the same pull request.
DOC-D1B05-006 — docs/reference/reverse_pii_subcommand.md: false claim — Every invocation emits exactly one data.access_request_query event; explicit --audit-dir values fail loudly rather than silently dropping t…
severity High · already tracked: KNOWN-OPEN TRUST-01/TRUST-07/TRUST-13/TRUST-14 (S9; per-unit split not public) — docs/roadmap/phase-16-trust-surface-hardening.md:574-575
Where. docs/reference/reverse_pii_subcommand.md:68,26 (Turkish mirror: docs/reference/reverse_pii_subcommand-tr.md:26,68) · type: false claim
The document says. Every invocation emits exactly one data.access_request_query event; explicit --audit-dir values fail loudly rather than silently dropping the Article 15 forensic record
What the code does. When AuditLogger raises ConfigError (no FORGELM_OPERATOR, getpass fails, no anonymous opt-in) reverse-pii logs a WARNING and runs the scan with no audit record, exit 0, even with an explicit --audit-dir. Runtime (getpass patched to fail, --audit-dir given): EXIT 0, audit_log.jsonl absent, raw snippet printed.
Code references. forgelm/cli/subcommands/_reverse_pii.py:545-558, forgelm/cli/subcommands/_reverse_pii.py:689-705, forgelm/compliance.py:219-229
User impact. In arbitrary-UID containers without FORGELM_OPERATOR, Article 15 searches over personal data (with raw snippets on stdout) leave no audit-chain record, while the reference promises one per invocation; purge and approve refuse in the same situation.
Proposed correction. Fix the code: treat ConfigError like every other AuditLogger failure (exit 1 or 2) so no access request runs unrecorded. Interim doc warning: "If the operator identity cannot be resolved (no FORGELM_OPERATOR, no resolvable username, no FORGELM_ALLOW_ANONYMOUS_OPERATOR=1), the scan still runs and NO audit event is written; set FORGELM_OPERATOR in CI/containers."
Turkish mirror. the Turkish page has the same error; fix both in the same PR
DOC-D1B05-023 — docs/reference/reverse_pii_subcommand.md: false claim — Every invocation writes data.access_request_query; explicit --audit-dir never silently drops the record
severity Medium · already tracked: KNOWN-OPEN TRUST-01/TRUST-07/TRUST-13/TRUST-14 (S9) — docs/roadmap/phase-16-trust-surface-hardening.md:574-575
Where. docs/reference/reverse_pii_subcommand.md:68 ('Every invocation emits exactly one event'), :26, :82; docs/guides/gdpr_erasure.md:165-168 · type: false claim
The document says. Every invocation writes data.access_request_query; explicit --audit-dir never silently drops the record
What the code does. When the operator identity is unresolvable (ConfigError), the scan succeeds with no event (Q10a, stubbed). See finding F4.
Code references. forgelm/cli/subcommands/_reverse_pii.py:545-558
Proposed correction. Fail closed (see F4), or document the exception.
Turkish mirror. the Turkish page has no such statement
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