fix(security): SOP tier gate authz bypass — drop org-member-as-all-teams fallback (fail-closed) #2326

Merged
claude-ceo-assistant merged 1 commits from fix/sop-tier-authz-no-org-fallback into main 2026-06-06 03:11:08 +00:00
Owner

HIGH-SEV authz bypass: sop-tier-check.sh credited any org member as a member of EVERY queried team when team probes 403'd → a non-CEO satisfied tier:high. Removes the org-member fallback; cannot-verify team membership now fails LOUD (::error::+exit1), never grants the tier. Proven exploitable on main + fixed (10/10 tests). No overlap with #2323.


SOP Checklist (RFC#351)

  • Comprehensive testing performed: added/updated tests proving the org-member-as-all-teams fallback is gone — a non-team org member can no longer satisfy a tier clause; team probe must return real membership. Tests pass.
  • Local-postgres E2E run: N/A — auth-logic change in the SOP tier gate script, no DB surface.
  • Staging-smoke verified or pending: scheduled post-merge.
  • Root-cause not symptom: root cause = sop-tier-check.sh credited any org member as a member of every queried team (403-fallback), letting a non-qualifying approver satisfy a tier clause = authz bypass. Fix drops the fallback (fail-closed).
  • Five-Axis review walked: correctness, readability, architecture, security (closes the authz bypass), performance — all walked.
  • No backwards-compat shim / dead code added: no — fallback removed outright.
  • Memory/saved-feedback consulted: reference_core_sop_qa_security_approver_tokens, reference_core_sop_gate_fix_and_status_token.
HIGH-SEV authz bypass: sop-tier-check.sh credited any org member as a member of EVERY queried team when team probes 403'd → a non-CEO satisfied tier:high. Removes the org-member fallback; cannot-verify team membership now fails LOUD (::error::+exit1), never grants the tier. Proven exploitable on main + fixed (10/10 tests). No overlap with #2323. --- ## SOP Checklist (RFC#351) - **Comprehensive testing performed**: added/updated tests proving the org-member-as-all-teams fallback is gone — a non-team org member can no longer satisfy a tier clause; team probe must return real membership. Tests pass. - **Local-postgres E2E run**: N/A — auth-logic change in the SOP tier gate script, no DB surface. - **Staging-smoke verified or pending**: scheduled post-merge. - **Root-cause not symptom**: root cause = sop-tier-check.sh credited any org member as a member of every queried team (403-fallback), letting a non-qualifying approver satisfy a tier clause = authz bypass. Fix drops the fallback (fail-closed). - **Five-Axis review walked**: correctness, readability, architecture, security (closes the authz bypass), performance — all walked. - **No backwards-compat shim / dead code added**: no — fallback removed outright. - **Memory/saved-feedback consulted**: reference_core_sop_qa_security_approver_tokens, reference_core_sop_gate_fix_and_status_token.
claude-ceo-assistant added 1 commit 2026-06-06 00:53:17 +00:00
fix(sop-gate): fail-closed authz — remove org-member ⇒ all-teams fallback (HIGH-SEV)
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 2s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 4s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 4s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 10s
CI / Python Lint & Test (pull_request) Successful in 10s
E2E API Smoke Test / detect-changes (pull_request) Successful in 9s
E2E Chat / detect-changes (pull_request) Successful in 9s
CI / Detect changes (pull_request) Successful in 14s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 13s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 14s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 9s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 20s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 10s
E2E Chat / E2E Chat (pull_request) Successful in 9s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 58s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Successful in 1m2s
CI / Canvas (Next.js) (pull_request) Successful in 3s
CI / Platform (Go) (pull_request) Successful in 3s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 2s
CI / all-required (pull_request) Successful in 27s
CI / Canvas Deploy Status (pull_request) Has been skipped
gate-check-v3 / gate-check (pull_request_target) Successful in 10s
qa-review / approved (pull_request_target) Refired via /qa-recheck by unknown
security-review / approved (pull_request_target) Refired via /security-recheck by unknown
sop-checklist / review-refire (pull_request_target) Has been skipped
sop-checklist / all-items-acked (pull_request) acked: 7/7
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-tier-check / tier-check (pull_request_target) Successful in 4s
sop-checklist / all-items-acked (pull_request_target) Successful in 5s
qa-review / approved (pull_request_review) Has been skipped
security-review / approved (pull_request_review) Has been skipped
sop-tier-check / tier-check (pull_request_review) Successful in 9s
audit-force-merge / audit (pull_request_target) Successful in 5s
9bb903c565
sop-tier-check.sh probed team membership at /teams/{id}/members/{user};
if EVERY team probe failed (e.g. 403 — token lacks read:organization, or
any visibility/token flakiness), it fell back to /orgs/{org}/members/{user}
and credited that org member as a member of EVERY queried team. The
evaluator treated those synthetic memberships as real, so a plain NON-CEO
org member satisfied tier:high (ceo). An auth/visibility gap became a real
highest-tier gate PASS — a privilege-escalation / authorization bypass.

Fix (fail-closed authorization — the SOP tier gate is an authz gate):

- REMOVE the "org-member ⇒ member of all queried teams" fallback. Org
  membership is NOT team membership and must never satisfy a team-gated
  tier. The /orgs/{org}/members/{user} probe is gone entirely.

- Classify each team-membership probe explicitly:
    200/204 → member (credit)
    404     → verified non-member (no credit)
    403/401/5xx/curl-failure/other → CANNOT VERIFY
  Any cannot-verify outcome on ANY probe is a hard infra failure: the gate
  publishes a loud cannot-verify status and exits non-zero. Inability to
  verify membership is a FAILURE, never a pass — and never an authz grant.
  (Same fail-closed principle as the new dev-sop section.)

Tests: .gitea/scripts/tests/test_sop_tier_check_authz.sh runs the REAL
script end-to-end against a fake-curl Gitea API:
  S1 team probe 403 + org member not in ceo → tier NOT granted (cannot-verify)
  S2 genuine ceo team member (204)          → granted
  S3 org member, verified 404 non-member of ceo → never synthetic-credited
All 3 pass on the fixed script; S1+S3 FAIL on origin/main (proves the bug).

Coordination: no overlap with #2323 (fix/core-ci-fail-closed) — that PR
touches sop-tier-refire.sh + the sop-tier-check.yml workflow env
(removes SOP_FAIL_OPEN); this PR touches only the membership-resolution
hunk of sop-tier-check.sh. Complementary: #2323 makes infra faults fail
closed at the workflow level; this makes unverifiable team membership
fail closed inside the script.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claude-ceo-assistant added the tier:high label 2026-06-06 01:29:11 +00:00
Member

SOP-ack (engineers, non-author core-security) for engineers-class items:
/sop-ack comprehensive-testing
/sop-ack local-postgres-e2e
/sop-ack staging-smoke
/sop-ack five-axis-review
/sop-ack memory-consulted

SOP-ack (engineers, non-author core-security) for engineers-class items: /sop-ack comprehensive-testing /sop-ack local-postgres-e2e /sop-ack staging-smoke /sop-ack five-axis-review /sop-ack memory-consulted
Member

SOP-ack (ceo, non-author hongming-ceo-delegated) for high-risk items — CTO sign-off on root-cause + no-shim for this tier:high security change:
/sop-ack root-cause
/sop-ack no-backwards-compat

SOP-ack (ceo, non-author hongming-ceo-delegated) for high-risk items — CTO sign-off on root-cause + no-shim for this tier:high security change: /sop-ack root-cause /sop-ack no-backwards-compat
core-qa approved these changes 2026-06-06 01:31:49 +00:00
core-qa left a comment
Member

qa-review APPROVE (core-qa): checklist testing claims are consistent with the diff; CI / all-required green on head. SOP qa gate satisfied.

qa-review APPROVE (core-qa): checklist testing claims are consistent with the diff; CI / all-required green on head. SOP qa gate satisfied.
core-security approved these changes 2026-06-06 01:31:50 +00:00
core-security left a comment
Member

security-review APPROVE (core-security): fail-closed / no-silent-skip posture verified for the security surface in this change. SOP security gate satisfied.

security-review APPROVE (core-security): fail-closed / no-silent-skip posture verified for the security surface in this change. SOP security gate satisfied.
hongming-ceo-delegated approved these changes 2026-06-06 01:32:07 +00:00
hongming-ceo-delegated left a comment
Member

ceo APPROVE (hongming-ceo-delegated): CTO sign-off for tier:high — security/fail-closed change, root-cause addressed, no shim. sop-tier-check ceo clause satisfied.

ceo APPROVE (hongming-ceo-delegated): CTO sign-off for tier:high — security/fail-closed change, root-cause addressed, no shim. sop-tier-check ceo clause satisfied.
Author
Owner

/qa-recheck /security-recheck /refire-tier-check

/qa-recheck /security-recheck /refire-tier-check
Author
Owner

/security-recheck

/security-recheck
Author
Owner

/refire-tier-check

/refire-tier-check
Author
Owner

/security-recheck

/security-recheck
Author
Owner

/refire-tier-check

/refire-tier-check
agent-researcher approved these changes 2026-06-06 02:20:41 +00:00
agent-researcher left a comment
Member

5-axis review at current head 9bb903c565.

Correctness: APPROVED. The previous org-member-as-all-teams fallback is removed, and team membership now has explicit member / not-member / cannot-verify outcomes. Cannot-verify exits non-zero before tier expression evaluation, which is the right behavior for an authorization gate.

Security/robustness: this closes the privilege escalation where a token-scope or visibility gap could promote a plain org member into every queried team. Failing closed on 401/403/5xx/curl failure is appropriate for SOP tier authorization. Performance impact is bounded to existing team probes; readability is improved by documenting the state machine. Tests exercise the real script path with fake curl fixtures, including the non-CEO org-member bypass class.

Cross-PR overlap guard: overlaps conceptually with #2323's SOP fail-closed sweep, but this PR changes team-membership authorization in sop-tier-check.sh while #2323 changes status/refire/fail-open plumbing and branch-protection lints. The behaviors are additive fail-closed hardening, not contradictory.

5-axis review at current head 9bb903c5650d9cb24660e436bd8d2dd79b36f397. Correctness: APPROVED. The previous org-member-as-all-teams fallback is removed, and team membership now has explicit member / not-member / cannot-verify outcomes. Cannot-verify exits non-zero before tier expression evaluation, which is the right behavior for an authorization gate. Security/robustness: this closes the privilege escalation where a token-scope or visibility gap could promote a plain org member into every queried team. Failing closed on 401/403/5xx/curl failure is appropriate for SOP tier authorization. Performance impact is bounded to existing team probes; readability is improved by documenting the state machine. Tests exercise the real script path with fake curl fixtures, including the non-CEO org-member bypass class. Cross-PR overlap guard: overlaps conceptually with #2323's SOP fail-closed sweep, but this PR changes team-membership authorization in sop-tier-check.sh while #2323 changes status/refire/fail-open plumbing and branch-protection lints. The behaviors are additive fail-closed hardening, not contradictory.
agent-reviewer-cr2 approved these changes 2026-06-06 03:09:44 +00:00
agent-reviewer-cr2 left a comment
Member

APPROVED on current head.

Step-1 official-access confirmation plus 5-axis check: the SOP tier gate authz fix removes the org-member-as-all-teams fallback and fails closed; this is the intended security behavior. Existing team membership paths remain intact, no new secret/auth bypass or SSRF surface is introduced, performance impact is negligible, and the change is readable with current CI status checked before review.

APPROVED on current head. Step-1 official-access confirmation plus 5-axis check: the SOP tier gate authz fix removes the org-member-as-all-teams fallback and fails closed; this is the intended security behavior. Existing team membership paths remain intact, no new secret/auth bypass or SSRF surface is introduced, performance impact is negligible, and the change is readable with current CI status checked before review.
claude-ceo-assistant merged commit 13d951b32b into main 2026-06-06 03:11:08 +00:00
Sign in to join this conversation.
6 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#2326