The doc comment on enforcePermission (src/permissions/enforce.ts) says:
Paths that fail validatePath are silently skipped — the tool's own input validation will surface a better error.
but the implementation calls validatePath(path) without catching, so any non-absolute (or ../~-carrying) path throws path must be absolute: ... out of the permission layer instead of reaching the tool's own validation:
function enforcePermission(rules, operation, path) {
if (rules.length === 0) return;
const canonical = validatePath(path); // throws — not "silently skipped"
if (decidePathAccess(rules, operation, canonical) === "deny") throw new Error(...);
}
Observed in deepagents@1.10.2. The practical consequence: on any graph configured with permissions, EVERY filesystem tool call with a relative path fails with the validation error — including pure reads, and including operations no rule covers — whereas the same relative path works fine on a rule-less graph (the legacy FilesystemBackend resolves it under rootDir).
Sibling detail in the same file: filterByPermissions DOES catch the validatePath throw (entries with unparsable paths are included), so the two halves of the module currently disagree about the documented contract.
Note for a potential fix: making the code match the doc (skip unparsable paths) has a subtle consequence — a deny rule would then never fire for relative paths at the rule layer; the backend's rootDir resolution would apply the operation instead. Depending on intent, canonicalizing relative paths against the backend root before rule evaluation (rather than skipping them) may be the safer contract.
Happy to provide more detail — we hit this downstream (stigmer/stigmer#429) and worked around it by normalizing paths in middleware before enforcement.
The doc comment on
enforcePermission(src/permissions/enforce.ts) says:but the implementation calls
validatePath(path)without catching, so any non-absolute (or../~-carrying) path throwspath must be absolute: ...out of the permission layer instead of reaching the tool's own validation:Observed in
deepagents@1.10.2. The practical consequence: on any graph configured withpermissions, EVERY filesystem tool call with a relative path fails with the validation error — including pure reads, and including operations no rule covers — whereas the same relative path works fine on a rule-less graph (the legacyFilesystemBackendresolves it underrootDir).Sibling detail in the same file:
filterByPermissionsDOES catch thevalidatePaththrow (entries with unparsable paths are included), so the two halves of the module currently disagree about the documented contract.Note for a potential fix: making the code match the doc (skip unparsable paths) has a subtle consequence — a deny rule would then never fire for relative paths at the rule layer; the backend's
rootDirresolution would apply the operation instead. Depending on intent, canonicalizing relative paths against the backend root before rule evaluation (rather than skipping them) may be the safer contract.Happy to provide more detail — we hit this downstream (stigmer/stigmer#429) and worked around it by normalizing paths in middleware before enforcement.