mirror of
https://github.com/samsonjs/FileOtter.git
synced 2026-03-25 08:25:49 +00:00
243 lines
7.3 KiB
Swift
243 lines
7.3 KiB
Swift
//
|
|
// FilePermissionTests.swift
|
|
// FileOtterTests
|
|
//
|
|
// Created by Sami Samhuri on 2025-08-19.
|
|
//
|
|
|
|
@testable import FileOtter
|
|
import XCTest
|
|
|
|
final class FilePermissionTests: XCTestCase {
|
|
var tempDir: URL!
|
|
var testFile: URL!
|
|
var readOnlyFile: URL!
|
|
var executableFile: URL!
|
|
|
|
override func setUpWithError() throws {
|
|
tempDir = URL.temporaryDirectory
|
|
.appendingPathComponent("FilePermissionTests-\(UUID().uuidString)")
|
|
try FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
|
|
|
|
testFile = tempDir.appendingPathComponent("test.txt")
|
|
try "Test content".write(to: testFile, atomically: true, encoding: .utf8)
|
|
|
|
readOnlyFile = tempDir.appendingPathComponent("readonly.txt")
|
|
try "Read only".write(to: readOnlyFile, atomically: true, encoding: .utf8)
|
|
|
|
executableFile = tempDir.appendingPathComponent("script.sh")
|
|
try "#!/bin/sh\necho hello".write(to: executableFile, atomically: true, encoding: .utf8)
|
|
}
|
|
|
|
override func tearDownWithError() throws {
|
|
if FileManager.default.fileExists(atPath: tempDir.path) {
|
|
try FileManager.default.removeItem(at: tempDir)
|
|
}
|
|
}
|
|
|
|
// MARK: - Basic Permission Tests
|
|
|
|
func testIsReadable() throws {
|
|
// Normal files should be readable
|
|
XCTAssertTrue(File.isReadable(testFile))
|
|
|
|
// System files are generally readable
|
|
XCTAssertTrue(File.isReadable(URL(fileURLWithPath: "/etc/hosts")))
|
|
|
|
// Non-existent files are not readable
|
|
let nonExistent = tempDir.appendingPathComponent("nonexistent")
|
|
XCTAssertFalse(File.isReadable(nonExistent))
|
|
}
|
|
|
|
func testIsWritable() throws {
|
|
// Files we created should be writable
|
|
XCTAssertTrue(File.isWritable(testFile))
|
|
|
|
// System files are generally not writable
|
|
XCTAssertFalse(File.isWritable(URL(fileURLWithPath: "/etc/hosts")))
|
|
|
|
// Non-existent files are not writable
|
|
let nonExistent = tempDir.appendingPathComponent("nonexistent")
|
|
XCTAssertFalse(File.isWritable(nonExistent))
|
|
}
|
|
|
|
func testIsExecutable() throws {
|
|
// Make the script executable
|
|
try FileManager.default.setAttributes(
|
|
[.posixPermissions: 0o755],
|
|
ofItemAtPath: executableFile.path,
|
|
)
|
|
XCTAssertTrue(File.isExecutable(executableFile))
|
|
|
|
// System executables
|
|
XCTAssertTrue(File.isExecutable(URL(fileURLWithPath: "/bin/ls")))
|
|
XCTAssertTrue(File.isExecutable(URL(fileURLWithPath: "/usr/bin/swift")))
|
|
}
|
|
|
|
func testIsExecutableForNonExecutable() throws {
|
|
// Regular text files are not executable
|
|
XCTAssertFalse(File.isExecutable(testFile))
|
|
XCTAssertFalse(File.isExecutable(readOnlyFile))
|
|
|
|
// Non-existent files are not executable
|
|
let nonExistent = tempDir.appendingPathComponent("nonexistent")
|
|
XCTAssertFalse(File.isExecutable(nonExistent))
|
|
}
|
|
|
|
// MARK: - Ownership Tests
|
|
|
|
func testIsOwned() throws {
|
|
// Files we create should be owned by us
|
|
XCTAssertTrue(File.isOwned(testFile))
|
|
XCTAssertTrue(File.isOwned(readOnlyFile))
|
|
XCTAssertTrue(File.isOwned(executableFile))
|
|
|
|
// System files may not be owned by us
|
|
// This depends on the user running the test
|
|
}
|
|
|
|
func testIsGroupOwned() throws {
|
|
// Files we create should be owned by our effective group
|
|
XCTAssertTrue(File.isGroupOwned(testFile))
|
|
XCTAssertTrue(File.isGroupOwned(readOnlyFile))
|
|
XCTAssertTrue(File.isGroupOwned(executableFile))
|
|
}
|
|
|
|
// MARK: - World Permission Tests
|
|
|
|
func testIsWorldReadable() throws {
|
|
// Make file world readable
|
|
try FileManager.default.setAttributes(
|
|
[.posixPermissions: 0o644],
|
|
ofItemAtPath: testFile.path,
|
|
)
|
|
|
|
let perms = File.isWorldReadable(testFile)
|
|
XCTAssertNotNil(perms)
|
|
if let perms {
|
|
XCTAssertEqual(perms & 0o004, 0o004) // Check world read bit
|
|
}
|
|
}
|
|
|
|
func testIsWorldReadableForPrivate() throws {
|
|
// Make file not world readable
|
|
try FileManager.default.setAttributes(
|
|
[.posixPermissions: 0o640],
|
|
ofItemAtPath: readOnlyFile.path,
|
|
)
|
|
|
|
XCTAssertNil(File.isWorldReadable(readOnlyFile))
|
|
}
|
|
|
|
func testIsWorldWritable() throws {
|
|
// Make file world writable (dangerous in practice!)
|
|
try FileManager.default.setAttributes(
|
|
[.posixPermissions: 0o666],
|
|
ofItemAtPath: testFile.path,
|
|
)
|
|
|
|
let perms = File.isWorldWritable(testFile)
|
|
XCTAssertNotNil(perms)
|
|
if let perms {
|
|
XCTAssertEqual(perms & 0o002, 0o002) // Check world write bit
|
|
}
|
|
}
|
|
|
|
func testIsWorldWritableForProtected() throws {
|
|
// Make file not world writable
|
|
try FileManager.default.setAttributes(
|
|
[.posixPermissions: 0o644],
|
|
ofItemAtPath: readOnlyFile.path,
|
|
)
|
|
|
|
XCTAssertNil(File.isWorldWritable(readOnlyFile))
|
|
}
|
|
|
|
// MARK: - Special Bit Tests
|
|
|
|
func testIsSetuid() throws {
|
|
// Setuid is rarely used on regular files
|
|
// Most files should not have setuid
|
|
XCTAssertFalse(File.isSetuid(testFile))
|
|
|
|
// /usr/bin/sudo typically has setuid (if it exists)
|
|
let sudo = URL(fileURLWithPath: "/usr/bin/sudo")
|
|
if FileManager.default.fileExists(atPath: sudo.path) {
|
|
// This might be true on some systems
|
|
_ = File.isSetuid(sudo)
|
|
}
|
|
}
|
|
|
|
func testIsSetgid() throws {
|
|
// Setgid is rarely used on regular files
|
|
XCTAssertFalse(File.isSetgid(testFile))
|
|
}
|
|
|
|
func testIsSticky() throws {
|
|
// Sticky bit is typically set on /tmp
|
|
let tmpDir = URL(fileURLWithPath: "/tmp")
|
|
if FileManager.default.fileExists(atPath: tmpDir.path) {
|
|
// /tmp usually has sticky bit
|
|
_ = File.isSticky(tmpDir)
|
|
}
|
|
|
|
// Regular files should not have sticky bit
|
|
XCTAssertFalse(File.isSticky(testFile))
|
|
}
|
|
|
|
// MARK: - chmod Tests
|
|
|
|
func testChmod() throws {
|
|
// TODO: Implement
|
|
// File.chmod(url, permissions) changes file permissions
|
|
}
|
|
|
|
func testChmodThrowsForNonExistent() throws {
|
|
// TODO: Implement
|
|
}
|
|
|
|
func testLchmod() throws {
|
|
// TODO: Implement
|
|
// File.lchmod(url, permissions) changes symlink permissions
|
|
}
|
|
|
|
func testInstanceChmod() throws {
|
|
// TODO: Implement
|
|
// file.chmod(permissions) changes open file permissions
|
|
}
|
|
|
|
// MARK: - chown Tests
|
|
|
|
func testChown() throws {
|
|
// TODO: Implement
|
|
// File.chown(url, owner, group) changes ownership
|
|
// Note: May require special privileges
|
|
}
|
|
|
|
func testChownWithNilValues() throws {
|
|
// TODO: Implement
|
|
// nil owner or group means don't change that value
|
|
}
|
|
|
|
func testLchown() throws {
|
|
// TODO: Implement
|
|
// File.lchown(url, owner, group) changes symlink ownership
|
|
}
|
|
|
|
func testInstanceChown() throws {
|
|
// TODO: Implement
|
|
// file.chown(owner, group) changes open file ownership
|
|
}
|
|
|
|
// MARK: - umask Tests
|
|
|
|
func testUmask() throws {
|
|
// TODO: Implement
|
|
// File.umask() returns current umask
|
|
}
|
|
|
|
func testUmaskSet() throws {
|
|
// TODO: Implement
|
|
// File.umask(mask) sets umask and returns previous value
|
|
}
|
|
}
|