fix(shared): validate tar hard-link targets during preflight

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-04-04 01:16:37 +09:00
parent 553a961338
commit ccbd646a29
3 changed files with 20 additions and 11 deletions
+15 -6
View File
@@ -2,7 +2,7 @@ import { dirname, isAbsolute, relative, resolve, sep } from "node:path"
export type ArchiveEntry = {
path: string
type: "file" | "directory" | "symlink"
type: "file" | "directory" | "symlink" | "hardlink"
linkPath?: string
}
@@ -49,26 +49,35 @@ export function validateArchiveEntries(entries: ArchiveEntry[], destDir: string)
for (const entry of entries) {
const resolvedEntryPath = resolveContainedPath(resolvedDestDir, entry.path, "path")
if (entry.type !== "symlink") {
if (entry.type !== "symlink" && entry.type !== "hardlink") {
continue
}
if (!entry.linkPath) {
throw new Error(`Unsafe archive entry: symlink target missing for ${entry.path}`)
throw new Error(
`Unsafe archive entry: ${entry.type === "symlink" ? "symlink" : "hard link"} target missing for ${entry.path}`
)
}
const normalizedLinkPath = normalizeArchivePath(entry.linkPath)
const linkTypeLabel = entry.type === "symlink" ? "symlink target" : "hard link target"
if (isArchiveAbsolutePath(normalizedLinkPath)) {
throw new Error(`Unsafe archive entry: symlink target uses an absolute path (${entry.linkPath})`)
throw new Error(
`Unsafe archive entry: ${linkTypeLabel} uses an absolute path (${entry.linkPath})`
)
}
if (containsTraversalSegment(normalizedLinkPath)) {
throw new Error(`Unsafe archive entry: symlink target contains path traversal (${entry.linkPath})`)
throw new Error(
`Unsafe archive entry: ${linkTypeLabel} contains path traversal (${entry.linkPath})`
)
}
const resolvedLinkPath = resolve(dirname(resolvedEntryPath), normalizedLinkPath)
if (escapesDirectory(resolvedDestDir, resolvedLinkPath)) {
throw new Error(`Unsafe archive entry: symlink target escapes extraction directory (${entry.linkPath})`)
throw new Error(
`Unsafe archive entry: ${linkTypeLabel} escapes extraction directory (${entry.linkPath})`
)
}
}
}