Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/pr-verification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ on:
branches:
- main

# This workflow only reads repository contents. Artifact and cache actions use
# their dedicated runtime tokens and do not require additional GITHUB_TOKEN
# permissions.
permissions:
contents: read

jobs:
checks:
runs-on: windows-2022
Expand Down
2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@
"@wdio/mocha-framework": "^9.29.1",
"@wdio/spec-reporter": "^9.29.1",
"cross-env": "^10.1.0",
"expect-webdriverio": "^5.7.0"
"expect-webdriverio": "^6.0.10"
}
}
50 changes: 50 additions & 0 deletions e2e/patches/extract-zip@2.0.1.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
diff --git a/index.js b/index.js
index 23384ea16bb848616886f356d8d69f94325535bf..2aac75b895f2b64b9cf914224d8d528d166b96a8 100644
--- a/index.js
+++ b/index.js
@@ -50,7 +50,7 @@ class Extractor {
return
}

- const destDir = path.dirname(path.join(this.opts.dir, entry.fileName))
+ const destDir = path.dirname(this.getDestinationPath(entry.fileName))

try {
await fs.mkdir(destDir, { recursive: true })
@@ -85,7 +85,7 @@ class Extractor {
this.opts.onEntry(entry, this.zipfile)
}

- const dest = path.join(this.opts.dir, entry.fileName)
+ const dest = this.getDestinationPath(entry.fileName)

// convert external file attr int into a fs stat mode int
const mode = (entry.externalFileAttributes >> 16) & 0xFFFF
@@ -126,10 +126,26 @@ class Extractor {

if (symlink) {
const link = await getStream(readStream)
+ this.assertPathContained(path.resolve(path.dirname(dest), link), entry.fileName)
debug('creating symlink', link, dest)
await fs.symlink(link, dest)
} else {
- await pipeline(readStream, createWriteStream(dest, { mode: procMode }))
+ // Refuse to follow an entry that was previously created as a symlink.
+ // This also closes the race between checking the destination and opening it.
+ await pipeline(readStream, createWriteStream(dest, { mode: procMode, flags: 'wx' }))
+ }
+ }
+
+ getDestinationPath (entryName) {
+ const dest = path.resolve(this.opts.dir, entryName)
+ this.assertPathContained(dest, entryName)
+ return dest
+ }
+
+ assertPathContained (candidate, entryName) {
+ const relative = path.relative(this.opts.dir, candidate)
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
+ throw new Error(`Out of bound path "${candidate}" found while processing file ${entryName}`)
}
}

1,128 changes: 533 additions & 595 deletions e2e/pnpm-lock.yaml

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions e2e/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,9 @@ allowBuilds:
geckodriver: false
onlyBuiltDependencies:
- esbuild
# Mocha 10 still pins a vulnerable serialize-javascript major.
overrides:
serialize-javascript@<7.0.5: 7.0.5
# No fixed extract-zip release exists, so keep the mitigation reproducible.
patchedDependencies:
extract-zip@2.0.1: patches/extract-zip@2.0.1.patch
Loading
Loading