Skip to content

Commit dacb7f5

Browse files
committed
Swift: Add a SensitiveExprs lib (and test it).
1 parent 2aa6dd2 commit dacb7f5

File tree

3 files changed

+201
-0
lines changed

3 files changed

+201
-0
lines changed
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/**
2+
* Provides classes for heuristically identifying expressions that contain
3+
* 'sensitive' data, meaning that they contain or return a password or other
4+
* credential, or sensitive private information.
5+
*/
6+
7+
import swift
8+
9+
private newtype TSensitiveDataType =
10+
TCredential() or
11+
TPrivateInfo()
12+
13+
/**
14+
* A type of sensitive expression.
15+
*/
16+
abstract class SensitiveDataType extends TSensitiveDataType {
17+
abstract string toString();
18+
19+
/**
20+
* Gets a regexp for identifying expressions of this type.
21+
*/
22+
abstract string getRegexp();
23+
}
24+
25+
/**
26+
* The type of sensitive expression for passwords and other credentials.
27+
*/
28+
class SensitiveCredential extends SensitiveDataType, TCredential {
29+
override string toString() { result = "credential" }
30+
31+
override string getRegexp() {
32+
result = ".*(password|passwd|accountid|account.?key|accnt.?key|license.?key|trusted).*"
33+
}
34+
}
35+
36+
/**
37+
* The type of sensitive expression for private information.
38+
*/
39+
class SensitivePrivateInfo extends SensitiveDataType, TPrivateInfo {
40+
override string toString() { result = "private information" }
41+
42+
override string getRegexp() {
43+
result =
44+
".*(" +
45+
// Inspired by the list on https://cwe.mitre.org/data/definitions/359.html
46+
// Government identifiers, such as Social Security Numbers
47+
"social.?security|national.?insurance|" +
48+
// Contact information, such as home addresses
49+
"post.?code|zip.?code|home.?address|" +
50+
// and telephone numbers
51+
"telephone|home.?phone|mobile|fax.?no|fax.?number|" +
52+
// Geographic location - where the user is (or was)
53+
"latitude|longitude|" +
54+
// Financial data - such as credit card numbers, salary, bank accounts, and debts
55+
"credit.?card|debit.?card|salary|bank.?account|" +
56+
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
57+
"email|" +
58+
// Health - medical conditions, insurance status, prescription records
59+
"birthday|birth.?date|date.?of.?birth|medical|" +
60+
// Relationships - work and family
61+
"employer|spouse" +
62+
// ---
63+
").*"
64+
}
65+
}
66+
67+
/**
68+
* A regexp string to identify variables etc that might be safe because they
69+
* contain hashed or encrypted data, or are only a reference to data that is
70+
* actually stored elsewhere.
71+
*/
72+
private string regexpProbablySafe() { result = ".*(hash|crypt|file|path|invalid).*" }
73+
74+
/**
75+
* A `VarDecl` that might be used to contain sensitive data.
76+
*/
77+
private class SensitiveVarDecl extends VarDecl {
78+
SensitiveDataType sensitiveType;
79+
80+
SensitiveVarDecl() { this.getName().toLowerCase().regexpMatch(sensitiveType.getRegexp()) }
81+
82+
predicate hasInfo(string label, SensitiveDataType type) {
83+
label = this.getName() and
84+
sensitiveType = type
85+
}
86+
}
87+
88+
/**
89+
* An `AbstractFunctionDecl` that might be used to contain sensitive data.
90+
*/
91+
private class SensitiveFunctionDecl extends AbstractFunctionDecl {
92+
SensitiveDataType sensitiveType;
93+
94+
SensitiveFunctionDecl() { this.getName().toLowerCase().regexpMatch(sensitiveType.getRegexp()) }
95+
96+
predicate hasInfo(string label, SensitiveDataType type) {
97+
label = this.getName() and
98+
sensitiveType = type
99+
}
100+
}
101+
102+
/**
103+
* An `Argument` that might be used to contain sensitive data.
104+
*/
105+
private class SensitiveArgument extends Argument {
106+
SensitiveDataType sensitiveType;
107+
108+
SensitiveArgument() { this.getLabel().toLowerCase().regexpMatch(sensitiveType.getRegexp()) }
109+
110+
predicate hasInfo(string label, SensitiveDataType type) {
111+
label = this.getLabel() and
112+
sensitiveType = type
113+
}
114+
}
115+
116+
/**
117+
* An expression whose value might be sensitive data.
118+
*/
119+
class SensitiveExpr extends Expr {
120+
string label;
121+
SensitiveDataType sensitiveType;
122+
123+
SensitiveExpr() {
124+
// variable reference
125+
this.(DeclRefExpr).getDecl().(SensitiveVarDecl).hasInfo(label, sensitiveType)
126+
or
127+
// member variable reference
128+
this.(MemberRefExpr).getMember().(SensitiveVarDecl).hasInfo(label, sensitiveType)
129+
or
130+
// function call
131+
this.(ApplyExpr).getStaticTarget().(SensitiveFunctionDecl).hasInfo(label, sensitiveType)
132+
or
133+
// sensitive argument
134+
exists(SensitiveArgument a |
135+
a.hasInfo(label, sensitiveType) and
136+
a.getExpr() = this
137+
)
138+
}
139+
140+
/**
141+
* Gets the label associated with this expression.
142+
*/
143+
string getLabel() { result = label }
144+
145+
/**
146+
* Gets the type of sensitive expression this is.
147+
*/
148+
SensitiveDataType getSensitiveType() { result = sensitiveType }
149+
150+
/**
151+
* Holds if this sensitive expression might be safe because it contains
152+
* hashed or encrypted data, or is only a reference to data that is stored
153+
* elsewhere.
154+
*/
155+
predicate isProbablySafe() { label.toLowerCase().regexpMatch(regexpProbablySafe()) }
156+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
| testCoreData.swift:48:15:48:15 | password | label:password, type:credential |
2+
| testCoreData.swift:49:15:49:15 | password_hash | isProbablySafe, label:password_hash, type:credential |
3+
| testCoreData.swift:51:24:51:24 | password | label:password, type:credential |
4+
| testCoreData.swift:52:24:52:24 | password_hash | isProbablySafe, label:password_hash, type:credential |
5+
| testCoreData.swift:58:15:58:15 | password | label:password, type:credential |
6+
| testCoreData.swift:59:15:59:15 | password_file | isProbablySafe, label:password_file, type:credential |
7+
| testCoreData.swift:61:25:61:25 | password | label:password, type:credential |
8+
| testCoreData.swift:62:25:62:25 | password_file | isProbablySafe, label:password_file, type:credential |
9+
| testCoreData.swift:64:16:64:16 | password | label:password, type:credential |
10+
| testCoreData.swift:65:16:65:16 | password_file | isProbablySafe, label:password_file, type:credential |
11+
| testCoreData.swift:77:2:77:25 | call to doSomething(password:) | label:doSomething(password:), type:credential |
12+
| testCoreData.swift:77:24:77:24 | x | label:password, type:credential |
13+
| testCoreData.swift:80:10:80:22 | call to getPassword() | label:getPassword(), type:credential |
14+
| testCoreData.swift:85:15:85:17 | .password | label:password, type:credential |
15+
| testCoreData.swift:91:10:91:10 | passwd | label:passwd, type:credential |
16+
| testCoreData.swift:92:10:92:10 | passwd | label:passwd, type:credential |
17+
| testCoreData.swift:93:10:93:10 | passwd | label:passwd, type:credential |
18+
| testRealm.swift:34:11:34:11 | myPassword | label:myPassword, type:credential |
19+
| testRealm.swift:38:11:38:11 | myHashedPassword | isProbablySafe, label:myHashedPassword, type:credential |
20+
| testRealm.swift:42:11:42:11 | myPassword | label:myPassword, type:credential |
21+
| testRealm.swift:46:11:46:11 | myHashedPassword | isProbablySafe, label:myHashedPassword, type:credential |
22+
| testRealm.swift:52:12:52:12 | myPassword | label:myPassword, type:credential |
23+
| testRealm.swift:55:12:55:12 | myHashedPassword | isProbablySafe, label:myHashedPassword, type:credential |
24+
| testSend.swift:29:19:29:19 | passwordPlain | label:passwordPlain, type:credential |
25+
| testSend.swift:30:19:30:19 | passwordHash | isProbablySafe, label:passwordHash, type:credential |
26+
| testSend.swift:33:19:33:19 | passwordPlain | label:passwordPlain, type:credential |
27+
| testSend.swift:34:19:34:19 | passwordHash | isProbablySafe, label:passwordHash, type:credential |
28+
| testURL.swift:13:54:13:54 | passwd | label:passwd, type:credential |
29+
| testURL.swift:14:54:14:54 | encrypted_passwd | isProbablySafe, label:encrypted_passwd, type:credential |
30+
| testURL.swift:16:55:16:55 | credit_card_no | label:credit_card_no, type:private information |
31+
| testURL.swift:20:22:20:22 | passwd | label:passwd, type:credential |
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import swift
2+
import codeql.swift.security.SensitiveExprs
3+
4+
string describe(SensitiveExpr e) {
5+
result = "label:" + e.getLabel()
6+
or
7+
result = "type:" + e.getSensitiveType().toString()
8+
or
9+
e.isProbablySafe() and result = "isProbablySafe"
10+
}
11+
12+
from SensitiveExpr e
13+
where e.getFile().getBaseName() != ""
14+
select e, concat(describe(e), ", ")

0 commit comments

Comments
 (0)