add NormalizePath to ensure IG paths are always full#723
add NormalizePath to ensure IG paths are always full#723
Conversation
📝 WalkthroughWalkthroughThree files introduce a NormalizePath utility function and integrate it into five datasource event getters. The function normalizes paths by prepending "/proc" to headless process paths, ensuring absolute paths, and cleaning redundant separators. This directly addresses missing root nodes in path captures. Changes
Sequence Diagram(s)This section is skipped because the changes are primarily a utility function addition with straightforward integration into existing getters without significant new control flow or multi-component interactions. Estimated code review effort🎯 2 (Simple) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
pkg/utils/path.go (2)
21-23: Redundant special case for".".The
"."early-return is dead logic. Without it,"."proceeds through: no headless-proc match →!strings.HasPrefix(".", "/")→p = "/."→path.Clean("/.")→"/". The result is identical. The special case can be safely removed.♻️ Proposed simplification
- if p == "." { - return "/" - } - if headlessProcRegex.MatchString(p) {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/utils/path.go` around lines 21 - 23, Remove the redundant special-case branch that returns "/" when p == "." in the path normalization logic: the early-return is dead code because the subsequent logic (the headless-proc match, the strings.HasPrefix check that adds "/" to p, and the final path.Clean call) already yields "/" for "."; delete the if p == "." { return "/" } block and leave the remaining code (including the strings.HasPrefix handling and path.Clean) intact so behavior is unchanged but simpler.
4-4: Preferpath/filepathoverpathfor OS filesystem paths.The
pathpackage is documented for slash-separated paths in URLs;path/filepathis the correct package for OS filesystem paths. On Linux both are functionally identical, but usingpathhere is non-idiomatic.♻️ Proposed fix
import ( - "path" + "path/filepath" "regexp" "strings" ) ... - return path.Clean(p) + return filepath.Clean(p)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/utils/path.go` at line 4, The code imports the net/URL-oriented "path" package for filesystem paths; replace the import with "path/filepath" and update all usages (e.g., path.Join, path.Clean, path.Dir, path.Base, path.Split, etc.) to use filepath.Join, filepath.Clean, filepath.Dir, filepath.Base, filepath.Split accordingly so the code uses the OS-specific filepath behavior; ensure any tests/imports compile after switching the package name.pkg/utils/normalize_path_test.go (1)
14-63: Consider adding edge-case test for"/"and explicit documentation of the headless-path false-positive behavior.Two cases worth covering:
- Input
"/"— already covered by general logic but having an explicit test guards against regression.- Input like
"/1234/fd/socket"(a legitimate app-level path matching the headless regex) — adding this as a documented test case makes it explicit that such paths will be prefixed with/proc, alerting future readers to the known trade-off.🧪 Suggested additions
+ { + name: "root path", + input: "/", + expected: "/", + }, + { + name: "only dotdot relative", + input: "../etc/passwd", + expected: "/etc/passwd", + },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/utils/normalize_path_test.go` around lines 14 - 63, Add explicit test cases to the table in pkg/utils/normalize_path_test.go: include an input of "/" with expected "/" to guard regressions, and include an input like "/1234/fd/socket" with expected "/proc/1234/fd/socket" (or whatever the current NormalizePath behavior is) to document the known headless-path false-positive; update the test table (the slice of test structs used to exercise NormalizePath) and add a short comment next to the second case explaining this is a documented trade-off of the headless-path prefixing logic so future readers see this intended behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/chart/templates/node-agent/daemonset.yaml`:
- Around line 119-125: Render only one of `.value` or `.valueFrom` for each
`nodeAgent.env` entry by making the template mutually exclusive: change the
current independent `if .value` / `if .valueFrom` checks so that `.value` is
rendered only when `.value` is set and `.valueFrom` is not, and `.valueFrom` is
rendered only when `.valueFrom` is set and `.value` is not (or use an if/else-if
structure) to ensure exactly one of the two blocks appears for any env entry.
---
Nitpick comments:
In `@pkg/utils/normalize_path_test.go`:
- Around line 14-63: Add explicit test cases to the table in
pkg/utils/normalize_path_test.go: include an input of "/" with expected "/" to
guard regressions, and include an input like "/1234/fd/socket" with expected
"/proc/1234/fd/socket" (or whatever the current NormalizePath behavior is) to
document the known headless-path false-positive; update the test table (the
slice of test structs used to exercise NormalizePath) and add a short comment
next to the second case explaining this is a documented trade-off of the
headless-path prefixing logic so future readers see this intended behavior.
In `@pkg/utils/path.go`:
- Around line 21-23: Remove the redundant special-case branch that returns "/"
when p == "." in the path normalization logic: the early-return is dead code
because the subsequent logic (the headless-proc match, the strings.HasPrefix
check that adds "/" to p, and the final path.Clean call) already yields "/" for
"."; delete the if p == "." { return "/" } block and leave the remaining code
(including the strings.HasPrefix handling and path.Clean) intact so behavior is
unchanged but simpler.
- Line 4: The code imports the net/URL-oriented "path" package for filesystem
paths; replace the import with "path/filepath" and update all usages (e.g.,
path.Join, path.Clean, path.Dir, path.Base, path.Split, etc.) to use
filepath.Join, filepath.Clean, filepath.Dir, filepath.Base, filepath.Split
accordingly so the code uses the OS-specific filepath behavior; ensure any
tests/imports compile after switching the package name.
| {{- if .value }} | ||
| value: "{{ .value }}" | ||
| {{- end }} | ||
| {{- if .valueFrom }} | ||
| valueFrom: | ||
| {{ toYaml .valueFrom | indent 16 }} | ||
| {{- end }} |
There was a problem hiding this comment.
Missing mutual exclusion guard between value and valueFrom.
If a nodeAgent.env entry sets both .value and .valueFrom, both blocks will be rendered, producing invalid Kubernetes YAML (the pod spec requires exactly one of value or valueFrom, not both). Kubernetes will reject the resulting pod spec.
🛡️ Proposed guard
- {{- if .value }}
- value: "{{ .value }}"
- {{- end }}
- {{- if .valueFrom }}
- valueFrom:
-{{ toYaml .valueFrom | indent 16 }}
- {{- end }}
+ {{- if and .value (not .valueFrom) }}
+ value: "{{ .value }}"
+ {{- else if .valueFrom }}
+ valueFrom:
+{{ toYaml .valueFrom | indent 16 }}
+ {{- end }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/chart/templates/node-agent/daemonset.yaml` around lines 119 - 125,
Render only one of `.value` or `.valueFrom` for each `nodeAgent.env` entry by
making the template mutually exclusive: change the current independent `if
.value` / `if .valueFrom` checks so that `.value` is rendered only when `.value`
is set and `.valueFrom` is not, and `.valueFrom` is rendered only when
`.valueFrom` is set and `.value` is not (or use an if/else-if structure) to
ensure exactly one of the two blocks appears for any env entry.
Signed-off-by: Matthias Bertschy <matthias.bertschy@gmail.com> # Conflicts: # pkg/utils/datasource_event.go # pkg/utils/struct_event.go
5febde9 to
eafe066
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/utils/path.go`:
- Around line 9-31: The headless /proc detection in NormalizePath misses cases
because headlessProcRegex requires a trailing slash and runs before
leading-slash normalization; update headlessProcRegex to match either a trailing
slash or end-of-string (use pattern like ^/\d+/(task|fd)(/|$)) and move the
MatchString check to after the leading-slash normalization in NormalizePath so
inputs like "46/task/46/fd", "/46/fd" and "/46/task" are correctly detected and
have "/proc" prepended.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
pkg/utils/datasource_event.gopkg/utils/normalize_path_test.gopkg/utils/path.go
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/utils/normalize_path_test.go
| var headlessProcRegex = regexp.MustCompile(`^/\d+/(task|fd)/`) | ||
|
|
||
| // NormalizePath normalizes a path by: | ||
| // 1. Prepending "/proc" to "headless" proc paths (e.g. /46/task/46/fd -> /proc/46/task/46/fd) | ||
| // 2. Ensuring it starts with "/" if it's not empty | ||
| // 3. Converting "." to "/" | ||
| // 4. Cleaning the path (removing redundant slashes, dot-dots, etc.) | ||
| func NormalizePath(p string) string { | ||
| if p == "" { | ||
| return "" | ||
| } | ||
|
|
||
| if p == "." { | ||
| return "/" | ||
| } | ||
|
|
||
| if headlessProcRegex.MatchString(p) { | ||
| p = "/proc" + p | ||
| } | ||
|
|
||
| if !strings.HasPrefix(p, "/") { | ||
| p = "/" + p | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n pkg/utils/path.go | head -40Repository: kubescape/node-agent
Length of output: 940
🏁 Script executed:
#!/bin/bash
python - <<'PY'
import re, posixpath
# Current regex in the code
current_regex = re.compile(r"^/\d+/(task|fd)/")
# Proposed regex in the fix
proposed_regex = re.compile(r"^/\d+/(task|fd)(/|$)")
def test_current(p):
"""Simulates current implementation"""
if p == "": return ""
if p == ".": return "/"
if current_regex.match(p):
p = "/proc" + p
if not p.startswith("/"):
p = "/" + p
return posixpath.normpath(p)
def test_proposed(p):
"""Simulates proposed implementation"""
if p == "": return ""
if p == ".": return "/"
if not p.startswith("/"):
p = "/" + p
if proposed_regex.match(p):
p = "/proc" + p
return posixpath.normpath(p)
test_cases = [
"/46/task/46/fd", # Full headless proc path with content
"/46/fd", # Headless proc path without trailing slash
"/46/task", # Headless proc path without trailing slash
"46/task/46/fd", # Headless path without leading slash
"/46/task/", # With trailing slash
"/46/fd/", # With trailing slash
"/normal/path", # Regular path
"/123/notask/456", # Should not match
]
print("Test Case | Current Result | Proposed Result | Issue")
print("-" * 100)
for tc in test_cases:
curr = test_current(tc)
prop = test_proposed(tc)
issue = "❌ Missing /proc" if curr.startswith("/") and not curr.startswith("/proc") and any(x in tc for x in ["task", "fd"]) and curr == "/" + tc.lstrip("/") else ""
print(f"{tc:28} | {curr:25} | {prop:25} | {issue}")
PYRepository: kubescape/node-agent
Length of output: 1021
Headless /proc detection misses valid edge cases due to regex requiring trailing slash.
The regex ^/\d+/(task|fd)/ requires a trailing slash after task or fd, causing valid headless proc paths like /46/fd, /46/task, and 46/task/46/fd (before leading slash normalization) to bypass /proc prepending. This breaks the "always full" guarantee.
Fix by changing the regex to ^/\d+/(task|fd)(/|$) to match paths ending with either "/" or end-of-string, and move the regex check after the leading slash normalization so 46/task/46/fd is correctly recognized after becoming /46/task/46/fd.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/utils/path.go` around lines 9 - 31, The headless /proc detection in
NormalizePath misses cases because headlessProcRegex requires a trailing slash
and runs before leading-slash normalization; update headlessProcRegex to match
either a trailing slash or end-of-string (use pattern like ^/\d+/(task|fd)(/|$))
and move the MatchString check to after the leading-slash normalization in
NormalizePath so inputs like "46/task/46/fd", "/46/fd" and "/46/task" are
correctly detected and have "/proc" prepended.
Fixes #721
Summary by CodeRabbit
Bug Fixes
Tests