Skip to content

fix(security): trust forwarding IPs only from configured proxies - #587

Open
seonghobae wants to merge 83 commits into
developfrom
fix/trusted-rate-limit-client-ip
Open

fix(security): trust forwarding IPs only from configured proxies#587
seonghobae wants to merge 83 commits into
developfrom
fix/trusted-rate-limit-client-ip

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Problem

ScopeWeave's opt-in fixed-window limiter historically trusted caller-controlled forwarding data without anchoring it to an explicitly trusted transport peer. This lane hardens that boundary and adjacent authentication/invite/webhook trust boundaries while preserving operator-visible abuse-control evidence.

Exact current identity

  • exact contributor head: fad52de37a9f749738118d27ca31e47b6fc547ad;
  • exact head tree: e63d1ad98427867519f9ac298fd163a1946d5959;
  • branch: fix/trusted-rate-limit-client-ip;
  • protected live base: develop@2c328875e00e86537df3e965170be80532571cad;
  • synthetic GitHub PR merge revision: 614f35b9dc083d165f0a842705d8bf69ce4a6bef;
  • Ready/non-draft and mechanically mergeable at the latest fresh refetch; and
  • no qualifying independent current-head APPROVED review exists.

Any head/base/review/check movement invalidates revision-sensitive evidence until freshly revalidated. This body records traceability and is not merge authority.

Security / availability contract

  • X-Forwarded-For is ignored unless the immediate network peer is explicitly listed in SCOPEWEAVE_TRUSTED_PROXY_IPS.
  • Trusted forwarding hops are parsed right-to-left and canonicalized, including equivalent IPv6 and IPv4-mapped IPv6 identities; malformed or all-trusted evidence fails closed to the transport peer.
  • SCOPEWEAVE_RATE_LIMIT_BUCKETS_MAX bounds regular in-memory client buckets; excess identities share a fail-closed overflow bucket.
  • Explicit limiter numeric configuration is safe-integer validated. SCOPEWEAVE_RATE_LIMIT_MAX=0 is the documented opt-out; malformed, negative, or infinite values fail startup rather than silently weakening protection.
  • The supported public and direct-consumer entrypoints use the shared transport-peer-aware limiter ahead of invitation/OIDC guards and their database lookups.
  • Blocked 429 requests remain represented in request/4xx observability without exposing client identity; current regressions cover both JSON and Prometheus behavior.
  • Pending invite bearer tokens are not returned by ordinary roster reads; the supported client receives a new invite token only from the explicit creation response.
  • OIDC authorization-state storage prunes expired entries and caps live state rather than growing for process lifetime.
  • This limiter remains process-local rather than a cross-replica quota system; deployment prerequisites are documented in docs/deploy.md.

Current repair evidence

The current descendant integrates the prior Stripe webhook, invite, and OIDC trust-boundary work that was previously isolated in #589. Current-head review findings that demonstrated real defects were repaired test-first, including trusted-proxy identity, bounded limiter state, fail-closed numeric configuration, blocked-request/Prometheus observability, direct-consumer limiter ordering, overflow-test determinism, shared limiter deduplication, bounded OIDC state, slashed stacked-base resolution, and the coverage producer-contract guards restored by fad52de3.

A fresh review-thread sweep on this exact head returns zero unresolved threads. Remaining historical/current informational observations are resolved only after verification against the current source; they are not being counted as approvals. Formal review submissions are COMMENTED/model or author submissions only; no qualifying independent current-head APPROVED review exists.

Exact-head workflow truth

For exact contributor head fad52de37a9f749738118d27ca31e47b6fc547ad, the associated repository/organization pull-request workflow runs are terminal GitHub-success at the run level:

  • Dependency Review 32854298556;
  • Server Tests 32854298576;
  • Security Scan 32854298537;
  • SAST Semgrep 32854298570;
  • Fuzz 32854298579; and
  • OSV Scanner 32854299261.

Repository Server Tests executed the current unit/API/coverage contract successfully; the current test:coverage path and its regression guards require the real coverage producer/reporter chain rather than recursive or report-only evidence.

The required central opencode-review check 97860655574 is terminal GitHub-success, but check success is not a qualifying formal APPROVED review. The central coverage-evidence check 97848057343 is also GitHub-success, but its executed log explicitly states that it preserves the stable branch-protection context without executing pull-request content. It is therefore a placeholder status rather than substantive current-head production-coverage evidence and cannot authorize integration by itself.

Known central exact-head evidence defect

Fresh logs for this exact PR reproduce the centrally owned checkout defect already tracked by ContextualWisdomLab/.github#1222:

  • SAST Semgrep run 32854298570, job 97822399158, checked out synthetic PR merge revision 614f35b9dc083d165f0a842705d8bf69ce4a6bef rather than immutable contributor head fad52de37a9f749738118d27ca31e47b6fc547ad.
  • Security Scan run 32854298537, Trivy job 97822400530, likewise checked out and scanned synthetic revision 614f35b9dc083d165f0a842705d8bf69ce4a6bef rather than the contributor head.

Those scans may still be useful merge-tree diagnostics, but under the repository's exact-head commercial evidence contract they are not immutable-head SAST/Security authority. The central .github dedicated owner path is already actively repairing this causal boundary; ScopeWeave does not create a competing source writer or a leaf workaround. Corrected central workflows must regenerate substantive evidence on this unchanged exact head or its freshly revalidated successor before merge classification.

