rotate: LIVE-REAL proof level + execute-time proof gate (M2)
Add ProofLiveReal (highest level, no driver yet) and enforce it at execute time: rotate --execute only runs drivers with proof >= LIVE-VM (MinExecuteProof). MOCK-ONLY/UNPROVEN drivers are skipped before any provider call unless --allow-mock-proven is passed. ExecuteResult gains Skipped/SkipReason; the CLI reports and audit-logs skips separately from failures. Closes the last structural gap where an emulator-only driver could touch a real credential unattended. Covered by execute_gate_test.go. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,8 @@ type ExecuteResult struct {
|
||||
Verified bool // the new secret authenticated
|
||||
Stored bool // the new secret was written to gopass and re-read OK
|
||||
Revoked bool // RevokeOld was invoked
|
||||
Skipped bool // gated off by the proof policy — the driver was NOT run
|
||||
SkipReason string // why it was gated (only when Skipped)
|
||||
Err error // first error for this credential, if any
|
||||
}
|
||||
|
||||
@@ -35,7 +37,14 @@ type ExecuteResult struct {
|
||||
// The MANDATORY backup gate (Snapshot) MUST already have succeeded — Execute does
|
||||
// not perform it. Each credential's StorePath is taken from c.Location (the exact
|
||||
// gopass path it was sourced from), so the new secret overwrites the same entry.
|
||||
func Execute(ctx context.Context, gp *sink.Gopass, creds []discover.Credential, v *vault.Vault) []ExecuteResult {
|
||||
//
|
||||
// Proof gate (Hard Rule spirit — no unattended real cutover through an unvalidated
|
||||
// path): a driver runs only if its recorded proof level is ≥ MinExecuteProof
|
||||
// (LIVE-VM). Drivers proven only against an emulator/mock (MOCK-ONLY) or with no
|
||||
// proof at all (UNPROVEN) are SKIPPED — their Rotate/Verify/RevokeOld are never
|
||||
// called — unless the caller passes allowMock, the explicit --allow-mock-proven
|
||||
// opt-in. Skipped credentials leave the old secret fully intact.
|
||||
func Execute(ctx context.Context, gp *sink.Gopass, creds []discover.Credential, v *vault.Vault, allowMock bool) []ExecuteResult {
|
||||
drivers := Drivers()
|
||||
var out []ExecuteResult
|
||||
for _, c := range creds {
|
||||
@@ -51,6 +60,16 @@ func Execute(ctx context.Context, gp *sink.Gopass, creds []discover.Credential,
|
||||
}
|
||||
res := ExecuteResult{Credential: c, Driver: d.Name(), StorePath: c.Location}
|
||||
|
||||
// Proof gate: refuse to run a driver that has not been proven against real
|
||||
// target software, unless the operator explicitly opted in. This is checked
|
||||
// BEFORE any provider call, so a gated driver touches nothing.
|
||||
if lvl := ProofLevelOf(d.Name()); lvl < MinExecuteProof && !allowMock {
|
||||
res.Skipped = true
|
||||
res.SkipReason = fmt.Sprintf("proof %s < %s; pass --allow-mock-proven to override", lvl, MinExecuteProof)
|
||||
out = append(out, res)
|
||||
continue
|
||||
}
|
||||
|
||||
newH, err := d.Rotate(ctx, c, v)
|
||||
if err != nil {
|
||||
res.Err = fmt.Errorf("rotate: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user