Skip to content

feat: on-node symbol allow-list filter for eBPF profiles (ADR 0011, slice 4) - #29

Merged
kfeofantov merged 1 commit into
mainfrom
feat/ebpf-symbol-filter
Aug 11, 2026
Merged

kfeofantov merged 1 commit into
mainfrom
feat/ebpf-symbol-filter

Conversation

@kfeofantov

Copy link
Copy Markdown
Contributor

Slice 4 (plan, ADR 0011) — the security boundary of the profiler. A stack trace maps the customer's code structure; this filter is what makes shipping one defensible. Deny-by-default (opposite of the scanner's infra deny-list): a new or misclassified frame errs toward not leaving.

Classification (on the node, before egress)

Frame Verdict
client (Go module path on the allow-list) keep
stdlib / runtime / the workload's own main (no domain in package path) keep
kernel keep — not the customer's code; keeps the profile readable (N1)
third-party module (a domain, not allow-listed) redact by default; keep only under ThirdPartyKeep
unsymbolized / other native redact

Classification keys on the Go package path extracted from the function name; stdlib vs external is the canonical "first segment contains a dot = a domain" heuristic (no-dot).

Redaction (R2)

A redacted frame becomes a single neutral [filtered] placeholder, and consecutive redacted frames collapse into one — the flame-graph shape survives, but no identity leaves: not the dependency, not even its depth. Only kept frames + aggregate per-bucket drop counts (ThirdPartyDropped, UnsymbolizedDropped, SamplesFiltered) survive; a redacted frame's identity is never recorded (invariant 6).

Tests

packagePath / hasDomain tables; full-stack classification with the R2 collapse and counters; ThirdPartyKeep; unsymbolized redaction; empty-allow-list still keeps stdlib/main/kernel; and an explicit no-redacted-identity-in-output assertion. go test -race ./..., build darwin + GOOS=linux, golangci-lint on both GOOS — clean.

Note

The filter is a standalone, tested component here. It is wired into the capture → validate → spool pipeline in slice 5; the allow-list / third-party policy come from the node ConfigMap in slice 6.

🤖 Generated with Claude Code

…lice 4)

The security boundary of the profiler: a stack trace maps the customer's code
structure, and this filter is what makes shipping one defensible (ADR 0011 §4,
security.md §8). Its posture is deny-by-default — the opposite of the scanner's
infra deny-list — so a new or misclassified frame errs toward not leaving.

Classification, on the node, before anything leaves it:
- client frames (Go module path on the allow-list): keep
- stdlib / runtime / the workload's own main package (no domain in the package
  path): keep
- kernel frames: keep (not the customer's code; they keep a profile readable)
- third-party module frames (a domain, not allow-listed): redact by default,
  keep only under ThirdPartyKeep
- unsymbolized / other native frames: redact

Redaction replaces a frame with a single neutral "[filtered]" placeholder and
collapses consecutive redacted frames into one, so the flame-graph shape is
preserved but no identity — not the dependency, not its depth — leaves the node.
Only kept frames and aggregate per-bucket drop counts survive; the identity of a
redacted frame is never recorded (CLAUDE.md invariant 6). Tested for
classification, collapse, counters, and that no redacted identity reaches the
output. The allow-list and third-party policy arrive from the node ConfigMap in
the targeting slice.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@kfeofantov
kfeofantov merged commit 7225307 into main Aug 11, 2026
3 checks passed
@kfeofantov
kfeofantov deleted the feat/ebpf-symbol-filter branch August 11, 2026 18:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant