* src/shred.c (wipename): Quote the names of the source and destination
when printing verbose messages for rename. Remove comment about not
needing quoting after the first message, since we still do not control
the contents of any parent directories contained in the source or
destination.
* tests/shred/verbose-quote.sh: New file.
* tests/local.mk (all_tests): Add the new test case.
* NEWS: Mention the bug fix.
Discovered by Anthropic's OSS Scanner, as bug ANT-2026-3UJ1SMSY.
---
NEWS | 4 +++
src/shred.c | 11 ++----
tests/local.mk | 1 +
tests/shred/verbose-quote.sh | 70 ++++++++++++++++++++++++++++++++++++
4 files changed, 78 insertions(+), 8 deletions(-)
create mode 100755 tests/shred/verbose-quote.sh
diff --git a/NEWS b/NEWS
index 786d11a4b..db64b400f 100644
--- a/NEWS
+++ b/NEWS
@@ -170,6 +170,10 @@ GNU coreutils NEWS -*-
outline -*-
time the file was renamed and/or unlinked.
[This bug was present in "the beginning".]
+ 'shred --verbose' now quotes file names in its output when renaming files.
+ I.e., when invoked with either --remove=wipe or --remove=wipesync.
+ [This bug was present in "the beginning".]
+
'sort --compress-program=PROG' no longer treats an unset PATH environment
variable as equivalent to a PATH environment variable defined to an empty
string. On typical platforms, that would result in PROG being executed from
diff --git a/src/shred.c b/src/shred.c
index 6e7c4e4ab..9d426f70c 100644
--- a/src/shred.c
+++ b/src/shred.c
@@ -1066,7 +1066,6 @@ wipename (int const dirfd, char *oldname, char const
*qoldname,
char *oldbase = oldname + (base - newname);
char *dir = dir_name (newname);
char *qdir = xstrdup (quotef (dir));
- bool first = true;
bool ok = true;
int readable_dirfd = -1;
@@ -1095,13 +1094,9 @@ wipename (int const dirfd, char *oldname, char const
*qoldname,
if (flags->verbose)
{
/* People seem to understand this better than talking
- about renaming OLDNAME. NEWNAME doesn't need
- quoting because we picked it. OLDNAME needs to be
- quoted only the first time. */
- char const *old = first ? qoldname : oldname;
- error (0, 0,
- _("%s: renamed to %s"), old, newname);
- first = false;
+ about renaming OLDNAME. */
+ error (0, 0, _("%s: renamed to %s"), quotef_n (0, oldname),
+ quotef_n (1, newname));
}
memcpy (oldbase, base, len + 1);
}
diff --git a/tests/local.mk b/tests/local.mk
index f88f1d8a0..33d830cd6 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -455,6 +455,7 @@ all_tests = \
tests/shred/shred-remove.sh \
tests/shred/shred-size.sh \
tests/shred/unlink-race.sh \
+ tests/shred/verbose-quote.sh \
tests/shuf/shuf.sh \
tests/shuf/shuf-reservoir.sh \
tests/misc/sleep.sh \
diff --git a/tests/shred/verbose-quote.sh b/tests/shred/verbose-quote.sh
new file mode 100755
index 000000000..d8ec0ec8f
--- /dev/null
+++ b/tests/shred/verbose-quote.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+# Test that 'shred --verbose' quotes file names in its output.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ shred printf
+
+NL='
+'
+
+# Test quoting when the file resides in the current working directory.
+qname=$(env printf '%q' file"$NL"1) &&
+cat <<EOF >exp-err &&
+shred: $qname: pass 1/3 (random)...
+shred: $qname: pass 2/3 (random)...
+shred: $qname: pass 3/3 (random)...
+shred: $qname: removing
+shred: $qname: renamed to 000000
+shred: 000000: renamed to 00000
+shred: 00000: renamed to 0000
+shred: 0000: renamed to 000
+shred: 000: renamed to 00
+shred: 00: renamed to 0
+shred: $qname: removed
+EOF
+echo a >file"$NL"1 || framework_failure_
+
+shred --verbose --remove=wipe file"$NL"1 >out 2>err || fail=1
+compare /dev/null out || fail=1
+compare exp-err err || fail=1
+returns_ 1 test -f file"$NL"1 || fail=1
+
+# Test quoting when the directory containing the file requires quoting.
+qname=$(env printf '%q' dir"$NL"1/abc) &&
+qname000=$(env printf '%q' dir"$NL"1/000) &&
+qname00=$(env printf '%q' dir"$NL"1/00) &&
+qname0=$(env printf '%q' dir"$NL"1/0) &&
+cat <<EOF >exp-err &&
+shred: $qname: pass 1/3 (random)...
+shred: $qname: pass 2/3 (random)...
+shred: $qname: pass 3/3 (random)...
+shred: $qname: removing
+shred: $qname: renamed to $qname000
+shred: $qname000: renamed to $qname00
+shred: $qname00: renamed to $qname0
+shred: $qname: removed
+EOF
+mkdir -p dir"$NL"1 &&
+echo a >dir"$NL"1/abc || framework_failure_
+
+shred --verbose --remove=wipe dir"$NL"1/abc >out 2>err || fail=1
+compare /dev/null out || fail=1
+compare exp-err err || fail=1
+returns_ 1 test -f dir"$NL"1/abc || fail=1
+
+Exit $fail
--
2.56.0