Working draft

N16 name-dotdot

A name with .., which climbs out of the directory the archive is extracted to.

Reject(draft) A strict parser must reject this archive.

Category
name
Default policy
rejects it
Files
N16-name-dotdot.tar test archive, 2,048 bytes sha256 b2e00a616ca5350ef2ffa84fd48c021f035ca8483b171b37658ac7ffb8ab3a63
N16-name-dotdot.jsonexpected result
N16-name-dotdot.txtannotated blocks
N16-name-dotdot.pygenerator script

The case

The name tar-minefield/../../tar-minefield-escaped resolves to a path outside the destination. The case is rejected because the name, taken as it is, leads out of the destination, not because it is ambiguous. The file is harmless: its name and contents say what happened.

Decision point: whether a name with .. is rejected, rewritten, skipped, or followed.

Seen in: 4 PyPI sdists, such as spdk 26.9 (spdk-26.9/../scripts/rpc.py) and bt-test-interfaces 0.0.5, whose bt-test-interfaces-0.0.5/../.git/config lands next to the top directory.

The archive

Every block of 512 bytes, to scale, with its offset. Hover over a field for its bytes.

N16-name-dotdot: every block of the archive0x000000header: regular filename [0,100): "tar-minefield/../../tar-minefield-escaped" + 59 NULnamemode [100,108): "0000644" + NUL, 644 (octal)uid [108,116): "0000000" + NUL, 0gid [116,124): "0000000" + NUL, 0size [124,136): "00000000125" + NUL, 85mtime [136,148): "00000000000" + NUL, 0chksum [148,156): "016416\0 ", 7438: the unsigned sum of the header, this field as spacestypeflag [156,157): "0", regular filelinkname [157,257): 100 NULlinknamemagic [257,263): "ustar" + NULversion [263,265): "00"uname [265,297): 32 NULunamegname [297,329): 32 NULgnamedevmajor [329,337): "0000000" + NUL, 0devminor [337,345): "0000000" + NUL, 0prefix [345,500): 155 NULprefixpad [500,512): 12 NULname "tar-minefield/../../tar-minefield-escaped" + 59 NUL0x000200data85 bytes, then 427 NUL to the end of the block; "If this file is outside the directory you extracted to, the extractor followed `..`.\n""If this file is outside the directory you extracted to, the extractor followed `..`.\n"0x0004002 zero blocks: the end-of-archive markerbytes 0 to 512 of 1024: 512 NUL0x0006002 zero blocks, continuedbytes 512 to 1024 of 1024: 512 NUL0x000800end of fileNULprintable ASCIIspacecontrol character, such as a newlinebyte over 0x7fwritten by the case itself
Every block, as text
N16-name-dotdot: must reject

A name with `..`, which climbs out of the directory the archive is extracted to.

The name `tar-minefield/../../tar-minefield-escaped` resolves to a path outside the destination. The
case is rejected because the name, taken as it is, leads out of the destination, not because it is
ambiguous. The file is harmless: its name and contents say what happened.

Decision point: whether a name with `..` is rejected, rewritten, skipped, or followed.
Seen in: 4 PyPI sdists, such as spdk 26.9 (`spdk-26.9/../scripts/rpc.py`) and bt-test-interfaces
0.0.5, whose `bt-test-interfaces-0.0.5/../.git/config` lands next to the top directory.

0x000000  header: regular file
          name     [  0,100)  "tar-minefield/../../tar-minefield-escaped" + 59 NUL
          mode     [100,108)  "0000644" + NUL  644 (octal)
          uid      [108,116)  "0000000" + NUL  0
          gid      [116,124)  "0000000" + NUL  0
          size     [124,136)  "00000000125" + NUL  85
          mtime    [136,148)  "00000000000" + NUL  0
          chksum   [148,156)  "016416\0 "  7438: the unsigned sum of the header, this field as spaces
          typeflag [156,157)  "0"  regular file
          linkname [157,257)  100 NUL
          magic    [257,263)  "ustar" + NUL
          version  [263,265)  "00"
          uname    [265,297)  32 NUL
          gname    [297,329)  32 NUL
          devmajor [329,337)  "0000000" + NUL  0
          devminor [337,345)  "0000000" + NUL  0
          prefix   [345,500)  155 NUL
          pad      [500,512)  12 NUL
