fix(orb): authenticate and harden /v1/orb/ingest - #1248
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | 8427c2d | Commit Preview URL Branch Preview URL |
Jun 24 2026, 06:31 PM |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1248 +/- ##
==========================================
- Coverage 94.97% 94.97% -0.01%
==========================================
Files 177 177
Lines 19806 19843 +37
Branches 7125 7139 +14
==========================================
+ Hits 18811 18846 +35
Misses 397 397
- Partials 598 600 +2
🚀 New features to boost your workflow:
|
| signatureHeader: string | null, | ||
| secret: string | undefined, | ||
| ): Promise<boolean> { | ||
| return verifyGitHubSignature(body, signatureHeader, secret ?? ""); |
There was a problem hiding this comment.
P1: verifyOrbIngestSignature falls back to empty secret when ORB_INGEST_SECRET is undefined
verifyOrbIngestSignature passes secret ?? "" to verifyGitHubSignature. When ORB_INGEST_SECRET is not configured, an attacker can compute the request-body HMAC with an empty key and forge a valid signature.
Reject authentication when the secret is missing or empty, and make ORB_INGEST_SECRET required in src/env.d.ts.
AI prompt
Check if this security scanner issue is valid. If so, understand the root cause and fix it. If appropriate, update or add tests. Keep the change focused and preserve intended behavior.
<file name="src/orb/ingest.ts">
<violation number="1" location="src/orb/ingest.ts:37">
<priority>P1</priority>
<title>verifyOrbIngestSignature falls back to empty secret when ORB_INGEST_SECRET is undefined</title>
<evidence>verifyOrbIngestSignature passes secret ?? "" to verifyGitHubSignature. When ORB_INGEST_SECRET is not configured, this falls back to an empty string secret. HMAC-SHA256 with an empty key produces a deterministic, easily computable signature, so an attacker can forge a valid x-orb-signature header for any request body.</evidence>
<recommendation>Reject authentication when the secret is missing or empty. Change verifyOrbIngestSignature to: if (!secret) return false; return verifyGitHubSignature(body, signatureHeader, secret); Also make ORB_INGEST_SECRET required in src/env.d.ts.</recommendation>
</violation>
</file>
|
Superseded by #1270-family work — see the registration-gate PR. We took your DoS hardening (the body-size cap + streaming read, now a 1 MiB ceiling → 413) but dropped the shared ORB_INGEST_SECRET requirement: it conflicts with #1257's hardwired-on telemetry (instances without the secret would 401, breaking "install the app, you're done"). Following das-github-mirror's actual model instead — open ingest + dedup + body limit + a registered=false-by-default instance gate, so only operator-registered instances count toward the fleet median. Reopen if you disagree with the trade-off. |
Motivation
/v1/orb/ingestreceiver accepted unauthenticated batches and parsed the full request body before any limits or signature checks, allowing remote attackers to poison the centralorb_signalsdataset and cause write/storage amplification.Description
x-orb-signatureHMAC (verified viaORB_INGEST_SECRET) before parsing or inserting any ingest payload by addingverifyOrbIngestSignatureand checking it in the route handler.readOrbIngestBodyand a 128 KiB cap that returns413on overflow so oversized JSON cannot be fully read into memory.instance_id,repo_hash,pr_hash,gate_verdict, andcreated_atlengths and normalizing optional fields beforeINSERT.x-orb-signatureheader and new401/413responses and regenerate the UI OpenAPI artifact, and add integration tests covering signed success, unsigned/oversized negative paths, and bounded-field validation.ORB_INGEST_SECRETinEnvtype (src/env.d.ts) for runtime configuration.Testing
npx vitest run test/integration/orb-ingest.test.tsand all integration tests passed (34/34).npm run ui:openapiandnpm run ui:openapi:check, which succeeded and updatedapps/gittensory-ui/public/openapi.json.npm run typecheckfailed due to missing optional self-host package type declarations (pg,ioredis),npm run test:coveragefailed in the local toolchain with a coverage remap error (TypeError: jsTokens is not a function), andnpm audit --audit-level=moderatereturned403from the registry; these are environmental/tooling issues and did not affect the added integration tests.Codex Task