Skip to content

fix: harden trace context propagation and collector ingest#622

Merged
cssbruno merged 1 commit into
mainfrom
fix/trace-collector-hardening
Jun 22, 2026
Merged

fix: harden trace context propagation and collector ingest#622
cssbruno merged 1 commit into
mainfrom
fix/trace-collector-hardening

Conversation

@cssbruno

Copy link
Copy Markdown
Owner

Summary

  • Preserve valid W3C tracestate through extract/start/inject while rejecting malformed or oversized traceparent and dropping invalid tracestate.
  • Harden the local trace collector/browser ingest boundary with JSON content-type enforcement, same-origin browser checks, per-client POST rate limiting, SSE subscriber caps, rejected counters, and slow-subscriber drop accounting.
  • Document the observability scope decision and the current realtime fanout audience-scoping gap.

Root Cause

  • Trace context propagation only handled traceparent, so valid vendor state was lost and header parsing had no explicit byte limits.
  • Collector POST/browser ingest was treated like a local helper endpoint instead of an untrusted HTTP boundary.

Compatibility / Public Contracts

  • trace.TraceContext now includes TraceState.
  • trace.ParseTraceContext(traceparent, tracestate) is added; ParseTraceparent remains compatible.
  • trace.NewCollector(limit, options...) remains source-compatible with existing NewCollector(limit) callers.
  • Collector JSON now includes rejected alongside spans and dropped.
  • POST ingest now requires JSON content type.

Issue Closure

Closes #552
Closes #553

Related: #551
Related: #538
Related: #520

Verification

  • I ran the relevant tests, lint, and build commands.
  • I ran scripts/test-go-modules.sh when Go code or compiler behavior changed.
  • I ran go build ./cmd/gowdk when CLI, compiler, runtime, addon, or release behavior changed.
  • Editor check not applicable; editor files unchanged.
  • I updated docs for behavior, setup, or architecture changes.
  • I added or updated tests for changed behavior.
  • I considered security-sensitive surfaces such as auth, CSRF, redirects, request-time handlers, logs, diagnostics, embedded assets, editor commands, WASM, contracts, and realtime behavior.

Commands run:

go test ./runtime/trace ./runtime/app
go test ./...
go build ./cmd/gowdk
scripts/test-go-modules.sh

LLM Assistance

  • LLM session summary: Implemented the requested trace context and collector hardening plan, added regression coverage, updated docs, and opened this PR from a clean worktree.
  • Human-reviewed assumptions: Keep addons/observability as the addon name; close only #552 and #553; leave full observability expansion and realtime audience scoping to follow-up issues.
  • Follow-up work: Implement audience/user/session scoping for realtime presentation fanout under #538, and decide or build the broader metrics/log correlation surface under #551.

@cssbruno cssbruno marked this pull request as ready for review June 22, 2026 02:46
@cssbruno cssbruno merged commit 2baf14c into main Jun 22, 2026
16 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8071edf50f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread runtime/trace/types.go
Comment on lines +355 to +356
if len(value) > 256 {
return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Reject empty tracestate values

When an upstream sends tracestate: vendor= or a value that becomes empty after trimming, this validator returns true because it only rejects values over 256 bytes. ParseTraceContext then preserves and Inject re-emits a malformed W3C tracestate instead of dropping invalid vendor state as the new propagation contract documents; add an empty-value/non-blank-final-character check before accepting the member value.

Useful? React with 👍 / 👎.

Comment on lines +221 to +222
if !requestHasJSONContentType(request) {
collector.rejectHTTP(response, "unsupported media type", http.StatusUnsupportedMediaType)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Sync observability manifest with hardened endpoints

When generated apps mount this hardened viewer, the runtime contract changed: browser POST now rejects non-JSON here and the /data//events viewer branches below are GET-only. The generated security manifest still declares trace.browser with ContentTypeRequired: "" and trace.data/trace.events with GET, POST in internal/securitymanifest/manifest.go, so gowdk audit can keep reporting audit_observability_content_type_missing while security tooling sees POST surfaces that now return 405. Update those manifest entries to match the enforced JSON ingest and GET-only stream/data routes.

Useful? React with 👍 / 👎.

if err != nil || parsed.Scheme == "" || parsed.Host == "" {
return false
}
return strings.EqualFold(parsed.Scheme, requestScheme(request)) && strings.EqualFold(parsed.Host, request.Host)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Allow same-origin ingest behind TLS termination

When the viewer/browser ingest is mounted behind an HTTPS-terminating reverse proxy with an app-owned access gate, Go receives request.TLS == nil and an empty URL.Scheme, while the browser sends Origin: https://host. This comparison treats the request as http and returns 403 for externally same-origin browser spans; add a trusted external scheme/origin option or trusted-proxy handling before enforcing the scheme comparison.

Useful? React with 👍 / 👎.

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

Labels

None yet

Projects

None yet

1 participant