mirror of
https://github.com/actions/toolkit.git
synced 2026-08-07 00:00:18 +02:00
Fix GNU tar path matching issue
This commit is contained in:
+4
-2
@@ -208,8 +208,10 @@ and bsdtar on macOS (and `tar.exe` on Windows):
|
||||
- `'error'` mode, clean archive → every approved member is extracted
|
||||
- `'error'` mode, PAX path newline archive → throws `CacheIntegrityError` and
|
||||
writes nothing to the workspace
|
||||
- `'error'` mode, leading `./` entry → still extracted (the `-T` allow-list
|
||||
uses the canonical `cache/f` name, so the member is not silently skipped)
|
||||
- `'error'` mode, leading `./` entry → still extracted; the `-T` allow-list
|
||||
uses the member name verbatim (exactly as node-tar derived it from the
|
||||
archive), which is what GNU tar matches anchored/exact — stripping the `./`
|
||||
would make GNU tar report "Not found in archive"
|
||||
- `'error'` mode, long path (> 100 bytes) delivered via PAX → extracted, not
|
||||
dropped by the allow-list (exercises long-name matching in `-T`)
|
||||
|
||||
|
||||
Vendored
+9
-17
@@ -431,11 +431,16 @@ function writeAllowList(approvedNames: string[]): string {
|
||||
.randomBytes(8)
|
||||
.toString('hex')}.lst`
|
||||
)
|
||||
// Write each approved name exactly as node-tar derived it from the archive
|
||||
// bytes. That is the same name system tar reads from the archive header, so
|
||||
// an anchored `-T` match succeeds for every member without us second-guessing
|
||||
// tar's name handling. In particular we must NOT strip a leading `./`: GNU
|
||||
// tar matches `-T` names anchored and exact and keeps the `./`, so a
|
||||
// `cache/f` pattern does not match a `./cache/f` member (it fails with
|
||||
// "Not found in archive", exit code 2) whereas the verbatim `./cache/f`
|
||||
// matches and extracts to `cache/f`.
|
||||
const payload = Buffer.concat(
|
||||
approvedNames.flatMap(name => [
|
||||
Buffer.from(canonicalMemberName(name), 'utf8'),
|
||||
Buffer.from([0])
|
||||
])
|
||||
approvedNames.flatMap(name => [Buffer.from(name, 'utf8'), Buffer.from([0])])
|
||||
)
|
||||
// `flag: 'wx'` (O_CREAT | O_EXCL | O_WRONLY) makes the open fail if the path
|
||||
// already exists, so a file or symlink pre-planted at the (randomized) temp
|
||||
@@ -446,19 +451,6 @@ function writeAllowList(approvedNames: string[]): string {
|
||||
return allowListPath
|
||||
}
|
||||
|
||||
/**
|
||||
* Canonicalize an approved entry name to the form system `tar` matches against
|
||||
* its archive members when reading the `-T` allow-list. node-tar already
|
||||
* converts backslashes to forward slashes; here we additionally strip any
|
||||
* leading `./` (both GNU tar and bsdtar normalize member names this way) so an
|
||||
* entry node-tar surfaced as `./cache/f` still matches the member `cache/f`
|
||||
* and is not silently skipped during extraction. A trailing slash on a
|
||||
* directory entry is preserved because tar matches directories with it.
|
||||
*/
|
||||
function canonicalMemberName(name: string): string {
|
||||
return name.replace(/^(?:\.\/)+/, '')
|
||||
}
|
||||
|
||||
function reportViolations(
|
||||
violations: PathValidationViolation[],
|
||||
mode: PathValidationMode
|
||||
|
||||
Reference in New Issue
Block a user