fix(relay): descriptive SEC-006 denial + structured skip log for workflow triggers - #5135
fix(relay): descriptive SEC-006 denial + structured skip log for workflow triggers#5135iroiro147 wants to merge 1 commit into
Conversation
…flow triggers When a manual workflow trigger's owner-authority check fails, the relay previously returned the same generic 'not authorized to trigger this workflow' for every failure mode — membership lapse, disabled workflow, SEC-006 elevated-role denial — leaving callers unable to act (issue block#5122's 'silent, no error, no run record'). Split the rejections by cause: * 'only the workflow owner may trigger' (wrong caller) * 'disabled or inactive' (lifecycle) * 'no channel scope' (cannot verify owner authority) * SEC-006 — 'contains exfiltration-capable actions (call_webhook) that require the owner to hold the owner or admin role in this channel' * generic fallback includes the underlying WorkflowError Also enrich the WARN emitted on event-trigger SEC-006 skip with workflow_id, owner_pubkey, and requires_elevated_authority so the same silent failure mode in the on_event path is diagnosable from relay logs alone. Regression tests cover both branches of the new denial helper — SEC-006 hint must name call_webhook + the required roles; the ordinary-denial branch must not leak SEC-006 vocabulary. Signed-off-by: iroiro147 <sarthak.singh@mastersunion.org>
|
Could this return a stable public cause such as |
Resolves #5122 (partial).
Root cause analysis
Issue #5122 reports that
call_webhookworkflow steps never deliver HTTP requests, with no error surfaced and no run record visible. The investigation identified three distinct, interlocking causes:Silent SEC-006 denial (actionable, fixed here): When a manual trigger's owner-authority check fails, the relay returned the same generic
"forbidden: not authorized to trigger this workflow"for every failure mode — membership lapse, disabled workflow, SEC-006 elevated-role denial — leaving callers unable to distinguish "you lack the admin role for call_webhook" from "workflow disabled." On the event-trigger path, the skip was only aWARNlog with the workflow_id and error, no owner_pubkey or requires_elevated flag.Run history unreadable (known, separately filed as Workflow run history is unreadable: always returns [] #2980 and workflow run history is write-never: 'buzz workflows runs' queries kinds 46001-46003 but relay never publishes them #4478):
buzz workflows runsqueries Nostr kinds 46001–46003, but the relay never emits those events. Run state lives only in Postgres. This means even whencall_webhookdoes fail (e.g. outbound HTTP blocked by infrastructure), the failure is invisible to the CLI. Not addressed here — it is a larger feature (event emission) tracked in its own issues.Outbound HTTP blocked by deployment (infrastructure, not code): If the relay is hosted where egress HTTP is restricted,
call_webhook_implreturnsErr(WorkflowError::WebhookError(...)), the run is marked Failed in Postgres, but cause Initial release — Sprout Nostr relay with enterprise extensions #2 hides the failure. Not fixable in-repo.What this PR fixes
Split the rejection text by cause in
handle_workflow_triggerso callers can act:"forbidden: only the workflow owner may trigger this workflow"(wrong caller)"forbidden: workflow is {status} (enabled={bool}); active runs require enabled=true and status=Active"(lifecycle)"forbidden: workflow has no channel scope; cannot verify owner authority"(fail-closed)"forbidden: SEC-006 — workflow contains exfiltration-capable actions (call_webhook) that require the owner to hold the 'owner' or 'admin' role in this channel; {underlying_error}"— names the cause and the fix"forbidden: workflow owner's channel authority check failed; {error}"Also enrich the
WARNlog on the event-trigger path (on_event→check_owner_authority) withowner_pubkeyandrequires_elevated_authority, so the same silent skip is diagnosable from relay logs.What this PR does NOT fix
finalize_run) tracked separately.call_webhookdelivery on hosts with blocked outbound HTTP — that is an infrastructure concern; the descriptive error from this PR at least makes the SEC-006 cause visible, and once Workflow run history is unreadable: always returns [] #2980 is fixed, run-history will surface the HTTP error too.Tests
cargo check -p buzz-relay -p buzz-workflow: cleancargo fmt --check -p buzz-relay -p buzz-workflow: cleancargo clippy -p buzz-relay -p buzz-workflow --all-targets -- -D warnings: cleancargo test -p buzz-relay --lib: 854 passed, 8 pre-existing failures (api::admin / api::media, verified identical on base, unrelated)cargo test -p buzz-workflow --lib: 154 passed, 0 faileddenial_for_elevated_definition_names_sec006_and_call_webhook,denial_for_ordinary_definition_has_no_sec006_hintFiles changed
crates/buzz-relay/src/handlers/command_executor.rs(+52/−7): descriptive rejections + extractedworkflow_owner_authority_denialhelper + unit testscrates/buzz-workflow/src/lib.rs(+9/−2): structuredWARNlog withowner_pubkey+requires_elevated_authority