Skip to content

caveat_interceptor: /dev allowlist is an un-canonicalized string compare evaluated before the leash check #355

Description

@hartsock

Found while reviewing #354 (FD-rooted bounded opens). Pre-existing and deliberately out of that PR's scope, filed so it is not lost.

What

agent-bridle-core/src/caveat_interceptor.rs:326-330 allowlists /dev/null, /dev/stdout and /dev/stderr using an un-canonicalized string comparison, and evaluates that allowlist before the leash check.

Why it matters

This is the same failure family #354 exists to eliminate: a pathname is treated as identity. A string compare against /dev/null decides authority, but the string is not the object:

  • A path that string-compares unequal may still resolve to the intended device (/dev/./null, //dev/null, a symlink chain, a bind mount), so the allowlist is not a reliable allow.
  • More seriously, a path that string-compares equal need not resolve to the real device. Inside a container, a chroot, a mount namespace, or a directory the agent can influence, /dev/null can be an ordinary file, a symlink, or a device the grantor never intended — and because the compare short-circuits ahead of the leash check, the leash never gets to adjudicate it.

Because the allowlist runs first, this is a bypass of the adjudicator rather than a widening of it. AdmittedFence::admit is supposed to be the sole adjudicator; a pre-check that can say "allow" ahead of it is a second, weaker authority path.

Suggested direction

Do not fix by adding more string normalization — that reproduces the bug class. Prefer identity over spelling:

  • Resolve the candidate to an object and compare identity (dev/ino, i.e. the RootIdentity shape fdguard: open_beneath_* — race-free bounded opens beneath a granted root (#351) #354 introduces in fdguard), not text; or
  • delete the pre-check entirely and let the leash adjudicate /dev/null like anything else, if the grant model can express it; or
  • if the allowlist must stay for bootstrap reasons, move it after the leash check so it can only ever narrow, never widen.

Acceptance

  • A regression test where a path that string-compares equal to an allowlisted device resolves to something else, and is REFUSED.
  • A regression test where a legitimately-spelled variant of the real device is handled correctly under whichever rule is chosen.
  • The adjudicator ordering invariant stated explicitly: nothing may return Allow ahead of the leash check.

Provenance

Surfaced by the #354 review sweep for validated_path -> string -> open(path) -> authority reconstruction patterns. Not fixed there because #354 is scoped to the fdguard seam and its call-site wiring is #351's second half.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions