integration-controller¶
The single internet-facing entry point, driven by Integration custom resources. Inbound: it validates provider
webhooks — POST /github/webhooks (per-Integration HMAC secrets), POST /google-cloud/webhooks (Pub/Sub-signed OIDC
tokens), POST /wiz/webhooks (per-Integration bearer tokens), POST /generic/<name>/webhooks (each generic
Integration's own HMAC secret) — and ingests scanner alerts into Finding resources — accumulation, duplicate merge.
Outbound: it projects Findings to their tracking issues (body, labels, enrichment and report comments) and applies human
signals (issue close and reopen, /approve comments, PR merge/close) back onto Findings. It holds no GitHub credential
itself — the credentials live in the Secrets your Integrations reference, read on demand.
Flags¶
The shared flags, plus:
| Flag | Env | Default | Purpose |
|---|---|---|---|
--accumulation-window |
PATCHY_ACCUMULATION_WINDOW |
1h |
How long alerts of one finding family accumulate into a single Finding |
--projection-concurrency |
PATCHY_PROJECTION_CONCURRENCY |
2 |
Findings projected to tracking issues in parallel (each projection may spend GitHub requests) |
The webhook receiver¶
One route per provider on --listen-addr, each authenticating on the provider's own terms before any handling happens:
POST /github/webhooks validates X-Hub-Signature-256 (constant-time HMAC) against the webhookSecret of every
configured github Integration; POST /google-cloud/webhooks validates the OIDC token a Pub/Sub push signs against the
SCC Integration's audience and service account; POST /wiz/webhooks validates the bearer token against every wiz
Integration's webhookToken (also constant-time). The exception to one-route-per-provider is generic: the wildcard
POST /generic/{name}/webhooks serves every generic Integration, validating X-Patchy-Signature-256 against only
the named Integration's webhookSecret — never the whole candidate set, so one integration's secret cannot admit a
delivery addressed to another. All of them answer the same way:
| Response | Meaning |
|---|---|
202 |
Accepted and queued (duplicates by delivery ID also get 202) |
204 |
ping |
401 |
No Integration's webhook secret matched the signature |
503 |
The delivery queue is full — GitHub redelivers |
Bodies are capped at 25 MiB (GitHub's own limit) and the last 1024 delivery IDs are deduplicated. A lost delivery is never fatal: the reconcile loops are the retry mechanism, and the webhook path only carries ingestion and human signals.
Behavior¶
- Ingestion — scanner deliveries go through the matching
pkg/sourcehandler, which normalizes them into findings. A first alert creates a Finding atOpened; alerts of the same advisory family against the same repository fold into the existing Finding until the accumulation window closes (theAccumulationCompletecondition — accumulation runs concurrently with enhancement, so it is not a phase). Later alerts open a fresh Finding. - Projection — a Finding reconciler renders each Finding to its tracking issue: the templated body, the projected labels, enrichments and investigation reports as comments, and open/closed state. One-way only.
- Human signals — issue close (
→ HandedOff), issue reopen (Dismissed → HandedOff), accepted/approvecomments (recorded onspec.approval), andpull_requestwebhooks onpatchy/<finding>branches (InReview → Remediatedon merge,→ Failedon unmerged close). - Credential revalidation — an Integration reconciler validates each Integration's referenced Secret on its
spec.intervaland maintains itsReadycondition.