From 79f746170951d54a2816a8935c30e28c5d59c24b Mon Sep 17 00:00:00 2001 From: Olle Lundberg Date: Fri, 19 Feb 2021 09:05:26 +0100 Subject: [PATCH] Recommend a safer find command Piping find to xargs is dangerous as xargs will interpret any characters defined in $IFS (Input Field Separator), usually , as separators in its input. This might lead to unintented operations on files if any of the input to xargs containts any of the characters defined in $IFS, most often this happens if a file contains a space in its name. The safest way to execute a find | xargs is to force find to separate its output with null characters and tell xargs to read null characters as the delimiter AND also tell xargs to put the argument to the command its supposed to run in quotation marks: `find ... -print0 | xargs -I {} -0 rm -rf "{}"` When running with GNU find you most likely also want to add --no-run-if-empty or -r for short: `find ... -print0 | xargs -I {} -0 --no-run-if-empty rm -rf "{}"` This stops the invocation of xargs if there is no input on stdin, this is however not portable and will break on BSD/macOS, the portability is not a concern in this case though as the find | xargs happens in docker. As you can see this gets unwieldly fast and despite using every precaution, it's still not safe. When xargs is run with -0 to treat null characters as the delimiter for its input and a file has a null character in its name, xargs will treat the null character in the file name as a delimiter and xargs will exhibit the same behaviour as it did with spaces in file names. Ahhh, isn't Unix wonderful? Loose APIs defined as untyped strings... There is a salvation though! Most find xargs pipes are unnecessary and can be replaced with built in functionality in find, the -exec flag. Now, -exec comes in 2 flavours, one that is terminated with \; (the most commonly used) and one terminated with \+ (the one most people actually want to use). \; spawns a new invocation per found entry, thus creating some process creation overhead. \+ instead concatenates the found entries as arguments to the program we want to run, resulting in less overhead and usually a faster execution. Since find is smart enough to be aware of what constitutes an entry (i.e it doesn't treat the entries as just a bunch of random strings to read from stdin) it makes the whole invocation of the program, rm in this case, safe even if it contains characters defines in $IFS or null characters. And with this overly elaborate commit message I bring you this 1 line change. --- docs/getting-started/deployment-docker.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started/deployment-docker.md b/docs/getting-started/deployment-docker.md index 9486e0aeb0..69158f9104 100644 --- a/docs/getting-started/deployment-docker.md +++ b/docs/getting-started/deployment-docker.md @@ -141,7 +141,7 @@ COPY package.json yarn.lock ./ COPY packages packages COPY plugins plugins -RUN find packages \! -name "package.json" -mindepth 2 -maxdepth 2 -print | xargs rm -rf +RUN find packages \! -name "package.json" -mindepth 2 -maxdepth 2 -exec rm -rf {} \+ # Stage 2 - Install dependencies and build packages FROM node:14-buster-slim AS build