Skip to content

Commit db40831

Browse files
authored
fix(sandbox): use succinct endpoint denial reason (#1584)
Signed-off-by: Kris Hicks <khicks@nvidia.com>
1 parent b2f0f22 commit db40831

2 files changed

Lines changed: 37 additions & 9 deletions

File tree

crates/openshell-sandbox/data/sandbox-policy.rego

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,16 @@ deny_reason := reason if {
3434
input.network
3535
input.exec
3636
not network_policy_for_request
37-
endpoint_misses := [r |
38-
some name
39-
policy := data.network_policies[name]
40-
not endpoint_allowed(policy, input.network)
41-
r := sprintf("endpoint %s:%d not in policy '%s'", [input.network.host, input.network.port, name])
42-
]
37+
not endpoint_policy_for_request
38+
count(data.network_policies) > 0
39+
reason := sprintf("endpoint %s:%d is not allowed by any policy", [input.network.host, input.network.port])
40+
}
41+
42+
deny_reason := reason if {
43+
input.network
44+
input.exec
45+
not network_policy_for_request
46+
endpoint_policy_for_request
4347
ancestors_str := concat(" -> ", input.exec.ancestors)
4448
cmdline_str := concat(", ", input.exec.cmdline_paths)
4549
binary_misses := [r |
@@ -49,9 +53,8 @@ deny_reason := reason if {
4953
not binary_allowed(policy, input.exec)
5054
r := sprintf("binary '%s' not allowed in policy '%s' (ancestors: [%s], cmdline: [%s]). SYMLINK HINT: the binary path is the kernel-resolved target from /proc/<pid>/exe, not the symlink. If your policy specifies a symlink (e.g., /usr/bin/python3) but the actual binary is /usr/bin/python3.11, either: (1) use the canonical path in your policy (run 'readlink -f /usr/bin/python3' inside the sandbox), or (2) ensure symlink resolution is working (check sandbox logs for 'Cannot access container filesystem')", [input.exec.path, name, ancestors_str, cmdline_str])
5155
]
52-
all_reasons := array.concat(endpoint_misses, binary_misses)
53-
count(all_reasons) > 0
54-
reason := concat("; ", all_reasons)
56+
count(binary_misses) > 0
57+
reason := concat("; ", binary_misses)
5558
}
5659

5760
deny_reason := "network connections not allowed by policy" if {
@@ -91,6 +94,12 @@ network_policy_for_request if {
9194
binary_allowed(data.network_policies[name], input.exec)
9295
}
9396

97+
endpoint_policy_for_request if {
98+
some name
99+
data.network_policies[name]
100+
endpoint_allowed(data.network_policies[name], input.network)
101+
}
102+
94103
# Endpoint matching: exact host (case-insensitive) + port in ports list.
95104
endpoint_allowed(policy, network) if {
96105
some endpoint

crates/openshell-sandbox/src/opa.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4585,6 +4585,25 @@ network_policies:
45854585
);
45864586
}
45874587

4588+
#[test]
4589+
fn deny_reason_collapses_endpoint_misses() {
4590+
let engine = test_engine();
4591+
let input = NetworkInput {
4592+
host: "not-configured.example.com".into(),
4593+
port: 443,
4594+
binary_path: PathBuf::from("/usr/local/bin/claude"),
4595+
binary_sha256: "unused".into(),
4596+
ancestors: vec![],
4597+
cmdline_paths: vec![],
4598+
};
4599+
let decision = engine.evaluate_network(&input).unwrap();
4600+
assert!(!decision.allowed);
4601+
assert_eq!(
4602+
decision.reason,
4603+
"endpoint not-configured.example.com:443 is not allowed by any policy"
4604+
);
4605+
}
4606+
45884607
/// Check if symlink resolution through `/proc/<pid>/root/` actually works.
45894608
/// Creates a real symlink in a tempdir and attempts to resolve it via
45904609
/// the procfs root path. This catches environments where the probe path

0 commit comments

Comments
 (0)