From 8fac8bdb4b8f76f0db1ef5f2456919ac22b17270 Mon Sep 17 00:00:00 2001 From: MindflareX <94793217+MindflareX@users.noreply.github.com> Date: Sun, 24 May 2026 16:03:16 +0530 Subject: [PATCH] validate_usr_symlinks: match `type=link` as a whole mtree field The aspect's awk script used `$0 !~ /type=link/` to decide whether an entry at /bin, /sbin, /lib, /lib32, /lib64, or /libx32 was a symlink. Because the pattern was a plain substring match, any mtree field value elsewhere on the line that happened to contain the literal string "type=link" (for example `uname=type=link`, `gname=type=link`, or a `flags` value spelled the same way) would satisfy the check and the aspect would silently accept a non-symlink at that path. A symmetric issue existed in the ` link=` match, which could be anchored on the prior field's trailing characters rather than a field boundary. Anchor both regexes to whitespace boundaries so the check operates on whole mtree fields, and add regression test cases that fail under the old script and pass under the new one. Signed-off-by: MindflareX <94793217+MindflareX@users.noreply.github.com> --- private/util/validate_usr_symlinks.awk | 10 ++++++++-- private/util/validate_usr_symlinks_test.sh | 23 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/private/util/validate_usr_symlinks.awk b/private/util/validate_usr_symlinks.awk index 23053a707..a96381570 100644 --- a/private/util/validate_usr_symlinks.awk +++ b/private/util/validate_usr_symlinks.awk @@ -18,9 +18,15 @@ BEGIN { sub(/^(\.\/|\/)+/, "", path) if (path in expected) { - if ($0 !~ /type=link/) { + # Match `type=link` only as a whole mtree field — bounded by whitespace + # on the left and whitespace or end-of-line on the right. A naive + # substring match was tricked by other mtree field values that happened + # to contain the literal string "type=link" (for example a uname, + # gname, or flags value), silently skipping the symlink check for a + # non-symlink entry at /bin, /sbin, /lib, etc. + if ($0 !~ /[[:space:]]type=link([[:space:]]|$)/) { VIOLATIONS[original_path] = original_path " is not a symlink (must link to " expected[path] ")" - } else if (match($0, / link=([^ \t]+)/, dest) && dest[1] != expected[path]) { + } else if (match($0, /[[:space:]]link=([^[:space:]]+)/, dest) && dest[1] != expected[path]) { VIOLATIONS[original_path] = original_path " symlinks to '" dest[1] "' instead of '" expected[path] "'" } } else if (path ~ ("^(" prefixes ")/")) { diff --git a/private/util/validate_usr_symlinks_test.sh b/private/util/validate_usr_symlinks_test.sh index 371f839f1..fe1d71b9e 100755 --- a/private/util/validate_usr_symlinks_test.sh +++ b/private/util/validate_usr_symlinks_test.sh @@ -87,4 +87,27 @@ run "/lib/libfoo.so.1 type=file mode=0644 nlink=1 uid=0 gid=0 size=4096" \ run "./bin/ls type=file mode=0755 nlink=1 uid=0 gid=0 size=12345" \ && fail "content under ./bin/ should fail" || true +# --- substring-match regression cases --- +# An earlier version of this check used `$0 !~ /type=link/`, which matched the +# string "type=link" anywhere on the line, including inside other mtree field +# values. A non-symlink entry whose uname/gname/flags happened to contain that +# substring would silently pass. The check is now field-bounded. + +run "./bin type=dir uname=type=link gname=root mode=0755 uid=0 gid=0" \ + && fail "./bin as a dir with uname=type=link should fail (substring bypass)" || true + +run "./bin type=dir uname=root gname=type=link mode=0755 uid=0 gid=0" \ + && fail "./bin as a dir with gname=type=link should fail (substring bypass)" || true + +run "./bin type=dir flags=type=link mode=0755 uid=0 gid=0" \ + && fail "./bin as a dir with flags=type=link should fail (substring bypass)" || true + +run "./lib type=dir uname=type=link mode=0755 uid=0 gid=0" \ + && fail "./lib as a dir with uname=type=link should fail (substring bypass)" || true + +# A legitimate symlink that happens to carry extra trailing fields must keep +# working under the field-bounded check. +run "./bin type=link mode=0777 nlink=1 uid=0 gid=0 link=usr/bin extra=ignored" \ + || fail "./bin -> usr/bin with extra trailing field should pass" + echo "All tests passed."