Skip to content

Commit 549c9ee

Browse files
authored
Merge pull request github#5739 from RasmusWL/share-sensitive-data-modeling
Python/JS: Share sensitive data modeling
2 parents a877311 + dc4a0c1 commit 549c9ee

File tree

8 files changed

+323
-181
lines changed

8 files changed

+323
-181
lines changed

config/identical-files.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,5 +440,9 @@
440440
"CryptoAlgorithms Python/JS": [
441441
"javascript/ql/src/semmle/javascript/security/CryptoAlgorithms.qll",
442442
"python/ql/src/semmle/crypto/Crypto.qll"
443+
],
444+
"SensitiveDataHeuristics Python/JS": [
445+
"javascript/ql/src/semmle/javascript/security/internal/SensitiveDataHeuristics.qll",
446+
"python/ql/src/semmle/python/security/internal/SensitiveDataHeuristics.qll"
443447
]
444-
}
448+
}

javascript/ql/src/semmle/javascript/security/SensitiveActions.qll

Lines changed: 31 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -10,67 +10,7 @@
1010
*/
1111

1212
import javascript
13-
14-
/**
15-
* Provides heuristics for identifying names related to sensitive information.
16-
*
17-
* INTERNAL: Do not use directly.
18-
*/
19-
module HeuristicNames {
20-
/**
21-
* Gets a regular expression that identifies strings that may indicate the presence of secret
22-
* or trusted data.
23-
*/
24-
string maybeSecret() { result = "(?is).*((?<!is)secret|(?<!un|is)trusted).*" }
25-
26-
/**
27-
* Gets a regular expression that identifies strings that may indicate the presence of
28-
* user names or other account information.
29-
*/
30-
string maybeAccountInfo() {
31-
result = "(?is).*acc(ou)?nt.*" or
32-
result = "(?is).*(puid|username|userid).*"
33-
}
34-
35-
/**
36-
* Gets a regular expression that identifies strings that may indicate the presence of
37-
* a password or an authorization key.
38-
*/
39-
string maybePassword() {
40-
result = "(?is).*pass(wd|word|code|phrase)(?!.*question).*" or
41-
result = "(?is).*(auth(entication|ori[sz]ation)?)key.*"
42-
}
43-
44-
/**
45-
* Gets a regular expression that identifies strings that may indicate the presence of
46-
* a certificate.
47-
*/
48-
string maybeCertificate() { result = "(?is).*(cert)(?!.*(format|name)).*" }
49-
50-
/**
51-
* Gets a regular expression that identifies strings that may indicate the presence
52-
* of sensitive data, with `classification` describing the kind of sensitive data involved.
53-
*/
54-
string maybeSensitive(SensitiveExpr::Classification classification) {
55-
result = maybeSecret() and classification = SensitiveExpr::secret()
56-
or
57-
result = maybeAccountInfo() and classification = SensitiveExpr::id()
58-
or
59-
result = maybePassword() and classification = SensitiveExpr::password()
60-
or
61-
result = maybeCertificate() and classification = SensitiveExpr::certificate()
62-
}
63-
64-
/**
65-
* Gets a regular expression that identifies strings that may indicate the presence of data
66-
* that is hashed or encrypted, and hence rendered non-sensitive, or contains special characters
67-
* suggesting nouns within the string do not represent the meaning of the whole string (e.g. a URL or a SQL query).
68-
*/
69-
string notSensitive() {
70-
result = "(?is).*([^\\w$.-]|redact|censor|obfuscate|hash|md5|sha|((?<!un)(en))?(crypt|code)).*"
71-
}
72-
}
73-
13+
import semmle.javascript.security.internal.SensitiveDataHeuristics
7414
private import HeuristicNames
7515

7616
/** An expression that might contain sensitive data. */
@@ -79,56 +19,44 @@ abstract class SensitiveExpr extends Expr {
7919
abstract string describe();
8020

8121
/** Gets a classification of the kind of sensitive data this expression might contain. */
82-
abstract SensitiveExpr::Classification getClassification();
22+
abstract SensitiveDataClassification getClassification();
8323
}
8424

