Skip to content

SONARJAVA-5683 S2077 Fix FN on strings built with String.format()/formatted(). #5246

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

Merged
merged 6 commits into from
Jul 17, 2025

Conversation

tomasz-tylenda-sonarsource
Copy link
Contributor

@tomasz-tylenda-sonarsource tomasz-tylenda-sonarsource commented Jul 7, 2025

Copy link

sonarqube-next bot commented Jul 8, 2025

Comment on lines 222 to 229
private static boolean hasDynamicStringParameters(MethodInvocationTree mit) {
for (ExpressionTree arg: mit.arguments()) {
if (arg.symbolType().is(JAVA_LANG_STRING) && arg.asConstant().isEmpty()) {
return true;
}
}
return false;
}
Copy link
Member

Choose a reason for hiding this comment

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

Is it intentional not to support String.format(String format, Object... args) when args is not a String?

For example, if the argument is a int. Why does the rule not support the following case?

  public void formatIntVar(int input) throws SQLException {
    String query = String.format("SELECT %s", input);
    this.stmt.execute(query); // Noncompliant
  }

  public void formatIntConst() throws SQLException {
    String query = String.format("SELECT %s", 1);
    this.stmt.execute(query);
  }

Could be done with this kind of logic:

Suggested change
private static boolean hasDynamicStringParameters(MethodInvocationTree mit) {
for (ExpressionTree arg: mit.arguments()) {
if (arg.symbolType().is(JAVA_LANG_STRING) && arg.asConstant().isEmpty()) {
return true;
}
}
return false;
}
private static boolean hasDynamicStringParameters(MethodInvocationTree mit) {
boolean firstArg = true;
for (ExpressionTree arg: mit.arguments()) {
Type type = arg.symbolType();
boolean notTheFirstLocaleArgument = !firstArg || !type.isUnknown() || !type.is("java.util.Locale");
if (notTheFirstLocaleArgument && arg.asConstant().isEmpty()) {
return true;
}
firstArg = false;
}
return false;
}

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 had to change it due to a failing test and then expanded it to exclude primitive arguments as well. Please see code comments for details.

// `format` has a variant with Locale as the first argument - we do not need to check that parameter.
boolean isFirstLocaleArgument = firstArg && !type.isUnknown() && type.is("java.util.Locale");
// Primitives will not lead to SQL injection, so the code is compliant.
if (!isFirstLocaleArgument && !type.isPrimitive() && arg.asConstant().isEmpty()) {
Copy link
Member

Choose a reason for hiding this comment

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

We could also use isPrimitiveWrapper

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

for (ExpressionTree arg: mit.arguments()) {
Type type = arg.symbolType();
// `format` has a variant with Locale as the first argument - we do not need to check that parameter.
boolean isFirstLocaleArgument = firstArg && !type.isUnknown() && type.is("java.util.Locale");
Copy link
Member

Choose a reason for hiding this comment

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

&& !type.isUnknown() seems useless, remove or invert the logic

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved and tested.

Copy link

@alban-auzeill alban-auzeill merged commit 2214434 into master Jul 17, 2025
17 of 18 checks passed
@alban-auzeill alban-auzeill deleted the tt/S2077 branch July 17, 2025 17:30
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
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy