Skip to content

Commit adb7790

Browse files
guillaume-dequennesonartech
authored andcommitted
SONARPY-1683 S5547 TLS cipher suite: support urllib3 SSLContext (#204)
GitOrigin-RevId: 45f0086dd063a2e81d7ff517d7d3ead814a1b81f
1 parent 2880594 commit adb7790

50 files changed

Lines changed: 1227 additions & 997 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

python-checks/src/main/java/org/sonar/python/checks/RobustCipherAlgorithmCheck.java

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.sonar.python.checks;
1818

1919
import java.util.LinkedHashSet;
20+
import java.util.List;
2021
import java.util.Optional;
2122
import java.util.Set;
2223
import java.util.function.Predicate;
@@ -34,17 +35,20 @@
3435
import org.sonar.plugins.python.api.tree.RegularArgument;
3536
import org.sonar.plugins.python.api.tree.StringLiteral;
3637
import org.sonar.plugins.python.api.tree.Tree;
38+
import org.sonar.plugins.python.api.types.v2.PythonType;
39+
import org.sonar.plugins.python.api.types.v2.TriBool;
3740
import org.sonar.python.checks.utils.Expressions;
3841
import org.sonar.python.tree.StringLiteralImpl;
3942
import org.sonar.python.tree.TreeUtils;
43+
import org.sonar.python.types.v2.TypeCheckBuilder;
4044

4145
// https://jira.sonarsource.com/browse/RSPEC-5547 (general)
4246
// https://jira.sonarsource.com/browse/RSPEC-5552 (python-specific)
4347
@Rule(key = "S5547")
4448
public class RobustCipherAlgorithmCheck extends PythonSubscriptionCheck {
4549

4650
private static final String MESSAGE = "Use a strong cipher algorithm.";
47-
private static final Set<String> INSECURE_CIPHERS_PREFIXES = Set.of("cryptography.hazmat.decrepit.ciphers.");
51+
private static final String INSECURE_CIPHERS_PREFIX = "cryptography.hazmat.decrepit.ciphers.";
4852
private static final Set<String> INSECURE_CIPHERS = Set.of(
4953
"NULL",
5054
"aNULL",
@@ -98,25 +102,40 @@ public class RobustCipherAlgorithmCheck extends PythonSubscriptionCheck {
98102
"pyDes.triple_des"
99103
);
100104

105+
private List<TypeCheckBuilder> sensitiveCalleesTypeCheckers;
106+
private List<TypeCheckBuilder> sslSetCipherTypeCheckers;
107+
private TypeCheckBuilder urllibSslContextTypeChecker;
101108

102109

103110
@Override
104111
public void initialize(Context context) {
105-
context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, RobustCipherAlgorithmCheck::checkCallExpression);
112+
context.registerSyntaxNodeConsumer(Tree.Kind.FILE_INPUT, ctx -> {
113+
sslSetCipherTypeCheckers = SSL_SET_CIPHERS_FQN.stream()
114+
.map(fqn -> ctx.typeChecker().typeCheckBuilder().isTypeWithFqn(fqn))
115+
.toList();
116+
sensitiveCalleesTypeCheckers = SENSITIVE_CALLEE_FQNS.stream()
117+
.map(fqn -> ctx.typeChecker().typeCheckBuilder().isTypeWithFqn(fqn))
118+
.toList();
119+
urllibSslContextTypeChecker = ctx.typeChecker().typeCheckBuilder().isTypeWithFqn("urllib3.util.ssl_.create_urllib3_context");
120+
});
121+
context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, this::checkCallExpression);
106122
}
107123

108-
private static void checkCallExpression(SubscriptionContext subscriptionContext) {
124+
private void checkCallExpression(SubscriptionContext subscriptionContext) {
109125
CallExpression callExpr = (CallExpression) subscriptionContext.syntaxNode();
110-
Optional.of(callExpr)
111-
.map(CallExpression::calleeSymbol)
112-
.map(Symbol::fullyQualifiedName)
113-
.ifPresent(fullyQualifiedName -> {
114-
if (SENSITIVE_CALLEE_FQNS.contains(fullyQualifiedName) || INSECURE_CIPHERS_PREFIXES.stream().anyMatch(fullyQualifiedName::startsWith)) {
115-
subscriptionContext.addIssue(callExpr.callee(), MESSAGE);
116-
} else if (SSL_SET_CIPHERS_FQN.contains(fullyQualifiedName)) {
117-
checkForInsecureCiphers(subscriptionContext, callExpr);
118-
}
119-
});
126+
PythonType calleeType = callExpr.callee().typeV2();
127+
String fullyQualifiedName = Optional.ofNullable(callExpr.calleeSymbol()).map(Symbol::fullyQualifiedName).orElse("");
128+
if (fullyQualifiedName.startsWith(INSECURE_CIPHERS_PREFIX)) {
129+
subscriptionContext.addIssue(callExpr.callee(), MESSAGE);
130+
} else if (sensitiveCalleesTypeCheckers.stream().anyMatch(checker -> checker.check(calleeType) == TriBool.TRUE)) {
131+
subscriptionContext.addIssue(callExpr.callee(), MESSAGE);
132+
} else if (sslSetCipherTypeCheckers.stream().anyMatch(checker -> checker.check(calleeType) == TriBool.TRUE)) {
133+
checkForInsecureCiphers(subscriptionContext, callExpr);
134+
} else if (urllibSslContextTypeChecker.check(calleeType) == TriBool.TRUE) {
135+
Optional.ofNullable(TreeUtils.nthArgumentOrKeyword(4, "ciphers", callExpr.arguments()))
136+
.map(RegularArgument::expression)
137+
.ifPresent(expression -> processCiphersArgument(subscriptionContext, callExpr, expression));
138+
}
120139
}
121140

122141
private static void checkForInsecureCiphers(SubscriptionContext ctx, CallExpression callExpression) {
@@ -125,18 +144,22 @@ private static void checkForInsecureCiphers(SubscriptionContext ctx, CallExpress
125144
.map(list -> list.get(0))
126145
.flatMap(TreeUtils.toOptionalInstanceOfMapper(RegularArgument.class))
127146
.map(RegularArgument::expression)
128-
.map(RobustCipherAlgorithmCheck::unpackArgument)
129-
.ifPresent(stringLiteral -> Optional.of(stringLiteral.trimmedQuotesValue())
130-
.map(RobustCipherAlgorithmCheck::findInsecureCiphers)
131-
.filter(Predicate.not(Set::isEmpty))
132-
.ifPresent(insecureCiphers -> {
133-
var secondaryMessage = insecureCiphers.size() > 1 ? "The following cipher strings are insecure: " :
134-
"The following cipher string is insecure: ";
135-
secondaryMessage = insecureCiphers.stream().collect(Collectors.joining("`, `", secondaryMessage + "`", "`"));
147+
.ifPresent(expression -> processCiphersArgument(ctx, callExpression, expression));
148+
}
136149

137-
ctx.addIssue(callExpression.callee(), MESSAGE)
138-
.secondary(stringLiteral, secondaryMessage);
139-
}));
150+
private static void processCiphersArgument(SubscriptionContext ctx, CallExpression callExpression, Expression expression) {
151+
StringLiteral stringLiteral = unpackArgument(expression);
152+
Optional.ofNullable(stringLiteral)
153+
.map(StringLiteral::trimmedQuotesValue)
154+
.map(RobustCipherAlgorithmCheck::findInsecureCiphers)
155+
.filter(Predicate.not(Set::isEmpty))
156+
.ifPresent(insecureCiphers -> {
157+
var secondaryMessage = insecureCiphers.size() > 1 ? "The following cipher strings are insecure: " :
158+
"The following cipher string is insecure: ";
159+
secondaryMessage = insecureCiphers.stream().collect(Collectors.joining("`, `", secondaryMessage + "`", "`"));
160+
ctx.addIssue(callExpression.callee(), MESSAGE)
161+
.secondary(stringLiteral, secondaryMessage);
162+
});
140163
}
141164

142165
@CheckForNull

python-checks/src/test/resources/checks/robustCipherAlgorithm.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ def pyssl_examples():
134134
ctx.set_ciphers(ciphers6) # Noncompliant
135135
# ^^^^^^^^^^^^^^^
136136

137+
context = ssl.create_default_context()
138+
context.set_ciphers("ECDH+3DES:DH+3DES:RSA+HIGH:RSA+3DES") # Noncompliant
139+
137140
def py_open_ssl_examples():
138141
import socket
139142
from OpenSSL import SSL
@@ -147,6 +150,14 @@ def py_open_ssl_examples():
147150
ctx.set_cipher_list(ciphers2) # Noncompliant
148151
# ^^^^^^^^^^^^^^^^^^^
149152

153+
154+
def urllib3_ssl_context():
155+
import urllib3
156+
ctx = urllib3.util.ssl_.create_urllib3_context()
157+
ctx.set_ciphers("ECDH+3DES:DH+3DES:RSA+HIGH:RSA+3DES") # Noncompliant
158+
159+
urllib3.util.ssl_.create_urllib3_context(ciphers="ECDH+3DES:DH+3DES:RSA+HIGH:RSA+3DES") # Noncompliant
160+
150161
def pycryptodome_compliant():
151162
from Crypto.Cipher import AES
152163
key = b'Sixteen byte key'
@@ -184,4 +195,3 @@ def pyssl_compliant(unknown_cipher):
184195

185196
ctx3 = ssl.create_default_context()
186197
ctx3.set_ciphers("DEFAULT:!eNULL:!aNULL:!MD5") # Compliant
187-

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@
1818
builtins.dict[builtins.str,Any]
1919
builtins.str" builtins.str
2020
Any"builtins.dict*$
21-
poolmanagerurllib3.poolmanager 
21+
poolmanagerurllib3.poolmanager *
22+
util urllib3.util 

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
urllib3.util*m
3+
__path__urllib3.util.__path__J
4+
builtins.list[builtins.str]
5+
builtins.str" builtins.str"builtins.list*�
6+
__annotations__urllib3.util.__annotations__W
7+
builtins.dict[builtins.str,Any]
8+
builtins.str" builtins.str
9+
Any"builtins.dict*
10+
ssl_urllib3.util.ssl_ 

python-frontend/src/main/resources/org/sonar/python/types/custom_protobuf/urllib3.util.ssl_.protobuf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
urllib3.util.ssl_d
3+
create_urllib3_context(urllib3.util.ssl_.create_urllib3_context"
4+
ssl.SSLContext"ssl.SSLContext*�
5+
__annotations__!urllib3.util.ssl_.__annotations__W
6+
builtins.dict[builtins.str,Any]
7+
builtins.str" builtins.str
8+
Any"builtins.dict

python-frontend/src/main/resources/org/sonar/python/types/third_party_protobuf/Xlib.ext.composite.protobuf

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
UnredirectSubindows&Xlib.ext.composite.UnredirectSubindows"Xlib.protocol.rq.Requestj38j39j310j311j312j313�
88
CreateRegionFromBorderClip-Xlib.ext.composite.CreateRegionFromBorderClip"Xlib.protocol.rq.Requestj38j39j310j311j312j313m
99
NameWindowPixmap#Xlib.ext.composite.NameWindowPixmap"Xlib.protocol.rq.Requestj38j39j310j311j312j313r
10-
GetOverlayWindow#Xlib.ext.composite.GetOverlayWindow"Xlib.protocol.rq.ReplyRequestj38j39j310j311j312j313�
10+
GetOverlayWindow#Xlib.ext.composite.GetOverlayWindow"Xlib.protocol.rq.ReplyRequestj38j39j310j311j312j313�
1111
query_version Xlib.ext.composite.query_version"B
12-
Xlib.ext.composite.QueryVersion"Xlib.ext.composite.QueryVersion*�
13-
self�
14-
:Union[Xlib.display.Display,Xlib.xobject.resource.Resource],
15-
Xlib.display.Display"Xlib.display.Display@
16-
Xlib.xobject.resource.Resource"Xlib.xobject.resource.Resourcez38z39z310z311z312z313�
12+
Xlib.ext.composite.QueryVersion"Xlib.ext.composite.QueryVersion*d
13+
selfZ
14+
Union[Xlib.display.Display,Any],
15+
Xlib.display.Display"Xlib.display.Display
16+
Anyz38z39z310z311z312z313�
1717
redirect_window"Xlib.ext.composite.redirect_window"
18-
None*F
19-
self<
20-
Xlib.xobject.drawable.Window"Xlib.xobject.drawable.Window*�
18+
None*
19+
self
20+
Any*�
2121
update�
2222
*TypeAlias[CallableType[builtins.function]]K
2323
CallableType[builtins.function]&
@@ -27,11 +27,11 @@
2727
*TypeAlias[CallableType[builtins.function]]K
2828
CallableType[builtins.function]&
2929
builtins.function"builtins.function"Xlib._typing.ErrorHandler
30-
None z38z39z310z311z312z313�
30+
None z38z39z310z311z312z313�
3131
redirect_subwindows&Xlib.ext.composite.redirect_subwindows"
32-
None*F
33-
self<
34-
Xlib.xobject.drawable.Window"Xlib.xobject.drawable.Window*�
32+
None*
33+
self
34+
Any*�
3535
update�
3636
*TypeAlias[CallableType[builtins.function]]K
3737
CallableType[builtins.function]&
@@ -41,11 +41,11 @@
4141
*TypeAlias[CallableType[builtins.function]]K
4242
CallableType[builtins.function]&
4343
builtins.function"builtins.function"Xlib._typing.ErrorHandler
44-
None z38z39z310z311z312z313�
44+
None z38z39z310z311z312z313�
4545
unredirect_window$Xlib.ext.composite.unredirect_window"
46-
None*F
47-
self<
48-
Xlib.xobject.drawable.Window"Xlib.xobject.drawable.Window*�
46+
None*
47+
self
48+
Any*�
4949
update�
5050
*TypeAlias[CallableType[builtins.function]]K
5151
CallableType[builtins.function]&
@@ -55,11 +55,11 @@
5555
*TypeAlias[CallableType[builtins.function]]K
5656
CallableType[builtins.function]&
5757
builtins.function"builtins.function"Xlib._typing.ErrorHandler
58-
None z38z39z310z311z312z313�
58+
None z38z39z310z311z312z313�
5959
unredirect_subwindows(Xlib.ext.composite.unredirect_subwindows"
60-
None*F
61-
self<
62-
Xlib.xobject.drawable.Window"Xlib.xobject.drawable.Window*�
60+
None*
61+
self
62+
Any*�
6363
update�
6464
*TypeAlias[CallableType[builtins.function]]K
6565
CallableType[builtins.function]&
@@ -69,23 +69,23 @@
6969
*TypeAlias[CallableType[builtins.function]]K
7070
CallableType[builtins.function]&
7171
builtins.function"builtins.function"Xlib._typing.ErrorHandler
72-
None z38z39z310z311z312z313�
72+
None z38z39z310z311z312z313�
7373
create_region_from_border_clip1Xlib.ext.composite.create_region_from_border_clip"
74-
builtins.int" builtins.int*F
75-
self<
76-
Xlib.xobject.drawable.Window"Xlib.xobject.drawable.Window*�
74+
builtins.int" builtins.int*
75+
self
76+
Any*�
7777
onerror�
7878
6Union[TypeAlias[CallableType[builtins.function]],None]�
7979
*TypeAlias[CallableType[builtins.function]]K
8080
CallableType[builtins.function]&
8181
builtins.function"builtins.function"Xlib._typing.ErrorHandler
82-
None z38z39z310z311z312z313�
83-
name_window_pixmap%Xlib.ext.composite.name_window_pixmap"<
84-
Xlib.xobject.drawable.Pixmap"Xlib.xobject.drawable.Pixmap*�
85-
self�
86-
:Union[Xlib.display.Display,Xlib.xobject.resource.Resource],
87-
Xlib.display.Display"Xlib.display.Display@
88-
Xlib.xobject.resource.Resource"Xlib.xobject.resource.Resource*�
82+
None z38z39z310z311z312z313�
83+
name_window_pixmap%Xlib.ext.composite.name_window_pixmap"
84+
Any*d
85+
selfZ
86+
Union[Xlib.display.Display,Any],
87+
Xlib.display.Display"Xlib.display.Display
88+
Any*�
8989
onerror�
9090
6Union[TypeAlias[CallableType[builtins.function]],None]�
9191
*TypeAlias[CallableType[builtins.function]]K

python-frontend/src/main/resources/org/sonar/python/types/third_party_protobuf/Xlib.ext.damage.protobuf

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,48 +6,48 @@
66
DamageDestroyXlib.ext.damage.DamageDestroy"Xlib.protocol.rq.Requestj38j39j310j311j312j313f
77
DamageSubtractXlib.ext.damage.DamageSubtract"Xlib.protocol.rq.Requestj38j39j310j311j312j313\
88
DamageAddXlib.ext.damage.DamageAdd"Xlib.protocol.rq.Requestj38j39j310j311j312j313`
9-
DamageNotifyXlib.ext.damage.DamageNotify"Xlib.protocol.rq.Eventj38j39j310j311j312j313�
9+
DamageNotifyXlib.ext.damage.DamageNotify"Xlib.protocol.rq.Eventj38j39j310j311j312j313�
1010
query_versionXlib.ext.damage.query_version"<
11-
Xlib.ext.damage.QueryVersion"Xlib.ext.damage.QueryVersion*�
12-
self�
13-
:Union[Xlib.display.Display,Xlib.xobject.resource.Resource],
14-
Xlib.display.Display"Xlib.display.Display@
15-
Xlib.xobject.resource.Resource"Xlib.xobject.resource.Resourcez38z39z310z311z312z313�
11+
Xlib.ext.damage.QueryVersion"Xlib.ext.damage.QueryVersion*d
12+
selfZ
13+
Union[Xlib.display.Display,Any],
14+
Xlib.display.Display"Xlib.display.Display
15+
Anyz38z39z310z311z312z313�
1616
damage_createXlib.ext.damage.damage_create"
17-
builtins.int" builtins.int*�
18-
self�
19-
:Union[Xlib.display.Display,Xlib.xobject.resource.Resource],
20-
Xlib.display.Display"Xlib.display.Display@
21-
Xlib.xobject.resource.Resource"Xlib.xobject.resource.Resource*'
17+
builtins.int" builtins.int*d
18+
selfZ
19+
Union[Xlib.display.Display,Any],
20+
Xlib.display.Display"Xlib.display.Display
21+
Any*'
2222
level
23-
builtins.int" builtins.intz38z39z310z311z312z313�
23+
builtins.int" builtins.intz38z39z310z311z312z313�
2424
damage_destroyXlib.ext.damage.damage_destroy"
25-
None*�
26-
self�
27-
:Union[Xlib.display.Display,Xlib.xobject.resource.Resource],
28-
Xlib.display.Display"Xlib.display.Display@
29-
Xlib.xobject.resource.Resource"Xlib.xobject.resource.Resource*(
25+
None*d
26+
selfZ
27+
Union[Xlib.display.Display,Any],
28+
Xlib.display.Display"Xlib.display.Display
29+
Any*(
3030
damage
31-
builtins.int" builtins.intz38z39z310z311z312z313�
31+
builtins.int" builtins.intz38z39z310z311z312z313�
3232
damage_subtractXlib.ext.damage.damage_subtract"
33-
None*�
34-
self�
35-
:Union[Xlib.display.Display,Xlib.xobject.resource.Resource],
36-
Xlib.display.Display"Xlib.display.Display@
37-
Xlib.xobject.resource.Resource"Xlib.xobject.resource.Resource*(
33+
None*d
34+
selfZ
35+
Union[Xlib.display.Display,Any],
36+
Xlib.display.Display"Xlib.display.Display
37+
Any*(
3838
damage
3939
builtins.int" builtins.int**
4040
repair
4141
builtins.int" builtins.int *)
4242
parts
43-
builtins.int" builtins.int z38z39z310z311z312z313�
43+
builtins.int" builtins.int z38z39z310z311z312z313�
4444

4545
damage_addXlib.ext.damage.damage_add"
46-
None*�
47-
self�
48-
:Union[Xlib.display.Display,Xlib.xobject.resource.Resource],
49-
Xlib.display.Display"Xlib.display.Display@
50-
Xlib.xobject.resource.Resource"Xlib.xobject.resource.Resource*(
46+
None*d
47+
selfZ
48+
Union[Xlib.display.Display,Any],
49+
Xlib.display.Display"Xlib.display.Display
50+
Any*(
5151
repair
5252
builtins.int" builtins.int*'
5353
parts

0 commit comments

Comments
 (0)