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
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ jobs:
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/verify-staging-profile-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/firebase-auth-staging-contract.test.js tests/firebase-profile-functions-staging-contract.test.js tests/staging-authority-workflow.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/firebase-auth-staging-contract.test.js tests/firebase-profile-functions-staging-contract.test.js tests/staging-authority-workflow.test.js tests/staging-profile-authority.test.js tests/test-artifact-safety.test.js tests/root-dependency-security.test.js
- name: Build
env:
CI: 'true'
Expand Down
53 changes: 53 additions & 0 deletions .github/workflows/verify-staging-profile-authority.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Verify staging profile authority

on:
workflow_dispatch:

permissions: {}

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

jobs:
verify-authority:
name: Verify scoped staging profile identity without deployment
runs-on: ubuntu-latest
timeout-minutes: 5
environment:
name: backend-staging
permissions:
contents: read
actions: read
id-token: write
env:
TARGET_ENVIRONMENT: backend-staging
TARGET_PROJECT_ID: ${{ vars.FIREBASE_PROJECT_ID }}
DEPLOY_SERVICE_ACCOUNT: ${{ secrets.GCP_DEPLOY_SERVICE_ACCOUNT }}
WORKLOAD_IDENTITY_PROVIDER: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
GH_TOKEN: ${{ github.token }}
steps:
- name: Check out exact workflow source without persisted credentials
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
with:
persist-credentials: false
- name: Set up reviewed Node runtime
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: '22'
- name: Validate exact current-main context and all six CI jobs before authentication
run: node scripts/staging-profile-authority.js context
- id: auth
name: Obtain one five-minute staging identity with no credential file
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: Recheck exact source and verify project permissions without deploying
env:
GCP_ACCESS_TOKEN: ${{ steps.auth.outputs.access_token }}
run: node scripts/staging-profile-authority.js cloud
2 changes: 2 additions & 0 deletions GITHUB_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Before creating or claiming anything from this catalog, search GitHub, confirm t

## Current runtime prerequisite

