Repository navigation
Conversation
|
All contributors have signed the DCO ✍️ ✅ |
|
I have read the DCO document and I hereby sign the DCO. |
|
recheck |
|
The README conflict is resolved, and the updated branch passes the local checks. Could a maintainer please vet and approve the CI workflows? Fresh Linux/Collector/Tempo validation remains pending. |
rhuss
left a comment
There was a problem hiding this comment.
cc-review Summary
@HarryMoss, please have your agent assess each of these comments and check it against the code before acting on it. Some may turn out not to apply, and I would rather you confirm or refute them than take them at face value.
This review comes from an agent-assisted run with several reviewers (cc-review: seven prompt reviewers plus Codex and CodeRabbit, then a skeptic/calibration pass). The findings and the scenarios in them come from the agents reading the code. Nothing was executed or reproduced, and I have not verified each scenario myself.
What Went Well
crates/openshell-otel/src/correlation.rscurrent_ocsf_trace_correlationonly correlates when the span context is both valid and sampled.crates/openshell-ocsf/src/trace.rskeepsopenshell-ocsffree of OpenTelemetry through aOnceLockextractor, andenrichnever overrides an explicit trace.crates/openshell-ocsf/src/events/base_event.rsset_traceadds thetraceprofile only once.crates/openshell-ocsf/src/format/downgrade.rsstrips both thetracefield and the profile for 1.1 and 1.3, with tests for each target.crates/openshell-supervisor-network/src/telemetry/tests.rsexportedchecks real OTLP/gRPC exports instead of only the JSON shape.
Findings
| Severity | File | Description | Source |
|---|---|---|---|
| Important | crates/openshell-supervisor-network/src/telemetry.rs:32 |
Paired deny event and finding get different trace IDs | gator |
| Minor | crates/openshell-server/src/cli.rs:669 |
Gateway trace.uid is caller-propagated but documented as trusted |
security |
| Minor | crates/openshell-supervisor-network/src/l7/middleware.rs:466 |
Capacity-exhausted finding marked deny when allowed | correctness |
| Minor | crates/openshell-supervisor-network/src/l7/middleware.rs:487 |
Load shedding traced as policy deny | goal-alignment |
| Minor | crates/openshell-supervisor-network/src/l7/middleware.rs:1004 |
denied: predicates untested |
test-quality |
| Minor | crates/openshell-supervisor-network/src/proxy.rs:1261 |
Transparent middleware deny not parented on connect span (see below) | correctness |
| Minor | crates/openshell-supervisor-network/src/proxy.rs:5677 |
Non-canonical rejection gets no deny span | codex |
| Minor | crates/openshell-supervisor-network/src/proxy.rs:5956 |
CONNECT/FORWARD deny-span wiring untested | test-quality |
| Minor | crates/openshell-supervisor-network/src/proxy.rs:6072 |
Audit-mode L7 failure traced as enforced deny | codex |
| Minor | crates/openshell-supervisor-network/src/proxy.rs:6660 |
parent: is a no-op for non-deny events |
architecture |
| Minor | crates/openshell-supervisor-network/src/proxy/trace_tests.rs:80 |
Debug deny test lacks count and uid checks | test-quality |
| Minor | crates/openshell-supervisor-network/src/telemetry.rs:11 |
Local ocsf_emit! shadows the upstream macro |
architecture |
| Minor | crates/openshell-supervisor-network/src/telemetry.rs:41 |
Deny classification split between event and flag | architecture |
| Minor | crates/openshell-supervisor-network/src/telemetry.rs:55 |
Deny-span policy/host attributes never asserted | test-quality |
| Minor | crates/openshell-supervisor-network/src/telemetry.rs:64 |
One exported root span per deny, no rate gate | production |
| Minor | crates/openshell-ocsf/src/builders/network.rs:121 |
.trace() setter on 10 builders used only by tests |
architecture |
| Minor | crates/openshell-otel/src/lib.rs:49 |
Supervisor filter moved into shared public API | architecture |
| Minor | docs/observability/ocsf-json-export.mdx:281 |
Docs mention the internal builder API | architecture |
Outside the diff: crates/openshell-supervisor-network/src/proxy.rs:1261. The transparent-TCP fail-closed middleware deny calls emit_middleware_uninspectable without the live connect_span, unlike the sibling transparent denies, so at debug level this deny is not linked to the connect/authorize trace even though the docs say it should be. An optional parent parameter, or wrapping the call in connect_span.in_scope(...), would fix it.
Observation: the supervisor.egress.deny span and its openshell.policy.* keys live in telemetry.rs, separate from the supervisor.egress.* spans in proxy/egress.rs, and the attribute values come from string-keyed evidence lookups. Shared constants would keep the two in sync.
Review Details
- Findings posted: 18
- Findings reviewed and not posted: 0
- Gate outcome: FAIL (one Important finding)
- Participating reviewers: architecture, correctness, goal-alignment, production, security, test-quality, gator, codex (CodeRabbit ran with no findings)
|
Thanks for the review, @rhuss. The follow-up at 910509993e18659404e923a2aa33d772a4ae5655, following the upstream integration, groups companion deny records and terminal response failures under one decision span, corrects caller-propagated correlation wording, and keeps ordinary I/O failures out of enforced-denial spans. Forward evaluation-error spans retain the HTTP policy context, and The final source tree is The docs now explicitly identify Trace applicability on the eight extra classes as an OpenShell extension beyond official OCSF 1.8 HTTP/API applicability. Please confirm whether that extension is acceptable. Fresh Linux and default-level Collector/Tempo validation with allowed/schema controls, NVIDIA CI approval/completion and reviewer reassessment remain pending. This remains Draft; earlier independent deployment results apply only to |
|
@krishicks @TaylorMutch wdyt? Could we try to get this PR in? All findings from my review are addressed (I checked each against
CI also still needs @HarryMoss is there anything left before moving the PR out of |
|
Thanks @rhuss. Yes please, a fresh Podman + Collector/Tempo check on Local macOS validation on that exact source tree is complete, including the affected Rust suites, six-crate all-target Clippy, schema checks and docs checks. Fresh Linux CI and the deployed check are still pending. I have resolved the 16 review threads you confirmed as addressed and left the public @krishicks @TaylorMutch, could you please vet/approve CI and advise on the three choices above: the Trace applicability extension beyond HTTP/API, keeping or dropping the public setters, and vendored versus generated profile schemas? I can make the corresponding changes once the intended direction is clear. I will keep this as a draft pending those decisions and the outstanding validation. Thanks for helping move this forward. |
|
Written by Claude Code, reviewed by @krishicks Thanks @HarryMoss for the thorough work, and @rhuss for driving the review. Here are maintainer decisions on the three open questions, plus a few more items. Severity follows rhuss's scale. References are to DecisionsTrace profile applicability: emit Public Schema catalog: vendor only what Findings
Important: restrict
Important: changes to audit records need calling out. Besides adding correlation, several existing records change meaning:
These look like correct fixes. Those were denials that lacked an action. But they shift SIEM deny counts. Please put them in their own commit and mention them in the PR description and docs. Minor: make Minor: set the OTLP log record's trace context. Minor: derive deny classification from the event. The decision still comes from three places:
Minor: guard against the upstream Minor: bound deny-span volume. Every denied decision exports a root span, and the workload controls how often it is denied. A tight retry loop against a blocked host can fill the batch exporter queue and cause the supervisor's other spans (startup, policy) to be dropped. Please add a per-destination rate limit on deny-span creation. OCSF records stay unaffected. Nit: Nit: test harness cost. Each trace test starts a loopback OTLP gRPC server behind a global lock. Keep one or two end-to-end export tests and use an in-memory exporter for the rest. Nit: commit structure. This would review and bisect better as separate commits:
|
fed4f94 to
a0b27ba
Compare
|
Written by Claude Code, reviewed by @krishicks I rebased this branch onto Commits
Changed audit recordsThese records describe refusals and now say so. Counts of
VerificationI ran a local Docker gateway exporting to an OpenTelemetry Collector, Jaeger, and Aspire, then drove a sandbox through allowed, L7-denied, forward-denied, L4-denied, and rapid-retry traffic:
Follow-ups
|
|
/ok to test a0b27ba |
Add optional trace.uid enrichment through a plain sampled-context extractor, with explicit correlation taking precedence on all ten event builders. Register the extractor at gateway and supervisor startup and keep an INFO span active around network and HTTP deny emissions at the default log level. Integrate the Authentication event and current network APIs while retaining sanitization, policy decisions, credential handling and conditional DEBUG parenting. Document per-decision traces and export/retention limits. Signed-off-by: Harry Turner <harry.github@loopfeed.com> Signed-off-by: Kris Hicks <khicks@nvidia.com>
TraceCorrelation accepted any string in a field documented as a W3C trace ID, and every builder exposed a .trace() override that only tests called. Build the correlation from the 16-byte trace ID, reject malformed or all-zero IDs on deserialization, and drop the builder setters so the registered extractor is the only source of trace.uid. Signed-off-by: Kris Hicks <khicks@nvidia.com>
OCSF 1.8.0 declares the Trace profile only for HTTP Activity and API Activity, but trace.uid was added to every class, validated against schemas composed by grafting http_activity onto eight other classes. Strict consumers reject such records outright. Gate enrichment on the event class, drop the composed schemas and their tooling, and vendor only the native HTTP/API +trace exports. The supervisor now opens a deny span only for decisions with an HTTP Activity record, so every exported deny span has a record pointing at it; the wiring that served Network Activity and finding-only denials is removed. Signed-off-by: Kris Hicks <khicks@nvidia.com>
Whether a decision opened a deny span came from three places: the event action, a denied: macro arm, and a flag passed to emit_decision. Several refusals were recorded only by those flags, so the audit records themselves did not say the request was refused. Mark the refusal in the records instead: fail-closed response middleware invocations, fail-closed and transformed-body policy failure findings, and enforced response delivery failures now carry action Denied. Deny classification reads only the records, and the flags are gone. Signed-off-by: Kris Hicks <khicks@nvidia.com>
An HTTP deny emitted with openshell_ocsf::ocsf_emit! instead of egress_emit! silently gets no deny span, and the crate still used the upstream macro in several places. Disallow it in the crate clippy config and route every emission through egress_emit!. Signed-off-by: Kris Hicks <khicks@nvidia.com>
Every denied HTTP decision exported a root span, and the workload controls how often it is denied. A tight retry loop against a blocked host could fill the batch exporter queue and push out the supervisor's other spans. Give each destination a token bucket of 10 deny spans that refills at one per second, tracking at most 1024 destinations. Denials past the limit still emit their OCSF records, without a trace. Signed-off-by: Kris Hicks <khicks@nvidia.com>
OCSF records exported over OTLP carried trace correlation only in the JSON body, and only for HTTP and API Activity. OTLP backends link logs to traces through the log record's own trace and span ID fields. Fill those fields from the sampled span active at emission. They are OTLP fields, not OCSF attributes, so every class gets them without affecting schema conformance; Network Activity and findings regain trace links on the OTLP path. Signed-off-by: Kris Hicks <khicks@nvidia.com>
…n tests Each deny-span test started a loopback OTLP gRPC server and waited for a batch export, which made the suite slow. Capture spans with an in-memory exporter instead, converted to the same OTLP span shape, and keep two tests that export over OTLP end to end. Signed-off-by: Kris Hicks <khicks@nvidia.com>
Several records that describe a refusal gained action Denied in this branch: FORWARD parse, canonicalization, GraphQL and JSON-RPC rejections, enforced L7 evaluation errors, metadata-server 403/405 responses, and fail-closed middleware and response delivery records. These shift deny counts in a SIEM, so list them where operators look for denied records. Signed-off-by: Kris Hicks <khicks@nvidia.com>
The deny-span API still carried paths for nested and non-HTTP deny scopes that no caller reaches now that only HTTP decisions get spans, and a decision whose first HTTP record was allowed opened its deny span with that record's destination and no reason. emit_decision is now the single entry point, behind a one-arm egress_emit!. It anchors the span on the denied HTTP record and sets optional attributes directly. Records that never share a span are emitted inline again, response diagnostics return their events so each error path adds its own terminal failure, and the trace tests share deny-span helpers and fixtures. Signed-off-by: Kris Hicks <khicks@nvidia.com>
At debug level, FORWARD credential-binding and request middleware denials opened root deny spans, because the saved connect span exists there but is not entered. Deny spans also charged the per-destination limiter even when they were disabled, and a fail-closed WebSocket middleware finding did not record the denial its HTTP counterparts do. Those denials now pass or run under the saved connect span, the limiter is consulted only when INFO spans are enabled, and the fail-closed WebSocket finding sets action Denied. Signed-off-by: Kris Hicks <khicks@nvidia.com>
a0b27ba to
2bb7e01
Compare
|
/ok to test 2bb7e01 |
rhuss
left a comment
There was a problem hiding this comment.
cc-review Summary
Re-review of the reworked branch at 2bb7e01, focused on the ten commits on top of the contributor's work (bb96bb570..2bb7e0155). Thanks @krishicks for turning the decisions into commits so quickly. The agent run found no regression of my earlier findings, and the rework answers my questions from Nov 2.
This comes from an agent-assisted run (cc-review: seven prompt reviewers, then a skeptic/calibration pass; Codex and CodeRabbit did not run this time). The scenarios come from reading the code. Nothing was executed, and I have not reproduced them myself.
What Went Well
TraceCorrelationkeepsuidprivate and can only be built from 16 bytes or through validated deserialization, which rejects uppercase, wrong-length and all-zero IDs (crates/openshell-ocsf/src/trace.rs).- The class gate sits in
enrich,set_traceand the serializer, so a deserializedtraceon Network Activity is never re-emitted (base_event.rs). emit_decisionis the only deny-span entry point, and the crate-leveldisallowed-macrosrule enforces it (telemetry.rs,clippy.toml).deny_spancheckstracing::enabled!before taking a limiter token, so a supervisor without deny spans spends nothing (disabled_deny_spans_spend_no_tokens).- OTLP log records get trace and span IDs only from sampled spans, with tests for both samplers (
openshell-otel/src/ocsf.rs).
Findings
| Severity | File | Description | Source |
|---|---|---|---|
| Important | crates/openshell-supervisor-network/src/l7/middleware.rs:172 | Several refusal findings still lack action: Denied, although the new Refusal Records docs promise it |
security (also architecture, correctness, gator) |
| Important | PR metadata | Closes #2640 would close the issue while its network-deny scenario is not delivered |
goal-alignment |
The second one is about the PR text, not a line of code, so it is here instead of inline:
Important: The PR body still says Closes #2640. The issue's user story and acceptance criteria are about network deny events carrying trace.uid. With the HTTP/API-only decision, accepts_trace (trace.rs:69) excludes Network Activity, and emit_decision opens deny spans only for HTTP records, so L4 and DNS denials have no trace link at the default log level.
Why this matters: Merging as is closes #2640 while its headline scenario is still open, and nothing tracks the Network Activity follow-up. The Nov 5 comment mentions proposing new wording for the issue; the issue has not changed since Oct 19.
Suggested fix: Amend the acceptance criteria in #2640 and open a follow-up issue for Network Activity correlation before merge, or change the link to Part of #2640.
Source: goal-alignment agent
Nonblocking suggestions
- PR description: The body still describes
fed4f94(builder setters, the extension to eight classes, 18 schemas, its test counts) and lacks the "Changed audit records" table from the Nov 3 request. Replacing it with the Nov 5 summary would match the code. - Deny-span limiter: The bucket key is the
host:portthe workload chooses, and there is no global budget, so rotating hostnames could still produce about 10k spans in a burst and about 1k per second after that, more than the default 2048-span batch queue (telemetry.rs:144). Only spans are affected, never OCSF records, and the per-destination design is what I suggested earlier. A small global token bucket in front of it would close the gap. - Tests:
response_trace_upstream_io_failure_is_not_an_enforced_denialandresponse_trace_downstream_body_failure_is_not_an_enforced_denial(l7/rest.rs:6981-7092) only assert that no deny span exists, which holds whateveraction_idthehttp_response_failurefinding carries. Assertingaction_idthere would guard the "unchanged for I/O failures" row of the audit table.
Review Details
- Findings posted: 2 (one inline, one in this summary), plus 3 nonblocking suggestions
- Findings reviewed and not posted: 0
- Gate outcome: FAIL
- Participating reviewers: security, architecture, correctness, gator, goal-alignment, production, test-quality
Mark remaining inspection, capacity, admission and response refusals as Denied while preserving fail-open and I/O error classification. Add a process-wide deny-span budget alongside destination limits, with audit-preservation tests and matching documentation. Signed-off-by: Harry Turner <harry.github@loopfeed.com>
Head branch was pushed to by a user without write access
|
Thanks @rhuss, and thanks @krishicks for reworking the branch. I've pushed 4f827abc on top of Local macOS validation on the exact source tree I've updated the PR description with the current scope, changed-audit-records table and Fresh Linux and deployed Collector/backend validation are still pending for this revision. Could a maintainer please vet/approve CI for One tracking detail needs your guidance: |
|
/ok to test 4f827ab |
Preserve the prior primitive-only review history while adopting the tested HTTP core built on merged NVIDIA#4789. Signed-off-by: Alex Fournier <afournier@nvidia.com>
Summary
Correlates native HTTP and API OCSF records with sampled OpenTelemetry traces, and attaches emitting-thread trace/span context to OTLP log records. Dedicated supervisors can create a bounded INFO deny span for an HTTP refusal and its companion records.
Network Activity and DNS denials do not receive a new default-level deny span or native Trace body field in this revision. The issue should remain open unless its acceptance criteria and follow-up tracking are agreed before merge.
Related Issue
Part of #2640. The remaining Network Activity correlation needs follow-up; this PR should not close that broader issue.
Changes
Changed audit records
These changes describe the complete proposed PR relative to its upstream base. Additional refusal labels can increase counts from queries such as
select(.action == "Denied").Testing
The published parent is 2bb7e01. Its NVIDIA Branch Checks passed Rust tests, lint and build-mode checks on Linux x86_64/aarch64 and macOS aarch64; the x86_64 workspace run recorded 9,237 passed and 51 skipped. The advisory compatibility assessment was unavailable.
The follow-up at
4f827abc93d017fd9fb55b1ae171853bc425bbd0has source treea8782b1a942ef1f830bae8301bb186fc030b9366, based on that parent. New local validation used macOS arm64, Rust 1.95.0, locked dependencies and repository default profiles:-D warnings: passed.These local results cover the follow-up tree. The parent's CI does not validate these new changes.
Fresh Linux and deployed Collector/backend validation of this follow-up, and reviewer reassessment, remain pending. Local in-memory/loopback exporter tests and historical results do not establish that deployed pass.
Correlation IDs do not guarantee trace export or retention. Limited records can retain an already active sampled trace, caller-propagated gateway IDs are correlation hints rather than identity evidence, and shared supervisors currently initialise no OTLP exporters.
Checklist