Skip to content

fix(ocsf): widen the shorthand [reason:] budget so denial endpoints stay readable (NVIDIA/NemoClaw#4760)#1799

Open
latenighthackathon wants to merge 1 commit into
NVIDIA:mainfrom
latenighthackathon:fix/4760-ocsf-reason-truncation
Open

fix(ocsf): widen the shorthand [reason:] budget so denial endpoints stay readable (NVIDIA/NemoClaw#4760)#1799
latenighthackathon wants to merge 1 commit into
NVIDIA:mainfrom
latenighthackathon:fix/4760-ocsf-reason-truncation

Conversation

@latenighthackathon

@latenighthackathon latenighthackathon commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

When a sandbox blocks an outbound connection, it writes a denial log line that should explain what was blocked and why: the destination address and the policy that rejected it. That explanation was being cut off at 80 characters and replaced with a literal ..., so nemoclaw <sb> logs --tail showed something like [reason:endpoint host.example:44...], with the port and the policy name chopped off. The operator can see that something was denied but not which endpoint to allow or which policy to adjust.

This widens the reason budget from 80 to 256 characters so the full endpoint and policy name fit. It also makes the truncation cut on a character boundary instead of a raw byte position, which keeps multi-byte text intact and removes a latent crash (the old byte-position slice could panic if it landed in the middle of a multi-byte character). The same safe truncation now also covers the related [msg:...] field.

Related Issue

Fixes NemoClaw issue #4760.

The user-facing symptom surfaces via nemoclaw <sb> logs --tail, but the OCSF shorthand emitter that truncates the reason is OpenShell code (crates/openshell-ocsf), so the fix lands in this repo.

Changes

  • MAX_REASON_LEN 80 -> 256 in crates/openshell-ocsf/src/format/shorthand.rs so a denial reason (endpoint <fqdn>:<port> not in policy <name>) stays readable instead of being cut mid-endpoint.
  • New truncate_with_ellipsis(text, max) helper that steps back to the nearest UTF-8 char boundary before slicing; used by both reason_tag and message_tag (the latter carried the same latent &text[..MAX] panic on multibyte input).

Testing

Ran the full gate in a container matching CI (rust 1.95.0 per rust-toolchain.toml plus the mise toolchain):

  • mise run pre-commit passes end-to-end (rust:format:check, rust:lint, rust:check, test:rust, python:format:check/lint/typecheck/proto, helm:lint, helm:docs:check, markdown:lint, license:check).
  • Unit tests added/updated: test_shorthand_reason_keeps_full_endpoint_and_policy (regression for NemoClaw issue #4760: a full endpoint <fqdn>:443 not in policy balanced reason renders end-to-end with no ...), test_shorthand_reason_truncated_beyond_max_len (an over-256 reason still truncates), and test_shorthand_reason_truncates_on_multibyte_char_boundary (multibyte input truncates without panicking).
  • E2E tests: N/A. This is a display-only projection in the openshell-ocsf leaf crate (it changes the rendered width of one log tag); there is no cluster or gateway-runtime behavior to assert, so the crate's own #[cfg(test)] module is the appropriate coverage.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)
  • Architecture docs: N/A (no bootstrap/manifest/entrypoint change).

@latenighthackathon latenighthackathon force-pushed the fix/4760-ocsf-reason-truncation branch from bacdb37 to 041c98e Compare June 7, 2026 21:05
@latenighthackathon latenighthackathon changed the title fix(ocsf): widen the shorthand [reason:] budget so denial endpoints stay readable (#4760) fix(ocsf): widen the shorthand [reason:] budget so denial endpoints stay readable (NVIDIA/NemoClaw#4760) Jun 7, 2026
/// denial reason carries the full destination endpoint plus the rejecting
/// policy name (e.g. `endpoint host.example:443 not in policy <name>`), so the
/// budget has to be wide enough to keep the port and policy name readable in
/// `nemoclaw <sb> logs --tail`; 80 chars cut a typical reason mid-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.

nit: remove nemoclaw ref

@johntmyers

Copy link
Copy Markdown
Collaborator

gator-agent

PR Review Status

Validation: This is a small concentrated openshell-ocsf display-path bug fix. It keeps denial reasons readable in shorthand logs and fixes a UTF-8 byte-slice panic class without changing sandbox policy behavior.
Head SHA: 041c98e1d922e57a118a5e16121491384f959e8f

Review findings:

  • crates/openshell-ocsf/src/format/shorthand.rs:93: reason_tag still preserves \n/\r, while message_tag normalizes them. Since shorthand output is intended to be single-line and status_detail can include endpoint/error-derived text, please normalize newline characters before truncating the reason tag as well. This is a log-safety issue (CWE-117).
  • crates/openshell-ocsf/src/format/shorthand.rs:704: test_shorthand_reason_truncates_on_multibyte_char_boundary does not currently put the slice boundary inside a multibyte character. "é" is 2 bytes and MAX_REASON_LEN is 256, so the previous &text[..256] path would still land on a valid boundary. Please adjust the test to use an odd boundary case, for example "a".repeat(MAX_REASON_LEN - 1) + "é" + "tail".
  • Existing maintainer review comment remains unresolved: remove the product-specific NemoClaw reference from the code comment.

Docs: No Fern docs or docs/index.yml update is required; existing logging docs already describe [reason:...], and this PR does not introduce a new user-facing command, workflow, or documented contract.

E2E: No test:* label needed. This is a leaf formatter/display change with focused Rust unit coverage expected.

Next state: gator:in-review

@johntmyers johntmyers added the gator:in-review Gator is reviewing or awaiting PR review feedback label Jun 9, 2026
…tay readable (NVIDIA/NemoClaw#4760)

A denied egress logs an OCSF NET:OPEN DENIED line whose [reason:] field carries the full destination endpoint and the rejecting policy name, but the shorthand formatter capped the reason at 80 bytes and appended a literal "...". A typical reason is longer than that, so the shorthand denial logs showed the endpoint cut mid-string with the port and policy name lost:

  NET:OPEN [MED] DENIED ... [reason:endpoint host.example:44...]

Raise MAX_REASON_LEN to 256 so a full endpoint plus policy name fits, and route both reason_tag and message_tag through a shared char-boundary truncation helper. The previous `&text[..MAX]` byte slice would also panic if the cut fell inside a multibyte UTF-8 character.

Per review: reason_tag now also normalizes embedded newlines to spaces before truncating (matching message_tag), so endpoint- or error-derived text cannot inject CR/LF into the single-line shorthand (CWE-117); the multibyte-boundary test now slices inside a multibyte character to actually exercise the step-back.

The issue was reported on the NemoClaw tracker (NVIDIA/NemoClaw#4760), but the OCSF shorthand emitter is OpenShell code, so the fix lands here. Cross-repo references do not auto-close, so no closing keyword is used.

Ref: NVIDIA/NemoClaw#4760  (NVIDIA/NemoClaw#4760)
Signed-off-by: latenighthackathon <latenighthackathon@users.noreply.github.com>
@latenighthackathon latenighthackathon force-pushed the fix/4760-ocsf-reason-truncation branch from 041c98e to 334b4df Compare June 9, 2026 23:12
@latenighthackathon

Copy link
Copy Markdown
Contributor Author

Thanks @johntmyers — all three addressed.

  • reason_tag now normalizes \n/\r to spaces before truncating, matching message_tag, so endpoint/error-derived text can't inject CR/LF into the single-line shorthand (CWE-117). Added a focused test for it.
  • The multibyte-boundary test now slices inside a multibyte char: format!("{}étail", "a".repeat(MAX_REASON_LEN - 1)) puts the byte limit in the middle of the 2-byte é, so it actually exercises the step-back.
  • Removed the product-specific reference from the doc comment.

Full mise run pre-commit green. Cheers!

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