Skip to content

pkg/cmd/sources is dead code: source auth validation never runs #341

Description

@leggetter

Summary

validateSourceAuthFromSpec has been inert for every source type, silently. It is not merely unreliable for non-STRIPE types — it never executes its checks at all.

Mechanism

parseOpenAPISpec (pkg/cmd/sources/types.go:78-98) unmarshals into a struct rooted at:

components.schemas.SourceCreateRequest.properties.verification_configs

Neither exists in the current 2025-07-01 spec:

$ curl -s https://api.hookdeck.com/2025-07-01/openapi | python3 -c "
import json,sys; s=json.load(sys.stdin); c=s['components']['schemas']
print('SourceCreateRequest present?', 'SourceCreateRequest' in c)
print('schemas mentioning verification_configs:', sum('verification_configs' in json.dumps(v) for v in c.values()))
print('any *CreateRequest schemas:', [n for n in c if 'CreateRequest' in n])"
SourceCreateRequest present? False
schemas mentioning verification_configs: 0
any *CreateRequest schemas: []

json.Unmarshal into a struct with no matching keys succeeds with zero values. So the type enum is empty, the returned map is empty, and the error is nil.

That nil is what makes it silent. validateSourceAuthFromSpec (pkg/cmd/source_common.go:236-291) never reaches its warn-and-continue branch:

sourceTypes, err := sources.FetchSourceTypes()
if err != nil {
    fmt.Printf("Warning: could not fetch source types for validation: %v\n", err)  // never reached
    return nil
}
st, ok := sourceTypes[strings.ToUpper(sourceType)]
if !ok {
    return nil                                                                     // always taken
}

Consequences:

  • No --type is ever validated against the enum.
  • No type-specific required-auth rule ever fires.
  • Both STRIPE hardcodes are moot: optionalAuthSourceTypes (source_common.go) and the manual correction at types.go:144-149 ("Manually correcting Stripe for the sake of the test").
  • The cache at os.TempDir()/hookdeck_source_types.json stores the empty map for 24h.

pkg/cmd/sources/types_test.go passes because it serves its own mockOpenAPISpec in the old shape, so it tests the parser against a spec the API no longer publishes.

What a correct parser should derive

The per-type auth shape is available, just under different names — SourceTypeConfig<TYPE>.auth, a $ref to a per-provider schema:

"SourceConfigStripeAuth": {
  "properties": {"webhook_secret_key": {"type": "string"}},
  "required": ["webhook_secret_key"],
  "nullable": true
}

Across the current spec: 153 source types, 152 with an auth property, and auth is nullable — i.e. optional — for 148 of them. Only HTTP and WEBHOOK have non-nullable auth.

That last number matters for scoping: a spec-driven "auth is required for this type" rule would correctly not fire for STRIPE, ELEVENLABS, or almost anything else. So fixing this parser would not have caught #335 — that needed the type-independent empty-value check shipped in v2.5.0. The two are orthogonal.

What fixing it would buy

  • Real per-type auth scheme knowledge: reject inapplicable flags (--hmac-secret on a STRIPE source) and drive --type-specific help (AGENTS.md §3 "Help System Integration").
  • --type validated against the real 153-value enum instead of a round trip.
  • Deletion of both STRIPE hardcodes in favour of spec-derived nullability.
  • An honest failure mode: return an error when the spec shape does not match, so it warns instead of disabling itself.

Migration risk

This turns currently-inert validation live across every source and connection command, so commands that succeed today could start failing — --type typos, and HTTP/WEBHOOK sources created without auth. Worth staging behind the warn-and-continue path first, and checking what HTTP/WEBHOOK without auth means in practice before enforcing it.

Context

Found while triaging #335 for v2.5.0. Deliberately not fixed there to keep that a bug-fix release.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions