Skip to content

Generic webhook endpoint is unauthenticated β€” implement <SOURCE>_WEBHOOK_SECRET HMAC validationΒ #1040

@kelos-bot

Description

@kelos-bot

πŸ€– 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

  1. Add a validateGenericSignature(payload, signature, secret) (or reuse validateHMACSignature) verifying X-Hub-Signature-256: sha256=<hex> (matching the GitHub scheme already in signature.go).
  2. 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.
  3. 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.
  4. Add handler tests: missing signature, wrong signature, valid signature; verify per-source env-var lookup.
  5. 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).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions