Skip to content

hold_records bypasses the shared access-check helper, so a denied hold skips the audit-denial log #264

Description

@brylie

src/lib/services/holds.ts:42-53 hand-rolls its own token/access check (resolveOwningParentId + resolveShardForParent + tokenAllowsParent) instead of calling the shared requireAccessibleParent/requireAccessibleRecord helper every other permission-gated service function uses (e.g. documents.ts:132, records.ts:436,686).

CLAUDE.md states the audit log is "populated on every write/delete tool call, denied MCP attempt" — but because holds.ts doesn't go through the shared helper, a denied hold request never produces the _denied audit-log entry logDenial (permissions.ts:45-52) generates for every other permission-denied path. An admin filtering the audit log for denied attempts (per the PRD's requirement: "An admin can filter the audit log by agent identity and time range and see every action that agent took") would silently miss every denied hold attempt — a real gap in a documented guarantee, not a style preference.

Surfaced during the 2026-09-12 architecture audit (code-level scan of src/lib/services and src/lib/mcp).

  • Replace holds.ts's hand-rolled access check with requireAccessibleParent/requireAccessibleRecord
  • Confirm a denied hold_records call now produces a _denied audit-log entry, matching every other permission-denied path
  • Add Tier A coverage: an agent without access to a record's parent calls hold_records, gets denied, and the audit log records the denial

Done when: a denied hold request appears in audit_log filtered by denial, the same as a denied write/delete/read already does, verified by a Tier A test.

Priority: P1 — a documented audit-completeness guarantee is silently broken for exactly one MCP tool. Added to 0.5.0 — Stability, Reliability, and QA alongside the milestone's other audit/reliability work (#58, #88).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions