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:
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user