Merge gate

Do not merge or enable auto-merge until the unchanged exact contributor head remains reconciled to protected develop, the centrally owned SAST/Security checkout repair is protected-shipped and regenerated against the exact contributor head where head-oriented semantics apply, every applicable CI/browser/owned statement-branch-function-line coverage/docstring/CodeQL/SAST/security/dependency/supply-chain/package/SBOM/provenance gate is substantively terminal-passing, every valid current finding remains resolved, and the live required approving-review rule is satisfied by a qualifying independent current-head approval (currently one approval under active ruleset 18156473; no latest-push approval is required by that ruleset).

Pending, queued, skipped-required, cancelled, absent, neutral-required, failed, stale, predecessor, synthetic-only, placeholder-only, status-only, author-only, model-only, rate-limited, or infrastructure-only evidence is non-passing.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

공개 앱을 보안 엔벨로프로 분리하고 rate-limit과 trusted proxy IP 처리를 강화했습니다. OIDC와 초대 경계를 추가했습니다. Stripe 웹훅은 원시 본문 HMAC 검증 후 확인 응답만 반환합니다. API, 단위 테스트와 CI 커버리지 계약도 갱신했습니다.

Changes

보안 경계와 라우팅

Layer / File(s) Summary
전송 엔벨로프와 rate-limit
.github/workflows/server-tests.yml, server/app.mjs, server/app_routes.mjs, docs/deploy.md, docs/doctoring/trusted-proxy-client-ip.md, tests/api/ratelimit.test.mjs, tests/unit/coverage-script-contract.test.mjs, tests/unit/toast-accessibility.test.mjs, package.json
공개 앱이 직접 연결된 peer와 trusted proxy 체인을 기준으로 rate-limit 신원을 결정합니다. IP 정규화, 제한된 버킷, overflow 버킷, 429 지표와 구조화 로그를 추가했습니다. 잘못된 설정은 시작 실패로 처리합니다. CI는 커버리지 명령을 실행합니다.
보호 라우트 그래프
server/application_routes.mjs, server/application_routes_core.mjs, tests/api/invite-security.test.mjs, tests/api/oidc-production-boundary.test.mjs, tests/api/security-guard-abuse-controls.test.mjs
공유 앱과 핵심 라우트 그래프를 분리했습니다. JWT와 PAT 인증, token version 검증, 조직 멤버십 경계, RBAC, OIDC fail-closed, 초대 이메일 일치, 초대 토큰 비공개 처리를 구성했습니다. 업무 API와 정적 파일 라우트도 핵심 그래프에 추가했습니다.
Stripe 웹훅 검증 경계
server/stripe_webhook.mjs, server/application_routes_core.mjs, docs/doctoring/stripe-webhook-trust-boundary.md, CHANGELOG.md, tests/api/stripe-webhook.test.mjs, tests/unit/stripe-webhook-boundary.test.mjs
웹훅 본문을 256KiB로 제한하고 원시 바이트를 HMAC-SHA-256으로 검증합니다. timestamp와 v1 서명을 확인한 뒤에만 JSON과 이벤트 필드를 파싱합니다. 유효한 이벤트는 { received: true }를 반환하지만 조직 플랜을 변경하지 않습니다.

Estimated code review effort: 5 (Critical) | ~120 minutes

Merge Risk: 🟠 High · up to e8ef4

The change hardens forwarded-IP trust and limiter controls, but a directly mounted application boundary is still described as using the older, weaker limiter policy and performing invite or authentication work before enforcement. That leaves a concrete security and availability exposure, and the required independent approval and exact-head merge evidence are absent, so the PR is not ready to merge.

Sequence Diagram(s)

sequenceDiagram
  participant Stripe
  participant PublicApp as server/app.mjs
  participant Verifier as verifyStripeWebhookRequest
  Stripe->>PublicApp: 원시 웹훅 요청
  PublicApp->>Verifier: 본문과 stripe-signature 전달
  Verifier-->>PublicApp: 검증된 이벤트 또는 오류
  PublicApp-->>Stripe: received 응답 또는 HTTP 오류
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 13 files. (6 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 구성된 프록시에서만 전달 IP를 신뢰하도록 변경한 핵심 보안 개선을 정확히 설명합니다. 간결하고 변경 범위와 직접 관련됩니다.
Full details: Docstring Coverage

Explanation

Docstring coverage is 32.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 37 functions across 13 files. (6 skipped: 6 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/trusted-rate-limit-client-ip

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Keep the public copy-and-replace composition and make the protected-graph
Stripe route fail closed on HMAC verification so a direct mount cannot
upgrade orgs.plan from unsigned JSON. Record the trust boundary in
CHANGELOG and doctoring.
Replace the source-text serve-path assertion with a runtime Hono route-graph regression that fails while the public facade registers /toast-state.css twice. Remove the redundant facade route so the protected route graph remains the single SaaS implementation.
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 3 new potential issues.

Open in Devin Review

Comment thread .github/workflows/dependency-review.yml Outdated
Comment thread server/rate_limit.mjs
Comment thread server/application_routes.mjs Outdated
devin-ai-integration[bot]

This comment was marked as resolved.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

Open in Devin Review

Comment thread .github/workflows/dependency-review.yml
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant