Skip to content

Conversation

@anton-haubner-sonarsource
Copy link
Contributor

@anton-haubner-sonarsource anton-haubner-sonarsource commented Oct 20, 2025

SONARJAVA-4895

This implementation potentially slightly deviates from the ticket requirements:
In the case where we can not trace a safe IV generating method because it is not defined in the same class, or because it uses too deeply nested calls, the FP will still be raised.
I placed a comment in the implementation that explains when this happens.
By contrast, the ticket description demands

If it is not possible to follow the flow for the IV generation, no issue should be raised.

However, if we apply this, we will lose too many TPs that are covered by the current implementation.

The reproducer from the ticket and many adjacent cases are still covered by the changes made in this PR.
If we want to improve detection beyond this point, I would recommend switching to an analysis engine that supports cross-procedural/cross-file dataflow (i.e. DBD, or maybe a taint analysis since this is a security issue.).

Reviewing commit-by-commit is strongly recommended.

@anton-haubner-sonarsource anton-haubner-sonarsource force-pushed the SONARJAVA-4895 branch 3 times, most recently from bd1715e to 4a121f6 Compare October 22, 2025 11:50
… IV to applicable to isolated byte vectors

This will be needed to handle the initialization of IV byte arrays in
separate methods.
…byte arrays into separate class

This will allow us to extend the logic in follow up commits so that
user-defined safe factory methods are taken into account.
if (!mitVisit.secureRandomFound) {
reportIssue(newClassTree, "Use a dynamically-generated, random IV.");
}
var mTree = ExpressionUtils.getEnclosingMethod(newClassTree);
Copy link
Contributor Author

@anton-haubner-sonarsource anton-haubner-sonarsource Oct 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit does not change any functionality, yet
It only adds the ability to check for the secure initialization of arbitrary expression trees (we will need in a later commit).
(Also, I did some renaming, to make the meaning of some classes/fields a little bit clearer)


private static boolean isSecureRandomGenerateSeed(@Nullable ExpressionTree tree) {
return tree != null && tree.is(Tree.Kind.METHOD_INVOCATION) && SECURE_RANDOM_GENERATE_SEED.matches((MethodInvocationTree) tree);
private static Stream<MethodInvocationTree> findConstructingMethods(ExpressionTree expressionTree) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isDynamicallyGenerated contained some logic to find any methods that have been used to construct a given identifier.
This commit factors this logic out into a separate method, as we will need it in a later commit.

Otherwise, no functionality is yet changed by this commit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for documenting the cases we do and do not cover clearly that will help when sorting out future feedback on the rule. There is a single case where I am not sure if it should explicitly be marked as a FP.

In addition, if we can find a way to join the finder and detector in a way that avoids having them cycle in their relationship that would help with future maintenance.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this file contains 1 TPs and 2 FPs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you clarify the concern / the action?
I.e. should I make it more clear in the comments that this test file is about edge cases for non-compiling code, causing possible FPs?

Comment on lines 53 to 54
final byte[] iv = initIvWithByteBuffer(12);
cipher.init(ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); // Compliant

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is FN, isn't it? If so, we should mark it the same way we marked the FPs below

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily. It is unclear here, how the byte buffer is initialized and as far as I can tell, the previous implementation explicitly does not raise when a byte buffer is used for initialization (perhaps to avoid FPs?).
I.e. I tried to adhere to the existing behaviour here, and I would say we can neither say its necessarily a FN or TN because the byte buffer contents are unknown.

I can add comment about this, though.

}

private static class SecureByteArrayGeneratorDetector {
private @Nullable SecureByteArrayFactoryFinder secureByteArrayFactoryFinder = null;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There seems to be a bi-directonial connection between the finder and the detector. Is there a way we can simplify this?

Copy link
Contributor Author

@anton-haubner-sonarsource anton-haubner-sonarsource Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bi-directional connection is somewhat intentional, and an artifact of alternating between method-local analysis and cross-method analysis. I.e.

  • SecureByteArrayGeneratorDetector detects local secure dynamic initialization, and refers to SecureByteArrayFactoryFinder when it needs to go cross-method. It also implicitly decides when to stop the cycle (i.e. at call depth 1 - when the reference to the finder is null).

  • SecureByteArrayFactoryFinder detects which methods are insecure by invoking SecureByteArrayGeneratorDetector to analyze their local context.

So indeed, the relation is cyclic, and the cycle can be resolved by joining the components. At the same time, their current implementation separates their responsibilities, and the cycle only exists between private nested classes.

Still, if you would strongly prefer to avoid this cycle, I'll try to spend some more time refactoring this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I introduced some interfaces that abstract from the direct cyclic relation, and that make the dependency between the components more clear, and where we cut the cycle:
23474de

At the same time, I would like to keep the two components and their responsibilities separated, so I decided against joining them.

Please let me know what you think about this tradeoff.

@sonarqube-next
Copy link

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a second look at this. The setup is more complex but I think we gained overall with cleaner method and object names. I think we can merge as-is

this.secureByteArrayFactoryFinder = secureByteArrayFactoryFinder;
return this;
static IvFactoryFinder disabled() {
return methodInvocation -> false;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got a little confused by the notation here but it should work OK

@anton-haubner-sonarsource anton-haubner-sonarsource merged commit 1c24c62 into master Oct 27, 2025
18 checks passed
@anton-haubner-sonarsource anton-haubner-sonarsource deleted the SONARJAVA-4895 branch October 27, 2025 08:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants