Swift: Tests for regex injection query.

This commit is contained in:
Geoffrey White
2023-07-03 10:48:28 +01:00
parent b41fd52be5
commit 315cae21ce
3 changed files with 145 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
edges
| tests.swift:93:22:93:46 | call to String.init(contentsOf:) | tests.swift:118:19:118:19 | taintedString |
nodes
| tests.swift:93:22:93:46 | call to String.init(contentsOf:) | semmle.label | call to String.init(contentsOf:) |
| tests.swift:118:19:118:19 | taintedString | semmle.label | taintedString |
subpaths
#select
| tests.swift:118:19:118:19 | taintedString | tests.swift:93:22:93:46 | call to String.init(contentsOf:) | tests.swift:118:19:118:19 | taintedString | This regular expression is constructed from a $@. | tests.swift:93:22:93:46 | call to String.init(contentsOf:) | user-provided value |

View File

@@ -0,0 +1 @@
queries/Security/CWE-730/RegexInjection.ql

View File

@@ -0,0 +1,136 @@
// --- stubs ---
struct URL {
init?(string: String) {}
}
struct AnyRegexOutput {
}
protocol RegexComponent<RegexOutput> {
associatedtype RegexOutput
}
struct Regex<Output> : RegexComponent {
struct Match {
}
init(_ pattern: String) throws where Output == AnyRegexOutput { }
func firstMatch(in string: String) throws -> Regex<Output>.Match? { return nil}
typealias RegexOutput = Output
}
extension RangeReplaceableCollection {
mutating func replace<Replacement>(_ regex: some RegexComponent, with replacement: Replacement, maxReplacements: Int = .max) where Replacement : Collection, Replacement.Element == Character { }
}
extension StringProtocol {
func replacingOccurrences<Target, Replacement>(of target: Target, with replacement: Replacement, options: String.CompareOptions = [], range searchRange: Range<Self.Index>? = nil) -> String where Target : StringProtocol, Replacement : StringProtocol { return "" }
}
extension String : RegexComponent {
typealias CompareOptions = NSString.CompareOptions
typealias Output = Substring
typealias RegexOutput = String.Output
}
class NSObject {
}
class NSString : NSObject {
struct CompareOptions : OptionSet {
var rawValue: UInt
static var regularExpression: NSString.CompareOptions { get { return CompareOptions(rawValue: 1) } }
}
convenience init(string aString: String) { self.init() }
func replacingOccurrences(of target: String, with replacement: String, options: NSString.CompareOptions = [], range searchRange: NSRange) -> String { return "" }
var length: Int { get { return 0 } }
}
struct _NSRange {
init(location: Int, length: Int) { }
}
typealias NSRange = _NSRange
func NSMakeRange(_ loc: Int, _ len: Int) -> NSRange { return NSRange(location: loc, length: len) }
class NSTextCheckingResult : NSObject {
}
class NSRegularExpression : NSObject {
struct Options : OptionSet {
var rawValue: UInt
}
struct MatchingOptions : OptionSet {
var rawValue: UInt
}
init(pattern: String, options: NSRegularExpression.Options = []) throws { }
func firstMatch(in string: String, options: NSRegularExpression.MatchingOptions = [], range: NSRange) -> NSTextCheckingResult? { return nil }
}
extension String {
init(contentsOf: URL) {
let data = ""
self.init(data)
}
}
// --- tests ---
func regexInjectionTests(cond: Bool, varString: String, myUrl: URL) throws {
let constString = ".*"
let taintedString = String(contentsOf: myUrl) // tainted
// --- Regex ---
_ = try Regex(constString).firstMatch(in: varString)
_ = try Regex(varString).firstMatch(in: varString)
_ = try Regex(taintedString).firstMatch(in: varString) // BAD [NOT DETECTED]
_ = try Regex("(a|" + constString + ")").firstMatch(in: varString)
_ = try Regex("(a|" + taintedString + ")").firstMatch(in: varString) // BAD [NOT DETECTED]
_ = try Regex("(a|\(constString))").firstMatch(in: varString)
_ = try Regex("(a|\(taintedString))").firstMatch(in: varString) // BAD [NOT DETECTED]
_ = try Regex(cond ? constString : constString).firstMatch(in: varString)
_ = try Regex(cond ? taintedString : constString).firstMatch(in: varString) // BAD [NOT DETECTED]
_ = try Regex(cond ? constString : taintedString).firstMatch(in: varString) // BAD [NOT DETECTED]
_ = try (cond ? Regex(constString) : Regex(constString)).firstMatch(in: varString)
_ = try (cond ? Regex(taintedString) : Regex(constString)).firstMatch(in: varString) // BAD [NOT DETECTED]
_ = try (cond ? Regex(constString) : Regex(taintedString)).firstMatch(in: varString) // BAD [NOT DETECTED]
// --- RangeReplaceableCollection ---
var inputVar = varString
inputVar.replace(constString, with: "")
inputVar.replace(taintedString, with: "") // BAD
inputVar.replace(constString, with: taintedString)
// --- StringProtocol ---
_ = inputVar.replacingOccurrences(of: constString, with: "", options: .regularExpression)
_ = inputVar.replacingOccurrences(of: taintedString, with: "", options: .regularExpression) // BAD [NOT DETECTED]
// --- NSRegularExpression ---
_ = try NSRegularExpression(pattern: constString).firstMatch(in: varString, range: NSMakeRange(0, varString.utf16.count))
_ = try NSRegularExpression(pattern: taintedString).firstMatch(in: varString, range: NSMakeRange(0, varString.utf16.count)) // BAD [NOT DETECTED]
// --- NSString ---
let nsString = NSString(string: varString)
_ = nsString.replacingOccurrences(of: constString, with: "", options: .regularExpression, range: NSMakeRange(0, nsString.length))
_ = nsString.replacingOccurrences(of: taintedString, with: "", options: .regularExpression, range: NSMakeRange(0, nsString.length)) // BAD [NOT DETECTED]
}