PUT source-group silently discards pre_hook/post_hook — hooks are settable only via the UI #54
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
PUT /api/hosts/{id}/source-groups/{gid}accepts a body containingpre_hook/post_hook, returns 200 with the updated group, and silently discards the hook. Nothing is persisted and no error or warning is returned. The only way to configure hooks is the web UI.The silent part is the bug. An API client has no way to tell the difference between "hook configured" and "hook ignored" — the 200 response looks like success, and because the response body also omits hook fields, a read-back cannot detect the discrepancy either.
Reproduce
Returns
200with the group JSON.updated_atadvances. The hook is not stored — verified by running a backup afterwards and observing nohook(pre):lines in the job log and no side effects from the script.Cause
internal/server/http/source_groups.gohas no hook fields in either the response view or the request struct:Meanwhile the persistence and UI layers fully support them —
internal/store/sources.goreads and writespre_hook/post_hookcolumns, andinternal/server/http/ui_sources.gohandles them includingEncryptHookForGroup. So the capability exists everywhere except the JSON API.Unknown JSON fields are ignored by default in Go, which is why the field vanishes without complaint.
Impact
Medium. Hooks are the supported mechanism for database quiescing, and quiescing is exactly the kind of thing an operator wants to configure reproducibly rather than by hand in a browser. Today that configuration cannot be scripted, version-controlled, or applied to several hosts programmatically — and an attempt to do so fails silently.
It also blocks automating a known-good configuration across a fleet: every host with a database needs the same hook, and each one has to be typed into the UI individually.
Suggested fix
Either:
Support hooks in the API — add
pre_hook/post_hookto the request struct, encrypt on write exactly as the UI path does, and return a redacted indicator (e.g.has_pre_hook: true) rather than the plaintext body, so secrets are not exposed on GET while clients can still verify configuration landed.Or reject them explicitly — return
400 unknown_fieldwhen a hook key is present, so the failure is loud. This is much less useful but strictly better than silence.Option 1 is preferable. If hook bodies must stay UI-only for secret-handling reasons, a boolean indicator on GET would still let a client detect "hook not configured" without exposing the script.
Environment
v1.2.1, agentv1.2.1internal/server/http/source_groups.go,internal/store/sources.go,internal/server/http/ui_sources.go