fix: adversarial review round — replay-rescue double-charge, discount credit, 2FA/per-IP limits, snapshot encryption, refund reconciliation, VAT, frontend parity, tests+docs
Addresses the adversarial fresh-eyes audit (findings A1-A20) plus review-round fixes: - CRITICAL A1: replay-by-key rescue cross-checks replayed CreatedAt; ccof blind-fail leaves pending with CRITICAL + notification instead of clawing back - A2/A3/A4: till idempotency key restored to unconditional hash; tip rejected in CreateBookingPayment; campaign discount now reduces the charged amount (deposit credit) - A5: admin notifications on blind-fail, manual-refund re-arm, cap-stranded charge-group, webhook FAILED/REJECTED refunds - A6/A10: BuyGiftCard idempotency user-scoped; gift-card slot scan advances past failed rows - A7/A14/A15: 2FA user+IP limiter, SNAPSHOT_ENC_KEY startup validation, accurate pepper/log-delivery docs - A8/A9: snapshot encryption on all write+reuse sites; MPV->SPV effective voucher type (single VAT point) - A11/A12/A13/A16: amount-aware refund reconciliation; completed-booking refund re-validation; till retry dedup; PaymentWasRefunded on SquareClient interface - A17/A18/A19/A20: CI runs npm test; confirm_overflow_tip frontend dialog; unknown-event admin notification; mock token redaction - M7 ConfirmOverflowTip, M9 snapshot encryption, C1 discount ordering regression test - Frontend vitest framework (41 tests), backend coverage for fixed functions, docs corrected (2,269 tests, SUPPORT_EMAIL tokens, resolution status) All 25 backend packages pass; frontend 41/41; build + env-docs green.
This commit is contained in:
+38
-3
@@ -55,7 +55,10 @@ SQUARE_ENVIRONMENT=mock
|
||||
# deliberately route a dev build to the real production API. Never set in a
|
||||
# deployed production build.
|
||||
SQUARE_ALLOW_REAL_API=
|
||||
# 2FA (PSD2 SCA stand-in) for online card payments. Enforcement is FAIL-CLOSED:
|
||||
# 2FA — merchant-level authorization gate on saved-card online payments (NOT
|
||||
# PSD2 SCA; Square buyer verification is the SCA mechanism and is wired for
|
||||
# new-card charges). Kept as an additional fraud control until Square buyer
|
||||
# verification is wired for saved-card charges. Enforcement is FAIL-CLOSED:
|
||||
# ON unless REQUIRE_2FA explicitly disables it (false/0/off/no, case-insensitive)
|
||||
# OR SQUARE_ENVIRONMENT explicitly equals one of mock/dev/development/test.
|
||||
# Empty or unknown SQUARE_ENVIRONMENT values are treated as production-enforced
|
||||
@@ -67,9 +70,29 @@ SQUARE_ALLOW_REAL_API=
|
||||
# environments an operator must relay the logged code to the user out-of-band;
|
||||
# the API never returns the code while enforcement is ON.
|
||||
REQUIRE_2FA=true
|
||||
# TWO_FACTOR_PEPPER — server-side pepper for HMAC-hashing 2FA codes (optional
|
||||
# pre-launch; if unset, codes are hashed without pepper and a warning is logged)
|
||||
# TWO_FACTOR_PEPPER — server-side pepper for HMAC-hashing 2FA codes. REQUIRED
|
||||
# in production builds: code issuance FAILS CLOSED when it is unset (an
|
||||
# unsalted SHA-256 digest in the 1M code space would be offline-brute-forceable
|
||||
# from a log/DB leak), mirroring JWT_SECRET_KEY's fail-fast stance. Optional
|
||||
# only in dev/test builds, where an unset pepper falls back to the legacy
|
||||
# digest with a one-time warning. Generate with:
|
||||
# openssl rand -base64 32
|
||||
TWO_FACTOR_PEPPER=
|
||||
# TWO_FACTOR_ALLOW_LOG_DELIVERY — defaults false. Production 2FA code issuance
|
||||
# FAILS CLOSED without a delivery channel: there is no email/SMS transport yet,
|
||||
# so the ONLY production channel is the operator's explicit opt-in to the
|
||||
# insecure server-log delivery ([2FA] prefix — anyone with backend log access
|
||||
# can defeat the gate on saved-card charges). MUST be set to true to deliver
|
||||
# 2FA codes via the server log in production until email/SMS lands. Dev/test
|
||||
# builds always deliver via the log and never consult this flag.
|
||||
TWO_FACTOR_ALLOW_LOG_DELIVERY=false
|
||||
# SNAPSHOT_ENC_KEY — base64-encoded 32-byte AES-256 key for encrypting stored
|
||||
# square_request_snapshot rows (buyer PII: email + ccof card tokens) at rest in
|
||||
# non-mock (production/sandbox) deployments. If unset/invalid, snapshots fall
|
||||
# back to PLAINTEXT with a one-time CRITICAL log warning (money-safety first:
|
||||
# the replayable snapshot must not be lost). Generate with:
|
||||
# openssl rand -base64 32
|
||||
SNAPSHOT_ENC_KEY=
|
||||
# Webhook config MUST exactly match the Square Dashboard webhook subscription
|
||||
# (URL + signature key). If SQUARE_WEBHOOK_NOTIFICATION_URL is left unset it
|
||||
# defaults to http://localhost:8080/webhooks/square, which is fail-closed (503
|
||||
@@ -119,6 +142,18 @@ VITE_BACKEND_URL=http://localhost:8080
|
||||
# http://localhost:5173 when unset.
|
||||
FRONTEND_ORIGIN=http://localhost:5173
|
||||
|
||||
# TRUST_PROXY_HEADERS — defaults false. Set to true ONLY when a trusted proxy
|
||||
# (nginx and/or the Cloudflare edge) sits between clients and this backend and
|
||||
# overwrites X-Real-IP / CF-Connecting-IP with the real client IP. When true,
|
||||
# the per-IP rate limiter keys requests on those proxy-set headers and main.go
|
||||
# registers chi's ClientIPFromHeader("X-Real-IP") middleware. MUST be true
|
||||
# behind nginx/Cloudflare, or every request keyed by IP collapses onto the
|
||||
# proxy's IP — one client exhausting the limit throttles everyone, and per-IP
|
||||
# limiter protection is effectively bypassed. MUST stay false when the backend
|
||||
# is origin-exposed: a client talking directly to the backend could otherwise
|
||||
# rotate X-Real-IP/CF-Connecting-IP to bypass per-IP rate limiting.
|
||||
TRUST_PROXY_HEADERS=false
|
||||
|
||||
# Local S3 (Rustfs) — requires GO_TESTING=1 or dev build tag
|
||||
# These are dev-only overrides used by the dev S3 implementation
|
||||
RUSTFS_ENDPOINT=http://rustfs:9000
|
||||
|
||||
Reference in New Issue
Block a user