diff --git a/packages/yarnpkg-fslib/sources/ZipOpenFS.ts b/packages/yarnpkg-fslib/sources/ZipOpenFS.ts index 205ac2dd8cca..6e62537881ef 100644 --- a/packages/yarnpkg-fslib/sources/ZipOpenFS.ts +++ b/packages/yarnpkg-fslib/sources/ZipOpenFS.ts @@ -21,15 +21,20 @@ const DOT_ZIP = `.zip`; * The indexOf-based implementation is ~3.7x faster than a RegExp-based implementation. */ export const getArchivePart = (path: string) => { - const idx = path.indexOf(DOT_ZIP); + let idx = path.indexOf(DOT_ZIP); if (idx <= 0) return null; - // Disallow files named ".zip" - if (path[idx - 1] === ppath.sep) - return null; - - const nextCharIdx = idx + DOT_ZIP.length; + let nextCharIdx = idx; + while (idx >= 0) { + nextCharIdx = idx + DOT_ZIP.length; + if (path[nextCharIdx] === ppath.sep) + break; + // Disallow files named ".zip" + if (path[idx - 1] === ppath.sep) + return null; + idx = path.indexOf(DOT_ZIP, nextCharIdx); + } // The path either has to end in ".zip" or contain an archive subpath (".zip/...") if (path.length > nextCharIdx && path[nextCharIdx] !== ppath.sep) diff --git a/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts b/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts index 78bf9ab5c96f..b96ead312d89 100644 --- a/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts +++ b/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts @@ -30,6 +30,9 @@ describe(`getArchivePart`, () => { [`./a/b/c/.zip`, null], [`./a/b/c/foo.zipp`, null], [`./a/b/c/foo.zip/bar/baz/qux.zip`, `./a/b/c/foo.zip`], + [`./a/b/c/foo.zip-bar.zip`, `./a/b/c/foo.zip-bar.zip`], + [`./a/b/c/foo.zip-bar.zip/bar/baz/qux.zip`, `./a/b/c/foo.zip-bar.zip`], + [`./a/b/c/foo.zip-bar/foo.zip-bar/foo.zip-bar.zip/d`, `./a/b/c/foo.zip-bar/foo.zip-bar/foo.zip-bar.zip`], ] as const; for (const [path, result] of tests) {