Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Every issue inherits `AGENTS.md` and the definition of done in `IMPLEMENTATION_P
| 2 | CI-001 | Repair test gates and secure the deployment pipeline | P0 | L | partial: #103 baseline + #124 hosted Jest; #126 SPA gate and remaining gates open | SAFETY-001 |
| 3 | SUPPLY-001 | Remove vulnerable dependency chains and stage SDK/build upgrades | P0 | L | partial: SUPPLY-001A tracked by #127; remaining upgrade/scanning slices open | CI-001 baseline |
| 4 | SEC-001 | Replace the Firestore admin catch-all with resource-specific rules | P0 | M | source merged #123; Firebase live unproven | — |
| 5 | CONFIG-001 | Fail closed on server environment and commerce configuration | P0 | M | ready | CI-001A recommended |
| 5 | CONFIG-001 | Fail closed on server environment and commerce configuration | P0 | M | CONFIG-001A tracked in [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149); CONFIG-001B proposed; not deployed | CI-001A recommended |
| 6 | AUTH-001 | Require verified email for member and privileged claims | P0 | M | partial: #98 merged; backend live unproven; parent open | SEC-001 recommended |
| 7 | AUTH-002 | Replace the legacy static-key membership synchronization endpoint | P0 | M | ready | AUTH-001 |
| 8 | OAUTH-001 | Make Strava token lifecycle server-only, transactional, and auditable | P0 | M | represented by live [#88](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/88); proposed/unclaimed | SAFETY-001/#99; SEC-001 and AUTH-003 for A; staged deferral/cutover with ABUSE-001A for C |
Expand Down Expand Up @@ -274,13 +274,15 @@ Do not compensate by moving sensitive data into a different admin-readable docum
## CONFIG-001 — Fail closed on server environment and commerce configuration

**Labels:** `priority:P0`, `type:security`, `type:reliability`, `area:firebase`, `area:stripe`, `size:M`
**Status:** Ready
**Status:** CONFIG-001A source/tests are tracked in live issue [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149), which remains the status source for review and merge evidence. CONFIG-001B remains proposed. No Firebase/Stripe configuration or live behavior is proven by source.
**Depends on:** CI-001A recommended

### Problem

Several Functions default a missing `SITE_ORIGIN` to the production domain; until this pass the webhook accepted a missing expected Stripe mode. `ENVIRONMENT_NAME` and `COMMERCE_ENABLED` are documented but not validated or enforced. A missing/mistyped deployment variable can therefore create unsafe URLs, mix live/test behavior, or leave no operational kill switch.

CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) owns only the typed invocation-time environment/origin/expected-mode boundary, server-key mode compatibility on the four key-bound Functions, removal of production-origin fallbacks, and fixed no-side-effect tests. Webhook and mail remain free of the Stripe API key. CONFIG-001B separately owns `COMMERCE_ENABLED`, global/per-domain switches, refund/incident continuity, and switch-race tests; do not combine the children.

### Scope

- Create one typed server-configuration module with an allowlisted environment name, required validated site origin, explicit expected Stripe livemode, commerce-enabled state, and safe emulator behavior.
Expand Down
2 changes: 1 addition & 1 deletion GITHUB_ISSUE_SLICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de

| ID | Status/dependency | Bounded deliverable | Close evidence |
| --- | --- | --- | --- |
| CONFIG-001A | ready; CI-001A | Add typed server configuration requiring environment name, validated HTTPS/local origin, explicit Stripe mode, and environment/key compatibility; remove production fallbacks. | Missing/malformed/local-live tests fail before Firestore/Stripe/email. |
| CONFIG-001A | source/tests tracked in live [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149); CI-001A; not deployed/configured | Add typed server configuration requiring environment name, exact hosted/loopback origin, explicit Stripe mode, and key-bound environment/key compatibility; remove production fallbacks while keeping webhook/mail least-privilege. | Missing/malformed/local-live tests fail after auth/signature but before rate-limit/Firestore/Stripe/mail/ledger work; merge and provider evidence remain separate. |
| CONFIG-001B | proposed; CONFIG-001A | Implement a server-owned global/per-domain commerce kill switch checked by every Session/refund/catalog command while webhook/reconciliation remain active. | Disabled-command tests prove no external call; webhook/reconcile tests still pass. |
| PROMO-001 | source/tests complete in [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102); provider inventory and deployment remain owner action; PAY-001A for future enablement | Disable promotion entry and automatic tax in both Session creators; require a complete Stripe breakdown; quarantine unknown or nonzero discount, tax, or shipping adjustments. | Exact Session payload assertions and signed zero/partial/full/malformed/failed/expired adjustment tests pass; pre-change Session/provider inventory and live verification remain open. |
| PAY-001A | ready; CI-001A | Add shared strict-object, bounds/depth, integer-cents/currency/date/URL/email primitives, safe errors, and redacted logging. | Prototype/deep/Unicode/money/unknown-key tests prove no external call. |
Expand Down
2 changes: 1 addition & 1 deletion IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ The first tranche deliberately combines one urgent safety repair with the most c
2. **PAY-003 — webhook event inbox and safe reducer:** PAY-003A [#101](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/101) source merged through [PR #140](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/140) as `87bd1210575882cdd06214bcb98ed38ce00f71c2`, adding event replay protection, async-aware payment confirmation, expected total/currency validation, direct metadata lookup, terminal-state guards, and lifecycle tests. Firebase and Stripe live behavior remain unproven; PAY-003B/C remain open.
3. **SEC-001 — Firestore trust boundaries:** #100 merged through [PR #123](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/123) at `a7fc301e85b0aeabe396e771faea21d3fc8e7b2b`. Source and 295 Rules tests remove recursive browser-admin authority. Firebase deployment explicitly skipped, so deployed/live Rules remain unproven under #105.
4. **PROMO-001 — fail-safe monetary policy:** [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102) disables promotion entry and automatic tax in both current creators, adds exact payload tests, and quarantines unknown or nonzero discount/tax/shipping adjustments on success, failure, and expiry. The private pre-change Session/provider inventory, protected deployment, provider readback, and live verification remain owner actions; no discount, tax, or shipping feature is authorized.
5. **Identity/config next:** AUTH-001A [#98](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/98) merged at `ce22c110`, proving source/tests for unverified-target rejection at the existing role-grant endpoints. Its Firebase deployment skipped, so backend-live status is unproven and the remaining AUTH-001 parent work stays open. Continue CONFIG-001 and the uncompleted AUTH-001 guards/Rules/refresh/audit scope without duplicating #98.
5. **Identity/config next:** AUTH-001A [#98](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/98) merged at `ce22c110`, proving source/tests for unverified-target rejection at the existing role-grant endpoints. Its Firebase deployment skipped, so backend-live status is unproven and the remaining AUTH-001 parent work stays open. CONFIG-001A is tracked in live child [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) for the typed environment/origin/Stripe-mode source boundary; CONFIG-001B retains the later commerce stop-switch matrix. Follow the live issues and do not duplicate either #98 or #149.
6. **Then:** PAY-001/PAY-002 so checkout creation, refunds, and every later handler share strict schemas and idempotent state.
7. **Then:** RACE-001 and MERCH-001 transactional reservations.

Expand Down
27 changes: 27 additions & 0 deletions OPERATIONS_RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,33 @@ COMMERCE_ENABLED
ENVIRONMENT_NAME
```

### Typed server configuration gate — source in #149, not deployed

[CONFIG-001A / #149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) makes the current commerce and confirmation-mail Functions validate configuration at each invocation. Parsing happens at runtime, not while Firebase discovers Functions for deployment. The returned configuration is frozen and contains no Stripe key.

| Environment | Accepted site origin | Expected Stripe mode | Accepted server-key mode |
| --- | --- | --- | --- |
| `local` | Exact loopback `http` or `https` origin | Test | Test |
| `test` | Canonical HTTPS `.test` origin | Test | Test |
| `staging` | `https://dev.runmprc.com` | Test | Test |
| `production` | `https://runmprc.com` | Live | Live |

An origin is only the scheme, host, and optional non-default port. Credentials, paths, trailing slashes, queries, fragments, padded values, and default-port aliases are rejected. Test/live compatibility uses only the documented `sk_` or restricted `rk_` mode marker. The complete key is never returned, logged, stored in configuration, or copied into evidence.

```mermaid
flowchart TD
Entry["Commerce or mail Function invoked"] --> First["Check caller, webhook signature, or whether mail work applies"]
First --> Parse{"Environment, origin, mode, and required key marker valid?"}
Parse -- "No" --> Stop["Fixed configuration error\nno rate-limit, business, mail, ledger, or Stripe write"]
Parse -- "Yes" --> Existing["Run existing validation and business logic"]
```

Text alternative: after identity, signature, or mail-eligibility checks, invalid server settings stop the invocation before any business, mail, event-ledger, or Stripe side effect.

The webhook and confirmation-mail triggers validate environment, origin, and expected mode without receiving the Stripe API key. Only the two checkout creators and two admin action Functions bind and validate that key. `COMMERCE_ENABLED` and global/per-domain stop-switch behavior remain **NOT AVAILABLE YET** under CONFIG-001B.

Source tests do not prove Firebase parameters, Secret Manager bindings, Stripe account mode, or production behavior. Before deployment, record only parameter names in public evidence. Store values and provider identifiers in the approved private continuity record. Use a protected staging release with made-up data; never discover configuration by trying a real payment or email.

`functions.config()` is deprecated and scheduled for decommissioning in March 2027. The legacy member-sync API key must be retired or migrated as its own security issue, not copied into a new generic secret without redesign.

### Secret rotation record
Expand Down
1 change: 1 addition & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ These entries are implementation evidence, not a production risk-acceptance deci
| RISK-006, part of RISK-024/RISK-032 | #100 merged at `a7fc301e85b0aeabe396e771faea21d3fc8e7b2b`. OAuth secrets and server-owned financial/operational collections are denied; current catalog writes are restricted to inert drafts/display fields. | Firebase deployment skipped. Stage/deploy the exact Rules revision under #105, verify synthetic deny behavior, then move remaining catalog mutation to scoped APIs under ADMIN-001. |
| RISK-009 | #99 preserves same-origin path, query, and fragment state through the Pages fallback, clears temporary state, rejects unsafe targets, and suppresses monitoring startup on initial capability callbacks. #126 adds the standalone suite as the named blocking `Run SPA callback safety tests` hosted step. | Keep exact hosted evidence on #126; verify exact website publication and later run approved test-mode Stripe/Strava callbacks; WEB-001 still owns controlled hosting. |
| RISK-010 | #99 uses a fully synthetic `demo-mprc-local` configuration, connects Functions/Auth/Firestore to loopback in development/test, routes the direct CSV Function URL locally, fails startup on connector errors, and completed a demo-only three-port CLI smoke. | Current optimized previews still target production; provider calls are not isolated by Firebase. Add protected staging/config and test-provider/sink evidence before end-to-end flows. |
| Configuration part of RISK-001/RISK-010 | CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) source adds one invocation-time server configuration boundary. It requires an exact environment/origin/expected Stripe mode, validates test/live `sk_` or restricted `rk_` server-key compatibility only on the four key-bound Functions, removes production-origin fallbacks, and gives webhook/mail no Stripe API key. Fixed negative tests stop before rate-limit, Firestore, mail, event-ledger, or Stripe work; returned configuration and errors contain no complete key or unsafe supplied value. | Source review and merge do not configure Firebase or Stripe. Verify non-secret parameter names, Secret Manager bindings, exact staging/production modes, protected deployment, and provider readback privately. CONFIG-001B still owns the commerce stop-switch matrix; no live payment or email is authorized. |
| Part of RISK-007 | AUTH-001A [#98](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/98) was merged through [PR #107](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/107) as `ce22c110f2132b157bd8a0d43b065585e0b43cb5`. Source and focused tests reject unverified targets at the two existing role-grant endpoints. | The workflow skipped Firebase because `FIREBASE_SERVICE_ACCOUNT` was absent, so backend-live behavior is unproven. Remaining AUTH-001 guards, Rules, verification mirror/refresh, demotion propagation, audit, and authoritative membership work stay open. |
| RISK-013 | PROMO-001 [#102](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/102) sets both current Checkout creators to promotion entry off and automatic tax off. Exact payload and signed event tests require an explicit all-zero breakdown; malformed or nonzero discount, tax, or shipping data creates durable review evidence while definitive failed/expired Sessions still cancel. | Privately inventory pre-change Sessions/provider promotion settings, keep the guard through protected staging/deploy, verify provider and live state separately, and implement an approved authoritative discount/tax/shipping contract before ever enabling it. |
| Part of RISK-014 | SUPPLY-001A [#127](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/127) removes the unused direct `gpxparser` dependency and 50 lockfile entries without changing any retained package version, source URL, or integrity hash. A fresh Node 20 install lowers the root production audit from 33 findings (3 critical, 9 high) to 22 (1 critical, 8 high), and the optimized production asset hashes remain identical. | Remediate or time-bound the remaining root and Functions findings through the separately tested router, Firebase, Stripe, build-system, and scanning slices; do not use a forced audit upgrade. |
Expand Down
2 changes: 2 additions & 0 deletions STRIPE_COMMERCE_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ The issue sequence in [GITHUB_ISSUES.md](./GITHUB_ISSUES.md) closes these gaps i

Never let a development build fall back to production Functions. Never share a webhook signing secret between endpoints or modes. A test-mode Event cannot mutate a production business record; the webhook processor must compare the incoming Stripe `livemode` flag to its environment configuration.

CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) implements this matrix as an invocation-time source guard for the current Functions. Configuration parsing returns only the environment name, canonical site origin, and expected livemode; it never returns or stores the complete Stripe key. The two checkout creators and two admin action Functions validate only the documented test/live marker on their bound `sk_` or restricted `rk_` server key. Webhook and confirmation-mail Functions validate the non-secret matrix without receiving that key. Invalid configuration returns a fixed failure before any local business or outside-provider side effect. Provider parameters, secrets, account mode, deployment, and live behavior still require separate private evidence.

### Secret inventory

| Name | Consumer | Storage | Notes |
Expand Down
2 changes: 2 additions & 0 deletions SYSTEM_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,8 @@ Optimized builds—including current Netlify previews and a locally served `buil

`SITE_ORIGIN`, Firebase project IDs, App Check keys, Stripe keys, webhook endpoints, Sentry environments, and email providers must be environment-scoped. Live and test webhook secrets are never interchangeable. The repository still needs an explicit staging project before end-to-end rollout.

CONFIG-001A [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) adds the source boundary for current commerce and confirmation-mail Functions: local is exact loopback, tests use HTTPS `.test`, staging is exactly `https://dev.runmprc.com`, and production is exactly `https://runmprc.com`. Test/live expected mode must match the server-key marker on the four key-bound Functions. Webhook and mail receive no Stripe API key, and invalid settings stop before rate-limit, business, mail, event-ledger, or Stripe writes. This source boundary does not create staging, configure Secret Manager or Stripe, deploy Firebase, enable commerce, or prove live behavior; CONFIG-001B and #105/#133/#136 retain those responsibilities.

## 11. Security, privacy, and compliance posture

Hosted Checkout reduces MPRC's card-data scope because payment details are entered on Stripe's domain, but it does not make the surrounding system automatically secure or compliant. MPRC still owns access control, application security, privacy disclosures, waiver evidence, fulfillment, refunds, disputes, incident response, vendor configuration, and data retention.
Expand Down
29 changes: 29 additions & 0 deletions docs/officers/PUBLISH_AND_CHECK.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,34 @@ As of **2026-07-13**:
- Git-triggered Netlify production builds are paused. Netlify build hooks are not controlled by that repository rule and remain unverified.
- Live race signup, merchandise payments, and refunds remain unavailable.

### Commerce server safety gate — SOURCE ONLY, NOT DEPLOYED

Issue [#149](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/149) adds a source-code check for the server environment, website address, Stripe test/live mode, and server-key mode. It does not configure Firebase or Stripe. It does not make payments available.

```mermaid
flowchart LR
Request["Signup, shop, admin, webhook, or mail work"] --> Identity["Check App Check, officer access, webhook signature, or whether mail work applies"]
Identity --> Config{"Server settings match?"}
Config -- "No" --> Stop["Stop before database, mail, or Stripe work"]
Config -- "Yes" --> Continue["Continue with the existing safety checks"]
```

In words: the server first checks identity, the webhook signature, or whether mail work applies. Wrong or missing settings then stop qualifying work before business data, email, or Stripe can change.

If a member or officer sees **Server configuration is unavailable**:

1. Stop.
2. Do not retry checkout, refund, or late registration.
3. Record the page and time without member or payment details.
4. Ask the platform owner to check the private environment record.
5. Never paste a key, setting value, screenshot of a console, or member details into an issue or AI tool.

**Expected result:** no registration, order, refund, mail, or Stripe object is created while settings are invalid.

**Undo:** revert the reviewed source change if it causes a false stop. Never restore a default production website address.

**Escalation:** platform owner, then the finance owner if a payment might already exist in Stripe.

## Before merge

1. Open the pull request.
Expand Down Expand Up @@ -142,6 +170,7 @@ Stop and contact the platform owner if:
- The commit is not merged into `main`.
- The release request is more than 24 hours old.
- The environment, project, or fixed scope is missing or wrong.
- The site reports **Server configuration is unavailable**.
- Anyone asks for a service-account key, token, password, or recovery code.
- Firebase is skipped, partial, failed, or unverified.
- A website publication job starts before backend verification.
Expand Down
Loading
Loading