0x000200  data
          85 bytes, then 427 NUL to the end of the block
          "If this file is outside the directory you extracted to, the extractor followed `..`.\n"
0x000400  2 zero blocks: the end-of-archive marker
0x000800  end of file
Every byte, as a hex dump

As xxd writes it: xxd -r makes the archive of it.

00000000: 7461 722d 6d69 6e65 6669 656c 642f 2e2e  tar-minefield/..
00000010: 2f2e 2e2f 7461 722d 6d69 6e65 6669 656c  /../tar-minefiel
00000020: 642d 6573 6361 7065 6400 0000 0000 0000  d-escaped.......
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 3030 3030 3634 3400 3030 3030  ....0000644.0000
00000070: 3030 3000 3030 3030 3030 3000 3030 3030  000.0000000.0000
00000080: 3030 3030 3132 3500 3030 3030 3030 3030  0000125.00000000
00000090: 3030 3000 3031 3634 3136 0020 3000 0000  000.016416. 0...
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0075 7374 6172 0030 3000 0000 0000 0000  .ustar.00.......
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0000 0030 3030 3030 3030  .........0000000
00000150: 0030 3030 3030 3030 0000 0000 0000 0000  .0000000........
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000190: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000200: 4966 2074 6869 7320 6669 6c65 2069 7320  If this file is 
00000210: 6f75 7473 6964 6520 7468 6520 6469 7265  outside the dire
00000220: 6374 6f72 7920 796f 7520 6578 7472 6163  ctory you extrac
00000230: 7465 6420 746f 2c20 7468 6520 6578 7472  ted to, the extr
00000240: 6163 746f 7220 666f 6c6c 6f77 6564 2060  actor followed `
00000250: 2e2e 602e 0a00 0000 0000 0000 0000 0000  ..`.............
00000260: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000270: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000280: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000290: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000002f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000300: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000310: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000320: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000330: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000340: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000350: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000360: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000370: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000380: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000390: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000400: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000410: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000420: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000430: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000440: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000450: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000460: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000470: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000480: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000490: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000004a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000004b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000004c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000004d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000004e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000004f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000500: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000510: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000520: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000530: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000540: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000550: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000560: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000570: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000580: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000590: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000005a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000005b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000005c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000005d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000005e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000005f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000600: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000610: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000620: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000630: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000640: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000650: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000660: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000670: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000680: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000690: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000006f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000700: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000710: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000720: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000730: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000740: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000750: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000760: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000770: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000780: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000790: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000007a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000007b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000007c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000007d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000007e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000007f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

What implementations make of it

