Skip to content

fix(github): canonicalize accepted API base URLs - #430

Merged
seonghobae merged 113 commits into
mainfrom
fix/github-api-base-normalization
Aug 22, 2026
Merged

fix(github): canonicalize accepted API base URLs#430
seonghobae merged 113 commits into
mainfrom
fix/github-api-base-normalization

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Scope

Repair GitHub App exchange/readiness and credential-egress integrity without widening GitHub authority, weakening OIDC/replay controls, or relaxing release/security gates.

Current repair lineage

This canonical branch now includes the bounded Bearer-envelope repair plus follow-on exact-head regressions for request-body cleanup and installation-token expiry authority. During the current run, Application CI exposed two uncovered defensive branches rather than a product-policy failure:

  • bodyless POST /exchange requests intentionally remain unchanged at boundExchangeJsonBody() because there is no runtime stream to consume; the regression now covers that exact contract instead of manufacturing a 413 from an unobservable body;
  • parseable but non-canonical GitHub installation-token expiry strings such as a +00:00 offset are explicitly rejected, preserving canonical UTC Z authority.

Central .github protected main also advanced to b04a40807a71e807700bb67f2a0ea4d776f58b22. The audited noema-review.yml blob remains unchanged (59b25e343444d0b97fc1c7ba33cb15543dd70102), but GitHub OIDC job_workflow_sha binds the source commit, so stale Noema configuration was a runtime trust defect. The executable trust regression was moved first, then wrangler.toml ALLOWED_WORKFLOW_SHA was minimally rolled forward to the same central commit.

Existing canonical GitHub API origin, App/installation identifier, immutable owner/repository/workflow identity, replay, token-expiry, cleanup-liveness and no-redirect egress controls remain in force.

Current exact identity and evidence

  • protected base: 046c1043b93ba2183b80edad441efc238f643fc7
  • exact head: fecd911fc71fd7fe0517e74a1d6e25a71e9c6555
  • application CI 32560097543: in progress at the latest exact-head refetch
  • reviewer-ci 32560097523: success
  • central Security Scan 32560097517: queued
  • unresolved review threads: 0 at the latest pre-write refetch
  • Draft; predecessor evidence is not reused

Merge boundary

Keep Draft until application CI, reviewer-ci, and eligible central Security Scan are terminal-success on this unchanged exact head, review/thread state is freshly clean, protected main remains the live base, and central scanner authority is freshly revalidated.

Summary by CodeRabbit

  • 보안 강화
    • OIDC 토큰의 저장소 정보, 시간 클레임 및 형식을 더욱 엄격하게 검증합니다.
    • GitHub App 및 설치 ID의 유효 범위를 확인하고 잘못된 요청을 사전에 차단합니다.
    • GitHub API의 비정상 응답, 네트워크 오류, 응답 읽기 실패에 안전하게 대응합니다.
  • 신뢰성 개선
    • 요청 본문과 응답 스트림 정리가 중단되거나 실패해도 적절한 오류 응답을 유지합니다.
  • CI 개선
    • 타입 검사, 테스트, 보안 및 무결성 검증을 독립 단계로 실행하고 실패 로그를 제한적으로 제공합니다.
  • 구성 업데이트
    • 승인된 워크플로 기준이 최신 커밋으로 갱신되었습니다.

@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ae33d8c-a8ec-4a05-b64f-5efb50160c37

📥 Commits

Reviewing files that changed from the base of the PR and between 046c104 and 31062bb.

📒 Files selected for processing (36)
  • .github/workflows/ci.yml
  • src/entrypoint.ts
  • src/index.ts
  • src/outbound-fetch-policy.ts
  • src/runtime-readiness.ts
  • test/ci-exact-head-contract.test.ts
  • test/exchange-body-cleanup-liveness.test.ts
  • test/exchange-body-cleanup-sync-throw.test.ts
  • test/exchange-body-early-rejection-cleanup.test.ts
  • test/exchange-body-limit.test.ts
  • test/exchange-success-path-coverage.test.ts
  • test/github-api-egress.test.ts
  • test/github-api-malformed-json.test.ts
  • test/github-app-explicit-installation-id-validation.test.ts
  • test/github-app-id-validation.test.ts
  • test/github-app-installation-id-edge-validation.test.ts
  • test/github-app-runtime-coverage.test.ts
  • test/github-installation-expiry-defensive-coverage.test.ts
  • test/github-installation-token-expiry-calendar-integrity.test.ts
  • test/oidc-bearer-whitespace-envelope.test.ts
  • test/oidc-numeric-date-finite.test.ts
  • test/oidc-repository-owner-id-binding.test.ts
  • test/oidc-verification-residual-coverage.test.ts
  • test/outbound-fetch-cleanup-liveness.test.ts
  • test/outbound-fetch-installation-id-range.test.ts
  • test/outbound-fetch-policy.test.ts
  • test/outbound-fetch-transport-failure.test.ts
  • test/package-manager-reproducibility.test.ts
  • test/replay-request-core-coverage.test.ts
  • test/runtime-readiness-id-range.test.ts
  • test/runtime-readiness-ref-format.test.ts
  • test/runtime-readiness-workflow-source-coherence.test.ts
  • test/runtime-workflow-prefilter-coverage.test.ts
  • test/trusted-workflow-source-rollforward.test.ts
  • test/worker.test.ts
  • wrangler.toml

📝 Walkthrough

Walkthrough

CI 검증 단계를 분리했습니다. OIDC와 GitHub API 입력 검증을 강화했습니다. 요청 및 응답 스트림의 fail-closed 처리를 보완했습니다. App ID, installation ID, workflow SHA의 canonical 검사를 추가했습니다.

Changes

자격 증명 교환 검증

Layer / File(s) Summary
CI 검증 단계 분리
.github/workflows/ci.yml, test/ci-exact-head-contract.test.ts, test/package-manager-reproducibility.test.ts, test/trusted-workflow-source-rollforward.test.ts, wrangler.toml
릴리스 검증을 타입 검사, 테스트, 보안 스캔, KPI 검증, acquisition manifest 및 무결성 단계로 분리했습니다. 허용 workflow SHA를 갱신했습니다.
요청 및 egress 경계 강화
src/entrypoint.ts, src/outbound-fetch-policy.ts, test/exchange-body-*.test.ts, test/github-app-*.test.ts, test/outbound-fetch-*.test.ts, test/github-api-egress.test.ts
Bearer envelope와 App 설정을 검증합니다. 요청 본문과 응답 스트림 취소를 best-effort로 처리합니다. 응답 읽기 및 credential-bearing 전송 오류를 제한된 fail-closed 응답으로 변환합니다.
OIDC 및 GitHub 응답 검증
src/index.ts, test/github-api-malformed-json.test.ts, test/github-installation-*.test.ts, test/oidc-*.test.ts, test/exchange-success-path-coverage.test.ts, test/github-app-runtime-coverage.test.ts, test/worker.test.ts
저장소 소유자 ID, 저장소 ID, NumericDate, GitHub JSON 응답, 설치 ID, 설치 토큰 및 canonical 만료 시각을 검증합니다.
런타임 readiness 일관성 검사
src/runtime-readiness.ts, test/runtime-readiness-*.test.ts, test/runtime-workflow-prefilter-coverage.test.ts
불변 workflow commit과 허용 SHA의 일치 여부를 검사합니다. App ID와 installation ID에 canonical safe integer 검사를 적용합니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Entrypoint
  participant OIDCVerifier
  participant GitHubAPI
  Client->>Entrypoint: /exchange 요청
  Entrypoint->>Entrypoint: 본문 및 Bearer envelope 검증
  Entrypoint->>OIDCVerifier: OIDC 토큰 검증
  OIDCVerifier->>GitHubAPI: 설치 및 토큰 요청
  GitHubAPI-->>OIDCVerifier: JSON 응답
  OIDCVerifier->>OIDCVerifier: 저장소와 토큰 만료 검증
  OIDCVerifier-->>Client: 교환 결과 또는 제한된 오류
Loading
✨ 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/github-api-base-normalization

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.

@seonghobae
seonghobae marked this pull request as ready for review August 22, 2026 08:05
@seonghobae
seonghobae merged commit 671fdf2 into main Aug 22, 2026
15 of 17 checks passed
@seonghobae
seonghobae deleted the fix/github-api-base-normalization branch August 22, 2026 08:05

@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 potential issues.

Open in Devin Review

Comment thread src/index.ts
Comment on lines +147 to +151
const expectedRepositoryOwnerId = "295022177";
const expectedRepositoryIds = new Map<string, string>([
["ContextualWisdomLab/noema", "1285107801"],
["ContextualWisdomLab/.github", "1274066402"],
]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Hardcoded owner/repository IDs become mandatory 403 gates

expectedRepositoryOwnerId and expectedRepositoryIds (index.ts) hardcode GitHub numeric IDs with no configuration. Real OIDC tokens always carry repository_owner_id, so the owner-id check (index.ts) applies to every request; a wrong constant fails all exchanges with 403. Tests reuse the same literals and cannot catch an incorrect value. Verify the IDs match the live org and repositories.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread wrangler.toml
ALLOWED_WORKFLOW_REPOSITORY = "ContextualWisdomLab/.github"
ALLOWED_WORKFLOW_REF_PREFIX = "ContextualWisdomLab/.github/.github/workflows/noema-review.yml@refs/heads/main"
ALLOWED_WORKFLOW_SHA = "fce028b4c3bf8e2e5e4819c1c5622e90cfa6ab39"
ALLOWED_WORKFLOW_SHA = "5a8b83773bd5190d972eec3d7c76ac9504665f21"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Workflow source SHA must match live central commit

ALLOWED_WORKFLOW_SHA is rolled forward to 5a8b83773bd5190d972eec3d7c76ac9504665f21, and the test constant matches. OIDC verification requires job_workflow_sha to equal this value exactly (index.ts), so a stale or wrong SHA rejects all legitimate central-workflow tokens. Confirm the configured SHA is the current source commit for the audited workflow.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread src/entrypoint.ts
Comment on lines 99 to 108
export function isBoundedOidcBearer(value: string | null): boolean {
if (value === null) return true;
if (!/^Bearer(?:\s|$)/i.test(value)) return true;
if (value.length > MAX_AUTHORIZATION_HEADER_LENGTH) return false;

const match = value.match(/^Bearer\s+(\S+)$/i);
if (!match) return true;
if (value.length > MAX_AUTHORIZATION_HEADER_LENGTH) return false;
if (!match) return false;

const segments = match[1].split(".");
if (segments.length !== 3) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📝 Info: Bearer envelope now returns 400 instead of 401 for whitespace-only tokens

isBoundedOidcBearer now treats any value starting with Bearer (then whitespace or end) as a JWT envelope that must match ^Bearer\s+(\S+)$. Whitespace-only and embedded-whitespace Bearer values that previously reached the 401 missing-token path now fail closed with 400 ERR_TOKEN_MALFORMED. The prefilter test was updated to match; any client depending on the old 401 sees a changed status and error code.

(Refers to this code)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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