feat(github-app): Phase 1 webhook receiver foundation (#246)#289
feat(github-app): Phase 1 webhook receiver foundation (#246)#289ChrisJr404 wants to merge 3 commits intoPwnKit-Labs:mainfrom
Conversation
Lays the in-tree groundwork for the GitHub App distribution path discussed in PwnKit-Labs#246, scoped strictly to the foundation so the architecture can be reviewed before the scan/comment pipeline lands. What's in: - src/github_app/webhook.rs — HMAC-SHA256 signature verification with constant-time comparison via hmac::Mac::verify_slice, plus the EventKind router enum that maps X-GitHub-Event values to the routed kinds (Installation, PullRequest, Ping, Other). 10 unit tests pin the verification contract: known-good vector, modified body, wrong secret, missing/empty/non-hex/short-length digest, trailing-whitespace tolerance, and the kind-routing map. - src/bin/foxguard_github_app.rs — axum HTTP server with /healthz and /webhook endpoints. Verifies signatures, routes by event, returns 202 for known kinds, 401 for verification failures, with actual handler bodies stubbed and clearly TODO-marked. 1 MiB request-body cap layered in front of the handler. - Dockerfile.github-app — multi-stage build that compiles the receiver with the github-app feature, drops to a non-root user, exposes :8080. Refuses to start without FOXGUARD_WEBHOOK_SECRET. - src/github_app/README.md — what's here, what's next, how to run. Build is gated behind a new `github-app` feature flag so the default `cargo build` remains untouched. Optional deps (axum, tokio, hmac, hex, tower-http, tracing) only enter the dependency closure when the feature is enabled, keeping the core scanner crate lean for users who only want the CLI. What's NOT here (deliberately deferred for follow-up review): - JWT-based App→installation auth (jsonwebtoken dep). - pull_request handler: clone, scan, comment with --github-pr. - installation handler + persistent install metadata. - Check Runs API for inline annotations. Verified locally: cargo build → clean cargo build --features github-app --bin foxguard-github-app → clean cargo test --lib → 413 ok cargo test --features github-app --lib → +10 ok
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
peaktwilight
left a comment
There was a problem hiding this comment.
Read the diff end-to-end. Architecture is approved from my side — Phase-1 scope is right, the security model is right, the gating is clean. Specific things I checked:
What's right
verify_slicelength-shortcut.hmac::Mac::verify_slicereturns early non-constant-time on length mismatch. You sidestep that by rejecting any non-32-byte digest withMalformedHeaderbefore callingverify_slice, so by the time we hit the constant-time compare the slice is always 32 bytes. Correct.- Same-status response on both failure modes.
MalformedHeaderandMismatchboth 401 — won't leak which check failed. - Body cap before handler.
RequestBodyLimitLayer::new(1 MiB)as a tower layer means a hostile multi-GB delivery dies before allocating anything app-side, not after the handler tries to buffer it. - Feature gating. Default
cargo builddoesn't pull axum/tokio/etc; thefoxguard-mcpbinary closure is unaffected. Verified the optional dep block inCargo.toml. - Docker: non-root user, refuses to start without
FOXGUARD_WEBHOOK_SECRETrather than silently accepting every delivery.
Answers to your three open questions
- App ownership — own it under
PwnKit-Labs. No benefit to a separate org until there's a second product; real cost in extra billing/secret surfaces. The brand isPwnKit-Labs/foxguardalready. - Tracing default
info,foxguard=debug— keep it. Overridable viaRUST_LOG, sane default. - Routes
/webhook+/healthz— keep simple. Layer/v2/webhooklater if versioning is ever needed; premature abstraction otherwise.
Phase-2 follow-up items (none block this PR)
These are worth tracking as separate issues so the next handler-PR doesn't have to rediscover them:
- Delivery-ID dedup. GitHub retries on 5xx with the same
X-GitHub-Delivery. Stub handlers don't care, but the moment thepull_request→ scan handler lands, dedup is required or you double-scan / double-comment. SQLite or in-memory LRU both fine for Phase-1 scale. - Body extractor must stay
axum::body::Bytes— neverJson<Payload>. The framework eats the body before HMAC verification can run otherwise. Worth a comment inwebhookwhen handlers are added. - Webhook secret zeroization is paranoid-level. For a long-running web service the process boundary is the trust boundary, so plain
Vec<u8>is fine — butsecrecy::Secretis a one-line upgrade if you ever ship multi-tenant. - No timestamp/freshness check because GitHub doesn't sign one (unlike Stripe). Documenting this acceptance in the module-level doc would make it explicit for anyone auditing later.
Mark ready-for-review whenever you want this in. Phase-2 = clone + scan + comment lands as the next PR.
Draft — opening for architecture review before sinking another round of work into this. Scoped strictly to the Phase-1 foundation called out in #246 so the bones can land cleanly first; the actual scan/comment pipeline is staged as the immediate follow-up.
What's in
Webhook signature verification (`src/github_app/webhook.rs`)
Webhook server (`src/bin/foxguard_github_app.rs`)
Self-hosting (`Dockerfile.github-app`)
Build gating
New `github-app` Cargo feature flag. The optional dep closure (axum, tokio, hmac, hex, tower-http, tracing, tracing-subscriber) only enters the build when the feature is enabled, so the default `cargo build` and the `foxguard-mcp` binary stay untouched. Verified:
```
cargo build → clean
cargo build --features github-app --bin foxguard-github-app → clean
cargo test --lib → 413 existing tests still ok
cargo test --features github-app --lib → 10 new tests added, all green
```
What's NOT in
Deliberately deferred so the architecture above can land in isolation. Each is the next PR:
Open questions for you
Test plan