Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions tests/utilities/mkdir_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -441,4 +441,89 @@ test_mkdir() {

rm -rf "$pm_base"
fi

echo -e "${CYAN}Testing IMPORTANT audit findings...${NC}"

# AUDIT: mkdir -m must accept symbolic mode strings (e.g. u=rwx,go=rx)
# GNU mkdir accepts symbolic modes like "u=rwx" or "a+rx".
# Our implementation only accepts octal modes and rejects symbolic ones.
if [[ "$PLATFORM" != "windows" ]]; then
# Symbolic mode: u=rwx,go=rx should produce 755
local sym_mode_dir="$TEMP_DIR/sym_mode_test"
rm -rf "$sym_mode_dir" 2>/dev/null || true
test_command_succeeds "mkdir -m symbolic mode u=rwx,go=rx" \
"$binary" -m "u=rwx,go=rx" "$sym_mode_dir"
if [[ -d "$sym_mode_dir" ]]; then
local sym_perms
sym_perms=$(get_file_permissions "$sym_mode_dir")
if [[ "$sym_perms" == "755" ]]; then
print_test_result "mkdir -m symbolic mode u=rwx,go=rx permissions" "PASS"
else
print_test_result "mkdir -m symbolic mode u=rwx,go=rx permissions" "FAIL" \
"Expected 755, got $sym_perms"
fi
fi
rm -rf "$sym_mode_dir"

# Symbolic mode: a+rx
local sym_mode_dir2="$TEMP_DIR/sym_mode_test2"
rm -rf "$sym_mode_dir2" 2>/dev/null || true
test_command_succeeds "mkdir -m symbolic mode a+rx" \
"$binary" -m "a+rx" "$sym_mode_dir2"
rm -rf "$sym_mode_dir2"

# Symbolic mode: go-w
local sym_mode_dir3="$TEMP_DIR/sym_mode_test3"
rm -rf "$sym_mode_dir3" 2>/dev/null || true
test_command_succeeds "mkdir -m symbolic mode go-w" \
"$binary" -m "go-w" "$sym_mode_dir3"
rm -rf "$sym_mode_dir3"
fi

# AUDIT: mkdir -p -m applies mode to leaf only (GNU behavior)
# GNU mkdir -p -m MODE applies MODE only to the final (leaf) directory.
# Intermediate directories are created with default permissions
# (modified by umask), not MODE. Our implementation applies MODE
# to ALL directories (intermediates and leaf alike).
if [[ "$PLATFORM" != "windows" ]]; then
local leaf_base="$TEMP_DIR/leaf_mode_test"
rm -rf "$leaf_base" 2>/dev/null || true

# Use mode 700 which is clearly different from the default ~755
test_command_succeeds "mkdir -p -m 700 creates tree (leaf-only audit)" \
"$binary" -p -m 700 "$leaf_base/a/b/c"

# Leaf directory should have mode 700
local leaf_perms
leaf_perms=$(get_file_permissions "$leaf_base/a/b/c")
if [[ "$leaf_perms" == "700" ]]; then
print_test_result "mkdir -p -m 700 leaf dir has mode 700 (audit)" "PASS"
else
print_test_result "mkdir -p -m 700 leaf dir has mode 700 (audit)" "FAIL" \
"Expected 700, got $leaf_perms"
fi

# Intermediate directories should NOT have mode 700 -- they should
# have the default mode (typically 755, affected by umask).
# GNU mkdir: intermediates get (0777 & ~umask) | u+wx
local mid_perms
mid_perms=$(get_file_permissions "$leaf_base/a/b")
if [[ "$mid_perms" != "700" ]]; then
print_test_result "mkdir -p -m 700 intermediate dir is NOT 700 (audit)" "PASS"
else
print_test_result "mkdir -p -m 700 intermediate dir is NOT 700 (audit)" "FAIL" \
"Expected default mode (not 700) for intermediate, got $mid_perms"
fi

local top_perms
top_perms=$(get_file_permissions "$leaf_base/a")
if [[ "$top_perms" != "700" ]]; then
print_test_result "mkdir -p -m 700 top dir is NOT 700 (audit)" "PASS"
else
print_test_result "mkdir -p -m 700 top dir is NOT 700 (audit)" "FAIL" \
"Expected default mode (not 700) for top-level, got $top_perms"
fi

rm -rf "$leaf_base"
fi
}
50 changes: 50 additions & 0 deletions tests/utilities/mktemp_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -291,4 +291,54 @@ test_mktemp() {
"Expected non-zero exit, got 0 (output: $f55_stderr)"
rm -f "$f55_stderr" 2>/dev/null || true
fi

echo -e "${CYAN}Testing IMPORTANT audit findings...${NC}"

# AUDIT: Implicit suffix should be accepted (GNU mktemp behavior)
# GNU mktemp treats characters after the last run of X's as an
# implicit suffix. For example, "myapp.XXXXXXtxt" is treated as
# template "myapp.XXXXXX" with implicit suffix "txt".
# Our implementation counts only strictly trailing X's and rejects
# this template with "too few X's".
local imp_testcwd="$TEMP_DIR/mktemp_implicit_suffix_test"
mkdir -p "$imp_testcwd"

local imp_out
imp_out=$(cd "$imp_testcwd" && "$binary" "myapp.XXXXXXtxt" 2>&1)
local imp_exit=$?
if [[ $imp_exit -eq 0 ]]; then
# Verify the file was created and has the right suffix
local imp_base
imp_base=$(basename "$imp_out")
if [[ "$imp_base" == myapp.* && "$imp_base" == *txt ]]; then
print_test_result "mktemp implicit suffix myapp.XXXXXXtxt succeeds (audit)" "PASS"
else
print_test_result "mktemp implicit suffix myapp.XXXXXXtxt succeeds (audit)" "FAIL" \
"Created file '$imp_base' doesn't match expected pattern myapp.*txt"
fi
rm -f "$imp_out" 2>/dev/null || true
else
print_test_result "mktemp implicit suffix myapp.XXXXXXtxt succeeds (audit)" "FAIL" \
"Expected exit 0, got $imp_exit (output: $imp_out)"
fi

# Also test with .log suffix
imp_out=$(cd "$imp_testcwd" && "$binary" "test.XXXXXX.log" 2>&1)
imp_exit=$?
if [[ $imp_exit -eq 0 ]]; then
local imp_base2
imp_base2=$(basename "$imp_out")
if [[ "$imp_base2" == test.*.log ]]; then
print_test_result "mktemp implicit suffix test.XXXXXX.log succeeds (audit)" "PASS"
else
print_test_result "mktemp implicit suffix test.XXXXXX.log succeeds (audit)" "FAIL" \
"Created file '$imp_base2' doesn't match expected pattern test.*.log"
fi
rm -f "$imp_out" 2>/dev/null || true
else
print_test_result "mktemp implicit suffix test.XXXXXX.log succeeds (audit)" "FAIL" \
"Expected exit 0, got $imp_exit (output: $imp_out)"
fi

rm -rf "$imp_testcwd"
}
62 changes: 62 additions & 0 deletions tests/utilities/mv_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -785,4 +785,66 @@ test_mv() {
"GNU mv returned: '$gnu_nf_content'"
fi
fi

echo -e "${CYAN}Testing IMPORTANT audit findings...${NC}"

# AUDIT: Same-file move via hardlink should exit 0, not error
# GNU mv: when source and dest are hardlinks to the same file
# (same inode, same device, but different names), GNU mv exits 0
# and removes the source link. Our implementation detects same-file
# and aborts with an error message.
local audit_hl_src="$TEMP_DIR/audit_hardlink_src.txt"
local audit_hl_dst="$TEMP_DIR/audit_hardlink_dst.txt"
echo "hardlink content" > "$audit_hl_src"
ln "$audit_hl_src" "$audit_hl_dst"

# Verify hardlink was created (same inode)
local src_inode dst_inode
src_inode=$(stat -c %i "$audit_hl_src" 2>/dev/null || stat -f %i "$audit_hl_src" 2>/dev/null)
dst_inode=$(stat -c %i "$audit_hl_dst" 2>/dev/null || stat -f %i "$audit_hl_dst" 2>/dev/null)
if [[ "$src_inode" == "$dst_inode" ]]; then
print_test_result "mv hardlink audit: setup verified same inode" "PASS"
else
print_test_result "mv hardlink audit: setup verified same inode" "FAIL" \
"Inodes differ: src=$src_inode dst=$dst_inode"
fi

local audit_hl_stderr_file="$TEMP_DIR/audit_hl_stderr"
"$binary" "$audit_hl_src" "$audit_hl_dst" >"$TEMP_DIR/audit_hl_stdout" 2>"$audit_hl_stderr_file"
local audit_hl_exit=$?
local audit_hl_stderr
audit_hl_stderr=$(<"$audit_hl_stderr_file")

# GNU mv exits 0 for hardlink same-file move
if [[ $audit_hl_exit -eq 0 ]]; then
print_test_result "mv hardlink same-file exits 0 (audit)" "PASS"
else
print_test_result "mv hardlink same-file exits 0 (audit)" "FAIL" \
"Expected exit 0, got $audit_hl_exit (stderr: '$audit_hl_stderr')"
fi

# Source should be removed (the link is unlinked)
if [[ ! -e "$audit_hl_src" ]]; then
print_test_result "mv hardlink same-file removes source (audit)" "PASS"
else
print_test_result "mv hardlink same-file removes source (audit)" "FAIL" \
"Source still exists after same-file move"
fi

# Destination should still exist with original content
if [[ -f "$audit_hl_dst" ]]; then
local audit_hl_content
audit_hl_content=$(<"$audit_hl_dst")
if [[ "$audit_hl_content" == "hardlink content" ]]; then
print_test_result "mv hardlink same-file preserves dest content (audit)" "PASS"
else
print_test_result "mv hardlink same-file preserves dest content (audit)" "FAIL" \
"Content changed: '$audit_hl_content'"
fi
else
print_test_result "mv hardlink same-file preserves dest content (audit)" "FAIL" \
"Destination file missing after move"
fi

rm -f "$audit_hl_src" "$audit_hl_dst" "$audit_hl_stderr_file" "$TEMP_DIR/audit_hl_stdout" 2>/dev/null
}
22 changes: 22 additions & 0 deletions tests/utilities/rmdir_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,26 @@ test_rmdir() {
print_test_result "rmdir .. refused with message" "FAIL" \
"Expected non-zero exit and 'refusing' in stderr, got exit=$dotdot_exit stderr='$dotdot_err'"
fi

echo -e "${CYAN}Testing IMPORTANT audit findings...${NC}"

# AUDIT: -v --ignore-fail-on-non-empty should still print verbose output
# GNU rmdir -v --ignore-fail-on-non-empty prints the "removing directory"
# message even when the directory cannot be removed because it is non-empty.
# Our implementation suppresses verbose output because it returns early
# before the verbose print when ignore_fail_on_non_empty is true.
local audit_vi_dir=$(create_temp_dir)
create_temp_file "content" "$audit_vi_dir/file.txt"
local audit_vi_out=""
local audit_vi_err=""
local audit_vi_exit=""
run_command audit_vi_cmd audit_vi_out audit_vi_err audit_vi_exit \
"$binary" -v --ignore-fail-on-non-empty "$audit_vi_dir"
if [[ $audit_vi_exit -eq 0 && "$audit_vi_out" =~ "removing directory" ]]; then
print_test_result "rmdir -v --ignore-fail-on-non-empty still prints verbose (audit)" "PASS"
else
print_test_result "rmdir -v --ignore-fail-on-non-empty still prints verbose (audit)" "FAIL" \
"Expected 'removing directory' in stdout, got: '$audit_vi_out' (exit: $audit_vi_exit)"
fi
rm -rf "$audit_vi_dir"
}
50 changes: 50 additions & 0 deletions tests/utilities/touch_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -814,4 +814,54 @@ test_touch() {
# This is a baseline test - bare datetimes should work as before
local f53_bare_file="$TEMP_DIR/f53_bare_test.txt"
test_command_succeeds "touch -d bare datetime (no tz)" "$binary" -d "2024-01-15T00:00:00" "$f53_bare_file"

echo -e "${CYAN}Testing IMPORTANT audit findings...${NC}"

# AUDIT: touch -h should imply -c (no-create) for non-existent paths
# GNU touch -h (--no-dereference) does not create new files. It only
# operates on existing symlinks (changing the symlink's own timestamp
# without following it). When the target path does not exist at all,
# GNU touch -h silently does nothing and exits 0.
# Our implementation creates a regular file when -h is used on a
# non-existent path, because -h does not set no_create.
local audit_h_file="$TEMP_DIR/audit_touch_h_nonexistent.txt"
rm -f "$audit_h_file" 2>/dev/null || true

# touch -h on a non-existent path should NOT create the file
local audit_h_out="" audit_h_err="" audit_h_exit=""
run_command audit_h_cmd audit_h_out audit_h_err audit_h_exit \
"$binary" -h "$audit_h_file"

# Exit code should be 0 (not an error, just a no-op like -c)
if [[ $audit_h_exit -eq 0 ]]; then
print_test_result "touch -h nonexistent exits 0 (audit)" "PASS"
else
print_test_result "touch -h nonexistent exits 0 (audit)" "FAIL" \
"Expected exit 0, got $audit_h_exit (stderr: '$audit_h_err')"
fi

# The file must NOT have been created
if [[ ! -e "$audit_h_file" ]]; then
print_test_result "touch -h does not create file (audit)" "PASS"
else
print_test_result "touch -h does not create file (audit)" "FAIL" \
"File was created despite -h flag (should imply -c)"
rm -f "$audit_h_file"
fi

# Also verify with --no-dereference long form
local audit_nd_file="$TEMP_DIR/audit_touch_nodereference_nonexistent.txt"
rm -f "$audit_nd_file" 2>/dev/null || true

local audit_nd_out="" audit_nd_err="" audit_nd_exit=""
run_command audit_nd_cmd audit_nd_out audit_nd_err audit_nd_exit \
"$binary" --no-dereference "$audit_nd_file"

if [[ ! -e "$audit_nd_file" ]]; then
print_test_result "touch --no-dereference does not create file (audit)" "PASS"
else
print_test_result "touch --no-dereference does not create file (audit)" "FAIL" \
"File was created despite --no-dereference flag"
rm -f "$audit_nd_file"
fi
}
Loading