Sep 7, 2026 · v0.4.6

A SAFE You Could Forge

Refactron measured coverage by running the diff's own test suite in the same process as the measurement, so a hostile diff controlled the evidence. Six lines of conftest.py marked a never-executed line as covered and earned a false SAFE. The default changed: an untrusted diff can no longer earn a coverage-based SAFE.

Refactron measured coverage and the pass/fail gates by running the diff's own test suite in the same process as the measurement. The attacker owns that process. A roughly six-line conftest.py, using nothing but the public coverage API (Coverage.current().get_data().add_arcs), marked a never-executed changed line as covered and earned a false SAFE for an untested change.

Reproduced end to end, and present in every release from 0.3.1 to 0.4.5. A false SAFE is the one defect this tool exists to prevent, so the fix changes a default.

No launcher can stop in-process tampering, because the attacker owns the process. So Refactron stops trusting the forgeable measurement rather than trying to detect the forgery. Trust is asserted by the operator and never inferred from the diff: pass --trusted on the CLI for a diff whose author you trust, or trusted: true from MCP and the library, where the default is false.

Every report now carries trustMode, and a withheld verdict's reason begins "SAFE is withheld", so a consumer can tell a trust withhold from a real coverage gap. A trust-grade SAFE over an untrusted diff will require a hermetic, out-of-process run (ADR-19).

Also hardened

  • Coverage-driver isolation. A repo-root coverage.py can no longer shadow the driver (ADR-17). Defence in depth for the trusted path.
  • Excluded-statement floor. A changed statement the diff excludes from coverage, through # pragma: no cover or a coverage config, now floors the verdict instead of being subtracted (ADR-18).
  • Credential redaction is strengthened from a name denylist to segment-name plus value-shape matching: token prefixes, user:pass@ connection strings, PEM blocks, high-entropy blobs. This closes GHSA-7gr9-rqqx-xg6m. REFACTRON_FORWARD_ENV re-admits a var an operator knows is not a secret, and redaction stays best-effort, not a sandbox (ADR-20).

--flaky-check, an opt-in stability check

A default SAFE assumes your tests are deterministic. The suite runs once, so a test that passes because of randomness, ordering, timing or hash-seed dependence, rather than because behaviour is preserved, still counts toward SAFE. A single run cannot tell a stable green from a lucky one.

verify-diff --flaky-check reruns the suite K times (default 3) on fresh trees after a would-be SAFE, each under a different PYTHONHASHSEED. If any test's outcome varies, the green was never stable and the verdict floors at UNPROVEN, naming the flaky test. Off by default and slower, downgrade-only, and an inconclusive rerun that times out is skipped rather than held against you.

--mutate now perturbs constants

--mutate swapped only operators, so a changed line whose behaviour lives in a constant, a number, a string, True/False/None, or a bare return <literal>, generated zero mutants and earned SAFE even when no test asserted the value. Constants are now mutated too. Docstrings and other behaviour-inert literals are excluded by AST span, so an inert string cannot manufacture a false survivor, and the budget is filled operators-first so a constant never evicts a higher-signal mutant.