- 
                Notifications
    You must be signed in to change notification settings 
- Fork 704
SONARJAVA-4895: Fix S3329 FPs when random IV is generated in separate function #5325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
bd1715e    to
    4a121f6      
    Compare
  
    … IV to applicable to isolated byte vectors This will be needed to handle the initialization of IV byte arrays in separate methods.
…oss re-assignments
…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.
4a121f6    to
    dafb0d2      
    Compare
  
    | if (!mitVisit.secureRandomFound) { | ||
| reportIssue(newClassTree, "Use a dynamically-generated, random IV."); | ||
| } | ||
| var mTree = ExpressionUtils.getEnclosingMethod(newClassTree); | 
There was a problem hiding this comment.
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) { | 
There was a problem hiding this comment.
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.
…ray generator detection
dafb0d2    to
    92a9f13      
    Compare
  
    There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
| final byte[] iv = initIvWithByteBuffer(12); | ||
| cipher.init(ENCRYPT_MODE, secretKey, new IvParameterSpec(iv)); // Compliant | 
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; | 
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
…n intra- and cross-procedural components
| 
 | 
There was a problem hiding this comment.
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; | 
There was a problem hiding this comment.
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




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
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.