cli: reject path-traversal in archive entry names on extraction

extract_cb appended a decoded entry name to the destination path with
no validation, so a crafted archive whose entry name contained "..",
a path separator, or an absolute form could write files outside the
chosen destination directory (a Zip-Slip). Each UC2 entry name is a
single path component -- the directory tree is rebuilt from dirid
parents -- so reject any name that is empty, ".", "..", or contains
'/' or '\'. The bundled writer only ever stores basenames, so this
affects malformed or hostile archives only; normal extraction
(including names like "..foo" and nested directories) is unchanged.
This commit is contained in:
Eremey Valetov
2026-06-13 08:35:59 -04:00
parent 5e0f3852c6
commit 43cf875dfe

View File

@@ -483,6 +483,19 @@ static bool extract_cb(struct node *ne, void *ctx, enum cause cause)
switch (cause) {
case VisitFile:
case EnterDir:;
/* Each UC2 entry name is a single path component (the directory
tree is rebuilt from dirid parents). A name that is empty,
".", "..", or contains a path separator is malformed or a
path-traversal attempt -- refuse to extract it rather than
write outside the destination. */
if (l == 0
|| (l == 1 && e->name[0] == '.')
|| (l == 2 && e->name[0] == '.' && e->name[1] == '.')
|| memchr(e->name, '/', l)
|| memchr(e->name, '\\', l))
errx(EXIT_FAILURE, "unsafe archive entry name: %.*s",
(int)l, e->name);
char *p = path->ptr + l;
if (p + 1 >= endof(path->buffer))
errx(EXIT_FAILURE, "Path too long");