Skip to content

fix(security): name no nodes in authorization denials - #1322

Merged
kvlonge merged 5 commits into
mainfrom
fix/listing-denial-text
Nov 6, 2026
Merged

kvlonge merged 5 commits into
mainfrom
fix/listing-denial-text

Conversation

@kvlonge

@kvlonge kvlonge commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Why

Security fix. A refused request named the nodes the caller lacked permission on. Some of those nodes the caller could not see at all: a whole location, or a job's or trigger's write set. Refusals also read differently for existing and unknown locations, jobs and triggers, so a signed-in person could list another location's nodes or probe which objects exist.

Changes

  • Every person authorization refusal now returns fixed text per action, such as missing view permission for this target, plus the decision ID. It names no nodes and gives no count. This covers listings, check SQL and evidence, job runs, trigger pause and resume, sensor cursor reset, cancel and rerun.
  • An unknown location, job, trigger or run is refused only after authorization runs over the target. It gets the same status, body and headers as a forbidden one, apart from the decision ID and date, and both are audited.
  • The audit event still records the scope and the missing nodes for operators.
  • The server docs describe the new denial text.
  • No DuckShop case is added, because this changes HTTP authorization responses, not a project shape, data value or lifecycle.

Verification

  • New test on SQLite and PostgreSQL. It covers 8 location reads (partial viewer, unassigned location, unknown location) and job run, trigger pause, sensor reset, cancel and rerun (forbidden vs unknown). It checks:
    • no node key appears in any body;
    • responses are identical after masking the UUID and date;
    • every refusal is audited with the detail.
  • The new test fails on the previous code, and fails again when the unknown-trigger refusal is removed.
  • Updated the Rust, Python and TypeScript tests that asserted the old text; they pass against this branch's server.
  • make check-ci passes.

A refused location listing named the nodes the person could not view and
read differently for a location that does not exist. Listing refusals now
carry one fixed message with the decision ID, identical for unassigned and
unknown locations. The audit event keeps the missing nodes.
Every refusal from person authorization now shows fixed text per action
and its decision ID; the audit event keeps the missing nodes. Unknown
jobs and triggers are refused only after the caller is authorized for
the target, so an unknown object reads the same as a forbidden one.
@kvlonge kvlonge changed the title fix(security): stop refused listings from naming nodes or revealing locations fix(security): name no nodes in authorization denials Nov 6, 2026
@kvlonge
kvlonge enabled auto-merge (squash) November 6, 2026 11:43
@kvlonge
kvlonge merged commit 8d2d2da into main Nov 6, 2026
50 checks passed
@kvlonge
kvlonge deleted the fix/listing-denial-text branch November 6, 2026 18:36
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.

1 participant