π€ Kelos Agent @gjkim42
Summary
The spec.when.webhook (GenericWebhook) source accepts unauthenticated POSTs at /webhook/<source>. The handler does not read or validate any signature header, despite the API doc-comment, the Helm chart's webhookServer.sources.generic.secretName field, and the <SOURCE>_WEBHOOK_SECRET envFrom mount all suggesting otherwise.
Evidence
internal/webhook/handler.go (GenericSource branch, ~lines 211β231) extracts the source name, lists spawners, and computes a delivery ID. There is no signature header read, no env-var lookup of <SOURCE>_WEBHOOK_SECRET, and no HMAC validation call.
internal/webhook/signature.go only exposes ValidateGitHubSignature and ValidateLinearSignature. There is no generic equivalent.
internal/webhook/handler_test.go β TestGenericServeHTTP_CreatesTaskForMatchingSpawner and TestGenericServeHTTP_AcceptsUnknownSource both succeed with no signature header set; there is no RejectsInvalidSignature counterpart.
internal/manifests/charts/kelos/templates/webhook-server.yaml mounts webhookServer.sources.generic.secretName via envFrom, but no code consumes those env vars.
Impact
Operators who follow the chart's secretName knob (or the API doc-comment for GenericWebhook) will deploy what they believe is an HMAC-protected endpoint, but in practice any unauthenticated client that can reach /webhook/<source> and matches a registered spawner can trigger Task creation.
Proposed implementation
- Add a
validateGenericSignature(payload, signature, secret) (or reuse validateHMACSignature) verifying X-Hub-Signature-256: sha256=<hex> (matching the GitHub scheme already in signature.go).
- In the
GenericSource branch of WebhookHandler.ServeHTTP, look up <SOURCE>_WEBHOOK_SECRET (uppercased source name) from the process environment, validate before any spawner work, and return 401 on mismatch.
- Decide policy when the env var is unset for a source: reject (safe default) vs. accept with a warning. Reject is consistent with the current GitHub/Linear behavior of requiring
WEBHOOK_SECRET.
- Add handler tests: missing signature, wrong signature, valid signature; verify per-source env-var lookup.
- Update
docs/integration.md and examples/13-taskspawner-generic-webhook/README.md to reaffirm the security guarantee once the implementation lands.
Workaround until implemented
Restrict ingress to the generic webhook pod (NetworkPolicy, private LB, IP allowlist on the Ingress/Gateway). The current docs in PR #1035 reflect this state.
Related
Surfaced during review of #1035 (docs PR for the GenericWebhook source).
π€ Kelos Agent @gjkim42
Summary
The
spec.when.webhook(GenericWebhook) source accepts unauthenticated POSTs at/webhook/<source>. The handler does not read or validate any signature header, despite the API doc-comment, the Helm chart'swebhookServer.sources.generic.secretNamefield, and the<SOURCE>_WEBHOOK_SECRETenvFrom mount all suggesting otherwise.Evidence
internal/webhook/handler.go(GenericSource branch, ~lines 211β231) extracts the source name, lists spawners, and computes a delivery ID. There is no signature header read, no env-var lookup of<SOURCE>_WEBHOOK_SECRET, and no HMAC validation call.internal/webhook/signature.goonly exposesValidateGitHubSignatureandValidateLinearSignature. There is no generic equivalent.internal/webhook/handler_test.goβTestGenericServeHTTP_CreatesTaskForMatchingSpawnerandTestGenericServeHTTP_AcceptsUnknownSourceboth succeed with no signature header set; there is noRejectsInvalidSignaturecounterpart.internal/manifests/charts/kelos/templates/webhook-server.yamlmountswebhookServer.sources.generic.secretNameviaenvFrom, but no code consumes those env vars.Impact
Operators who follow the chart's
secretNameknob (or the API doc-comment forGenericWebhook) will deploy what they believe is an HMAC-protected endpoint, but in practice any unauthenticated client that can reach/webhook/<source>and matches a registered spawner can trigger Task creation.Proposed implementation
validateGenericSignature(payload, signature, secret)(or reusevalidateHMACSignature) verifyingX-Hub-Signature-256: sha256=<hex>(matching the GitHub scheme already insignature.go).GenericSourcebranch ofWebhookHandler.ServeHTTP, look up<SOURCE>_WEBHOOK_SECRET(uppercased source name) from the process environment, validate before any spawner work, and return 401 on mismatch.WEBHOOK_SECRET.docs/integration.mdandexamples/13-taskspawner-generic-webhook/README.mdto reaffirm the security guarantee once the implementation lands.Workaround until implemented
Restrict ingress to the generic webhook pod (NetworkPolicy, private LB, IP allowlist on the Ingress/Gateway). The current docs in PR #1035 reflect this state.
Related
Surfaced during review of #1035 (docs PR for the GenericWebhook source).