fetch-unpack-archive: New program for the mix.
[distorted-bits] / fetch-unpack-archive
diff --git a/fetch-unpack-archive b/fetch-unpack-archive
new file mode 100755 (executable)
index 0000000..e9b3d3e
--- /dev/null
@@ -0,0 +1,46 @@
+#! /bin/sh
+###
+### Fetch an archive, and unpack it into a directory in a safe manner.
+
+set -e
+
+## Parse the command line.
+case $# in
+  3) ;;
+  *) echo >&2 "usage: $0 DIR LABEL URL"; exit 1 ;;
+esac
+dir=$1 label=$2 url=$3
+cd "$dir"
+
+## Fetch the archive.
+rm -rf tmp; mkdir tmp
+curl -s -o tmp/"$label.tar.gz" "$url"
+
+## Check the archive for unpleasantness.  The GNU and FreeBSD versions of
+## tar(1) do something vaguely sensible with `..' components in the pathnames
+## of archive members.  (Specifically, FreeBSD simply ignores the affected
+## members; GNU strips leading components in a bizarre way.)  But OpenBSD
+## gets a special security award for cheerily following the `..' components.
+## So we have to do this complicated laundering thing.
+##
+## The archive ought to unpack everything into a single directory and not
+## contain anythig weird.  So check.  Actually, this won't catch newlines in
+## member names, so we'll have to be careful about those.  The regular
+## expression insists that everything be in a single directory identified by
+## the LABEL, and that the rest of the name contains no two adjacent dots.
+## We use the LABEL as part of an ERE, so it ought not contain bad things.
+if
+  tar tzf tmp/"$label.tar.gz" |
+  grep -Ev "^$label/([^.]+|\.[^.])*$" >&2
+then
+  echo >&2 "$0: archive has bad member pathnames"
+  exit 1
+fi
+
+## Unpack the archive now that we know it's safe.
+(cd tmp; tar xzf "$label.tar.gz")
+
+## Replace any existing tree with the new one.
+rm -rf "$label"
+mv tmp/"$label" .
+rm -rf tmp