Skip to content

Commit aaae513

Browse files
committed
test(execpolicy): pin absolute fallback exact matching
Pin the never-matchable-traversal stance through the rooted absolute fallback and the tilde-rooted channel: traversal spellings of an exact rule path must stay unmatchable. Also document that approved_for_session is deliberately clone-private (a remembered grant must not authorize delegated calls in cloned executors).
1 parent aa0bf1e commit aaae513

1 file changed

Lines changed: 33 additions & 0 deletions

File tree

crates/execpolicy/src/lib.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,9 @@ pub struct ExecPolicyEngine {
324324
/// Legacy flat lists kept for backward compatibility with `new()`.
325325
trusted_prefixes: Vec<String>,
326326
denied_prefixes: Vec<String>,
327+
/// Deliberately clone-private, unlike `rulesets`: a remembered grant is a
328+
/// decision the parent session made for its own calls, so it must not
329+
/// silently authorize a delegated call in a cloned executor.
327330
approved_for_session: HashSet<String>,
328331
/// Arity dictionary for command-prefix allow-rule matching.
329332
arity_dict: BashArityDict,
@@ -2285,6 +2288,22 @@ mod tests {
22852288
})
22862289
.unwrap();
22872290
assert_eq!(decision.matched_rule, None);
2291+
2292+
// The fallback is exact: a traversal spelling of the same file is a
2293+
// different token string and must stay unmatchable (the documented
2294+
// "traversal is never matchable" stance, pinned through the rooted
2295+
// fallback too).
2296+
let decision = engine
2297+
.check(ExecPolicyContext {
2298+
command: "",
2299+
cwd: "/workspace",
2300+
tool: Some("read_file"),
2301+
path: Some("/root/../root/.ssh/config"),
2302+
ask_for_approval: AskForApproval::OnFailure,
2303+
sandbox_mode: Some("workspace-write"),
2304+
})
2305+
.unwrap();
2306+
assert_eq!(decision.matched_rule, None);
22882307
}
22892308

22902309
#[test]
@@ -2312,6 +2331,20 @@ mod tests {
23122331
})
23132332
.unwrap();
23142333
assert_eq!(decision.matched_action, Some(PermissionAction::Deny));
2334+
2335+
// The tilde-rooted channel is exact as well: a traversal spelling of
2336+
// the same file must not match (never-matchable-traversal stance).
2337+
let decision = engine
2338+
.check(ExecPolicyContext {
2339+
command: "",
2340+
cwd: "/workspace",
2341+
tool: Some("read_file"),
2342+
path: Some("~/.ssh/../ssh/config"),
2343+
ask_for_approval: AskForApproval::OnFailure,
2344+
sandbox_mode: Some("workspace-write"),
2345+
})
2346+
.unwrap();
2347+
assert_eq!(decision.matched_rule, None);
23152348
}
23162349

23172350
#[test]

0 commit comments

Comments
 (0)