Skip to content

Commit

Permalink
Add a test case to check intentional symlinking outside of the destin…
Browse files Browse the repository at this point in the history
…ation directory
  • Loading branch information
weichsel committed Dec 22, 2023
1 parent 4b20296 commit a455746
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 5 deletions.
11 changes: 8 additions & 3 deletions Sources/ZIPFoundation/FileManager+ZIP.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,12 @@ extension FileManager {
/// - sourceURL: The file URL pointing to an existing ZIP file.
/// - destinationURL: The file URL that identifies the destination directory of the unzip operation.
/// - skipCRC32: Optional flag to skip calculation of the CRC32 checksum to improve performance.
/// - allowUncontainedSymlinks: Optional flag to allow symlinks that point to paths outside the destination.
/// - progress: A progress object that can be used to track or cancel the unzip operation.
/// - pathEncoding: Encoding for entry paths. Overrides the encoding specified in the archive.
/// - Throws: Throws an error if the source item does not exist or the destination URL is not writable.
public func unzipItem(at sourceURL: URL, to destinationURL: URL, skipCRC32: Bool = false,
public func unzipItem(at sourceURL: URL, to destinationURL: URL,

Check failure on line 94 in Sources/ZIPFoundation/FileManager+ZIP.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
skipCRC32: Bool = false, allowUncontainedSymlinks: Bool = false,
progress: Progress? = nil, pathEncoding: String.Encoding? = nil) throws {
let fileManager = FileManager()
guard fileManager.itemExists(at: sourceURL) else {
Expand All @@ -114,9 +116,12 @@ extension FileManager {
if let progress = progress {
let entryProgress = archive.makeProgressForReading(entry)
progress.addChild(entryProgress, withPendingUnitCount: entryProgress.totalUnitCount)
crc32 = try archive.extract(entry, to: entryURL, skipCRC32: skipCRC32, progress: entryProgress)
crc32 = try archive.extract(entry, to: entryURL,

Check failure on line 119 in Sources/ZIPFoundation/FileManager+ZIP.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
skipCRC32: skipCRC32, allowUncontainedSymlinks: allowUncontainedSymlinks,
progress: entryProgress)
} else {
crc32 = try archive.extract(entry, to: entryURL, skipCRC32: skipCRC32)
crc32 = try archive.extract(entry, to: entryURL,

Check failure on line 123 in Sources/ZIPFoundation/FileManager+ZIP.swift

View workflow job for this annotation

GitHub Actions / SwiftLint

Trailing Whitespace Violation: Lines should not have trailing whitespace (trailing_whitespace)
skipCRC32: skipCRC32, allowUncontainedSymlinks: allowUncontainedSymlinks)
}

func verifyChecksumIfNecessary() throws {
Expand Down
16 changes: 14 additions & 2 deletions Tests/ZIPFoundationTests/ZIPFoundationFileManagerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ extension ZIPFoundationTests {
for entry in archive {
let directoryURL = destinationURL.appendingPathComponent(entry.path)
itemsExist = fileManager.itemExists(at: directoryURL)
if !itemsExist { break }
if itemsExist == false { break }
}
XCTAssert(itemsExist)
}
Expand Down Expand Up @@ -142,12 +142,24 @@ extension ZIPFoundationTests {
throws: Archive.ArchiveError.missingEndOfCentralDirectoryRecord)
}

func testUnzipUncontainedSymlink() {
func testUnzipUncontainedSymlink() throws {
let fileManager = FileManager()
let archive = self.archive(for: #function, mode: .read)
let destinationURL = self.createDirectory(for: #function)
XCTAssertSwiftError(try fileManager.unzipItem(at: archive.url, to: destinationURL),
throws: Archive.ArchiveError.uncontainedSymlink)

var linkArchiveURL = ZIPFoundationTests.tempZipDirectoryURL
linkArchiveURL.appendPathComponent(self.archiveName(for: #function))
let linkURL = linkArchiveURL.deletingLastPathComponent()
let linkTarget = linkURL.path
let linkArchive = try XCTUnwrap(try? Archive(url: linkArchiveURL, accessMode: .create))
try? linkArchive.addEntry(with: "link", type: .symlink, uncompressedSize: Int64(4),
provider: { (_, _) -> Data in
return linkTarget.data(using: .utf8) ?? Data()
})
try? fileManager.unzipItem(at: linkArchiveURL, to: destinationURL, allowUncontainedSymlinks: true)
XCTAssert(fileManager.itemExists(at: destinationURL.appendingPathComponent("link")))
}

// On Darwin platforms, we want the same behavior as the system-provided ZIP utilities.
Expand Down

0 comments on commit a455746

Please sign in to comment.