Skip to content

fix(supervisor-network): L7 endpoint validation edge cases - #2464

Open
andrewwhitecdw wants to merge 4 commits into
NVIDIA:mainfrom
andrewwhitecdw:fix-l7-policy-validation-edge-cases/aw
Open

fix(supervisor-network): L7 endpoint validation edge cases#2464
andrewwhitecdw wants to merge 4 commits into
NVIDIA:mainfrom
andrewwhitecdw:fix-l7-policy-validation-edge-cases/aw

Conversation

@andrewwhitecdw

Copy link
Copy Markdown
Contributor

Summary

Two small robustness fixes in L7 network policy handling:

  1. validate_l7_policies assumed every entry in network_policies.<name>.endpoints was a JSON object, and expand_access_presets called ep.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.
  2. The scalar port field was already validated to be > 0, but the ports array accepted 0 values 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; replace as_object_mut().unwrap() with safe matching; filter ports array to > 0
  • policy_local.rs: filter zero entries from endpoint.ports

Testing

  • mise run pre-commit passes (mise unavailable in this environment; ran equivalent cargo fmt + cargo clippy -p openshell-supervisor-network --all-targets — clean)
  • Unit tests pass (cargo test -p openshell-supervisor-network --lib — 969 passed)
  • E2E tests added/updated (if applicable)

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs updated (if applicable)

…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>
@copy-pr-bot

copy-pr-bot Bot commented Jul 24, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@andrewwhitecdw

Copy link
Copy Markdown
Contributor Author

I have read the DCO document and I hereby sign the DCO.

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will get to these later today thanks for raising this issue.

@johntmyers johntmyers added the gator:in-review Gator is reviewing or awaiting PR review feedback label Jul 24, 2026
@johntmyers

Copy link
Copy Markdown
Collaborator

@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 johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@andrewwhitecdw

Copy link
Copy Markdown
Contributor Author

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>
@andrewwhitecdw

Copy link
Copy Markdown
Contributor Author

Both review items addressed:

  • Critical (opa.rs): proto_to_opa_data_json now filters zero ports in the production path, same as the YAML preprocess path — ports: [0, 443][443], zero-only arrays fall back to a positive scalar port, and a zero scalar port is not promoted. Zero ports can no longer reach OPA from OpaEngine::from_proto.
  • Warning (policy_local.rs): added proposal_chunks_from_body tests for mixed [0, 443], zero-only [0] rejection, and [0] with positive scalar fallback, asserting the resulting port/ports.

Verified: cargo test -p openshell-supervisor-network --lib → 977 passed.

@johntmyers johntmyers left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ports precedence.
  • One focused test gap is anchored inline for the removed expand_access_presets unwraps.

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));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gator:in-review Gator is reviewing or awaiting PR review feedback

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants