|
30 | 30 | import org.sonar.plugins.python.api.tree.Expression; |
31 | 31 | import org.sonar.plugins.python.api.tree.HasSymbol; |
32 | 32 | import org.sonar.plugins.python.api.tree.QualifiedExpression; |
| 33 | +import org.sonar.plugins.python.api.tree.RegularArgument; |
33 | 34 | import org.sonar.plugins.python.api.tree.Tree; |
| 35 | +import org.sonar.plugins.python.api.types.v2.PythonType; |
| 36 | +import org.sonar.plugins.python.api.types.v2.TriBool; |
34 | 37 | import org.sonar.python.checks.utils.Expressions; |
35 | 38 | import org.sonar.python.tree.TreeUtils; |
| 39 | +import org.sonar.python.types.v2.TypeCheckBuilder; |
36 | 40 |
|
37 | 41 | @Rule(key = "S5527") |
38 | 42 | public class UnverifiedHostnameCheck extends PythonSubscriptionCheck { |
39 | 43 |
|
40 | 44 | private static final String MESSAGE = "Enable server hostname verification on this SSL/TLS connection."; |
| 45 | + private static final String SECONDARY_OPENSSL = "This context does not perform hostname verification."; |
41 | 46 |
|
42 | 47 | private static final Set<String> SECURE_BY_DEFAULT = new HashSet<>(Arrays.asList("ssl.create_default_context", "ssl._create_default_https_context")); |
43 | 48 | private static final Set<String> UNSECURE_BY_DEFAULT = new HashSet<>(Arrays.asList("ssl._create_unverified_context", "ssl._create_stdlib_context")); |
44 | 49 |
|
45 | 50 | private static Set<String> functionsToCheck; |
46 | 51 |
|
| 52 | + private TypeCheckBuilder openSSLConnectionTypeCheckBuilder; |
| 53 | + private TypeCheckBuilder openSSLContextTypeCheckBuilder; |
| 54 | + |
47 | 55 | private static Set<String> functionsToCheck() { |
48 | 56 | if (functionsToCheck == null) { |
49 | 57 | functionsToCheck = new HashSet<>(); |
@@ -97,17 +105,43 @@ private static boolean opensUnsecureConnection(Symbol calleeSymbol, CallExpressi |
97 | 105 |
|
98 | 106 | @Override |
99 | 107 | public void initialize(Context context) { |
100 | | - context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, UnverifiedHostnameCheck::checkCallExpression); |
| 108 | + context.registerSyntaxNodeConsumer(Tree.Kind.CALL_EXPR, this::checkCallExpression); |
| 109 | + context.registerSyntaxNodeConsumer(Tree.Kind.FILE_INPUT, ctx -> { |
| 110 | + openSSLConnectionTypeCheckBuilder = ctx.typeChecker().typeCheckBuilder().isTypeWithName("OpenSSL.SSL.Connection"); |
| 111 | + openSSLContextTypeCheckBuilder = ctx.typeChecker().typeCheckBuilder().isInstanceOf("OpenSSL.SSL.Context"); |
| 112 | + }); |
101 | 113 | } |
102 | 114 |
|
103 | | - private static void checkCallExpression(SubscriptionContext ctx) { |
| 115 | + private void checkCallExpression(SubscriptionContext ctx) { |
104 | 116 | CallExpression callExpression = (CallExpression) ctx.syntaxNode(); |
105 | 117 | Symbol calleeSymbol = callExpression.calleeSymbol(); |
106 | | - if (calleeSymbol == null) { |
| 118 | + if (calleeSymbol != null && functionsToCheck().contains(calleeSymbol.fullyQualifiedName())) { |
| 119 | + checkSuspiciousCall(callExpression, calleeSymbol, ctx); |
| 120 | + } |
| 121 | + checkOpenSSLConnection(ctx, callExpression); |
| 122 | + } |
| 123 | + |
| 124 | + private void checkOpenSSLConnection(SubscriptionContext ctx, CallExpression callExpression) { |
| 125 | + Expression callee = callExpression.callee(); |
| 126 | + PythonType pythonType = callee.typeV2(); |
| 127 | + |
| 128 | + if (openSSLConnectionTypeCheckBuilder.check(pythonType) != TriBool.TRUE) { |
107 | 129 | return; |
108 | 130 | } |
109 | | - if (functionsToCheck().contains(calleeSymbol.fullyQualifiedName())) { |
110 | | - checkSuspiciousCall(callExpression, calleeSymbol, ctx); |
| 131 | + |
| 132 | + RegularArgument contextArg = TreeUtils.nthArgumentOrKeyword(0, "context", callExpression.arguments()); |
| 133 | + if (contextArg == null) { |
| 134 | + return; |
| 135 | + } |
| 136 | + |
| 137 | + Expression contextExpr = contextArg.expression(); |
| 138 | + PythonType contextType = contextExpr.typeV2(); |
| 139 | + if (openSSLContextTypeCheckBuilder.check(contextType) == TriBool.TRUE) { |
| 140 | + PreciseIssue issue = ctx.addIssue(callee, MESSAGE); |
| 141 | + Expressions.ifNameGetSingleAssignedNonNameValue(contextExpr) |
| 142 | + .ifPresentOrElse(e -> issue.secondary(e, SECONDARY_OPENSSL), |
| 143 | + () -> issue.secondary(contextExpr, SECONDARY_OPENSSL) |
| 144 | + ); |
111 | 145 | } |
112 | 146 | } |
113 | 147 | } |
0 commit comments