Skip to content

fix(relay): descriptive SEC-006 denial + structured skip log for workflow triggers - #5135

Open
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:fix/5122-descriptive-workflow-auth-errors
Open

fix(relay): descriptive SEC-006 denial + structured skip log for workflow triggers#5135
iroiro147 wants to merge 1 commit into
block:mainfrom
iroiro147:fix/5122-descriptive-workflow-auth-errors

Conversation

@iroiro147

Copy link
Copy Markdown
Contributor

Resolves #5122 (partial).

Root cause analysis

Issue #5122 reports that call_webhook workflow steps never deliver HTTP requests, with no error surfaced and no run record visible. The investigation identified three distinct, interlocking causes:

  1. 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 a WARN log with the workflow_id and error, no owner_pubkey or requires_elevated flag.

  2. 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 runs queries Nostr kinds 46001–46003, but the relay never emits those events. Run state lives only in Postgres. This means even when call_webhook does 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.

  3. Outbound HTTP blocked by deployment (infrastructure, not code): If the relay is hosted where egress HTTP is restricted, call_webhook_impl returns Err(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_trigger so 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)
  • SEC-006 branch: "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
  • Ordinary-denial fallback: "forbidden: workflow owner's channel authority check failed; {error}"

Also enrich the WARN log on the event-trigger path (on_eventcheck_owner_authority) with owner_pubkey and requires_elevated_authority, so the same silent skip is diagnosable from relay logs.

What this PR does NOT fix

Tests

  • cargo check -p buzz-relay -p buzz-workflow: clean
  • cargo fmt --check -p buzz-relay -p buzz-workflow: clean
  • cargo clippy -p buzz-relay -p buzz-workflow --all-targets -- -D warnings: clean
  • cargo 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 failed
  • New unit tests: denial_for_elevated_definition_names_sec006_and_call_webhook, denial_for_ordinary_definition_has_no_sec006_hint

Files changed

  • crates/buzz-relay/src/handlers/command_executor.rs (+52/−7): descriptive rejections + extracted workflow_owner_authority_denial helper + unit tests
  • crates/buzz-workflow/src/lib.rs (+9/−2): structured WARN log with owner_pubkey + requires_elevated_authority

…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>
@iroiro147
iroiro147 requested a review from a team as a code owner August 7, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Workflow call_webhook action never delivers — silent, no error, no run record

1 participant