Skip to content

Commit 5f3854a

Browse files
committed
fix: address DCG binary service feedback
1 parent 899edc0 commit 5f3854a

3 files changed

Lines changed: 64 additions & 50 deletions

File tree

src/services/destructive-command-guard/__tests__/manager.spec.ts

Lines changed: 58 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,13 @@ describe("Destructive Command Guard manager", () => {
130130

131131
it("allows trusted relative redirects and rejects unsafe or exhausted redirects", () => {
132132
expect(resolveTrustedRedirect("https://github.com/release", "/asset", 5)).toBe("https://github.com/asset")
133+
expect(
134+
resolveTrustedRedirect(
135+
"https://github.com/release",
136+
"https://release-assets.githubusercontent.com/asset",
137+
5,
138+
),
139+
).toBe("https://release-assets.githubusercontent.com/asset")
133140
expect(() => resolveTrustedRedirect("https://github.com/release", "https://example.com/asset", 5)).toThrow(
134141
"DCG download redirected to an untrusted host",
135142
)
@@ -322,56 +329,59 @@ describe("Destructive Command Guard manager", () => {
322329
}
323330
})
324331

325-
it("downloads, verifies, extracts, and installs a ZIP archive", async () => {
326-
const info = getDcgArchiveInfo()
327-
if (!info?.archive.endsWith(".zip")) return
328-
329-
const archive = Buffer.from("test ZIP archive")
330-
const originalChecksum = info.sha256
331-
Object.defineProperty(info, "sha256", {
332-
value: createHash("sha256").update(archive).digest("hex"),
333-
configurable: true,
334-
})
335-
const response = Object.assign(new PassThrough(), {
336-
statusCode: 200,
337-
headers: { "content-length": String(archive.length) },
338-
})
339-
const request = Object.assign(new EventEmitter(), { setTimeout: vi.fn(), destroy: vi.fn() })
340-
mockGet.mockImplementation((_url, optionsOrCallback, optionalCallback) => {
341-
const callback = typeof optionsOrCallback === "function" ? optionsOrCallback : optionalCallback
342-
setImmediate(() => {
343-
callback?.(response as unknown as IncomingMessage)
344-
response.end(archive)
332+
it.skipIf(!getDcgArchiveInfo()?.archive.endsWith(".zip"))(
333+
"downloads, verifies, extracts, and installs a ZIP archive",
334+
async () => {
335+
const info = getDcgArchiveInfo()
336+
if (!info) throw new Error("Expected a ZIP archive in this test")
337+
338+
const archive = Buffer.from("test ZIP archive")
339+
const originalChecksum = info.sha256
340+
Object.defineProperty(info, "sha256", {
341+
value: createHash("sha256").update(archive).digest("hex"),
342+
configurable: true,
345343
})
346-
return request as unknown as ReturnType<typeof get>
347-
})
348-
mockSpawn.mockImplementation((_executable, args) => {
349-
const child = Object.assign(new EventEmitter(), {
350-
stdout: new PassThrough(),
351-
stderr: new PassThrough(),
352-
kill: vi.fn(),
344+
const response = Object.assign(new PassThrough(), {
345+
statusCode: 200,
346+
headers: { "content-length": String(archive.length) },
353347
})
354-
setImmediate(async () => {
355-
const destinationIndex = args.indexOf(
356-
"$archivePath = $args[0]; $destination = $args[1]; Expand-Archive -LiteralPath $archivePath -DestinationPath $destination -Force",
357-
)
358-
const stagingDir = args[destinationIndex + 2]
359-
await writeFile(path.join(stagingDir, info.binary), "ZIP executable")
360-
child.emit("close", 0)
348+
const request = Object.assign(new EventEmitter(), { setTimeout: vi.fn(), destroy: vi.fn() })
349+
mockGet.mockImplementation((_url, optionsOrCallback, optionalCallback) => {
350+
const callback = typeof optionsOrCallback === "function" ? optionsOrCallback : optionalCallback
351+
setImmediate(() => {
352+
callback?.(response as unknown as IncomingMessage)
353+
response.end(archive)
354+
})
355+
return request as unknown as ReturnType<typeof get>
356+
})
357+
mockSpawn.mockImplementation((_executable, args) => {
358+
const child = Object.assign(new EventEmitter(), {
359+
stdout: new PassThrough(),
360+
stderr: new PassThrough(),
361+
kill: vi.fn(),
362+
})
363+
setImmediate(async () => {
364+
const destinationIndex = args.indexOf(
365+
"$archivePath = $args[0]; $destination = $args[1]; Expand-Archive -LiteralPath $archivePath -DestinationPath $destination -Force",
366+
)
367+
const stagingDir = args[destinationIndex + 2]
368+
await writeFile(path.join(stagingDir, info.binary), "ZIP executable")
369+
child.emit("close", 0)
370+
})
371+
return child as unknown as ReturnType<typeof spawn>
361372
})
362-
return child as unknown as ReturnType<typeof spawn>
363-
})
364373

365-
try {
366-
const binaryPath = await ensureDcgInstalled(tempDir)
367-
if (!binaryPath) throw new Error("Expected DCG to be supported in this test")
368-
expect(await readFile(binaryPath, "utf8")).toBe("ZIP executable")
369-
expect(await readFile(path.join(tempDir, "destructive-command-guard", ".dcg-version"), "utf8")).toBe(
370-
DCG_VERSION,
371-
)
372-
await expect(access(path.join(tempDir, `${DCG_VERSION}-${info.archive}`))).rejects.toThrow()
373-
} finally {
374-
Object.defineProperty(info, "sha256", { value: originalChecksum, configurable: true })
375-
}
376-
})
374+
try {
375+
const binaryPath = await ensureDcgInstalled(tempDir)
376+
if (!binaryPath) throw new Error("Expected DCG to be supported in this test")
377+
expect(await readFile(binaryPath, "utf8")).toBe("ZIP executable")
378+
expect(await readFile(path.join(tempDir, "destructive-command-guard", ".dcg-version"), "utf8")).toBe(
379+
DCG_VERSION,
380+
)
381+
await expect(access(path.join(tempDir, `${DCG_VERSION}-${info.archive}`))).rejects.toThrow()
382+
} finally {
383+
Object.defineProperty(info, "sha256", { value: originalChecksum, configurable: true })
384+
}
385+
},
386+
)
377387
})

src/services/destructive-command-guard/__tests__/runner.spec.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,19 +83,23 @@ describe("runDcg", () => {
8383
const child = createChild()
8484
useChild(child)
8585
const originalToken = process.env.GITHUB_TOKEN
86+
const originalTmpdir = process.env.TMPDIR
8687
process.env.GITHUB_TOKEN = "secret"
88+
process.env.TMPDIR = "/sandbox/tmp"
8789

8890
try {
8991
const result = runDcg("/dcg", "echo test", "/workspace")
9092
emitResult(child, { schema_version: 1, decision: "allow" }, 0)
9193
await result
9294

9395
const options = mockSpawn.mock.calls[0][2]
94-
expect(options?.env).toMatchObject({ NO_COLOR: "1" })
96+
expect(options?.env).toMatchObject({ NO_COLOR: "1", TMPDIR: "/sandbox/tmp" })
9597
expect(options?.env).not.toHaveProperty("GITHUB_TOKEN")
9698
} finally {
9799
if (originalToken === undefined) delete process.env.GITHUB_TOKEN
98100
else process.env.GITHUB_TOKEN = originalToken
101+
if (originalTmpdir === undefined) delete process.env.TMPDIR
102+
else process.env.TMPDIR = originalTmpdir
99103
}
100104
})
101105

src/services/destructive-command-guard/runner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type DcgJsonOutput = {
1313
pack_id?: string
1414
}
1515

16-
const DCG_ENV_KEYS = ["HOME", "PATH", "TEMP", "TMP", "USERPROFILE", "SystemRoot", "WINDIR"] as const
16+
const DCG_ENV_KEYS = ["HOME", "PATH", "TEMP", "TMPDIR", "TMP", "USERPROFILE", "SystemRoot", "WINDIR"] as const
1717

1818
function getDcgEnvironment(): NodeJS.ProcessEnv {
1919
const env: NodeJS.ProcessEnv = { NO_COLOR: "1" }

0 commit comments

Comments
 (0)