Skip to content

Commit 23474de

Browse files
SONARJAVA-4895: Review feedback: Abstract from cyclic relation between intra- and cross-procedural components
1 parent 59616a0 commit 23474de

File tree

1 file changed

+50
-37
lines changed

1 file changed

+50
-37
lines changed

java-checks/src/main/java/org/sonar/java/checks/security/CipherBlockChainingCheck.java

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public class CipherBlockChainingCheck extends AbstractMethodDetection {
5353
// * ...
5454
//
5555
// In other words, there will be FPs when secure IV generating methods are used that are defined across (top-level) classes or files.
56-
// This could be handled by doing a pass of SecureByteArrayFactoryFinder across all classes and files first, before performing
56+
// This could be handled by doing a pass of IvFactoryFinderImpl across all classes and files first, before performing
5757
// CipherBlockChainingCheck.
5858
//
5959
// However, this is likely not worth the additional complexity.
@@ -63,10 +63,8 @@ public class CipherBlockChainingCheck extends AbstractMethodDetection {
6363
//
6464
// Furthermore, we can not simply mute this check as soon as a call is involved in generating IVs because then we will lose a lot of the
6565
// cases that are already detected by previous iterations of this check.
66-
private final SecureByteArrayFactoryFinder secureByteArrayFactoryFinder = new SecureByteArrayFactoryFinder();
67-
private final SecureByteArrayGeneratorDetector secureByteArrayGeneratorDetector = new SecureByteArrayGeneratorDetector()
68-
.countParametersAsDynamic()
69-
.detectCustomFactories(secureByteArrayFactoryFinder);
66+
private final IvFactoryFinderImpl ivFactoryFinder = new IvFactoryFinderImpl();
67+
private final IvInitializationAnalysis ivInitializationAnalysis = IvInitializationAnalysis.crossProcedural(ivFactoryFinder);
7068

7169
private static final MethodMatchers SECURE_RANDOM_GENERATE_SEED = MethodMatchers.create()
7270
.ofTypes("java.security.SecureRandom")
@@ -89,10 +87,10 @@ public List<Tree.Kind> nodesToVisit() {
8987
@Override
9088
public void visitNode(Tree tree) {
9189
if (outermostClass == null && tree.is(Tree.Kind.CLASS)) {
92-
// We only need run SecureByteArrayFactoryFinder once on the outermost class to find all secure IV byte array factory methods.
93-
// If we apply the finder again to nested classes then we explore the same sub-trees multiple times.
90+
// We only need run IvFactoryFinderImpl once on the outermost class to find all secure IV byte array factory methods.
91+
// If we apply the finder again to nested classes then we would explore the same sub-trees multiple times.
9492
outermostClass = tree;
95-
tree.accept(secureByteArrayFactoryFinder);
93+
tree.accept(ivFactoryFinder);
9694
}
9795

9896
super.visitNode(tree);
@@ -101,7 +99,7 @@ public void visitNode(Tree tree) {
10199
@Override
102100
public void leaveNode(Tree tree) {
103101
if (tree == outermostClass) {
104-
secureByteArrayFactoryFinder.clear();
102+
ivFactoryFinder.clear();
105103
}
106104
super.leaveNode(tree);
107105
}
@@ -115,7 +113,7 @@ protected MethodMatchers getMethodInvocationMatchers() {
115113

116114
@Override
117115
protected void onConstructorFound(NewClassTree newClassTree) {
118-
if (newClassTree.arguments().isEmpty() || secureByteArrayGeneratorDetector.isDynamicallyGenerated(newClassTree.arguments().get(0))) {
116+
if (newClassTree.arguments().isEmpty() || ivInitializationAnalysis.isDynamicallyGenerated(newClassTree.arguments().get(0))) {
119117
return;
120118
}
121119

@@ -264,30 +262,47 @@ private static Symbol ivSymbol(NewClassTree newClassTree) {
264262
}
265263
}
266264

267-
private static class SecureByteArrayGeneratorDetector {
268-
private @Nullable SecureByteArrayFactoryFinder secureByteArrayFactoryFinder = null;
269-
private boolean shouldCountParametersAsDynamic = false;
265+
private interface IvFactoryFinder {
266+
boolean producesSecureBytesArray(MethodInvocationTree methodInvocation);
270267

271-
/**
272-
* When called, this detector will treat all byte arrays produced by method calls to secure IV-generating functions as secure.
273-
* It uses the given {@link SecureByteArrayFactoryFinder} to judge whether a method call is secure.
274-
*/
275-
public SecureByteArrayGeneratorDetector detectCustomFactories(SecureByteArrayFactoryFinder secureByteArrayFactoryFinder) {
276-
this.secureByteArrayFactoryFinder = secureByteArrayFactoryFinder;
277-
return this;
268+
static IvFactoryFinder disabled() {
269+
return methodInvocation -> false;
270+
}
271+
}
272+
273+
private interface IvInitializationAnalysis {
274+
boolean isDynamicallyGenerated(ExpressionTree tree);
275+
276+
static IvInitializationAnalysis crossProcedural(IvFactoryFinder ivFactoryFinder) {
277+
return new IvInitializationAnalysisImpl(ivFactoryFinder, true);
278278
}
279279

280+
static IvInitializationAnalysis intraProcedural() {
281+
return new IvInitializationAnalysisImpl(IvFactoryFinder.disabled(), false);
282+
}
283+
}
284+
285+
private static class IvInitializationAnalysisImpl implements IvInitializationAnalysis {
286+
private final IvFactoryFinder ivFactoryFinder;
287+
private final boolean shouldCountParametersAsDynamic;
288+
280289
/**
281-
* If called, this detector will treat parameters as secure byte arrays.
282-
* This is optional, since we can not trace arguments across methods and hence can not always assume them to be secure.
283-
* However, for the methods where we see parameters being directly passed into IvParameterSpec, we do want to treat them as secure
284-
* to match the behaviour of previous iterations of CipherBlockChainingCheck
290+
*
291+
* @param ivFactoryFinder when IV byte arrays are initialized using a method call, then this {@link IvFactoryFinder} will be used to
292+
* judge whether the method produces a secure byte array.
293+
* @param shouldCountParametersAsDynamic when set to {@code true}, this detector will treat parameters as secure byte arrays.
294+
* This is optional, since we can not trace arguments across methods and hence can not always
295+
* assume them to be secure.
296+
* However, for the methods where we see parameters being directly passed into IvParameterSpec, we
297+
* do want to treat them as secure to match the behaviour of previous iterations of
298+
* CipherBlockChainingCheck.
285299
*/
286-
public SecureByteArrayGeneratorDetector countParametersAsDynamic() {
287-
this.shouldCountParametersAsDynamic = true;
288-
return this;
300+
IvInitializationAnalysisImpl(IvFactoryFinder ivFactoryFinder, boolean shouldCountParametersAsDynamic) {
301+
this.ivFactoryFinder = ivFactoryFinder;
302+
this.shouldCountParametersAsDynamic = shouldCountParametersAsDynamic;
289303
}
290304

305+
@Override
291306
public boolean isDynamicallyGenerated(ExpressionTree tree) {
292307
if (shouldCountParametersAsDynamic && tree instanceof IdentifierTree identifierTree) {
293308
Symbol symbol = identifierTree.symbol();
@@ -302,11 +317,7 @@ public boolean isDynamicallyGenerated(ExpressionTree tree) {
302317
return true;
303318
}
304319

305-
if (secureByteArrayFactoryFinder == null) {
306-
return false;
307-
}
308-
309-
return secureByteArrayFactoryFinder.producesSecureBytesArray(methodInvocationTree);
320+
return ivFactoryFinder.producesSecureBytesArray(methodInvocationTree);
310321
});
311322
}
312323

@@ -340,18 +351,20 @@ private static Stream<MethodInvocationTree> findConstructingMethods(ExpressionTr
340351

341352
/**
342353
* Collects all methods that construct IV byte arrays in a secure way.
343-
* After applying this visitor to a class tree, {@link SecureByteArrayFactoryFinder#producesSecureBytesArray(MethodInvocationTree)} can
354+
* After applying this visitor to a class tree, {@link IvFactoryFinder#producesSecureBytesArray(MethodInvocationTree)} can
344355
* be used to check whether the invocation of a method belonging to that tree will return such a secure array.
345356
*/
346-
private static class SecureByteArrayFactoryFinder extends BaseTreeVisitor {
357+
private static class IvFactoryFinderImpl extends BaseTreeVisitor implements IvFactoryFinder {
347358
private @Nullable MethodTree currentMethodTree = null;
348359
private final Set<String> secureByteArrayFactories = new HashSet<>();
349-
// Note, that we do not call detectCustomFactories() on secureByteArrayGeneratorDetector.
360+
361+
// Note, that we use the intra-procedural analysis here.
350362
// Meaning, that we will not trace whether methods called inside IV factories are secure.
351363
// In other words, CipherBlockChainingCheck supports a call-depth of at most 1.
352364
// See also the note on cross-procedural FPs in the surrounding class.
353-
private final SecureByteArrayGeneratorDetector secureByteArrayGeneratorDetector = new SecureByteArrayGeneratorDetector();
365+
private final IvInitializationAnalysis ivInitializationAnalysis = IvInitializationAnalysis.intraProcedural();
354366

367+
@Override
355368
public boolean producesSecureBytesArray(MethodInvocationTree methodInvocation) {
356369
return secureByteArrayFactories.contains(methodInvocation.methodSymbol().signature());
357370
}
@@ -386,7 +399,7 @@ public void visitReturnStatement(ReturnStatementTree tree) {
386399
return;
387400
}
388401

389-
if (secureByteArrayGeneratorDetector.isDynamicallyGenerated(returnedExpression)) {
402+
if (ivInitializationAnalysis.isDynamicallyGenerated(returnedExpression)) {
390403
markAsSecureFactory();
391404
return;
392405
}

0 commit comments

Comments
 (0)