Skip to content

Commit af01a05

Browse files
joke1196sonartech
authored andcommitted
SONARPY-3974 Fix serializer test and migrated HardcodedCredentials rule to typeV2 to properly raise on pg.DB (#1009)
GitOrigin-RevId: 1336278bad4790a29c57f4c69e363ac82002d996
1 parent 9e64039 commit af01a05

File tree

8 files changed

+106
-39
lines changed

8 files changed

+106
-39
lines changed

python-checks/src/main/java/org/sonar/python/checks/hotspots/HardCodedCredentialsCheck.java

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,7 @@
2121
import java.net.URISyntaxException;
2222
import java.net.URL;
2323
import java.util.Collection;
24-
import java.util.Collections;
25-
import java.util.HashMap;
2624
import java.util.List;
27-
import java.util.Map;
2825
import java.util.Optional;
2926
import java.util.regex.Pattern;
3027
import java.util.stream.Collectors;
@@ -74,32 +71,28 @@ public class HardCodedCredentialsCheck extends PythonSubscriptionCheck {
7471
private static final TypeMatcher ENVIRON_MATCHER = TypeMatchers.isType("os.environ");
7572
private static final TypeMatcher GET_ENVIRON_MATCHER = TypeMatchers.isType("typing.Mapping.get");
7673

74+
private static final TypeMatcher SENSITIVE_ARG_2_MATCHER = TypeMatchers.any(
75+
TypeMatchers.isType("mysql.connector.connect"),
76+
TypeMatchers.isType("mysql.connector.connection.MySQLConnection"),
77+
TypeMatchers.isType("pymysql.connect"),
78+
TypeMatchers.isType("pymysql.connections.connect"),
79+
TypeMatchers.isType("pymysql.connections.Connection"),
80+
TypeMatchers.isType("psycopg2.connect"),
81+
// pgdb.connect is a module whose connect function has FQN pgdb.connect.connect.
82+
// isType can't resolve it through the type table, so use withFQN to match on the FQN directly.
83+
TypeMatchers.withFQN("pgdb.connect.connect"));
84+
85+
private static final TypeMatcher SENSITIVE_ARG_5_MATCHER = TypeMatchers.any(
86+
TypeMatchers.isType("pg.DB"),
87+
TypeMatchers.isType("pg.connect"));
88+
7789
@RuleProperty(
7890
key = "credentialWords",
7991
description = "Comma separated list of words identifying potential credentials",
8092
defaultValue = DEFAULT_CREDENTIAL_WORDS)
8193
public String credentialWords = DEFAULT_CREDENTIAL_WORDS;
8294
private List<Pattern> variablePatterns = null;
8395
private List<Pattern> literalPatterns = null;
84-
private Map<String, Integer> sensitiveArgumentByFQN;
85-
86-
private Map<String, Integer> sensitiveArgumentByFQN() {
87-
if (sensitiveArgumentByFQN == null) {
88-
sensitiveArgumentByFQN = new HashMap<>();
89-
sensitiveArgumentByFQN.put("mysql.connector.connect", 2);
90-
sensitiveArgumentByFQN.put("mysql.connector.connection.MySQLConnection", 2);
91-
sensitiveArgumentByFQN.put("pymysql.connect", 2);
92-
sensitiveArgumentByFQN.put("pymysql.connections.connect", 2);
93-
sensitiveArgumentByFQN.put("pymysql.connections.Connection", 2);
94-
sensitiveArgumentByFQN.put("psycopg2.connect", 2);
95-
sensitiveArgumentByFQN.put("pgdb.connect", 2);
96-
sensitiveArgumentByFQN.put("pgdb.connect.connect", 2);
97-
sensitiveArgumentByFQN.put("pg.DB", 5);
98-
sensitiveArgumentByFQN.put("pg.connect", 5);
99-
sensitiveArgumentByFQN = Collections.unmodifiableMap(sensitiveArgumentByFQN);
100-
}
101-
return sensitiveArgumentByFQN;
102-
}
10396

10497
private Stream<Pattern> variablePatterns() {
10598
if (variablePatterns == null) {
@@ -175,13 +168,15 @@ private void handleRegularArgument(RegularArgument regularArgument, Subscription
175168
}
176169
}
177170

178-
private void handleCallExpression(CallExpression callExpression, SubscriptionContext ctx) {
171+
private static void handleCallExpression(CallExpression callExpression, SubscriptionContext ctx) {
179172
if (callExpression.arguments().isEmpty()) {
180173
return;
181174
}
182-
Symbol calleeSymbol = callExpression.calleeSymbol();
183-
if (calleeSymbol != null && sensitiveArgumentByFQN().containsKey(calleeSymbol.fullyQualifiedName())) {
184-
checkSensitiveArgument(callExpression, sensitiveArgumentByFQN().get(calleeSymbol.fullyQualifiedName()), ctx);
175+
Expression callee = callExpression.callee();
176+
if (SENSITIVE_ARG_2_MATCHER.isTrueFor(callee, ctx)) {
177+
checkSensitiveArgument(callExpression, 2, ctx);
178+
} else if (SENSITIVE_ARG_5_MATCHER.isTrueFor(callee, ctx)) {
179+
checkSensitiveArgument(callExpression, 5, ctx);
185180
}
186181
}
187182

python-frontend/src/main/resources/org/sonar/python/types/custom_protobuf/pg.connection.protobuf

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
11

2-
pg.connection�
2+
pg.connection�
33

4-
Connectionpg.connection.Connection"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
4+
Connectionpg.connection.Connection"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
5+
__init__!pg.connection.Connection.__init__"
6+
None*>
7+
self4
8+
pg.connection.Connection"pg.connection.Connection*R
9+
dbnameD
10+
Union[builtins.str,None]
11+
builtins.str" builtins.str
12+
None *P
13+
hostD
14+
Union[builtins.str,None]
15+
builtins.str" builtins.str
16+
None *(
17+
port
18+
builtins.int" builtins.int *O
19+
optD
20+
Union[builtins.str,None]
21+
builtins.str" builtins.str
22+
None *P
23+
userD
24+
Union[builtins.str,None]
25+
builtins.str" builtins.str
26+
None *R
27+
passwdD
28+
Union[builtins.str,None]
29+
builtins.str" builtins.str
30+
None *,
31+
nowait
32+
builtins.bool"builtins.bool *�
533
querypg.connection.Connection.query"
634
None*>
735
self4

python-frontend/src/main/resources/org/sonar/python/types/custom_protobuf/pg.db.protobuf

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

2-
pg.db�
3-
DBpg.db.DB"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
2+
pg.db�
3+
DBpg.db.DB"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
44
__init__pg.db.DB.__init__"
55
None*
66
self
@@ -26,7 +26,9 @@
2626
passwdD
2727
Union[builtins.str,None]
2828
builtins.str" builtins.str
29-
None *
29+
None *,
30+
nowait
31+
builtins.bool"builtins.bool *
3032
querypg.db.DB.query"
3133
None*
3234
self

python-frontend/src/main/resources/org/sonar/python/types/custom_protobuf/pg.protobuf

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,35 @@
11

2-
pg�
2+
pg�
33

4-
Connectionpg.connection.Connection"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
4+
Connectionpg.connection.Connection"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
5+
__init__!pg.connection.Connection.__init__"
6+
None*>
7+
self4
8+
pg.connection.Connection"pg.connection.Connection*R
9+
dbnameD
10+
Union[builtins.str,None]
11+
builtins.str" builtins.str
12+
None *P
13+
hostD
14+
Union[builtins.str,None]
15+
builtins.str" builtins.str
16+
None *(
17+
port
18+
builtins.int" builtins.int *O
19+
optD
20+
Union[builtins.str,None]
21+
builtins.str" builtins.str
22+
None *P
23+
userD
24+
Union[builtins.str,None]
25+
builtins.str" builtins.str
26+
None *R
27+
passwdD
28+
Union[builtins.str,None]
29+
builtins.str" builtins.str
30+
None *,
31+
nowait
32+
builtins.bool"builtins.bool *�
533
querypg.connection.Connection.query"
634
None*>
735
self4
@@ -13,8 +41,8 @@ Connectionpg.connection.Connection"*SonarPythonAnalyzerFakeStub.CustomStubBase
1341
closepg.connection.Connection.close"
1442
None*>
1543
self4
16-
pg.connection.Connection"pg.connection.Connection�
17-
DBpg.db.DB"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
44+
pg.connection.Connection"pg.connection.Connection�
45+
DBpg.db.DB"*SonarPythonAnalyzerFakeStub.CustomStubBase*�
1846
__init__pg.db.DB.__init__"
1947
None*
2048
self
@@ -40,7 +68,9 @@ Connectionpg.connection.Connection"*SonarPythonAnalyzerFakeStub.CustomStubBase
4068
passwdD
4169
Union[builtins.str,None]
4270
builtins.str" builtins.str
43-
None *
71+
None *,
72+
nowait
73+
builtins.bool"builtins.bool *
4474
querypg.db.DB.query"
4575
None*
4676
self
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
0e133142aff9e9d8fee6f4c7205e699f7865502ae3921345895cc814acf0e877
2-
c137c6a178c97aca6334c983fabbed631672e730554ae8ee66b0aff1949cc90a
1+
774d53e2cc547be2fbc3e6fc9a9b5b4fadfe72eb7f7fd3444a4566d9110a0a7f
2+
5c09d20b8cbc107906625c2325a16ebe4e2795c0f866ef3a2c636a0e23488018
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
from SonarPythonAnalyzerFakeStub import CustomStubBase
2+
from typing import Optional
23

34
class Connection(CustomStubBase):
5+
def __init__(
6+
self,
7+
dbname: Optional[str] = None,
8+
host: Optional[str] = None,
9+
port: int = -1,
10+
opt: Optional[str] = None,
11+
user: Optional[str] = None,
12+
passwd: Optional[str] = None,
13+
nowait: bool = False,
14+
) -> None: ...
415
def query(self, command: str, *args) -> None: ...
516
def close(self) -> None: ...

python-frontend/typeshed_serializer/resources/custom/pg/db.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ class DB(CustomStubBase):
1010
opt: Optional[str] = None,
1111
user: Optional[str] = None,
1212
passwd: Optional[str] = None,
13+
nowait: bool = False,
1314
) -> None: ...
1415
def query(self, command: str, *args) -> None: ...
1516
def close(self) -> None: ...

python-frontend/typeshed_serializer/tests/test_serializers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def test_custom_stubs_serializer(typeshed_custom_stubs):
7474
custom_stubs_serializer.serialize()
7575
assert custom_stubs_serializer.get_build_result.call_count == 1
7676
# Not every files from "typeshed_custom_stubs" build are serialized, as some are builtins
77-
assert symbols.save_module.call_count == 344
77+
assert symbols.save_module.call_count == 347
7878

7979

8080
def test_importer_serializer():

0 commit comments

Comments
 (0)