diff --git a/.yarn/versions/f01fa2ef.yml b/.yarn/versions/f01fa2ef.yml new file mode 100644 index 000000000000..78489db73a4d --- /dev/null +++ b/.yarn/versions/f01fa2ef.yml @@ -0,0 +1,36 @@ +releases: + "@yarnpkg/cli": patch + "@yarnpkg/core": patch + "@yarnpkg/fslib": patch + "@yarnpkg/plugin-pnp": patch + "@yarnpkg/pnp": patch + vscode-zipfs: patch + +declined: + - "@yarnpkg/esbuild-plugin-pnp" + - "@yarnpkg/plugin-compat" + - "@yarnpkg/plugin-constraints" + - "@yarnpkg/plugin-dlx" + - "@yarnpkg/plugin-essentials" + - "@yarnpkg/plugin-exec" + - "@yarnpkg/plugin-file" + - "@yarnpkg/plugin-git" + - "@yarnpkg/plugin-github" + - "@yarnpkg/plugin-http" + - "@yarnpkg/plugin-init" + - "@yarnpkg/plugin-interactive-tools" + - "@yarnpkg/plugin-link" + - "@yarnpkg/plugin-node-modules" + - "@yarnpkg/plugin-npm" + - "@yarnpkg/plugin-npm-cli" + - "@yarnpkg/plugin-pack" + - "@yarnpkg/plugin-patch" + - "@yarnpkg/plugin-stage" + - "@yarnpkg/plugin-typescript" + - "@yarnpkg/plugin-version" + - "@yarnpkg/plugin-workspace-tools" + - "@yarnpkg/builder" + - "@yarnpkg/doctor" + - "@yarnpkg/json-proxy" + - "@yarnpkg/pnpify" + - "@yarnpkg/shell" diff --git a/packages/yarnpkg-fslib/sources/ZipOpenFS.ts b/packages/yarnpkg-fslib/sources/ZipOpenFS.ts index de0c2ee01819..a4ca0de934fa 100644 --- a/packages/yarnpkg-fslib/sources/ZipOpenFS.ts +++ b/packages/yarnpkg-fslib/sources/ZipOpenFS.ts @@ -9,11 +9,34 @@ import {NodeFS} import {ZipFS} from './ZipFS'; import {watchFile, unwatchFile, unwatchAllFiles} from './algorithms/watchFile'; import * as errors from './errors'; -import {Filename, FSPath, PortablePath} from './path'; +import {Filename, FSPath, PortablePath, ppath} from './path'; const ZIP_FD = 0x80000000; -const FILE_PARTS_REGEX = /.*?(? { + const 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; + + // The path either has to end in ".zip" or contain an archive subpath (".zip/...") + if (path.length > nextCharIdx && path[nextCharIdx] !== ppath.sep) + return null; + + return path.slice(0, nextCharIdx) as PortablePath; +}; export type ZipOpenFSOptions = { baseFs?: FakeFS, @@ -851,11 +874,11 @@ export class ZipOpenFS extends BasePortableFakeFS { let filePath = `` as PortablePath; while (true) { - const parts = FILE_PARTS_REGEX.exec(p.substr(filePath.length)); - if (!parts) + const archivePart = getArchivePart(p.substr(filePath.length)); + if (!archivePart) return null; - filePath = this.pathUtils.join(filePath, parts[0] as PortablePath); + filePath = this.pathUtils.join(filePath, archivePart); if (this.isZip.has(filePath) === false) { if (this.notZip.has(filePath)) diff --git a/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts b/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts index 9fd91efb6609..78bf9ab5c96f 100644 --- a/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts +++ b/packages/yarnpkg-fslib/tests/ZipOpenFS.test.ts @@ -1,5 +1,6 @@ import {getLibzipSync} from '@yarnpkg/libzip'; +import {getArchivePart} from '../sources/ZipOpenFS'; import {ppath, npath, Filename} from '../sources/path'; import {ZipOpenFS} from '../sources'; @@ -17,6 +18,27 @@ const ZIP_DIR2 = ppath.join( export const ZIP_FILE1 = ppath.join(ZIP_DIR1, `foo.txt` as Filename); const ZIP_FILE2 = ppath.join(ZIP_DIR2, `foo.txt` as Filename); +describe(`getArchivePart`, () => { + const tests = [ + [`.zip`, null], + [`foo`, null], + [`foo.zip`, `foo.zip`], + [`foo.zip/bar`, `foo.zip`], + [`foo.zip/bar/baz`, `foo.zip`], + [`/a/b/c/foo.zip`, `/a/b/c/foo.zip`], + [`./a/b/c/foo.zip`, `./a/b/c/foo.zip`], + [`./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`], + ] as const; + + for (const [path, result] of tests) { + test(`getArchivePart(${JSON.stringify(path)}) === ${JSON.stringify(result)}`, () => { + expect(getArchivePart(path)).toStrictEqual(result); + }); + } +}); + describe(`ZipOpenFS`, () => { it(`can read from a zip file`, () => { const fs = new ZipOpenFS({libzip: getLibzipSync()});