Skip to content

Commit 29885a8

Browse files
authored
Merge pull request #1724 from actions/bethanyj28/update-unzip-stream
Use latest `unzip-stream` and `unzip.Extract`
2 parents d82fd09 + 9eb3d3a commit 29885a8

5 files changed

Lines changed: 28 additions & 64 deletions

File tree

packages/artifact/RELEASES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# @actions/artifact Releases
22

3+
### 2.1.7
4+
5+
- Update unzip-stream dependency and reverted to using `unzip.Extract()`
6+
37
### 2.1.6
48

59
- Will retry on invalid request responses.

packages/artifact/__tests__/download-artifact.test.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,12 @@ describe('download-artifact', () => {
200200
}
201201
)
202202

203-
await expect(
204-
downloadArtifactPublic(
205-
fixtures.artifactID,
206-
fixtures.repositoryOwner,
207-
fixtures.repositoryName,
208-
fixtures.token
209-
)
210-
).rejects.toBeInstanceOf(Error)
203+
const response = await downloadArtifactPublic(
204+
fixtures.artifactID,
205+
fixtures.repositoryOwner,
206+
fixtures.repositoryName,
207+
fixtures.token
208+
)
211209

212210
expect(downloadArtifactMock).toHaveBeenCalledWith({
213211
owner: fixtures.repositoryOwner,
@@ -223,6 +221,16 @@ describe('download-artifact', () => {
223221
expect(mockGetArtifactMalicious).toHaveBeenCalledWith(
224222
fixtures.blobStorageUrl
225223
)
224+
225+
// ensure path traversal was not possible
226+
expect(
227+
fs.existsSync(path.join(fixtures.workspaceDir, 'x/etc/hosts'))
228+
).toBe(true)
229+
expect(
230+
fs.existsSync(path.join(fixtures.workspaceDir, 'y/etc/hosts'))
231+
).toBe(true)
232+
233+
expect(response.downloadPath).toBe(fixtures.workspaceDir)
226234
})
227235

228236
it('should successfully download an artifact to user defined path', async () => {

packages/artifact/package-lock.json

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/artifact/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@actions/artifact",
3-
"version": "2.1.6",
3+
"version": "2.1.7",
44
"preview": true,
55
"description": "Actions artifact lib",
66
"keywords": [

packages/artifact/src/internal/download/download-artifact.ts

Lines changed: 2 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import fs from 'fs/promises'
2-
import * as stream from 'stream'
3-
import {createWriteStream} from 'fs'
4-
import * as path from 'path'
52
import * as github from '@actions/github'
63
import * as core from '@actions/core'
74
import * as httpClient from '@actions/http-client'
@@ -47,11 +44,6 @@ async function streamExtract(url: string, directory: string): Promise<void> {
4744
await streamExtractExternal(url, directory)
4845
return
4946
} catch (error) {
50-
if (error.message.includes('Malformed extraction path')) {
51-
throw new Error(
52-
`Artifact download failed with unretryable error: ${error.message}`
53-
)
54-
}
5547
retryCount++
5648
core.debug(
5749
`Failed to download artifact after ${retryCount} retries due to ${error.message}. Retrying in 5 seconds...`
@@ -86,8 +78,6 @@ export async function streamExtractExternal(
8678
}
8779
const timer = setTimeout(timerFn, timeout)
8880

89-
const createdDirectories = new Set<string>()
90-
createdDirectories.add(directory)
9181
response.message
9282
.on('data', () => {
9383
timer.refresh()
@@ -99,46 +89,8 @@ export async function streamExtractExternal(
9989
clearTimeout(timer)
10090
reject(error)
10191
})
102-
.pipe(unzip.Parse())
103-
.pipe(
104-
new stream.Transform({
105-
objectMode: true,
106-
transform: async (entry, _, callback) => {
107-
const fullPath = path.normalize(path.join(directory, entry.path))
108-
if (!directory.endsWith(path.sep)) {
109-
directory += path.sep
110-
}
111-
if (!fullPath.startsWith(directory)) {
112-
reject(new Error(`Malformed extraction path: ${fullPath}`))
113-
}
114-
115-
if (entry.type === 'Directory') {
116-
if (!createdDirectories.has(fullPath)) {
117-
createdDirectories.add(fullPath)
118-
await resolveOrCreateDirectory(fullPath).then(() => {
119-
entry.autodrain()
120-
callback()
121-
})
122-
} else {
123-
entry.autodrain()
124-
callback()
125-
}
126-
} else {
127-
core.info(`Extracting artifact entry: ${fullPath}`)
128-
if (!createdDirectories.has(path.dirname(fullPath))) {
129-
createdDirectories.add(path.dirname(fullPath))
130-
await resolveOrCreateDirectory(path.dirname(fullPath))
131-
}
132-
133-
const writeStream = createWriteStream(fullPath)
134-
writeStream.on('finish', callback)
135-
writeStream.on('error', reject)
136-
entry.pipe(writeStream)
137-
}
138-
}
139-
})
140-
)
141-
.on('finish', async () => {
92+
.pipe(unzip.Extract({path: directory}))
93+
.on('close', () => {
14294
clearTimeout(timer)
14395
resolve()
14496
})

0 commit comments

Comments
 (0)