fix: loop-B adversarial (503c326 baseline) — IDEMPOTENCY_KEY_REUSED reclassified ambiguous, 2FA reissue fail-closed alerts, family-cache crash window, consolidation regression checks
Loop B red-team (money/security/dup-mod adversarial) findings on the full payments overhaul: - CRITICAL-ish: IDEMPOTENCY_KEY_REUSED (409) no longer classified as a definitive 402 in chargeFailureStatus — it means the ORIGINAL charge may have landed with a different body, so it is now AMBIGUOUS (503): the frontend keeps the same idempotency key, the pending row stays rescuable by the sweep (which already treated it as ambiguous), and the frontend no longer regenerates the key into a possible double charge. SCA verification-required codes remain definitive 402. - HIGH: reissueTwoFACodeAfterFailedCharge now writes a CRITICAL admin notification (insertCriticalPaymentNotification) when issuance is refused (missing pepper / unavailable delivery) instead of silently stranding the customer; documented that a pepper CHANGE invalidates all pending codes. - MEDIUM: family-alive cache invalidation crash window documented (invalidate-after- commit leaves up to 30s warm on a crash; the near-TTL DB re-check bounds it). - Consolidation regression checks (8b2fe3b helpers): writeChargeSnapshot guard preserved at all sites, postChargeRecheck identical, squareRefundStatusToLocal mappings verified, reissue fresh-only semantics confirmed at all 5 call sites. Verified: 26/26 dev packages, both vet tags, frontend tests + build, env-docs 42/42.
This commit is contained in:
@@ -75,6 +75,16 @@ func twoFADeliveryAvailable() bool {
|
||||
// stored code would be an offline-brute-forceable unsalted digest. Either way
|
||||
// issuance is refused (fail-closed). Dev/test builds always allow issuance
|
||||
// (twofa_dev.go).
|
||||
//
|
||||
// The pepper check is the ONLY hard gate here (plus the delivery channel), and
|
||||
// it is also the ONLY hard gate on the payments re-issue path
|
||||
// (payments.twoFAReissueIssueAllowed). PEPPER-CHANGE HAZARD (Loop B finding 2):
|
||||
// the pepper keys the HMAC-SHA256 of every stored pending-code hash, so
|
||||
// CHANGING TWO_FACTOR_PEPPER invalidates ALL pending codes — every stored hash
|
||||
// was computed with the old pepper and can never match a code minted under the
|
||||
// new one. An operator who changes the pepper must re-mint every user's code
|
||||
// (or have each user re-run 2FA setup), or enforced saved-card charges will
|
||||
// strand customers with 400 ErrMissingOrExpired forever.
|
||||
func twoFAEnsureIssueAllowed() error {
|
||||
if os.Getenv(twoFAPepperEnv) == "" {
|
||||
return errTwoFAPepperRequired
|
||||
|
||||
Reference in New Issue
Block a user