Similarly to commit v9.10-115-gb3fe24213, which applied
the same quoting mechanism to --check operation,
also quote file names when generating checksums to tty.
This change gives these advantages:
1. Avoids dumping arbitrary data to vulnerable terminals
2. Avoids visual deception with ansi codes with unknown file names
3. More secure if users copy and paste file names from output
* doc/coreutils.texi (sum invocation): Mention the tty quoting
and reference the details from ...
(cksum output modes): Describe the two forms of quoting used.
* tests/misc/tty-quoting.sh: Add test cases.
* NEWS: Mention the improvement.
Discovered by Anthropic's OSS Scanner, as bug ANT-2026-Z25BP6MW.
---
NEWS | 4 ++
doc/coreutils.texi | 148 +++++++++++++++++++-------------------
src/cksum.c | 26 +++++--
tests/misc/tty-quoting.sh | 16 ++++-
4 files changed, 117 insertions(+), 77 deletions(-)
diff --git a/NEWS b/NEWS
index 73f0ccb2e..4be22db47 100644
--- a/NEWS
+++ b/NEWS
@@ -288,6 +288,10 @@ GNU coreutils NEWS -*-
outline -*-
'basenc --base58' encoding speed is 20x faster when built without libgmp.
+ 'cksum' when generating checksums to tty, uses shell quoting when required,
+ to more robustly escape presented file names, honoring $QUOTING_STYLE to
+ adjust or disable quoting. This also affects md5sum, sha*sum, b2sum, and sum.
+
'cut -c' is up to 10x faster in UTF-8 locales, and
'cut -f -w' is up to 10x faster in uni-byte locales.
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 15eaef800..2905c1d75 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -3882,6 +3882,8 @@ sum [@var{option}]@dots{} [@var{file}]@dots{}
@command{sum} prints the checksum for each @var{file} followed by the
number of blocks in the file (rounded up). If at least one @var{file}
is given, file names are also printed.
+When standard output is a terminal, file names are quoted
+as described for @command{cksum}. @xref{cksum output modes}.
By default, GNU @command{sum} computes checksums using an algorithm
compatible with BSD @command{sum} and prints file sizes in units of
@@ -3984,13 +3986,85 @@ otherwise text mode is the default.
@end table
Without @option{--zero}, and with non legacy output formats,
-if @var{file} contains a backslash, newline, or carriage return,
+and when not outputting to a terminal or if terminal quoting has been disabled,
+the following quoting mode is in effect.
+If @var{file} contains a backslash, newline, or carriage return,
the line is started with a backslash, and each problematic character
in the file name is escaped with a backslash, making the output unambiguous
even in the presence of arbitrary file names.
Since the backslash character itself is escaped, any other backslash
escape sequences are reserved for future use.
+@macro quotingStyles
+@table @samp
+@item literal
+Output strings as-is;
+@ifset LS_COMMAND
+this is the same as the @option{--literal} (@option{-N}) option.
+@end ifset
+@item shell
+Quote strings for the shell if they contain shell metacharacters or would
+cause ambiguous output.
+The quoting is suitable for POSIX-compatible shells like
+@command{bash}, but it does not always work for incompatible shells
+like @command{csh}.
+@item shell-always
+Quote strings for the shell, even if they would normally not require quoting.
+@item shell-escape
+Like @samp{shell}, but also quote non-printable characters using the POSIX
+@samp{$''} syntax suitable for most shells. This is the most general format
+as any file name is represented unambiguously and safely. I.e., the full
+quoted string can be pasted back to the shell to refer to any file.
+@ifset LS_COMMAND
+Consequently, this is the default format used when @command{ls}
+is outputting to a tty.
+@end ifset
+Note also that specifying the C locale with @code{LC_ALL=C} can be useful
+with this output format, to avoid character set conversion issues
+with some terminals; e.g., xterm always converting to unicode composed form.
+See also
+@uref{https://www.gnu.org/software/coreutils/quotes.html, Quoting file names}.
+@item shell-escape-always
+Like @samp{shell-escape}, but quote strings even if they would
+normally not require quoting.
+@item c
+Quote strings as for C character string literals, including the
+surrounding double-quote characters;
+@ifset LS_COMMAND
+this is the same as the @option{--quote-name} (@option{-Q}) option.
+@end ifset
+@item c-maybe
+Quote strings as for C character string literals, except omit the
+surrounding double-quote if no escaping is required.
+@item escape
+Quote strings as for C character string literals, except omit the
+surrounding double-quote characters;
+@ifset LS_COMMAND
+this is the same as the @option{--escape} (@option{-b}) option.
+@end ifset
+@item clocale
+Quote strings as for C character string literals, except use
+surrounding quotation marks appropriate for the
+locale.
+@item locale
+@c Use @t instead of @samp to avoid duplicate quoting in some output styles.
+Quote strings as for C character string literals, except use
+surrounding quotation marks appropriate for the locale, and quote
+@t{'like this'} instead of @t{"like
+this"} in the default C locale. This looks nicer on many displays.
+@end table
+@end macro
+
+@macro terminalQuoted
+Without @option{--zero} and when standard output is a terminal,
+file names are quoted using the @samp{shell-escape} style.
+The environment variable @env{QUOTING_STYLE} specifies the quoting style.
+Valid quoting styles are:
+@quotingStyles
+@end macro
+
+@terminalQuoted
+
@node cksum general options
@subsection cksum general options
@@ -8503,65 +8577,6 @@ Use style @var{word} to quote file names and other
strings that may
contain arbitrary characters. The @var{word} should
be one of the following:
-@macro quotingStyles
-@table @samp
-@item literal
-Output strings as-is;
-@ifset LS_COMMAND
-this is the same as the @option{--literal} (@option{-N}) option.
-@end ifset
-@item shell
-Quote strings for the shell if they contain shell metacharacters or would
-cause ambiguous output.
-The quoting is suitable for POSIX-compatible shells like
-@command{bash}, but it does not always work for incompatible shells
-like @command{csh}.
-@item shell-always
-Quote strings for the shell, even if they would normally not require quoting.
-@item shell-escape
-Like @samp{shell}, but also quote non-printable characters using the POSIX
-@samp{$''} syntax suitable for most shells. This is the most general format
-as any file name is represented unambiguously and safely. I.e., the full
-quoted string can be pasted back to the shell to refer to any file.
-@ifset LS_COMMAND
-Consequently, this is the default format used when @command{ls}
-is outputting to a tty.
-@end ifset
-Note also that specifying the C locale with @code{LC_ALL=C} can be useful
-with this output format, to avoid character set conversion issues
-with some terminals; e.g., xterm always converting to unicode composed form.
-See also
-@uref{https://www.gnu.org/software/coreutils/quotes.html, Quoting file names}.
-@item shell-escape-always
-Like @samp{shell-escape}, but quote strings even if they would
-normally not require quoting.
-@item c
-Quote strings as for C character string literals, including the
-surrounding double-quote characters;
-@ifset LS_COMMAND
-this is the same as the @option{--quote-name} (@option{-Q}) option.
-@end ifset
-@item c-maybe
-Quote strings as for C character string literals, except omit the
-surrounding double-quote if no escaping is required.
-@item escape
-Quote strings as for C character string literals, except omit the
-surrounding double-quote characters;
-@ifset LS_COMMAND
-this is the same as the @option{--escape} (@option{-b}) option.
-@end ifset
-@item clocale
-Quote strings as for C character string literals, except use
-surrounding quotation marks appropriate for the
-locale.
-@item locale
-@c Use @t instead of @samp to avoid duplicate quoting in some output styles.
-Quote strings as for C character string literals, except use
-surrounding quotation marks appropriate for the locale, and quote
-@t{'like this'} instead of @t{"like
-this"} in the default C locale. This looks nicer on many displays.
-@end table
-@end macro
@set LS_COMMAND
@quotingStyles
@clear LS_COMMAND
@@ -11114,14 +11129,6 @@ variable is set.
@end table
-@c This is also used by basename and dirname.
-@macro terminalQuoted
-When standard output is a terminal, output is quoted using the
-@samp{shell-escape} style. The environment variable
-@env{QUOTING_STYLE} specifies the quoting style. Valid quoting styles
-are:
-@quotingStyles
-@end macro
@terminalQuoted
The @command{readlink} utility first appeared in OpenBSD 2.1.
@@ -14629,10 +14636,7 @@ only on the file name, and does not touch any actual
file.
@end table
-When standard output is a terminal, file names are quoted using
-the @samp{shell-escape} style. The environment variable
-@env{QUOTING_STYLE} can select the quoting style. Valid quoting styles are:
-@quotingStyles
+@terminalQuoted
@cindex exit status of @command{realpath}
Exit status:
diff --git a/src/cksum.c b/src/cksum.c
index bdd54c74a..1fdfcf8fd 100644
--- a/src/cksum.c
+++ b/src/cksum.c
@@ -21,9 +21,9 @@
#include <getopt.h>
#include <sys/types.h>
+#include "argmatch.h"
#include "assure.h"
#include "system.h"
-#include "argmatch.h"
#include "c-ctype.h"
#include "quote.h"
#include "xdectoint.h"
@@ -231,6 +231,9 @@ static bool base64_digest = false;
/* If true, print binary digests, not hex. */
static bool raw_digest = false;
+/* If true, quote output names according to the selected quoting style. */
+static bool quote_output;
+
/* blake2b and sha3 allow the -l option. Luckily they both have the same
maximum digest size. */
#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
@@ -1271,8 +1274,10 @@ output_file (char const *file, int binary_file, void
const *digest,
unsigned char const *bin_buffer = digest;
- /* Output a leading backslash if the file name contains problematic chars.
*/
- bool needs_escape = delim == '\n' && problematic_chars (file);
+ /* Output a leading backslash if the file name contains problematic chars,
+ unless we're already quoting output. */
+ bool needs_escape = (! quote_output && delim == '\n'
+ && problematic_chars (file));
if (needs_escape)
putchar ('\\');
@@ -1870,6 +1875,18 @@ main (int argc, char **argv)
else if (prefix_tag)
binary = 1;
+ if (!do_check && digest_delim == '\n' && isatty (STDOUT_FILENO))
+ {
+ int qs = getenv_quoting_style ();
+ if (qs < 0)
+ qs = shell_escape_quoting_style;
+ if (qs != literal_quoting_style)
+ {
+ set_quoting_style (NULL, qs);
+ quote_output = true;
+ }
+ }
+
char **operand_lim = argv + argc;
if (optind == argc)
*operand_lim++ = bad_cast ("-");
@@ -1892,7 +1909,8 @@ main (int argc, char **argv)
ok = false;
else
{
- DIGEST_OUT (file, binary_file, bin_buffer, raw_digest,
prefix_tag,
+ DIGEST_OUT (quote_output ? quoteN (file) : file, binary_file,
+ bin_buffer, raw_digest, prefix_tag,
digest_delim, optind != argc, length);
if (ferror (stdout))
write_error ();
diff --git a/tests/misc/tty-quoting.sh b/tests/misc/tty-quoting.sh
index b967c45d1..b4eee247c 100755
--- a/tests/misc/tty-quoting.sh
+++ b/tests/misc/tty-quoting.sh
@@ -17,7 +17,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
-print_ver_ basename dirname du ls readlink realpath printf test
+print_ver_ basename cksum dirname du ls readlink realpath printf test
require_strace_ ioctl
touch 'b ar' || framework_failure_
@@ -67,4 +67,18 @@ for cmd in basename du dirname 'ls -w0' readlink 'realpath
--relative-to=.' \
compare quoted printf_quoted || fail=1
done
+for cmd in 'cksum -a sha1 --untagged' 'cksum -a sha1'; do
+ file='f oo'
+
+ run_tty_ $cmd "$file" >full_quoted || fail=1
+
+ # Ensure shell quoting found
+ cut -d "'" -f2 full_quoted >unquoted || framework_failure_
+ test "$file" = "$(cat unquoted)" || fail=1
+
+ # Ensure unquoted doesn't match quoted
+ QUOTING_STYLE=literal run_tty_ $cmd "$file" >full_unquoted || fail=1
+ compare full_quoted full_unquoted && fail=1
+done
+
Exit $fail
--
2.56.0