Skip to content

Commit 360c5a0

Browse files
authored
fix(policy): require full binary scope when enabling uninspected credentials (#4171)
Closes #3942 Signed-off-by: Eric Curtin <eric.curtin@docker.com>
1 parent 9fd41e6 commit 360c5a0

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

‎crates/openshell-policy/src/merge.rs‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,10 @@ fn endpoint_attributes_cover(loaded: &NetworkEndpoint, proposed: &NetworkEndpoin
872872
loaded.request_body_credential_rewrite,
873873
proposed.request_body_credential_rewrite,
874874
)
875+
&& flag_covers(
876+
loaded.allow_uninspected_credentials,
877+
proposed.allow_uninspected_credentials,
878+
)
875879
// Fields the merge neither widens nor retains: it drops them entirely.
876880
// An unset proposal value asks for nothing and is satisfied by whatever
877881
// is loaded; a set value that differs was dropped, so the proposal is
@@ -5070,6 +5074,74 @@ mod tests {
50705074
));
50715075
}
50725076

5077+
/// An endpoint flag applies to every binary on the rule, so an update that
5078+
/// turns one on must declare the rule's whole binary scope.
5079+
#[test]
5080+
fn enabling_an_endpoint_flag_requires_the_whole_binary_scope() {
5081+
let base = endpoint("api.example.com", 443);
5082+
for flag in [
5083+
"allow_encoded_slash",
5084+
"websocket_credential_rewrite",
5085+
"request_body_credential_rewrite",
5086+
"allow_uninspected_credentials",
5087+
] {
5088+
let mut widened = base.clone();
5089+
match flag {
5090+
"allow_encoded_slash" => widened.allow_encoded_slash = true,
5091+
"websocket_credential_rewrite" => widened.websocket_credential_rewrite = true,
5092+
"request_body_credential_rewrite" => widened.request_body_credential_rewrite = true,
5093+
_ => widened.allow_uninspected_credentials = true,
5094+
}
5095+
let existing = rule_with_authorizations(
5096+
"realtime",
5097+
vec![base.clone()],
5098+
&["/usr/bin/tool-a", "/usr/bin/tool-b"],
5099+
);
5100+
let merge = |binaries: &[&str]| {
5101+
merge_policy(
5102+
policy_with_rule("realtime", existing.clone()),
5103+
&[PolicyMergeOp::AddRule {
5104+
rule_name: "realtime".to_string(),
5105+
rule: rule_with_authorizations("realtime", vec![widened.clone()], binaries),
5106+
}],
5107+
)
5108+
};
5109+
5110+
assert!(
5111+
matches!(
5112+
merge(&["/usr/bin/tool-a"]),
5113+
Err(PolicyMergeError::ExistingBinariesWouldInheritAuthorization {
5114+
undeclared_binaries,
5115+
..
5116+
}) if undeclared_binaries == ["/usr/bin/tool-b"]
5117+
),
5118+
"{flag}: tool-b would inherit the flag undeclared"
5119+
);
5120+
assert!(
5121+
merge(&["/usr/bin/tool-a", "/usr/bin/tool-b"]).is_ok(),
5122+
"{flag}: naming every binary must succeed"
5123+
);
5124+
}
5125+
}
5126+
5127+
#[test]
5128+
fn coverage_requires_a_proposed_uninspected_credentials_exception() {
5129+
let loaded = policy_with_rule(
5130+
"realtime",
5131+
rule_with_authorizations(
5132+
"realtime",
5133+
vec![endpoint("api.example.com", 443)],
5134+
&["/usr/bin/client"],
5135+
),
5136+
);
5137+
let mut proposed_endpoint = endpoint("api.example.com", 443);
5138+
proposed_endpoint.allow_uninspected_credentials = true;
5139+
let proposed =
5140+
rule_with_authorizations("realtime", vec![proposed_endpoint], &["/usr/bin/client"]);
5141+
5142+
assert!(!policy_covers_rule(&loaded, &proposed));
5143+
}
5144+
50735145
fn endpoint_with_ports(host: &str, ports: &[u32]) -> NetworkEndpoint {
50745146
NetworkEndpoint {
50755147
host: host.to_string(),

0 commit comments

Comments
 (0)