**Focused authority child, 2026-10-11:** CI-001C3 [#699](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/699) establishes a separate staging profile identity and protected no-op proof under #133, and applies the owner's restored administrator-bypass decision. Scope is staging IAM/approval metadata plus fixed read-only verification and tests, not runtime/data/payment or website deployment. Production/legacy #113, runtime/build identity, actual #136 deployment and hosted recovery proof remain open. GitHub records source, provider configuration, activation and no-op proof separately.

**Current staging cost prerequisite:** COST-002 [#693](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/693) records the October 11 club Free Trial readback, staging-only billing link, and monthly USD10 gross-usage warning budget with 50%/90%/100% actual-spend thresholds. Only the Budget API was enabled; production billing, runtime APIs, deployment and member/payment data remain unchanged. This supersedes historical staging billing-disabled checkpoints, not #113/#133/#136 release gates. GitHub holds the provider acceptance and documentation status; the [officer cost guide](./docs/officers/LOW_COST_COMMUNITY.md) is the no-terminal handoff.

RUNTIME-001 [#691](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/691) prepares the initial profile backend for Node 22. Scope: Functions engine and lock metadata; exact staging guard/provider readback; a required sixth CI job; backend builder runtime; a fixed immutable redacted error stack; and officer handoff. Existing Node 20 checks, first-generation triggers, schema, auth, create-once semantics, two-Function scope, and #683 limits stay unchanged. No dependency versions or migrations change. #113/#133/#136, billing, hosted checks, and existing security advisories remain open; this is not a release or runner-matching activation. GitHub holds exact review/merge/CI evidence.
Expand Down
2 changes: 2 additions & 0 deletions OFFICER_START_HERE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Attach the exact wording, public link, or approved photo when you have it. The A

## Choose the closest task

**Focused authority step, 2026-10-11:** [#699](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/699) keeps backend preparation limited to staging profile setup. Production still names Dave and main, but administrator bypass is allowed again by Dave's instruction. A separate `backend-staging` identity check reads only project/access metadata and deploys nothing. See [the short authority procedure](./docs/officers/ACCESS_CONTINUITY.md#scoped-staging-profile-authority--699). It is **NOT AVAILABLE YET** until #699 records its merged source and protected proof. Member/payment operation remains unavailable; no officer handles a key or member record.

**Backend ownership, 2026-10-11:** Dave approved continuing without a backup owner under [#697](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/697). Dave remains the sole backend release/recovery approver; backup coverage is absent, not complete. This removes only the backup-appointment gate. Protected deployment, staging proof, rollback and all data/payment approvals remain required. See [the sole-owner procedure](./docs/officers/ACCESS_CONTINUITY.md#sole-owner-backend-exception--approved-october-11-2026); no officer handles keys or runs commands.

| What you need | Open this short guide |
Expand Down
6 changes: 5 additions & 1 deletion OPERATIONS_RUNBOOK.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ Review access quarterly, when an officer changes, and after every incident. Remo

## 2. Emergency controls

**#697 provider readback, 2026-10-11:** GitHub `production` now has one required reviewer (`daliu`), a branch-only `main` policy, self-approval allowed, and administrator bypass disabled. The existing read-only `staging` settings are unchanged. No cloud IAM/API/billing, Firebase or website deployment occurred; a protected no-op/release run and deployment identity remain separate #133 proof.
**Superseding #699 owner decision, 2026-10-11:** Dave approved releases and restoring administrator bypass. GitHub `production` retains reviewer `daliu`, branch-only `main` and self-approval, but now allows administrator bypass. #697's earlier bypass-disabled readback is historical. A bypass is a recorded administrator override, not proof of reviewer approval; it does not bypass cloud/source/CI/target checks or appoint another owner. The existing read-only `staging` settings and Jeff's authority are unchanged.

**Scoped staging authority, #699:** a separate `backend-staging` environment and dedicated keyless principal/provider have been configured for `run-mprc-staging`. The custom project role contains the 26 permissions in [the reviewed contract](./config/staging-profile-authority.json), not Owner/Editor, general Firebase administration, secret access, direct member-data permissions or runtime/build impersonation. Existing runtime APIs remain disabled; no Function, Rule, website or payment is deployed by this step. The protected workflow is manual and read-only, including `testIamPermissions`, which checks access without changing IAM. Consult #699 for exact source, provider activation, protected run and readback evidence; until that acceptance is complete, the authority procedure is **NOT AVAILABLE YET**.

IAM creation/update permissions are project-scoped: they cannot by themselves restrict Function names to the two profile names. The reviewed workflow performs no deployment at all. A later release must separately add a fixed two-Function source path and reviewed runtime/build identities; do not advertise this role as per-Function IAM isolation or a working backend. Permission reasons: project/Firebase and Service Usage reads identify the target; `serviceusage.services.use` permits quota attribution, not API enabling; first-generation Function lifecycle/IAM/source-upload plus build-status reads support the targeted future release; Rules create/test/read/release-update supports reviewed Rules and roll-forward. Delete, secret, account administration and direct data access are omitted. Pinned CLI 15.24.0 inspection confirmed explicit runtime/`actAs` work remains necessary; do not give project-wide impersonation as a shortcut. [Google's deployment IAM guidance](https://docs.cloud.google.com/functions/docs/reference/iam/roles) and [GitHub claim reference](https://docs.github.com/en/actions/reference/security/oidc) informed this review.

### Commerce pause — B1 source only, operator control NOT AVAILABLE YET

Expand Down
2 changes: 2 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ RUNTIME-001 [#691](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/691) ta

The dual-runtime run exposed a Node 22 error-stack accessor that remained writable despite `Object.freeze`. The unused Checkout observation projector now replaces that accessor with a fixed non-enumerable, non-writable data property before freezing; the regression test also rejects a stack-formatting hook. No raw provider value or stack frame is retained in that diagnostic. Existing malformed-input, proxy/getter, no-output, and redaction tests remain intact. This is runtime compatibility, not payment activation or a dependency-audit fix. #113/#133/#136, existing dependency findings, and hosted verification remain separate release gates.

**Scoped staging authority, 2026-10-11 — #699:** Dave approved restoring GitHub administrator bypass while keeping named approval and main-only policy. A bypass must be recorded as an administrator override, not a normal reviewer approval. Separate `backend-staging` federation is bound to immutable repository/owner IDs, exact main workflow/manual event/environment and matching workflow/source SHA. The existing #667 read-only authority is unchanged. The current workflow only verifies project metadata and permissions; no deployment or direct member/account/secret access is part of it. A 26-permission custom staging role avoids Owner/Editor and broad Firebase administration; project-level Function creation is not a per-name IAM boundary. Runtime/build impersonation, an actual fixed-target deploy path, synthetic hosted profile tests and recovery remain later reviewed work. No production data, payments or legacy disposition are authorized by this staging slice.

## 2. Reporting a vulnerability

**Profile callable source boundary, 2026-10-11:** ABUSE-001A2A [#695](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/695) builds only `ensureMemberProfile` with native `enforceAppCheck: true`, independent of `ENFORCE_APP_CHECK`. Real installed-SDK request-wrapper tests use synthetic token verifiers and mocked profile stores; missing/invalid app tokens are rejected before business work with the flag absent, false or true. Valid app requests still require valid Auth and an empty payload. No shared helper, Auth-created trigger, resource limit, dependency, callback-dependent callable, provider setting or deployed service changes. This is source evidence, not cryptographic token verification, an emulator browser flow, or hosted enforcement. Other sensitive callables and #113/#133/#136 deployment/authorization/provider proof remain open.
Expand Down
48 changes: 48 additions & 0 deletions config/staging-profile-authority.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"schemaVersion": 1,
"projectId": "run-mprc-staging",
"environment": "backend-staging",
"workflow": "verify-staging-profile-authority.yml",
"authoritySha256": "ed13e7498b1517316611aa5efb5ae2be965e28adc03d3b1edf8d1e9bcaab0a6e",
"futureFunctionTargets": ["createMemberOnSignUp", "ensureMemberProfile"],
"permissions": [
"firebase.projects.get",
"resourcemanager.projects.get",
"serviceusage.services.get",
"serviceusage.services.list",
"serviceusage.services.use",
"cloudfunctions.functions.create",
"cloudfunctions.functions.get",
"cloudfunctions.functions.list",
"cloudfunctions.functions.update",
"cloudfunctions.functions.sourceCodeSet",
"cloudfunctions.functions.getIamPolicy",
"cloudfunctions.functions.setIamPolicy",
"cloudfunctions.locations.list",
"cloudfunctions.operations.get",
"cloudfunctions.operations.list",
"cloudbuild.builds.get",
"cloudbuild.builds.list",
"firebaserules.rulesets.create",
"firebaserules.rulesets.get",
"firebaserules.rulesets.list",
"firebaserules.rulesets.test",
"firebaserules.releases.create",
"firebaserules.releases.get",
"firebaserules.releases.getExecutable",
"firebaserules.releases.list",
"firebaserules.releases.update"
],
"deniedPermissions": [
"resourcemanager.projects.setIamPolicy",
"serviceusage.services.enable",
"iam.serviceAccountKeys.create",
"iam.serviceAccounts.actAs",
"firebaseauth.users.get",
"firebaseauth.users.update",
"datastore.entities.get",
"datastore.entities.create",
"secretmanager.versions.access",
"cloudfunctions.functions.delete"
]
}
Loading
Loading