Files
codeql/swift/ql/test/query-tests/Security/CWE-078/CommandInjection.swift
2024-08-01 15:58:43 +01:00

220 lines
6.2 KiB
Swift

// --- stubs ---
struct URL
{
init?(string: String) {}
init(fileURLWithPath: String) {}
}
class NSObject {
}
class Process : NSObject {
var launchPath: String? { get { nil } set {} }
var arguments: [String]? { get { nil } set {} }
func launch() {}
var executableURL: URL? { get { nil } set {} }
func run() throws {}
class func launchedProcess(launchPath path: String, arguments: [String]) -> Process {
return Process()
}
class func run(_ url: URL, arguments: [String], terminationHandler: (@Sendable (Process) -> Void)? = nil) throws -> Process {
return Process()
}
}
extension String {
init(contentsOf url: URL) throws {
self.init("")
}
}
class NSUserScriptTask : NSObject {
typealias CompletionHandler = @Sendable (Error?) -> Void
init(url: URL) throws {}
func execute(completionHandler handler: NSUserScriptTask.CompletionHandler? = nil) { }
}
class NSUserUnixTask : NSUserScriptTask {
func execute(withArguments arguments: [String]?, completionHandler handler: NSUserScriptTask.CompletionHandler? = nil) { }
}
protocol NSSecureCoding {
}
class NSUserAutomatorTask : NSUserScriptTask {
func execute(withInput input: NSSecureCoding?, completionHandler handler: NSUserScriptTask.CompletionHandler? = nil) { }
var variables: [String: Any]? { get { return nil } set { } }
}
class FileManager : NSObject {
class var `default`: FileManager { get { return 0 as! FileManager } }
func contentsOfDirectory(atPath path: String) throws -> [String] { [] }
}
// --- tests ---
func validateCommand(_ command: String) -> String? {
let allowedCommands = ["ls -l", "pwd", "echo"]
if allowedCommands.contains(command) {
return command
}
return nil
}
func testCommandInjectionQhelpExamples() {
guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else {
return
}
let task1 = Process()
task1.launchPath = "/bin/bash" // GOOD
task1.arguments = ["-c", userControlledString] // BAD
task1.launch()
if let validatedString = validateCommand(userControlledString) {
let task2 = Process()
task2.launchPath = "/bin/bash" // GOOD
task2.arguments = ["-c", validatedString] // GOOD [FALSE POSITIVE]
task2.launch()
}
}
func mkProcess() -> Process? {
return Process()
}
class MyProcess : Process {
var harmlessField: String?
func setArguments(_ arguments: [String]) {
self.arguments = arguments // BAD
}
}
func testCommandInjectionMore(mySafeString: String) {
guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else {
return
}
let task1 = Process()
task1.executableURL = URL(string: mySafeString)! // GOOD
task1.arguments = ["abc"] // GOOD
try! task1.run()
let task2 = Process()
task2.executableURL = URL(fileURLWithPath: mySafeString) // GOOD
task2.arguments = ["abc", "def"] // GOOD
try! task2.run()
let task3 = Process()
task3.executableURL = URL(string: userControlledString)! // BAD
task3.arguments = ["abc", userControlledString] // BAD
try! task3.run()
let task4 = Process()
task4.executableURL = URL(fileURLWithPath: userControlledString) // BAD
task4.executableURL = URL(string: userControlledString)! // BAD
task4.arguments = ["abc", "def" + userControlledString] // BAD
try! task4.run()
let task5 = mkProcess()
task5?.executableURL = URL(fileURLWithPath: userControlledString) // BAD
task5?.arguments = [userControlledString] // BAD
try! task5?.run()
let task6 = MyProcess()
task6.executableURL = URL(fileURLWithPath: userControlledString) // BAD
task6.executableURL = URL(string: userControlledString)! // BAD
task6.arguments = [userControlledString] // BAD
task6.setArguments([userControlledString]) // BAD (flagged inside `setArguments`)
task6.harmlessField = userControlledString // GOOD
try! task6.run()
let task7 = Process()
task7.executableURL = URL(fileURLWithPath: mySafeString) // GOOD
task7.arguments = ["abc", "def"]
task7.arguments?.append(userControlledString) // BAD [NOT DETECTED]
try! task7.run()
_ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", mySafeString]) // GOOD
_ = Process.launchedProcess(launchPath: userControlledString, arguments: ["abc", mySafeString]) // BAD
_ = Process.launchedProcess(launchPath: mySafeString, arguments: ["abc", userControlledString]) // BAD
_ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", mySafeString]) // GOOD
_ = try? Process.run(URL(string: userControlledString)!, arguments: ["abc", mySafeString]) // BAD
_ = try? Process.run(URL(string: mySafeString)!, arguments: ["abc", userControlledString]) // BAD
let task8 = try! NSUserScriptTask(url: URL(string: mySafeString)!) // GOOD
task8.execute()
let task9 = try! NSUserScriptTask(url: URL(string: userControlledString)!) // BAD
task9.execute()
let task10 = try! NSUserUnixTask(url: URL(string: userControlledString)!) // BAD
task10.execute(withArguments: [userControlledString]) // BAD
let task11 = try! NSUserAutomatorTask(url: URL(string: userControlledString)!) // BAD
task11.variables = ["abc": userControlledString] // BAD [NOT DETECTED]
task11.execute(withInput: nil)
let files = try! FileManager.default.contentsOfDirectory(atPath: "some/directory")
for file in files {
let task12 = Process()
task12.launchPath = "/bin/rm" // GOOD
task12.arguments = [file] // GOOD (cases like this vary, but our analysis doesn't work well on them)
task12.launch()
task12.arguments = files // GOOD (similar to previous)
task12.launch()
task12.arguments = [files[0]] // GOOD (similar to previous)
task12.launch()
}
}
struct MyClass {
let p1 : Process
let p2 : Process?
let p3 : Process!
var myValue: [String]? {
get {
return nil
}
set {
p1.arguments = newValue // BAD
p2!.arguments = newValue // BAD
p3.arguments = newValue // BAD
}
}
mutating func myFunc() {
guard let userControlledString = try? String(contentsOf: URL(string: "http://example.com/")!) else {
return
}
let tainted1 = [userControlledString]
p1.arguments = tainted1 // BAD
p2!.arguments = tainted1 // BAD
p3.arguments = tainted1 // BAD
let tainted2 : [String]? = [userControlledString]
p1.arguments = tainted2 // BAD
p2!.arguments = tainted2 // BAD
p3.arguments = tainted2 // BAD
myValue = tainted2
}
}