Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneesstack72

Relationships

#1858 swamp access check/can-i report ALLOW where enforcement DENIES (explain() not deny-first)

Opened by stack72 · 8/26/2026· Shipped 8/26/2026

Summary

swamp access check (and can-i in query mode) can report ALLOW for a subject/action/resource that server enforcement would DENY. Real enforcement (decide()) evaluates deny grants before allow grants, but explain() returns matching grants in candidate order with no deny-first sort, and the CLI/JSON renderers use decisions[0].effect as the verdict.

Steps to reproduce

In a fresh initialized repo (order of creation does not matter — reproduces both ways):

swamp access grant create --subject user:zed --deny run --on "workflow:secret"
swamp access grant create --subject user:zed --allow run --on "workflow:*"
swamp access check --subject user:zed --action run --on workflow:secret --json

Actual

"effect": "allow"

Log mode prints ALLOW via grant <id>… (user:zed → run → workflow:secret) and lists the matching DENY grant right below it in "All matching grants:". Exit code 0.

Expected

effect: "deny" — deny-overrides-allow, matching what decide() does at enforcement time (denies are evaluated first in grant_based_access_decision_service.ts, and its unit tests assert deny-over-allow). A server-side workflow.run for this subject is correctly rejected with Access denied: … is explicitly denied ….

Root cause pointers

  • src/domain/access/grant_based_access_decision_service.tsdecide() buckets denies first (~L139-187); explain() returns matches in candidate order with no deny-first sort (~L206-220).
  • src/presentation/renderers/access_check.ts (~L71-91) and the can-i renderer — verdict taken from decisions[0].effect.
  • Same surface in the server handler path (access.check / access.can-i).

Fix options: sort deny-first in explain(), or compute the verdict via decide() and use explain() only for the "All matching grants" listing.

Impact

The tool whose job is to explain authorization gives false positives: an operator auditing policy with check sees ALLOW, ships, and a user then hits is explicitly denied at runtime. It also makes --deny grants look inert in every reporting surface even though they are enforced.

Environment

swamp 20260826.175216.0-sha.88d26aa7, macOS (darwin), local repo evaluation; behavior identical regardless of grant creation order. Found while researching the grants UAT test plan in swamp-uat (the plan pins this as a characterization test until fixed).

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 7 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

8/26/2026, 11:34:59 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack728/26/2026, 11:07:25 PM

Sign in to post a ripple.