fix: cap archive entry path depth

Adding an entry checked `HasPath` on every prefix of its path, so a name with thousands of
components did O(depth^2) hashing per entry before anything was charged.

- entry names over 256 path components are refused and reported like other unsafe names
- implied parent directories are found walking from the deepest parent up, stopping at the first
  one that already exists

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2026-10-21 11:21:36 -04:00
parent 7717baa43d
commit 2897361337
2 changed files with 30 additions and 7 deletions
+16 -3
View File
@@ -215,6 +215,19 @@ func TestResolver_extract_dataEdgeCases(t *testing.T) {
}
})
t.Run("an entry name with too many path components is skipped and reported", func(t *testing.T) {
atCap := strings.Repeat("a/", maxEntryNameDepth-1) + "f"
data := zipBytes(t, map[string]string{
atCap: "at the cap",
strings.Repeat("b/", maxEntryNameDepth) + "f": "over the cap",
})
r, _ := extractBytes(t, data, "test.zip", nil)
names := storedNames(t, r)
assert.Contains(t, names, atCap)
assert.Len(t, names, maxEntryNameDepth, "only the entry at the cap and its implied directories")
assert.ErrorContains(t, r.Truncated, "path components")
})
t.Run("an over-long entry name is skipped", func(t *testing.T) {
data := zipBytes(t, map[string]string{
strings.Repeat("a/", maxEntryNameBytes) + "f": "deep",
@@ -526,12 +539,12 @@ func TestExtract_entryCountBombIsBoundedByTheMemoryLimit(t *testing.T) {
}
func TestExtract_deepPathBombIsBoundedByTheMemoryLimit(t *testing.T) {
// 100 entries whose 4 KiB names each imply 500 distinct directories would be 50,000 nodes; the
// implied directories are charged as nodes, so the memory limit bounds them too
// 100 entries whose names each imply 250 distinct directories would be 25,000 nodes; the implied
// directories are charged as nodes, so the memory limit bounds them too
files := map[string]string{}
for i := range 100 {
var b strings.Builder
for j := range 500 {
for j := range maxEntryNameDepth - 6 {
fmt.Fprintf(&b, "%03d-%03d/", i, j)
}
b.WriteString("f")