fix: fresh-review round — 2FA deliverability, disable re-verification, GDPR batch scrub, dispute alerting, docs accuracy
Second fresh-eyes review pass (7 agents: goal, security, code-quality, context-mining, webhooks+2FA, client+mock+sweep, refunds/giftcards/handlers). Money-safety core verified sound (identical-body replay byte-lossless, clawback gated on definitive proof, no double-charge window). This round fixes the issues the fresh pass surfaced: 2FA: - Setup now DELIVERS the code via the [2FA] server log in ALL modes (was: nothing in enforced mode -> production 2FA was an unbreakable dead-end and saved-card charges were permanently 403). Enforced mode still withholds the code from the API response; the log line is the fake delivery channel until email/SMS lands (P6). - Disabling 2FA now requires a fresh verification code when enforcement is ON (previously ignored the code -> a password-only attacker could lift the gate). Shares the 5-attempt lockout and timing-safe compare. Dev bypass retained. - REQUIRE_2FA parsing normalized (false/0/off/no, case-insensitive); startup warning extended to the empty-env/mock-client/enforced-2FA confusion. GDPR: - anonymize_user() SQL now scrubs two_factor_* columns + staff notes, so the idle-account batch cleanup (CleanupIdleAccounts) is erasure-clean, not just the user-initiated delete path. Webhooks: - dispute.created for an untracked Square payment now raises a critical_payment_log admin notification (chargeback the app can't reconcile is never silent). Reason strings truncated on rune boundaries (valid UTF-8). Stale at-most-once comment corrected; revertTillSaleGiftCardFunding duplication noted. Sweep/mock parity: - Mock CreatePayment dedup is now source-aware (IDEMPOTENCY_KEY_REUSED on source mismatch) matching ReplayPaymentByKey and real Square. - COMPLETED-but-never-polled terminal till-sale checkouts are now recorded by the sweep (previously only booking checkouts were; till charges were invisible until the 24h blind-fail WARN). - Legacy snapshot-less minimal-body replay, SQUARE_LOCATION_ID drift, and in-memory-mock-restart limitations documented. Docs: - Webhook path corrected everywhere (/webhooks/square, not /api/webhooks/square - a deployer following the old path would 404 and silently lose all webhook reconciliation). - 2FA enforcement semantics + code-delivery mechanism documented accurately (fail-closed default; log-delivery channel; disable re-verification). - README/User Manual note the 2FA requirement on online saved-card payments. Tests: 2,151 (up from 2,142). Backend 26/27 packages green (crussell/db fails only in this environment: local postgres doesn't offer scram-sha-256 for the test role; package is byte-identical to HEAD and untouched here). Frontend builds; svelte-check 0 errors.
This commit is contained in:
+18
-7
@@ -119,18 +119,29 @@ func initS3() {
|
||||
|
||||
func initSquare() {
|
||||
payments.SquareClient = square.NewClient()
|
||||
if env := os.Getenv("SQUARE_ENVIRONMENT"); env == "sandbox" || env == "production" {
|
||||
env := os.Getenv("SQUARE_ENVIRONMENT")
|
||||
if env == "sandbox" || env == "production" {
|
||||
fmt.Printf("Square client initialized (%s, real API)\n", env)
|
||||
} else {
|
||||
fmt.Println("Square client initialized (dev mock)")
|
||||
}
|
||||
// 2FA enforcement is fail-closed (see payments.twoFactorEnforced): it is
|
||||
// OFF only when REQUIRE_2FA=false or SQUARE_ENVIRONMENT explicitly selects
|
||||
// the dev/mock stack. Warn loudly when a non-dev env (empty/unknown — a
|
||||
// likely misconfiguration) leaves the gate disabled, so saved-card charges
|
||||
// can never silently ship without the PSD2 SCA stand-in.
|
||||
if os.Getenv("REQUIRE_2FA") == "false" && !payments.IsExplicitDevOrMockEnv() {
|
||||
log.Printf("WARNING: 2FA enforcement is OFF (REQUIRE_2FA=false) with SQUARE_ENVIRONMENT=%q (not an explicit mock/dev value). Online saved-card payments will NOT require 2FA.", os.Getenv("SQUARE_ENVIRONMENT"))
|
||||
// OFF only when REQUIRE_2FA explicitly disables it (false/0/off/no,
|
||||
// case-insensitive) or SQUARE_ENVIRONMENT explicitly selects the dev/mock
|
||||
// stack. Warn loudly when a non-dev env (empty/unknown — a likely
|
||||
// misconfiguration) leaves the gate disabled, so saved-card charges can
|
||||
// never silently ship without the PSD2 SCA stand-in.
|
||||
enforced := payments.NewPaymentService().TwoFactorEnforced()
|
||||
if !enforced && !payments.IsExplicitDevOrMockEnv() {
|
||||
log.Printf("WARNING: 2FA enforcement is OFF (REQUIRE_2FA=%q) with SQUARE_ENVIRONMENT=%q (not an explicit mock/dev value). Online saved-card payments will NOT require 2FA.", os.Getenv("REQUIRE_2FA"), env)
|
||||
}
|
||||
// The mirror-image confusion: enforcement is ON but the Square client fell
|
||||
// back to the in-memory mock (internal/square.NewDevClient only picks the
|
||||
// real API for sandbox/production) because SQUARE_ENVIRONMENT is empty or
|
||||
// unknown. The operator may believe they are in dev — warn so enforced-2FA
|
||||
// 403s on online saved-card payments do not arrive as a surprise.
|
||||
if enforced && env != "sandbox" && env != "production" {
|
||||
log.Printf("WARNING: 2FA enforcement is ON but SQUARE_ENVIRONMENT=%q is empty/unknown — the Square client is the dev mock while the 2FA gate stays enforced (fail-closed). Online saved-card payments will 403 until users enable 2FA; set SQUARE_ENVIRONMENT to a dev value (mock/dev/development/test) to lift the gate, or to sandbox/production for the real API.", env)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user