Generic (HTTP)¶
The enhance capability of a provider: generic Integration turns any external HTTP process into a
context enhancer: patchy POSTs each opened finding's issue view to your endpoint, and the response body is
the enrichment — exactly what an in-tree pkg/enhance plugin would
contribute. It is how a real CMDB integrates without a rebuild, and it runs for findings from every source, not just
your own.
One provider, three capabilities
The enhancer is one capability of the same provider: generic Integration as the
inbound source and verdict resolver — configuration, identity rules and the HMAC
authentication scheme live on that page. The integrations overview maps every provider's roles.
Configuration¶
The enhance block on the Integration (full configuration):
spec:
provider: generic
secretRef:
name: patchy-warehouse # key webhookSecret: the shared HMAC secret
generic:
enhance:
enabled: true # synchronous enrichment call per opened finding
url: https://warehouse.internal/patchy/enhance
timeout: 60s
A pure enhancer is a valid Integration — leave source off entirely. N generic Integrations may all enable enhance:
the context-controller calls each one per finding, in name order, after the cloud lookups, each request signed with that
Integration's own secret and each enrichment attributed to that Integration's name.
Every call is signed with the same HMAC scheme as every other generic exchange:
The signature is computed over the raw request body, keyed with the shared secret under the credential Secret's
webhookSecret key; verify with a constant-time comparison over the exact bytes received. Signing in Go:
mac := hmac.New(sha256.New, secret)
mac.Write(body)
sig := "sha256=" + hex.EncodeToString(mac.Sum(nil))
The enhancer call¶
For each freshly opened finding patchy POSTs to spec.generic.enhance.url, bounded by timeout (default 60s):
{
"version": "v1",
"integration": "warehouse",
"issue": {
"repo": { "owner": "acme", "name": "orders" },
"number": 17,
"title": "SQL injection in the nightly export",
"body": "…finding description markdown…",
"labels": ["security-finding: opened"],
"cloudResource": null
}
}
integration names the Integration the call is for, so one process can serve several from a single endpoint. Respond
204 (or 200 with an empty body) when you have nothing to contribute, else 200 with:
{
"owners": ["alice", "bob"],
"commentMarkdown": "Owned by team-warehouse; runbook: https://…",
"attributes": { "system": "warehouse", "tier": "1" },
"repository": { "provider": "github", "url": "https://github.com/acme/orders" }
}
ownersdrive tracking-issue assignment, in preference order.commentMarkdownbecomes one sticky comment per integration on the tracking issue, edited in place on change.attributesproject assecurity-context: k=vtracking labels; on key collisions the first enhancer in the chain wins, and generic integrations run in name order after the cloud enhancers.repositoryresolves a cloud finding that arrived repo-less. It is honoured only when the finding has no repository yet, only once, and only forprovider: "github".
Semantics to build against: the call is synchronous and per-finding; an error or timeout is logged and skipped — the finding advances without your enrichment — except a repo-less cloud finding, which holds and retries for as long as its accumulation window allows, because "could not find out" must not be confused with "no repository exists". The finding stores at most 8 enrichments (excess drop in chain order) and truncates each comment at 16384 characters.
Testing¶
patchy dev enhance fires a single signed enhancer exchange at your endpoint from a findings payload file — no cluster,
no server to host. The local-testing walkthrough covers it
alongside the full patchy dev generic harness.
Network posture¶
The context-controller dials your enhancer endpoint from inside the cluster; the default NetworkPolicy allows egress on
TCP 443 only, and an endpoint on another port needs the contextController.networkPolicy.extraEgress chart value. As
with every generic endpoint, treat it as part of your security boundary and
keep it off the public internet where you can.