fix(supervisor-network): L7 endpoint validation edge cases - #2464
fix(supervisor-network): L7 endpoint validation edge cases#2464andrewwhitecdw wants to merge 4 commits into
Conversation
…orts validate_l7_policies assumed every endpoint entry was a JSON object and used ep.as_object_mut().unwrap() in expand_access_presets. A non-object endpoint would panic. Add an object-shape validation error and replace the unwraps with safe pattern matching. Also filter zero values out of the ports array to match the scalar port validation (which already rejects port == 0). Signed-off-by: Andrew White <andrewh@cdw.com>
…arser network_endpoint_from_json accepted port 0 from the ports array without filtering, creating an endpoint with no usable ports. Drop zero entries so the array behaves consistently with the scalar port field. Signed-off-by: Andrew White <andrewh@cdw.com>
|
I have read the DCO document and I hereby sign the DCO. |
johntmyers
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Validation: This is project-valid small, concentrated work in supervisor network-policy handling: it replaces a malformed-input panic with fail-closed validation and attempts to make zero-port handling consistent. No duplicate work was found.
Head SHA: 583f62ef7019a4c54b23fe91e6ef289c2ca02d4a
Review findings:
- One correctness issue is anchored inline: the raw-policy zero-port filter does not normalize the policy data later supplied to OPA, so mixed arrays still retain zero.
- Focused regression coverage is missing for all three changed contracts. Please add unit tests showing that a non-object endpoint reports the expected validation error and preset expansion remains panic-free; raw-policy
[0]and[0, 443]follow the intended contract; and agent proposals reject an all-zero array while retaining only positive entries from mixed arrays, including scalar fallback.
Docs: Fern docs are not needed for this malformed-input robustness fix; the existing policy reference already describes endpoint objects and concrete MCP ports.
E2E: The changed network-policy path requires test:e2e after review findings are resolved.
Next state: gator:in-review pending an author update.
| |arr| { | ||
| arr.iter() | ||
| .filter_map(serde_json::Value::as_u64) | ||
| .filter(|p| *p > 0) |
There was a problem hiding this comment.
gator-agent
Warning: This filters only a temporary vector whose sole consumer already checks any(|port| *port > 0). Consequently, [0] was already rejected, while [0, 443] still passes and the zero remains in the JSON supplied to OPA. Please either reject any zero-valued ports member or remove zeros during endpoint normalization/proto serialization, then cover all-zero and mixed arrays with regression tests.
There was a problem hiding this comment.
I will get to these later today thanks for raising this issue.
|
@andrewwhitecdw Several of your PRs are awaiting feedback, please let us know if you plan on looking so we can plan accordingly |
…ion tests Addresses gator-agent review feedback on NVIDIA#2464: - Filter zero values from endpoint ports arrays in normalize_endpoint_ports so OPA never sees a zero port. - Promote positive scalar port to ports array; leave all-zero arrays empty. - Skip non-object endpoints during normalization instead of panicking. - Add regression tests for non-object endpoint validation, zero-port filtering, and scalar fallback. Signed-off-by: Andrew White <andrewh@cdw.com>
johntmyers
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Validation: This remains project-valid small, concentrated robustness work in supervisor network-policy handling.
Head SHA: 200ecd2f0444b5ef60d33c2332990c546e521dc5
Thanks @andrewwhitecdw. I checked the update you pushed after the earlier zero-port normalization and test feedback. The raw-YAML mixed-array case is now normalized and the added helper tests cover that path, but the production proto load/reload path still bypasses the new normalizer.
Review findings:
- One critical correctness/security-boundary issue is anchored inline: zero ports still reach OPA through
OpaEngine::from_proto. - One warning is anchored inline: the separate agent-proposal parser behavior still needs focused regression coverage.
Docs: Fern docs are not needed because this hardens malformed input without changing supported policy syntax or commands.
E2E: The network-policy path still requires test:e2e after review findings are resolved.
Next state: gator:in-review pending an author update.
| // If "ports" already exists, filter out zero values so OPA never | ||
| // sees a zero port. An all-zero array becomes empty and falls back | ||
| // to scalar "port" promotion below. | ||
| if let Some(ports) = ep_obj.get_mut("ports").and_then(|v| v.as_array_mut()) { |
There was a problem hiding this comment.
gator-agent
Critical (CWE-20): This normalizer runs only from preprocess_yaml_data; production OpaEngine::from_proto loads proto_to_opa_data_json without calling it. validate_l7_policies filters only a temporary vector, so a CLI policy with ports: [0, 443] still reaches OPA containing zero during normal sandbox load/reload. Please filter in proto_to_opa_data_json or apply shared normalization to proto-generated JSON, then add an OpaEngine::from_proto regression test.
| } | ||
|
|
||
| let mut ports = endpoint.ports; | ||
| ports.retain(|p| *p > 0); |
There was a problem hiding this comment.
gator-agent
Warning (CWE-20): This changes the untrusted agent-proposal parser without testing that path. Please add proposal_chunks_from_body cases for mixed [0, 443], zero-only [0] rejection, and [0] with positive scalar fallback, asserting the resulting proto port and ports. The new OPA helper tests cannot protect this separate parser.
|
Thanks for the nudge — I’ve gone through all five open PRs:
All relevant test suites pass. Please re-review when you have a chance. |
…arser tests proto_to_opa_data_json cloned e.ports unfiltered, so a policy with ports: [0, 443] still reached OPA with the zero even though the YAML preprocess path normalized it. Filter zero ports in the production path the same way (mixed arrays drop zeros, zero-only arrays fall back to a positive scalar port, zero scalar port is not promoted). Add tests: - proto_to_opa_data_json_filters_zero_ports_in_production_path - proposal_chunks_from_body_filters_mixed_zero_ports - proposal_chunks_from_body_rejects_zero_only_ports - proposal_chunks_from_body_zero_ports_falls_back_to_scalar_port cargo test -p openshell-supervisor-network --lib: 977 passed. Signed-off-by: Andrew White <andrewh@cdw.com>
|
Both review items addressed:
Verified: |
johntmyers
left a comment
There was a problem hiding this comment.
gator-agent
PR Review Status
Validation: This remains project-valid small, concentrated robustness work in supervisor network-policy handling.
Head SHA: ed4a4b63649a5857e2405a8f72bbb62f99a93282
Thanks @andrewwhitecdw. I checked your update about filtering zero ports in the production proto-to-OPA path and adding focused proposal-parser coverage. Those prior gaps are addressed, but the independent review found two remaining normalization cases that can reinterpret malformed or zero-only authorization input as a scalar-port allow; both are anchored inline. A focused preset-expansion regression gap is also anchored inline.
Review findings:
- Two blocking CWE-20 warnings are anchored inline: malformed JSON array elements can be discarded before scalar fallback, and zero-only proto arrays can activate a shadowed scalar despite documented
portsprecedence. - One focused test gap is anchored inline for the removed
expand_access_presetsunwraps.
Docs: Fern docs are not needed for the intended malformed-input robustness fix. If scalar fallback for an explicitly present zero-only ports array is intentional, the schema and proto precedence contract would need updating.
E2E: The network-policy path still requires test:e2e after review findings are resolved.
Next state: gator:in-review pending an author update.
| // sees a zero port. An all-zero array becomes empty and falls back | ||
| // to scalar "port" promotion below. | ||
| if let Some(ports) = ep_obj.get_mut("ports").and_then(|v| v.as_array_mut()) { | ||
| ports.retain(|p| p.as_u64().is_some_and(|n| n > 0)); |
There was a problem hiding this comment.
gator-agent
Warning (CWE-20): retain removes every value that is not a positive JSON u64, not only zero. In local-file mode, ports: ["443"] plus port: 8443 therefore becomes ports: [8443], silently converting malformed authorization input into an allow. Please reject non-integer entries before normalization, or remove exactly numeric zero (p.as_u64() != Some(0)) so malformed arrays remain fail-closed. Add a regression case.
| // never sees them, then ports takes precedence over a | ||
| // single promoted port. Rego always sees "ports". | ||
| let filtered: Vec<u32> = e.ports.iter().copied().filter(|&p| p > 0).collect(); | ||
| let ports: Vec<u32> = if !filtered.is_empty() { |
There was a problem hiding this comment.
gator-agent
Warning (CWE-20): This changes the documented proto precedence: {port: 8080, ports: [0]} now permits 8080, although a present ports array previously took precedence and matched nothing. Malformed or zero-only authorization data should not activate a shadowed scalar port. Branch on the original e.ports.is_empty() state and filter inside the ports branch, or reject the proto explicitly; then update the scalar-fallback test.
| use super::*; | ||
|
|
||
| #[test] | ||
| fn validate_l7_policies_rejects_non_object_endpoint() { |
There was a problem hiding this comment.
gator-agent
Suggestion: This test verifies validation, but not expand_access_presets, where the unwraps were removed. Please add a direct expansion test containing a non-object endpoint and a valid preset endpoint, asserting the invalid entry remains unchanged and the valid preset expands. It would also be stronger to assert indexed errors for both malformed entries.
Summary
Two small robustness fixes in L7 network policy handling:
validate_l7_policiesassumed every entry innetwork_policies.<name>.endpointswas a JSON object, andexpand_access_presetscalledep.as_object_mut().unwrap(). A malformed non-object endpoint caused a panic. This change adds an object-shape validation error and replaces the unwraps with safe pattern matching.portfield was already validated to be> 0, but theportsarray accepted0values silently. The same inconsistency existed in the agent-proposal endpoint parser. Both paths now filter out zero ports.Related Issue
N/A — small fixes found during code review.
Changes
l7/mod.rs: validate endpoint is an object; replaceas_object_mut().unwrap()with safe matching; filterportsarray to> 0policy_local.rs: filter zero entries fromendpoint.portsTesting
mise run pre-commitpasses (mise unavailable in this environment; ran equivalentcargo fmt+cargo clippy -p openshell-supervisor-network --all-targets— clean)cargo test -p openshell-supervisor-network --lib— 969 passed)Checklist