Skip to content

Commit a293fd1

Browse files
authored
Merge pull request #9638 from geoffw0/stringlengthconflation
Swift: String length conflation tests (for CVE-2022-23625)
2 parents 00b4070 + e07df0d commit a293fd1

File tree

5 files changed

+143
-0
lines changed

5 files changed

+143
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* @name String length conflation
3+
* @description TODO
4+
* @kind problem
5+
* @problem.severity TODO
6+
* @security-severity TODO
7+
* @precision TODO
8+
* @id swift/string-length-conflation
9+
* @tags security
10+
* external/cwe/cwe-135
11+
*/
12+
13+
import swift
14+
15+
select "TODO"

swift/ql/test/qlpack.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ name: codeql-swift-tests
22
version: 0.0.0
33
dependencies:
44
codeql/swift-all: "*"
5+
codeql/swift-queries: "*"
56
tests: .
67
extractor: swift
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
| TODO |
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
queries/Security/CWE-135/StringLengthConflation.ql
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
2+
// --- stubs ---
3+
4+
func print(_ items: Any...) {}
5+
6+
typealias unichar = UInt16
7+
8+
class NSObject
9+
{
10+
}
11+
12+
class NSString : NSObject
13+
{
14+
init(string: String) { length = string.count }
15+
16+
func character(at: Int) -> unichar { return 0 }
17+
func substring(from: Int) -> String { return "" }
18+
func substring(to: Int) -> String { return "" }
19+
20+
private(set) var length: Int
21+
}
22+
23+
class NSMutableString : NSString
24+
{
25+
func insert(_: String, at: Int) {}
26+
}
27+
28+
class NSRange
29+
{
30+
init(location: Int, length: Int) { self.description = "" }
31+
32+
private(set) var description: String
33+
}
34+
35+
func NSMakeRange(_ loc: Int, _ len: Int) -> NSRange { return NSRange(location: loc, length: len) }
36+
37+
38+
39+
40+
// --- tests ---
41+
42+
func test(s: String) {
43+
let ns = NSString(string: s)
44+
let nms = NSMutableString(string: s)
45+
46+
print("'\(s)'")
47+
print("count \(s.count) length \(ns.length)")
48+
print("utf8.count \(s.utf8.count) utf16.count \(s.utf16.count) unicodeScalars.count \(s.unicodeScalars.count)")
49+
50+
// --- constructing a String.Index from integer ---
51+
52+
let ix1 = String.Index(encodedOffset: s.count) // GOOD
53+
let ix2 = String.Index(encodedOffset: ns.length) // BAD: NSString length used in String.Index
54+
let ix3 = String.Index(encodedOffset: s.utf8.count) // BAD: String.utf8 length used in String.Index
55+
let ix4 = String.Index(encodedOffset: s.utf16.count) // BAD: String.utf16 length used in String.Index
56+
let ix5 = String.Index(encodedOffset: s.unicodeScalars.count) // BAD: string.unicodeScalars length used in String.Index
57+
print("String.Index '\(ix1.encodedOffset)' / '\(ix2.encodedOffset)' '\(ix3.encodedOffset)' '\(ix4.encodedOffset)' '\(ix5.encodedOffset)'")
58+
59+
let ix6 = s.index(s.startIndex, offsetBy: s.count / 2) // GOOD
60+
let ix7 = s.index(s.startIndex, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index
61+
print("index '\(ix6.encodedOffset)' / '\(ix7.encodedOffset)'")
62+
63+
var ix8 = s.startIndex
64+
s.formIndex(&ix8, offsetBy: s.count / 2) // GOOD
65+
var ix9 = s.startIndex
66+
s.formIndex(&ix9, offsetBy: ns.length / 2) // BAD: NSString length used in String.Index
67+
print("formIndex '\(ix8.encodedOffset)' / '\(ix9.encodedOffset)'")
68+
69+
// --- constructing an NSRange from integers ---
70+
71+
let range1 = NSMakeRange(0, ns.length) // GOOD
72+
let range2 = NSMakeRange(0, s.count) // BAD: String length used in NSMakeRange
73+
let range3 = NSMakeRange(0, s.reversed().count) // BAD: String length used in NSMakeRange
74+
let range4 = NSMakeRange(0, s.distance(from: s.startIndex, to: s.endIndex)) // BAD: String length used in NSMakeRange
75+
print("NSMakeRange '\(range1.description)' / '\(range2.description)' '\(range3.description)' '\(range4.description)'")
76+
77+
let range5 = NSRange(location: 0, length: ns.length) // GOOD
78+
let range6 = NSRange(location: 0, length: s.count) // BAD: String length used in NSMakeRange
79+
print("NSRange '\(range5.description)' / '\(range6.description)'")
80+
81+
// --- String operations using an integer directly ---
82+
83+
let str1 = s.dropFirst(s.count - 1) // GOOD
84+
let str2 = s.dropFirst(ns.length - 1) // BAD: NSString length used in String
85+
print("dropFirst '\(str1)' / '\(str2)'")
86+
87+
let str3 = s.dropLast(s.count - 1) // GOOD
88+
let str4 = s.dropLast(ns.length - 1) // BAD: NSString length used in String
89+
print("dropLast '\(str3)' / '\(str4)'")
90+
91+
let str5 = s.prefix(s.count - 1) // GOOD
92+
let str6 = s.prefix(ns.length - 1) // BAD: NSString length used in String
93+
print("prefix '\(str5)' / '\(str6)'")
94+
95+
let str7 = s.suffix(s.count - 1) // GOOD
96+
let str8 = s.suffix(ns.length - 1) // BAD: NSString length used in String
97+
print("suffix '\(str7)' / '\(str8)'")
98+
99+
let nstr1 = ns.character(at: ns.length - 1) // GOOD
100+
let nmstr1 = nms.character(at: nms.length - 1) // GOOD
101+
let nstr2 = ns.character(at: s.count - 1) // BAD: String length used in NSString
102+
let nmstr2 = nms.character(at: s.count - 1) // BAD: String length used in NString
103+
print("character '\(nstr1)' '\(nmstr1)' / '\(nstr2)' '\(nmstr2)'")
104+
105+
let nstr3 = ns.substring(from: ns.length - 1) // GOOD
106+
let nmstr3 = nms.substring(from: nms.length - 1) // GOOD
107+
let nstr4 = ns.substring(from: s.count - 1) // BAD: String length used in NSString
108+
let nmstr4 = nms.substring(from: s.count - 1) // BAD: String length used in NString
109+
print("substring from '\(nstr3)' '\(nmstr3)' / '\(nstr4)' '\(nmstr4)'")
110+
111+
let nstr5 = ns.substring(to: ns.length - 1) // GOOD
112+
let nmstr5 = nms.substring(to: nms.length - 1) // GOOD
113+
let nstr6 = ns.substring(to: s.count - 1) // BAD: String length used in NSString
114+
let nmstr6 = nms.substring(to: s.count - 1) // BAD: String length used in NString
115+
print("substring to '\(nstr5)' '\(nmstr5)' / '\(nstr6)' '\(nmstr6)'")
116+
117+
let nmstr7 = NSMutableString(string: s)
118+
nmstr7.insert("*", at: nms.length - 1) // GOOD
119+
let nmstr8 = NSMutableString(string: s)
120+
nmstr8.insert("*", at: s.count - 1) // BAD: String length used in NSString
121+
print("insert '\(nmstr7)' / '\(nmstr8)'")
122+
}
123+
124+
// `begin :thumbsup: end`, with thumbs up emoji and skin tone modifier
125+
test(s: "begin \u{0001F44D}\u{0001F3FF} end")

0 commit comments

Comments
 (0)