85-
module SensitiveExpr {
86-
/**
87-
* A classification of different kinds of sensitive data:
88-
*
89-
* - secret: generic secret or trusted data;
90-
* - id: a user name or other account information;
91-
* - password: a password or authorization key;
92-
* - certificate: a certificate.
93-
*
94-
* While classifications are represented as strings, this should not be relied upon.
95-
* Instead, use the predicates below to work with classifications.
96-
*/
97-
class Classification extends string {
98-
Classification() { this = "secret" or this = "id" or this = "password" or this = "certificate" }
99-
}
25+
/** DEPRECATED: Use `SensitiveDataClassification` and helpers instead. */
26+
deprecated module SensitiveExpr {
27+
/** DEPRECATED: Use `SensitiveDataClassification` instead. */
28+
deprecated class Classification = SensitiveDataClassification;
10029

101-
/** Gets the classification for secret or trusted data. */
102-
Classification secret() { result = "secret" }
30+
/** DEPRECATED: Use `SensitiveDataClassification::secret` instead. */
31+
deprecated predicate secret = SensitiveDataClassification::secret/0;
10332

104-
/** Gets the classification for user names or other account information. */
105-
Classification id() { result = "id" }
33+
/** DEPRECATED: Use `SensitiveDataClassification::id` instead. */
34+
deprecated predicate id = SensitiveDataClassification::id/0;
10635

107-
/** Gets the classification for passwords or authorization keys. */
108-
Classification password() { result = "password" }
36+
/** DEPRECATED: Use `SensitiveDataClassification::password` instead. */
37+
deprecated predicate password = SensitiveDataClassification::password/0;
10938

110-
/** Gets the classification for certificates. */
111-
Classification certificate() { result = "certificate" }
39+
/** DEPRECATED: Use `SensitiveDataClassification::certificate` instead. */
40+
deprecated predicate certificate = SensitiveDataClassification::certificate/0;
11241
}
11342

11443
/** A function call that might produce sensitive data. */
11544
class SensitiveCall extends SensitiveExpr, InvokeExpr {
116-
SensitiveExpr::Classification classification;
45+
SensitiveDataClassification classification;
11746

11847
SensitiveCall() {
11948
classification = this.getCalleeName().(SensitiveDataFunctionName).getClassification()
12049
or
12150
// This is particularly to pick up methods with an argument like "password", which
12251
// may indicate a lookup.
12352
exists(string s | this.getAnArgument().mayHaveStringValue(s) |
124-
s.regexpMatch(maybeSensitive(classification)) and
125-
not s.regexpMatch(notSensitive())
53+
nameIndicatesSensitiveData(s, classification)
12654
)
12755
}
12856

12957
override string describe() { result = "a call to " + getCalleeName() }
13058

131-
override SensitiveExpr::Classification getClassification() { result = classification }
59+
override SensitiveDataClassification getClassification() { result = classification }
13260
}
13361

13462
/** An access to a variable or property that might contain sensitive data. */
@@ -152,13 +80,10 @@ abstract class SensitiveWrite extends DataFlow::Node { }
15280

15381
/** A write to a variable or property that might contain sensitive data. */
15482
private class BasicSensitiveWrite extends SensitiveWrite {
155-
SensitiveExpr::Classification classification;
83+
SensitiveDataClassification classification;
15684

15785
BasicSensitiveWrite() {
158-
exists(string name |
159-
name.regexpMatch(maybeSensitive(classification)) and
160-
not name.regexpMatch(notSensitive())
161-
|
86+
exists(string name | nameIndicatesSensitiveData(name, classification) |
16287
exists(DataFlow::PropWrite pwn |
16388
pwn.getPropertyName() = name and
16489
pwn.getRhs() = this
@@ -173,18 +98,16 @@ private class BasicSensitiveWrite extends SensitiveWrite {
17398
}
17499

175100
/** Gets a classification of the kind of sensitive data the write might handle. */
176-
SensitiveExpr::Classification getClassification() { result = classification }
101+
SensitiveDataClassification getClassification() { result = classification }
177102
}
178103

179104
/** An access to a variable or property that might contain sensitive data. */
180105
private class BasicSensitiveVariableAccess extends SensitiveVariableAccess {
181-
SensitiveExpr::Classification classification;
106+
SensitiveDataClassification classification;
182107

183-
BasicSensitiveVariableAccess() {
184-
name.regexpMatch(maybeSensitive(classification)) and not name.regexpMatch(notSensitive())
185-
}
108+
BasicSensitiveVariableAccess() { nameIndicatesSensitiveData(name, classification) }
186109

187-
override SensitiveExpr::Classification getClassification() { result = classification }
110+
override SensitiveDataClassification getClassification() { result = classification }
188111
}
189112

190113
/** A function name that suggests it may be sensitive. */
@@ -199,16 +122,16 @@ abstract class SensitiveFunctionName extends string {
199122
/** A function name that suggests it may produce sensitive data. */
200123
abstract class SensitiveDataFunctionName extends SensitiveFunctionName {
201124
/** Gets a classification of the kind of sensitive data this function may produce. */
202-
abstract SensitiveExpr::Classification getClassification();
125+
abstract SensitiveDataClassification getClassification();
203126
}
204127

205128
/** A method that might return sensitive data, based on the name. */
206129
class CredentialsFunctionName extends SensitiveDataFunctionName {
207-
SensitiveExpr::Classification classification;
130+
SensitiveDataClassification classification;
208131

209-
CredentialsFunctionName() { this.regexpMatch(maybeSensitive(classification)) }
132+
CredentialsFunctionName() { nameIndicatesSensitiveData(this, classification) }
210133

211-
override SensitiveExpr::Classification getClassification() { result = classification }
134+
override SensitiveDataClassification getClassification() { result = classification }
212135
}
213136

214137
/**
@@ -240,11 +163,13 @@ class ProtectCall extends DataFlow::CallNode {
240163

241164
/** An expression that might contain a clear-text password. */
242165
class CleartextPasswordExpr extends SensitiveExpr {
243-
CleartextPasswordExpr() { this.(SensitiveExpr).getClassification() = SensitiveExpr::password() }
166+
CleartextPasswordExpr() {
167+
this.(SensitiveExpr).getClassification() = SensitiveDataClassification::password()
168+
}
244169

245170
override string describe() { none() }
246171

247-
override SensitiveExpr::Classification getClassification() { none() }
172+
override SensitiveDataClassification getClassification() { none() }
248173
}
249174

250175
/**

javascript/ql/src/semmle/javascript/security/dataflow/CleartextLoggingCustomizations.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ module CleartextLogging {
5454
*/
5555
private class NameGuidedNonCleartextPassword extends NonCleartextPassword {
5656
NameGuidedNonCleartextPassword() {
57-
exists(string name | name.regexpMatch(notSensitive()) |
57+
exists(string name | name.regexpMatch(notSensitiveRegexp()) |
5858
this.asExpr().(VarAccess).getName() = name
5959
or
6060
this.(DataFlow::PropRead).getPropertyName() = name
@@ -94,7 +94,7 @@ module CleartextLogging {
9494
* A call that might obfuscate a password, for example through hashing.
9595
*/
9696
private class ObfuscatorCall extends Barrier, DataFlow::InvokeNode {
97-
ObfuscatorCall() { getCalleeName().regexpMatch(notSensitive()) }
97+
ObfuscatorCall() { getCalleeName().regexpMatch(notSensitiveRegexp()) }
9898
}
9999

100100
/**
@@ -113,7 +113,7 @@ module CleartextLogging {
113113
ObjectPasswordPropertySource() {
114114
exists(DataFlow::PropWrite write |
115115
name.regexpMatch(maybePassword()) and
116-
not name.regexpMatch(notSensitive()) and
116+
not name.regexpMatch(notSensitiveRegexp()) and
117117
write = this.(DataFlow::SourceNode).getAPropertyWrite(name) and
118118
// avoid safe values assigned to presumably unsafe names
119119
not write.getRhs() instanceof NonCleartextPassword

javascript/ql/src/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ module CleartextStorage {
3535

3636
SensitiveExprSource() {
3737
// storing user names or account names in plaintext isn't usually a problem
38-
astNode.getClassification() != SensitiveExpr::id()
38+
astNode.getClassification() != SensitiveDataClassification::id()
3939
}
4040

4141
override string describe() { result = astNode.describe() }

javascript/ql/src/semmle/javascript/security/dataflow/InsufficientPasswordHashCustomizations.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ module InsufficientPasswordHash {
3434
class CleartextPasswordSource extends Source, DataFlow::ValueNode {
3535
override SensitiveExpr astNode;
3636

37-
CleartextPasswordSource() { astNode.getClassification() = SensitiveExpr::password() }
37+
CleartextPasswordSource() {
38+
astNode.getClassification() = SensitiveDataClassification::password()
39+
}
3840

3941
override string describe() { result = astNode.describe() }
4042
}

0 commit comments

Comments
 (0)