implementationversionoutcomewhat it makes of it
GNU tartar (GNU tar) 1.35✅ as the verdict sayserror: tar: Removing leading `tar-minefield/../../' from member names tar: tar-minefield/../../tar-minefield-escaped: Member name contains '..' tar: Exiting with failure status due to previous errors
bsdtarbsdtar 3.8.10 - libarchive 3.8.10✅ as the verdict sayserror: tar-minefield/../../tar-minefield-escaped: Path contains '..': Unknown error -1 bsdtar: Error exit delayed from previous errors
BusyBox tarBusyBox v1.38.0, defconfig⚠️ acceptstree A: tar-minefield-escaped (85 B, fc503701)
7-Zip7-Zip (z) 26.05 (x64)⚠️ acceptstree B: tar-minefield/, tar-minefield/tar-minefield-escaped (85 B, fc503701)
Python tarfile, filter=data, as in pipCPython 3.15.2✅ as the verdict sayserror: OutsideDestinationError: 'tar-minefield/../../tar-minefield-escaped' would be extracted to 'ROOT/x/y/tar-minefield-escaped', which is outside the destination
Python tarfile, filter=tarCPython 3.15.2✅ as the verdict sayserror: OutsideDestinationError: 'tar-minefield/../../tar-minefield-escaped' would be extracted to 'ROOT/x/y/tar-minefield-escaped', which is outside the destination
Python tarfile, filter=fully_trustedCPython 3.15.2💥 writes outside the destinationwrites outside: x/y/tar-minefield-escaped (85 B, fc503701)
node-tartar 7.5.22⚠️ acceptstree C: nothing
node-tar, stricttar 7.5.22✅ as the verdict sayserror: Error: TAR_ENTRY_ERROR: path contains '..'
tar-fstar-fs 3.1.3, on tar-stream 3.2.2⚠️ acceptstree A: tar-minefield-escaped (85 B, fc503701)
Java Commons CompressCommons Compress 1.28.0, Expander, on Java 27.0.1.0.2✅ as the verdict sayserror: java.io.IOException: Zip slip 'DEST' + 'tar-minefield/../../tar-minefield-escaped' -> 'ROOT/x/y/tar-minefield-escaped'
.NET System.Formats.TarSystem.Formats.Tar of .NET 10.0.14, ExtractToDirectory✅ as the verdict sayserror: IOException: Extracting the Tar entry 'tar-minefield/../../tar-minefield-escaped' would have resulted in a file outside the specified destination directory: 'DEST/'
PHP PharData, as in ComposerPharData of PHP 8.5.12, as Composer's TarDownloader calls it⚠️ acceptstree A: tar-minefield-escaped (85 B, fc503701)
Go containerd archive.Applycontainerd v2.4.2, archive.Apply, of go1.27.3⚠️ acceptstree A: tar-minefield-escaped (85 B, fc503701)
Rust tartar 0.4.46, unpack⚠️ acceptstree C: nothing
Rust astral-tokio-tarastral-tokio-tar 0.7.0, unpack⚠️ acceptstree C: nothing
Rust tar-codec, as in uvtar-codec 0.0.15, with the policies of uv 0.13✅ as the verdict sayserror: at byte 0: unsafe member path "tar-minefield/../../tar-minefield-escaped": contains a parent-directory component
RubyGemsRubyGems 4.0.20 of Ruby 4.0.9, extract_tar_gz✅ as the verdict sayserror: Gem::Package::PathError: installing into parent path ROOT/x/y/tar-minefield-escaped of DEST is not allowed
R untar, internalutils::untar of R 4.6.1, tar = "internal"✅ as the verdict sayserror: path contains '..'
Perl Archive::TarArchive::Tar 3.12 of Perl v5.44.0✅ as the verdict sayserror: Entry 'tar-minefield/../../tar-minefield-escaped' is attempting to leave the current working directory. Not extracting under SECURE EXTRACT MODE at contrib/matrix/runners/perl/extract.pl line 14. Cou…
Haskell tartar 0.7.2.0, extract, of GHC 9.14✅ as the verdict sayserror: Unsafe link target in tar archive: "tar-minefield/../../tar-minefield-escaped"
Julia Tar.jlTar 1.10.0 of Julia 1.13.3✅ as the verdict sayserror: path contains '..' component Tar.Header("tar-minefield/../../tar-minefield-escaped", :file, 0o644, 85, "")
live-bootstrap untarmescc-tools-extra a151c245e512, as live-bootstrap pins it, built with cc💥 writes outside the destinationwrites outside: x/y/tar-minefield-escaped (85 B, fc503701)
live-bootstrap untar, --non-strictmescc-tools-extra a151c245e512, as live-bootstrap pins it, built with cc💥 writes outside the destinationwrites outside: x/y/tar-minefield-escaped (85 B, fc503701)

Prevalence in release tarballs

Release tarballs of Spack, nixpkgs, Guix and live-bootstrap, and samples of PyPI, crates.io and RubyGems with name.dotdot. The default policy rejects all of them.

ecosystemtarballswith itshare
Spack37,5530none
nixpkgs18,9900none
Guix10,1680none
PyPI10,00040.04%
crates.io5,0000none
RubyGems5,0000none
live-bootstrap1710none
total (distinct)82,5864<0.01%

See the list of these 4 tarballs, with the sha256 of each and an example from it.