Skip to content

merge: upstream/main into wildcards (NetworkNeighbor IPAddresses + cleanup)#31

Open
entlein wants to merge 12 commits into
mainfrom
merge/upstream-profile-rearch
Open

merge: upstream/main into wildcards (NetworkNeighbor IPAddresses + cleanup)#31
entlein wants to merge 12 commits into
mainfrom
merge/upstream-profile-rearch

Conversation

@entlein
Copy link
Copy Markdown

@entlein entlein commented May 13, 2026

Summary

Brings the storage fork's main up to upstream parity AND keeps the v0.0.2 wildcard surface (PR #30) in one go. Merges upstream PR kubescape#317 (clean-standalone-pods cleanup edge case) on top of the wildcards branch.

This is a single landing point that supersedes #30 — pick whichever you prefer for review:

What's in the branch

Wildcards (already approved on #30)

  • pkg/registry/file/networkmatch/ — IP + DNS matchers (literal, CIDR, *, leading-/mid-/trailing-wildcards)
  • NetworkNeighbor.IPAddresses []string schema field (proto field 9)
  • REST admission validation: rejects malformed CIDRs, **, mid-position bare *, validates both deprecated IPAddress and DNS singular fields

Upstream merge (clean fast-forward)

Test plan

  • Full storage test suite green
  • Networkmatch microbenchmarks unchanged from feat: NetworkNeighbor wildcard surface (v0.0.2) #30 (12-18 ns/op compiled IP matcher, 47-167 ns/op DNS)
  • Protobuf round-trip on IPAddresses field 9 (admission → etcd → read)
  • REST strategy validation pinned with field-path assertions
  • go vet warnings unchanged from main baseline

Companion PR

Node-agent companion: k8sstormcenter/node-agent#<TBD> (merge branch carrying upstream PR #788 cache rearch + #799 projection + wildcards + exec-args matching on top).

matthyx and others added 11 commits April 30, 2026 15:09
… pods

Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com>
fix(cleanup): enhance pod deletion logic and add tests for standalone pods
Runtime counterpart to spec §5.7 (v0.0.2). Profile entries are matched
disjunctively — any single entry hit means match. Each entry MAY be:

  - a literal IP (canonicalised by net.ParseIP, IPv4 ↔ ::ffff:v4 unified)
  - a CIDR (a.b.c.d/n, validated by net.ParseCIDR)
  - the '*' sentinel for any IP (sugar for 0.0.0.0/0 ∪ ::/0)

Public API:

  CompileIP([]string) *IPMatcher    // compile-once for hot paths
  (*IPMatcher).Match(string) bool   // amortised match
  MatchIP([]string, string) bool    // cold-path convenience

The compiled-matcher pattern eliminates per-call allocs from net.ParseCIDR
on the hot path — node-agent's nn.* CEL functions call this on every
network event captured by the eBPF tracer.

Tests cover:
  - literal equality (incl. IPv4/IPv6 canonicalisation, ::ffff: mapping)
  - CIDR membership (incl. /32 == literal, edge addresses)
  - '*' sentinel (and rejection of empty/garbage observations)
  - 0.0.0.0/0 + ::/0 RFC-aligned alternatives (do NOT cross address families)
  - malformed entry skipping (admission validates, runtime defends)
  - mixed-list disjunction

Benchmarks (arm64, hot-path compiled form):
  Literal       : 12.73 ns/op   0 allocs
  CIDR          : 18.48 ns/op   0 allocs
  10-entry miss : 59.10 ns/op   0 allocs

All well under the README target of <200 ns/op for IP literal/CIDR.
…psis

Runtime counterpart to spec §5.8 (v0.0.2). Profile entries are matched
disjunctively against an observed FQDN. Each entry's labels are walked
left-to-right with positional token semantics:

  literal label   : byte-equality (case-insensitive)
  leading '*'     : matches EXACTLY ONE label (RFC 4592)
  trailing '*'    : matches ONE OR MORE labels (project extension)
  '⋯' anywhere    : matches EXACTLY ONE label (DynamicLabel)
  '**'            : reserved/recursive — admission rejects, runtime drops

Trailing dot is normalised on both sides (entry MAY or MAY NOT have it).
Empty inner labels (e.g. 'foo..bar') are treated as malformed and skipped.

Public API:

  CompileDNS([]string) *DNSMatcher
  (*DNSMatcher).Match(string) bool
  MatchDNS([]string, string) bool   // cold-path convenience

Tests cover:
  - literal equality (case-insensitive, exact-label-count)
  - trailing-dot normalisation in all four combinations
  - RFC 4592 leading '*' (exactly one label)
  - mid '⋯' DynamicLabel (the kubernetes service-FQDN case)
  - trailing '*' extension (one or more, never zero)
  - disjunction across entry list
  - malformed input rejection ('**', empty labels, lone '*')
  - compiled-form reuse contract

Benchmarks (arm64, hot-path compiled form):
  Literal           : 47.68 ns/op   1 alloc (label-split)
  Leading wildcard  : 53.20 ns/op   1 alloc
  Deep name (10 lbl): 144.9 ns/op   1 alloc
  Long mixed list   : 103.3 ns/op   1 alloc

All under the README target of <600 ns/op for DNS wildcards.
Adds IPAddresses []string to the NetworkNeighbor schema (internal +
v1beta1) as the v0.0.2 list-form replacement for the deprecated singular
IPAddress field. Each entry MAY be a literal IP, a CIDR, or '*'.

Hand-edited generated code (deepcopy, conversion, protobuf) because
the codegen pipeline isn't run as part of build; verified by a
new round-trip test that pins the protobuf wire contract for field
number 9.

Validation lives in two places:

  pkg/registry/file/networkmatch/validate.go
    - ValidateIPEntry  : literal | CIDR | '*'
    - ValidateDNSEntry : literal | leading-* | trailing-* | mid-⋯
                         rejects '**', empty inner labels, mid-* without ⋯

  pkg/registry/softwarecomposition/networkneighborhood/strategy.go
    REST Validate() walks every neighbor in containers/initContainers/
    ephemeralContainers × egress/ingress and surfaces a field.Invalid
    error per malformed entry. Apiserver translates this into a 400
    so the user gets immediate feedback at write time.

Runtime matchers (MatchIP/MatchDNS) keep their tolerance of malformed
entries — a misconfigured profile that slips past admission won't kill
the detection path.

New tests:
  - protobuf round-trip (field 9 survives Marshal → Unmarshal)
  - ValidateIPEntry / ValidateDNSEntry edge cases
  - REST strategy Validate rejects malformed entries with the right
    field paths
Five findings, all legit, all fixed:

- ValidateUpdate now also runs validateNetworkProfileEntries, so
  malformed ipAddresses / dnsNames can't land via PUT after a clean
  POST. New TestValidateUpdate_NetworkProfileEntries pins this.

- validateNeighborList now also validates the deprecated singular
  IPAddress field while it remains supported. Bypass via the old form
  is closed.

- TestValidate_NetworkProfileEntries asserts each error's field path
  (multiset, order-insensitive) instead of just the count. Field-path
  contract is now pinned: if validation starts emitting errors on the
  wrong path, downstream tooling that surfaces these to users will
  catch it here first. New case for deprecated-singular path.

- TestNetworkNeighbor_IPAddresses_EmptyOmitted now checks Marshal
  errors with t.Fatalf instead of swallowing them via _.

- README links to spec-v0.0.2 (was v0.0.1) and DNS normalisation
  wording matches runtime: trailing dot stripped, labels lowercased
  (was incorrectly described as 'appended').
Three findings, two fixed, one skipped:

- validateNetworkProfileEntries now iterates an ordered []struct of
  (groupName, items) pairs rather than a map. Go map iteration is
  non-deterministic and admission errors flow to clients via the
  apiserver — stable ordering keeps error messages reproducible.

- TestValidateUpdate_NetworkProfileEntries asserts each error's field
  path (multiset, order-insensitive). Same pattern already applied to
  TestValidate_NetworkProfileEntries; this closes the parallel gap.

Skipped:

- README.md "canonicalisation" → "canonicalization" (Trivial nit).
  Project already uses British spelling consistently across other docs;
  changing one occurrence creates inconsistency without fixing
  anything.

Tests pass on the new ordering — note the test uses an order-insensitive
multiset comparison, so the deterministic-order fix is a separate
concern from the test (the test would pass with either ordering, but
the runtime is now stable for downstream consumers).
Closes a parity gap with IPAddress (which round-1 already validates).
Now both deprecated singular fields receive the same admission grammar
check while they remain supported.

Test extended with a deprecated-DNS path case mirroring the existing
IPAddress one.
Reverses the round-2 skip on the CodeRabbit nitpick (PR #30, README
line 17). Files added in this branch now use American spelling
(canonicalize/normalize/behavior) matching the Go ecosystem convention
that 'canonicalisation' originally diverged from.

Files touched:
- pkg/registry/file/networkmatch/README.md
- pkg/registry/file/networkmatch/match_ip.go (comment)
- pkg/registry/file/networkmatch/match_dns.go (comment)
- pkg/registry/file/networkmatch/match_ip_test.go (comment)

No behaviour change.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 13, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e5ad780a-2f64-41cf-9955-731b218a3aff

📥 Commits

Reviewing files that changed from the base of the PR and between b23d85f and a15fb5a.

📒 Files selected for processing (1)
  • pkg/registry/file/dynamicpathdetector/tests/compare_exec_args_test.go

📝 Walkthrough

Walkthrough

This PR adds wildcard-aware network neighbor matching, implementing IP and DNS pattern matching against profiles (literals, CIDRs, RFC4592 leading-*, mid-, trailing-*), extends the NetworkNeighbor schema with IPAddresses[], validates entries at admission time, integrates validation into the strategy, and allows pods to be cleanable alongside other workload kinds.

Changes

NetworkNeighbor wildcard matching feature

Layer / File(s) Summary
NetworkNeighbor schema with IPAddresses field
pkg/apis/softwarecomposition/network_types.go, pkg/apis/softwarecomposition/v1beta1/network_types.go, pkg/apis/softwarecomposition/v1beta1/zz_generated.conversion.go, pkg/apis/softwarecomposition/v1beta1/zz_generated.deepcopy.go, pkg/apis/softwarecomposition/zz_generated.deepcopy.go, pkg/apis/softwarecomposition/v1beta1/network_types_protobuf_test.go, .gitignore
NetworkNeighbor gains IPAddresses []string field in both API versions to supersede the deprecated singular IPAddress; auto-generated conversion and deepcopy logic updated to handle the new field; protobuf round-trip tests confirm the field survives Marshal→Unmarshal at field number 9 and empty/absent behavior produces identical wire bytes.
IP matching with literals, CIDRs, and wildcards
pkg/registry/file/networkmatch/match_ip.go, pkg/registry/file/networkmatch/match_ip_test.go, pkg/registry/file/networkmatch/bench_test.go
MatchIP and CompileIP match observed IPs against profile entries supporting literal IPs, CIDR ranges, and the "*" any-sentinel; IPMatcher pre-compiles profiles for efficient reuse; tests cover IPv4/IPv6 literals, CIDR membership, wildcard behavior, malformed-entry tolerance, and disjunctive list semantics; benchmarks document performance targets including compiled-matcher 12–18 ns/op for literals and CIDRs, 59 ns/op for long-list miss.
DNS matching with RFC4592 and project-specific wildcards
pkg/registry/file/networkmatch/match_dns.go, pkg/registry/file/networkmatch/match_dns_test.go, pkg/registry/file/networkmatch/bench_test.go
MatchDNS and CompileDNS match observed DNS names against profile patterns supporting literal FQDNs, leading "*" (RFC4592 exactly-one-label), mid-"⋯" dynamic-label (exactly-one-label), and trailing "*" (one-or-more labels); DNSMatcher pre-compiles and caches parsed patterns; tests cover normalization, all wildcard variants, malformed rejection, and compiled-matcher reuse; benchmarks document 47–145 ns/op performance across variants.
Entry validation and package documentation
pkg/registry/file/networkmatch/validate.go, pkg/registry/file/networkmatch/validate_test.go, pkg/registry/file/networkmatch/doc.go, pkg/registry/file/networkmatch/README.md
ValidateIPEntry and ValidateDNSEntry provide admission-time validation, rejecting empty entries, malformed IPs/CIDRs, reserved "**" patterns, and positional wildcard violations; tests enumerate valid/invalid cases; doc.go provides package summary; README.md documents token vocabulary, API contracts, normalization rules, performance targets with benchmark thresholds, and test/fixture agreement requirements.
Strategy admission validation for network profiles
pkg/registry/softwarecomposition/networkneighborhood/strategy.go, pkg/registry/softwarecomposition/networkneighborhood/strategy_test.go
NetworkNeighborhoodStrategy.Validate and ValidateUpdate invoke validateNetworkProfileEntries, which walks spec containers (including init and ephemeral) and validates each NetworkNeighbor's ipAddresses/deprecated ipAddress and dnsNames/deprecated dns entries using networkmatch validators; produces field-level errors with stable paths for malformed entries; tests pin validation behavior across valid neighbors and multiple malformed-entry scenarios with multiset comparison of error field paths.
Pod cleanup using updated kind predicate
pkg/registry/file/cleanup.go, pkg/registry/file/cleanup_test.go
deleteByWlid now uses new isCleanableWlidKind helper to allow standalone pods as cleanable (kind "pod") alongside workloads in the Workloads set; test verifies deletion returns true when pod mapping is absent and false when pod is running.
dynamicpathdetector argv0 contract test
pkg/registry/file/dynamicpathdetector/tests/compare_exec_args_test.go
Adds TestCompareExecArgs_Argv0BareName documenting and asserting argv[0] bare-name semantics and wildcard behavior for CompareExecArgs.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: merging upstream code and preserving wildcard surface with NetworkNeighbor IPAddresses plus cleanup fixes.
Description check ✅ Passed The description provides relevant context about the PR objectives, contents, test plan, and relationship to linked issues, making it clearly related to the changeset.
Linked Issues check ✅ Passed The PR implements all major objectives from #30: wildcard support via networkmatch package, IPAddresses schema field, admission validation, tests, benchmarks, and includes upstream PR #317 cleanup changes.
Out of Scope Changes check ✅ Passed All changes align with stated objectives: networkmatch package implementation, schema additions, validation wiring, cleanup logic fixes, and supporting tests/benchmarks. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch merge/upstream-profile-rearch

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

Summary:

  • License scan: failure
  • Credentials scan: skipped
  • Vulnerabilities scan: skipped
  • Unit test: success
  • Go linting: failure

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 13, 2026

Test Results

2 tests   2 ✅  34m 20s ⏱️
1 suites  0 💤
1 files    0 ❌

Results for commit a15fb5a.

♻️ This comment has been updated with latest results.

@entlein
Copy link
Copy Markdown
Author

entlein commented May 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

…ExecArgs

Storage-side recording stores execs as [resolved_path, ...argv] and
returns to consumers as (Path=resolved, Args=full-argv-with-argv[0]).
The matcher does strict positional compare with no special argv[0]
normalisation. That means profile.Args[0] MUST equal runtime argv[0]
as captured by eBPF — typically the BARE program name ("sh"), not
the resolved exepath ("/bin/sh").

The realistic-patterns table earlier in this file uses 2-element
vectors that skip argv[0] entirely, which doesn't pin the convention.
Add TestCompareExecArgs_Argv0BareName covering all the Test_32
scenarios end-to-end against the matcher, plus the two
profile-shape-mismatch cases (Args[0]=full-path vs bare argv[0], and
vice versa) so future drift on either side trips the test.

Pairs with the rewrite of node-agent's Test_32_UnexpectedProcessArguments
that changes profile.Args from [/bin/sh, -c, *] to [sh, -c, *], so
R0040 can be tested in isolation from R0001's path-resolution path.
@github-actions
Copy link
Copy Markdown

Summary:

  • License scan: failure
  • Credentials scan: skipped
  • Vulnerabilities scan: skipped
  • Unit test: success
  • Go linting: failure

@entlein
Copy link
Copy Markdown
Author

entlein commented May 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@entlein
Copy link
Copy Markdown
Author

entlein commented May 14, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

2 participants