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
20 changes: 20 additions & 0 deletions .github/scripts/validate-staging-authority-context.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash

set -euo pipefail

fail() {
printf '%s\n' 'staging_authority_context_invalid' >&2
exit 1
}

[[ "${GITHUB_REPOSITORY:-}" == 'Run-MPRC/Run-MPRC.github.io' ]] || fail
[[ "${GITHUB_REPOSITORY_ID:-}" == '718285092' ]] || fail
[[ "${GITHUB_REPOSITORY_OWNER_ID:-}" == '150727922' ]] || fail
[[ "${GITHUB_REF:-}" == 'refs/heads/main' ]] || fail
[[ "${GITHUB_WORKFLOW_REF:-}" == 'Run-MPRC/Run-MPRC.github.io/.github/workflows/verify-staging-authority.yml@refs/heads/main' ]] || fail
[[ "${TARGET_ENVIRONMENT:-}" == 'staging' ]] || fail
[[ "${TARGET_PROJECT_ID:-}" == 'run-mprc-staging' ]] || fail
[[ "${DEPLOY_SERVICE_ACCOUNT:-}" == 'mprc-staging-deploy@run-mprc-staging.iam.gserviceaccount.com' ]] || fail
[[ "${WORKLOAD_IDENTITY_PROVIDER:-}" == 'projects/384996399199/locations/global/workloadIdentityPools/github-actions/providers/run-mprc-staging' ]] || fail

printf '%s\n' 'staging_authority_context_valid'
28 changes: 28 additions & 0 deletions .github/scripts/verify-staging-project-read.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash

set -euo pipefail

fail() {
printf '%s\n' 'staging_authority_read_failed' >&2
exit 1
}

[[ "${TARGET_PROJECT_ID:-}" == 'run-mprc-staging' ]] || fail
[[ -n "${GCP_ACCESS_TOKEN:-}" ]] || fail

response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT

curl --fail --silent --show-error --request GET \
--header "Authorization: Bearer ${GCP_ACCESS_TOKEN}" \
--header 'Accept: application/json' \
'https://cloudresourcemanager.googleapis.com/v3/projects/run-mprc-staging' \
--output "$response_file" || fail

jq -e '
.projectId == "run-mprc-staging"
and .name == "projects/384996399199"
and .state == "ACTIVE"
' "$response_file" >/dev/null || fail

printf '%s\n' 'staging_authority_read_verified'
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ jobs:
- name: Validate protected release workflow
run: |
ruby -e 'require "yaml"; YAML.safe_load(File.read(".github/workflows/deploy.yml"), permitted_classes: [], permitted_symbols: [], aliases: true)'
ruby -e 'require "yaml"; YAML.safe_load(File.read(".github/workflows/verify-staging-authority.yml"), permitted_classes: [], permitted_symbols: [], aliases: true)'
ruby -e 'require "yaml"; YAML.safe_load(File.read(".github/workflows/ci.yml"), permitted_classes: [], permitted_symbols: [], aliases: true)'
node --test tests/ci-workflow.test.js tests/release-workflow.test.js tests/firebase-release-verification.test.js tests/firebase-hosting-foundation.test.js tests/test-artifact-safety.test.js tests/root-dependency-security.test.js
node --test tests/ci-workflow.test.js tests/release-workflow.test.js tests/firebase-release-verification.test.js tests/firebase-hosting-foundation.test.js tests/staging-authority-workflow.test.js tests/test-artifact-safety.test.js tests/root-dependency-security.test.js
- name: Build
env:
CI: 'true'
Expand Down
50 changes: 50 additions & 0 deletions .github/workflows/verify-staging-authority.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Verify staging cloud authority

run-name: Verify protected staging authority from ${{ github.sha }}

on:
workflow_dispatch:

permissions: {}

concurrency:
group: verify-staging-authority
cancel-in-progress: false

jobs:
verify-staging-authority:
name: Read exact staging project through protected OIDC
runs-on: ubuntu-latest
timeout-minutes: 5
environment:
name: staging
permissions:
contents: read
id-token: write
env:
TARGET_ENVIRONMENT: staging
TARGET_PROJECT_ID: ${{ vars.FIREBASE_PROJECT_ID }}
steps:
- name: Check out reviewed authority verifier
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Validate immutable repository and protected environment context
env:
DEPLOY_SERVICE_ACCOUNT: ${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}
WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
run: .github/scripts/validate-staging-authority-context.sh
- id: auth
name: Obtain one short-lived read-only Google Cloud token
uses: google-github-actions/auth@7c6bc770dae815cd3e89ee6cdf493a5fab2cc093 # v3
with:
token_format: access_token
access_token_lifetime: 300s
create_credentials_file: false
export_environment_variables: false
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}
- name: Read and verify only the exact staging project identity
env:
GCP_ACCESS_TOKEN: ${{ steps.auth.outputs.access_token }}
run: .github/scripts/verify-staging-project-read.sh
2 changes: 2 additions & 0 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ Do not redesign routing or hosting in this issue; WEB-001 owns that. WEB-001A1 [
**Status:** Published as tracker [#105](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/105). [#103](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/103), [#124](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/124), and [#126](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/126) provide the local/hosted frontend and SPA baselines. CI-001B3 [#167](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/167) owns the dedicated hosted command-journal Firestore transaction gate. CI-001B4 [#186](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/186) merged through [PR #189](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/189) as exact main `bec7d5e365eacb418563a172029f241f660d9768`; exact PR run `29291402007` and post-merge run `29291515653` passed. CI-001B4A [#227](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/227), CI-001B4B [#239](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/239), and PAY-004C1 [#359](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/359) are focused reviewed reductions of that ledger. [PR #391](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/391) and [PR #392](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/392) add three TypeScript files while retiring four TSX errors during functional work. CI-001B4C [#449](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/449) retires four stale unknown-rule directive records through comment-only same-line substitutions. MEMBERS-CONTENT-001B [#492](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/492) removes one finding-free dormant JSX file. The gate now records 110 files, 113 configured errors, and 6 warnings. [#135](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/135) owns the manual exact-commit, backend-first source gate and Git-triggered Netlify production containment. [#133](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/133) owns protected OIDC/environment configuration, and [#136](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/136) owns the actual profile-recovery release. Branch-required-check configuration, scanning, staging/live evidence, remaining lint-debt cleanup, and protected Netlify publication remain open, so the pipeline is still non-compliant overall.
**Depends on:** SAFETY-001

**Current CI-001C boundary:** CI-001C1 [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) supplies the staging-only protected read-only OIDC proof: exact immutable repository/owner IDs, `main`, the `staging` environment, one verifier workflow, two named individual reviewers, and a keyless principal with only project-identity read permission. Parent #133 still owns Firebase deployment roles and production protection; #136 still owns an actual backend release.

### Problem

Before #103, frontend tests failed because the Jest environment lacked `TextEncoder`. That child repaired the local baseline, and #124 adds it to hosted CI. #167 originally added a separate named Node 20/Java 17 job for the PAY-002B2A Firestore transaction suite and made both protected-release CI reads require it. SUPPLY-001D9/#455 moves the current source to Java 21 and exact Firebase CLI 15.24.0 while preserving one explicit demo project, Firestore-only scope, committed lockfiles, and no cloud/provider authority. CI-001B4/#186 replaces the mutating, ignored `lint:fix` step with a blocking exact-record check that initially covered 102 JS/JSX/TS/TSX files. It disables the repository-wide severity-masking `eslint-plugin-only-warn` hook inside the lint process. CI-001B4A/#227 removes exactly one reviewed `arrow-body-style` record without changing the rule or gate. CI-001B4B/#239 removes exactly one stale `AdminMembers` unknown-rule suppression record without changing executable statements, the rule set, or the gate. PAY-004C1/#359 removes one `no-alert` warning and two label-association errors by removing the obsolete reusable-link prompt and amount/tier controls. Intervening reviewed source additions expanded coverage from 102 to 108 files by #359. PR #391 then adds one TypeScript file and removes two TSX errors; PR #392 adds two TypeScript files and removes two more TSX errors. CI-001B4C/#449 replaces the four remaining stale `react-hooks/exhaustive-deps` directives with ordinary comments on the same lines, preserving every executable statement and dependency array. MEMBERS-CONTENT-001B/#492 then removes one finding-free dormant JSX file. The gate therefore scans 110 files and records 113 configured errors and 6 warnings as reviewable individual records. Any added, removed, moved, or changed record and every fatal parser/configuration failure rejects with one fixed diagnostic. The checker runs directly before application tests in an exact prefix that pins the hosted runner, read-only permission, credential-free checkout, Node version, lifecycle-disabled install, lint, and adjacent clean check. Global overrides and prefix changes reject. The clean check compares tracked files with `HEAD` plus non-ignored untracked files; ignored dependency/build paths are outside that proof. This is an explicit debt baseline, not a clean-lint claim. #135 removes the automatic/fail-open release source defects: merge and release are separate, exact CI checks are re-read, release targets are fixed, OIDC replaces JSON-key wiring, lockfile tooling is used, backend precedes Pages, and Git-triggered Netlify production builds stop. The protected identity/environments and actual staged/live release remain outside that source slice.
Expand Down
1 change: 1 addition & 0 deletions GITHUB_ISSUE_SLICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ An agent-ready child has one observable outcome, one trust boundary, explicit de
| CI-001B1 | merged/closed as #124; CI-001A | Run the complete committed frontend Jest suite as a named blocking step in the hosted frontend CI job; update exact officer/engineering truth. | Exact pull-request and post-merge hosted steps passed; Firebase skipped and Netlify exact production commit remained unverified. |
| CI-001B1A | merged/closed as #126; #99 and CI-001B1 | Run the standalone same-origin SPA callback suite as a separate blocking hosted frontend step. | Local deliberate-failure proof plus the named hosted step passing on the exact pull-request and post-merge commits; no provider callback claim. |
| CI-001B2 | published as #135; CI-001B1 | Provide the tested manual exact-commit source gate: fixed Rules/Functions, lockfile CLI, pinned Actions, short-lived-auth wiring, backend-first Pages dependency, missing-config failure, no frontend credential, and Netlify Git-production containment. | Source/static negative tests and exact PR evidence. #133 separately owns protected OIDC/environment configuration; #136 owns staged/target release; WEB-001 owns protected Netlify publication. |
| CI-001C1 | in progress as [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667); CI-001B2, WEB-001A2/#665 | Configure staging-only exact-repository/ref/environment/workflow OIDC, one keyless principal with project-identity read permission, two named environment reviewers, and a separate protected read-only verifier. | Source negative tests, cloud/environment readback, zero user-managed keys, one protected exact-main project-read run, and a wrong-ref rejection; no Firebase deploy, billing, backend, provider sandbox, or production claim. |
| CI-001B3 | source/tests tracked in live [#167](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/167), with current Java 21 compatibility tracked in [#455](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/455); PAY-002B2A/#165 | Run the dedicated command-journal Firestore transaction suite as one named blocking Node 20/Java 21 job with exact demo project, explicit opt-in, Firestore-only scope, committed tooling, and no secrets/provider authority; require it at both protected-release CI reads. | Static negative workflow tests plus exact pull-request and post-merge hosted evidence; no Firebase deployment, Stripe, website, or live claim. |
| CI-001B4 | merged/closed through [#186](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/186) / [PR #189](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/189) as `bec7d5e365eacb418563a172029f241f660d9768`; focused debt reductions tracked in [#227](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/227), [#239](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/239), functional removal in [#359](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/359), functional/file-count changes in [PR #391](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/391) and [PR #392](https://github.com/Run-MPRC/Run-MPRC.github.io/pull/392), CI-001B4C [#449](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/449), and dormant-renderer removal [#492](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/492); CI-001B1 | Replace the mutating fail-open frontend lint line with a named blocking JS/JSX/TS/TSX exact-record gate. The original gate covered 102 files with 125 configured errors and 7 warnings. #227 removes one reviewed `arrow-body-style` error, #239 removes one stale `AdminMembers` suppression error, and #359 removes one prompt warning plus two label errors by deleting the unsafe reusable-link controls. Intervening reviewed source additions expanded coverage to 108 files by #359. PR #391 adds one TypeScript file and removes two TSX errors; PR #392 adds two TypeScript files and removes two more TSX errors. #449 replaces the four remaining stale `react-hooks/exhaustive-deps` directives with ordinary comments on the same lines. #492 removes one finding-free dormant JSX file. The gate now scans 110 files with 113 errors and 6 warnings. Disable the repository-wide severity-masking hook inside the lint process. Any added, removed, moved, or changed record and every fatal parser/configuration failure rejects with a fixed diagnostic; lint may not rewrite tracked files or create non-ignored untracked files. | Exact #186 PR run `29291402007` and post-merge run `29291515653` passed lifecycle-disabled install, direct lint, clean proof, tests, and build; each later focused issue records its separate source/hosted evidence. Ignored dependency/build paths remain outside this proof, with no clean-lint, branch-protection, deployment, website, Firebase, or provider claim. |
| SUPPLY-001A | published as [#127](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/127); CI-001A | Prove `gpxparser` unused and remove its obsolete transitive chain. | `rg`/tree proof, before/after audits, root lockfile diff, full frontend checks. |
Expand Down
2 changes: 2 additions & 0 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ MPRC can open a race or merchandise item for sale only when the platform can:

**WEB-001A2 isolated static staging:** [#665](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/665) creates the club-owned `run-mprc-staging` Firebase project, one web app, and the static [run-mprc-staging.web.app](https://run-mprc-staging.web.app) Hosting site from exact merged source `a614c68d9a1a2be631a3be874a686d61e5d170a0`. The executable artifact scanner found the staging identity and rejected known production identities; provider, direct-route, desktop, and phone readback passed. This is signed-out visual staging only. It does not configure App Check, Auth test users, Firestore, Functions, Rules, indexes, Storage data, provider sandboxes, protected short-lived authority, a release marker, tested predecessor rollback, security headers, `production-web`, custom domains, DNS/TLS, or production publication.

**CI-001C1 protected staging identity proof:** [#667](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/667) creates the protected `staging` GitHub environment, two named individual reviewers, exact-`main` policy, a dedicated GitHub OIDC pool/provider, and a keyless service account with only `resourcemanager.projects.get`. Its separate manual verifier fails on a wrong repository, immutable repository/owner ID, ref, environment, project, workflow, provider, or service account before authentication; after approval it reads only the exact staging project identity. It does not grant Firebase deployment roles, provision Auth/Firestore/Functions, configure outside providers, protect production, or make `.github/workflows/deploy.yml` available.

## 3. Dependency map

```mermaid
Expand Down
Loading
Loading