Anti-Tamper Probe Enablement LLD
This LLD defines how anti-tamper probe execution is enabled, how runner enforcement is gated, and how diagnostics differ from enforcement.
1. Purpose#
This LLD defines how anti-tamper probe execution is enabled, how runner enforcement is gated, and how diagnostics differ from enforcement.
2. Problem Statement#
Users often confuse:
- probe execution enablement
- process-protection enforcement enablement
- policy action after detection
This design separates these decisions clearly.
3. Gates#
There are no configurable inputs. Every gate below is a compile-time constant or is derived from the execution mode on the command line; none is settable, and none was ever read from a file. The four environment variables this section used to document -- a master probe gate, a process-protection gate, a tamper-response override and a protection-profile selector -- were removed along with every other environment read. Mutant takes no configuration from the environment; see CONFIGURATION_POLICY.md.
3.1 Master probe gate — always on#
antiTamperProbeEnabled in security/antitamper_probe.go is a package-level
true. isAntiTamperProbeEnabled() returns it, and RunAntiTamperProbe
short-circuits to (nil, false, nil) when it is false.
The only thing that flips it is SetAntiTamperProbeEnabledForTesting, which
exists so a test can assert the disabled path. In a shipped binary it is never
called, so probes always run.
3.2 Process-protection gate — always on#
isProcessProtectionEnabled() in runner/runner.go returns true. The runner
reaches it through the processProtectionOn package variable, which tests
replace to exercise the skip path.
3.3 What actually varies: the response#
Probing is unconditional; what a detection does is not. Two things decide that:
- The confidence threshold.
processProtectionTerminateConfidence = 80inrunner/runner.go. A signal below it is ignored by the runner, whatever it detected. - The execution mode, from argv.
ResolveTamperResponse(secureMode)returnsterminatein secure mode (the default) andwarnunder--compator--dev. The protection profile feeds into this, butResolveProtectionProfile()returns the constantstandard, so theminimalandparanoidbranches are unreachable at runtime; they survive only soProtectionProfileFromCodecan read back a V3 release trailer.
4. Decision Model#
The gate diamonds the previous version of this diagram carried are gone because the gates they tested are gone: both are constants, so the only branch left in the enablement path is the caller's own, and the only genuine decision points are the confidence threshold and the execution mode.
5. Caller Semantics#
5.1 Runner#
- Uses focused 5-probe enforcement set.
- Applies confidence threshold (
>= 80). - Triggers policy action on threshold hit.
5.2 Builtins#
- Use broader probe sets for diagnostics.
- Return probe signals to scripts/users.
- Do not directly enforce runner blocking path.
6. Observability#
Telemetry events:
anti_tamper_probe_invokedanti_tamper_probe_errorprocess_protection_detected
7. Risks and Mitigations#
Risk:
- False negatives because probing was switched off in a deployment.
Mitigation:
- Structurally removed. There is no switch: both gates are constants, and the only setter is test-only. A deployment cannot be misconfigured into running with probes off, and there is nothing to check for at deploy time.
Risk:
- False positives when a single heuristic is noisy.
Mitigation:
- Keep confidence thresholding and multi-probe context.
- Review
detailfield before escalation. --compatdowngrades the response to a warning, so a noisy environment can still run the program while the signal stays visible on stderr.
8. Student Takeaway#
There are three separate questions:
- Did probes run? Always yes.
- Was enforcement enabled? Always yes, on the runner path.
- What did policy decide? The only real variable -- the confidence threshold and the execution mode from argv.
Keeping these separate prevents most operational confusion. The first two used to be configurable and were the usual source of it.