diff --git a/its/autoscan/src/test/java/org/sonar/java/it/AutoScanTest.java b/its/autoscan/src/test/java/org/sonar/java/it/AutoScanTest.java index 879194f0a42..8e7b83a97e3 100644 --- a/its/autoscan/src/test/java/org/sonar/java/it/AutoScanTest.java +++ b/its/autoscan/src/test/java/org/sonar/java/it/AutoScanTest.java @@ -181,7 +181,7 @@ public void javaCheckTestSources() throws Exception { } // store new unexpected diffs in JSON files - serializable - Files.createDirectory(pathFor(TARGET_ACTUAL + "autoscan-diffs/")); + Files.createDirectories(pathFor(TARGET_ACTUAL + "autoscan-diffs/")); for (var newDiff : newDiffs) { if (!newDiff.equals(knownDiffs.get(newDiff.ruleKey))) { Files.writeString(pathFor(TARGET_ACTUAL + "autoscan-diffs/diff_" + newDiff.ruleKey + ".json"), GSON.toJson(newDiff)); diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1161.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1161.json index e729f237027..38a2033bc10 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1161.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1161.json @@ -1,6 +1,6 @@ { "ruleKey": "S1161", "hasTruePositives": true, - "falseNegatives": 7, + "falseNegatives": 10, "falsePositives": 0 } \ No newline at end of file diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1874.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1874.json index 2bbf94ab04a..2c73d86f284 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1874.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1874.json @@ -1,6 +1,6 @@ { "ruleKey": "S1874", "hasTruePositives": true, - "falseNegatives": 93, + "falseNegatives": 111, "falsePositives": 0 } \ No newline at end of file diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1948.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1948.json index 6f2b7c3888e..ef110d01891 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S1948.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S1948.json @@ -1,6 +1,6 @@ { "ruleKey": "S1948", "hasTruePositives": true, - "falseNegatives": 0, + "falseNegatives": 1, "falsePositives": 0 } \ No newline at end of file diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2092.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2092.json index 326683d061f..b561662b8a8 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2092.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2092.json @@ -1,6 +1,6 @@ { "ruleKey": "S2092", "hasTruePositives": true, - "falseNegatives": 42, + "falseNegatives": 93, "falsePositives": 0 } \ No newline at end of file diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2160.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2160.json index 89e709c409d..c2bb4c1a575 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2160.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2160.json @@ -1,6 +1,6 @@ { "ruleKey": "S2160", "hasTruePositives": true, - "falseNegatives": 1, + "falseNegatives": 2, "falsePositives": 0 } \ No newline at end of file diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2226.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2226.json index 774799bbb48..949f67a93e2 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2226.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2226.json @@ -1,6 +1,6 @@ { "ruleKey": "S2226", "hasTruePositives": false, - "falseNegatives": 5, + "falseNegatives": 9, "falsePositives": 0 } \ No newline at end of file diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2441.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2441.json index 3c6e3b5abec..53f5c4096ed 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S2441.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S2441.json @@ -1,6 +1,6 @@ { "ruleKey": "S2441", "hasTruePositives": true, - "falseNegatives": 0, + "falseNegatives": 1, "falsePositives": 0 } \ No newline at end of file diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S3330.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S3330.json index 9484918f2c5..9a143b57343 100644 --- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S3330.json +++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S3330.json @@ -1,6 +1,6 @@ { "ruleKey": "S3330", "hasTruePositives": true, - "falseNegatives": 51, + "falseNegatives": 77, "falsePositives": 0 } \ No newline at end of file diff --git a/java-checks-test-sources/default/src/main/java/checks/ServletInstanceFieldCheckJakarta.java b/java-checks-test-sources/default/src/main/java/checks/ServletInstanceFieldCheckJakarta.java new file mode 100644 index 00000000000..3806ed8cbdb --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/ServletInstanceFieldCheckJakarta.java @@ -0,0 +1,81 @@ +package checks; + +import java.util.function.Function; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServlet; +import org.apache.struts.action.Action; + +class HttpServletAJakarta { + private String userName; +} + +class HttpServletBJakarta extends HttpServlet { + private String userName; // Noncompliant [[sc=18;ec=26]] {{Remove this misleading mutable servlet instance field or make it "static" and/or "final"}} + private static String staticVar; + private final String finalVar; + private String storageType; + private static final Function LAMBDA = lambdaParam -> { + Integer lambdaVar = null; + return lambdaVar; + }; + + public HttpServletBJakarta(String x) { + String localVar; + finalVar = x; + } + + public void init(jakarta.servlet.ServletConfig config) { + storageType = StorageType.valueOf(config.getInitParameter("storageType")); + } + + private static class StorageType { + public static String valueOf(String storageType) { + return null; + } + } +} + +class HttpServletCJakarta extends Action { + + private String userName; // Noncompliant + private static String staticVar; + private final String finalVar; + + public HttpServletCJakarta(String x) { + finalVar = x; + } +} + +class HttpServletDJakarta extends HttpServlet { + + @jakarta.inject.Inject private String userName; // compliant annotated with inject; + @Inject private String userName1; // Noncompliant + @Resource private String city; // compliant annotated with resource; + private static String staticVar; +} + +public class ServletInstanceFieldCheckJakarta extends HttpServlet { + @org.springframework.beans.factory.annotation.Autowired + private javax.sql.DataSource myDB; // Noncompliant - filtered by the SpringFilter +} + +class HttpServletEJakarta extends HttpServlet { + private String userName; // Noncompliant [[sc=18;ec=26]] {{Remove this misleading mutable servlet instance field or make it "static" and/or "final"}} + private final String finalVar; + private String storageType; // Compliant, initialized in init() method + + public HttpServletEJakarta(String x) { + String localVar; + finalVar = x; + } + + public void init() { + storageType = StorageType.valueOf(getServletConfig().getInitParameter("storageType")); + } + + private static class StorageType { + public static String valueOf(String storageType) { + return null; + } + } +} diff --git a/java-checks-test-sources/default/src/main/java/checks/regex/RegexComplexityCheck.java b/java-checks-test-sources/default/src/main/java/checks/regex/RegexComplexityCheck.java index 18398483c0f..7d58d6876bc 100644 --- a/java-checks-test-sources/default/src/main/java/checks/regex/RegexComplexityCheck.java +++ b/java-checks-test-sources/default/src/main/java/checks/regex/RegexComplexityCheck.java @@ -16,6 +16,10 @@ public class RegexComplexityCheck { @Email(regexp = "((((a|b)|(c|d))+|((e|f)|(g|h))+)+|(((h|i)|(j|j))+|((k|l)|(m|n))+)+)") private String email; + // Noncompliant@+1 + @jakarta.validation.constraints.Email(regexp = "((((a|b)|(c|d))+|((e|f)|(g|h))+)+|(((h|i)|(j|j))+|((k|l)|(m|n))+)+)") + private String emailJakarta; + void noncompliant(String str) { // Noncompliant@+2 [[sc=7;ec=8]] {{Simplify this regular expression to reduce its complexity from 106 to the 20 allowed.}} str.matches( diff --git a/java-checks-test-sources/default/src/main/java/checks/regex/ReluctantQuantifierWithEmptyContinuationCheck.java b/java-checks-test-sources/default/src/main/java/checks/regex/ReluctantQuantifierWithEmptyContinuationCheck.java index bd293c937b6..fd255516ad2 100644 --- a/java-checks-test-sources/default/src/main/java/checks/regex/ReluctantQuantifierWithEmptyContinuationCheck.java +++ b/java-checks-test-sources/default/src/main/java/checks/regex/ReluctantQuantifierWithEmptyContinuationCheck.java @@ -50,6 +50,9 @@ void noncompliant(String str) { @Email(regexp = ".*?") // Noncompliant {{Remove the '?' from this unnecessarily reluctant quantifier.}} void fullMatch() { } + @jakarta.validation.constraints.Email(regexp = ".*?") // Noncompliant + void fullMatchJakarta() { } + Matcher compliant(String str) { str.matches(".*?x"); str.matches(".*?x?"); diff --git a/java-checks-test-sources/default/src/main/java/checks/security/SecureCookieCheck.java b/java-checks-test-sources/default/src/main/java/checks/security/SecureCookieCheck.java index 8533dd6bde1..17535926705 100644 --- a/java-checks-test-sources/default/src/main/java/checks/security/SecureCookieCheck.java +++ b/java-checks-test-sources/default/src/main/java/checks/security/SecureCookieCheck.java @@ -30,10 +30,10 @@ void foo(Cookie cookie) { } Cookie servletCookie( - Cookie firstParam, - Cookie secondParam, - Cookie thirdParam, - boolean param) { + Cookie firstParam, + Cookie secondParam, + Cookie thirdParam, + boolean param) { firstParam.setSecure(false); // Noncompliant [[sc=25;ec=32]] {{Make sure creating this cookie without the "secure" flag is safe here.}} secondParam.setSecure(true); @@ -61,7 +61,7 @@ Cookie servletCookie( Cookie c7 = new Cookie("name", "value"); boolean b = false; - c7.setSecure(b); // Noncompliant [[secondary=63]] + c7.setSecure(b); // Noncompliant [[secondary=-1]] Cookie c8 = new Cookie("name", "value"); c8.setSecure(param); diff --git a/java-checks-test-sources/default/src/main/java/checks/security/SecureCookieCheckJakarta.java b/java-checks-test-sources/default/src/main/java/checks/security/SecureCookieCheckJakarta.java new file mode 100644 index 00000000000..8ac1b72d466 --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/security/SecureCookieCheckJakarta.java @@ -0,0 +1,142 @@ +package checks.security; + +import java.util.Date; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.ws.rs.core.NewCookie; + +class SecureCookieCheckJakarta { + + Cookie field1 = new Cookie("name", "value"); // Noncompliant + jakarta.ws.rs.core.Cookie field3 = new jakarta.ws.rs.core.Cookie("name", "value"); // Noncompliant + jakarta.ws.rs.core.Cookie cookie; + NewCookie secureCookie = new NewCookie(cookie, "2", 3, true); + NewCookie unsecureCookie = new NewCookie(cookie, "2", 3, false); // Noncompliant + Cookie field4; + Cookie field5; + + private static final boolean FALSE_CONSTANT = false; + + void foo(Cookie cookie) { + } + + Cookie servletCookie( + Cookie firstParam, + Cookie secondParam, + Cookie thirdParam, + boolean param) { + firstParam.setSecure(false); // Noncompliant [[sc=25;ec=32]] {{Make sure creating this cookie without the "secure" flag is safe here.}} + secondParam.setSecure(true); + + field5.setSecure(false); // Noncompliant + this.field4 = new Cookie("name", "value"); // Noncompliant + + Cookie cookie = new Cookie("name", "value"); + cookie.setSecure(true); + + Cookie cookie2 = new Cookie("name", "value"); // Noncompliant [[sc=26;ec=32]] {{Make sure creating this cookie without the "secure" flag is safe here.}} + + Cookie cookie3 = new Cookie("name", "value"); + cookie3.setSecure(false); // Noncompliant {{Make sure creating this cookie without the "secure" flag is safe here.}} + + Cookie cookie5 = new Cookie("name", "value"); + cookie5.setSecure(FALSE_CONSTANT); // Noncompliant + + Cookie c6 = new Cookie("name", "value"); + if (param) { + c6.setSecure(false); // Noncompliant + } else { + c6.setSecure(true); + } + + Cookie c7 = new Cookie("name", "value"); + boolean b = false; + c7.setSecure(b); // Noncompliant [[secondary=-1]] + + Cookie c8 = new Cookie("name", "value"); + c8.setSecure(param); + + Object c9 = new Cookie("name", "value"); // Noncompliant + + Cookie c10; + c10 = new Cookie("name", "value"); + c10.setSecure(true); + + Object c12; + c12 = new Cookie("name", "value"); // Noncompliant [[sc=15;ec=21]] {{Make sure creating this cookie without the "secure" flag is safe here.}} + + Cookie c13 = new Cookie("name", "value"); + boolean value = false; + c13.setSecure(!value); + + return new Cookie("name", "value"); // Noncompliant + } + + NewCookie jaxRsNewCookie(jakarta.ws.rs.core.Cookie cookie) { + NewCookie c1 = new NewCookie(cookie); // Noncompliant + NewCookie c2 = new NewCookie(cookie, "2", 3, false); // Noncompliant + NewCookie c3 = new NewCookie(cookie, "2", 3, true); + NewCookie c4 = new NewCookie(cookie, "2", 3, new Date(), false, true); // Noncompliant + NewCookie c5 = new NewCookie(cookie, "2", 3, new Date(), true, false); + + NewCookie c6 = new NewCookie("1", "2"); // Noncompliant + + NewCookie c7 = new NewCookie("1", "2", "3", "4", "5", 6, false, true); // Noncompliant + NewCookie c8 = new NewCookie("1", "2", "3", "4", "5", 6, true, true); + NewCookie c9 = new NewCookie("1", "2", "3", "4", 5, "6", 7, new Date(), false, true); // Noncompliant + NewCookie c10 = new NewCookie("1", "2", "3", "4", 5, "6", 7, new Date(), true, false); + + NewCookie c11 = new NewCookie("1", "2", "3", "4", "5", 6, true); + NewCookie c12 = new NewCookie("1", "2", "3", "4", "5", 6, false); // Noncompliant + NewCookie c13 = new NewCookie("1", "2", "3", "4", "5", 6, false, false); // Noncompliant + NewCookie c14 = new NewCookie("1", "2", "3", "4", "5", 6, true, false); + + return new NewCookie(cookie); // Noncompliant + } + + class SecureCookieCheckBJakarta extends Cookie { + public Cookie c; + + public SecureCookieCheckBJakarta(String name, String value) { + super(name, value); + } + + public void setSecure(boolean bool) { + } + + void foo() { + setSecure(false); // FN (to avoid implementation complexity) + } + + Date d = new Date(); + + void bar(boolean x) { + setSecure(x); + } + + void baz() { + setSecure(true); + return; // code coverage + } + + Date codeCoverage(Cookie cookie) { + SecureCookieCheckJakarta a = new SecureCookieCheckJakarta(); + a.foo(cookie); + Date d1 = new Date(); + Date d2; + d2 = d1; + d2 = new Date(); + d = d1; + d = new Date(); + return new Date(); + } + + class JavaNet { + Cookie httpCookie(HttpServletResponse response) { + Cookie cookie = new Cookie("name", "value"); // Noncompliant + response.addCookie(new Cookie("name", "value")); // Noncompliant + return new Cookie("name", "value"); // Noncompliant + } + } + } +} diff --git a/java-checks-test-sources/default/src/main/java/checks/serialization/SerializableObjectInSessionCheck.java b/java-checks-test-sources/default/src/main/java/checks/serialization/SerializableObjectInSessionCheck.java index 117c24d8988..d64fbfd5b81 100644 --- a/java-checks-test-sources/default/src/main/java/checks/serialization/SerializableObjectInSessionCheck.java +++ b/java-checks-test-sources/default/src/main/java/checks/serialization/SerializableObjectInSessionCheck.java @@ -98,6 +98,12 @@ void foo(HttpServletRequest request) { session.setAttribute("name", notSerializableClass); // Noncompliant {{Make "Class" and its parameters serializable or don't store it in the session.}} } + // Make sure we also cover Jakarta based on one example + void jakarta(jakarta.servlet.http.HttpServletRequest request) { + var session = request.getSession(); + session.setAttribute("address", new Address()); // Noncompliant + } + public class Address { } public class Person { diff --git a/java-checks/src/main/java/org/sonar/java/checks/ServletInstanceFieldCheck.java b/java-checks/src/main/java/org/sonar/java/checks/ServletInstanceFieldCheck.java index 28e203f3b5a..cb441c6ed2f 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/ServletInstanceFieldCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/ServletInstanceFieldCheck.java @@ -43,17 +43,26 @@ public class ServletInstanceFieldCheck extends IssuableSubscriptionVisitor { private final List issuableVariables = new ArrayList<>(); private final List excludedVariables = new ArrayList<>(); - - private static final MethodMatchers INIT_METHOD_WITH_PARAM_MATCHER = MethodMatchers.create() - .ofSubTypes("javax.servlet.Servlet").names("init").addParametersMatcher("javax.servlet.ServletConfig").build(); - private static final MethodMatchers INIT_METHOD_NO_PARAMS_MATCHER = MethodMatchers.create() - .ofSubTypes("javax.servlet.GenericServlet").names("init").addWithoutParametersMatcher().build(); + private static final MethodMatchers INIT_METHOD_WITH_PARAM_MATCHER = MethodMatchers.or( + MethodMatchers.create() + .ofSubTypes("javax.servlet.Servlet") + .names("init").addParametersMatcher("javax.servlet.ServletConfig").build(), + MethodMatchers.create() + .ofSubTypes("jakarta.servlet.Servlet") + .names("init").addParametersMatcher("jakarta.servlet.ServletConfig").build()); + + private static final MethodMatchers INIT_METHOD_NO_PARAMS_MATCHER = MethodMatchers.create() + .ofSubTypes("javax.servlet.GenericServlet", "jakarta.servlet.GenericServlet") + .names("init").addWithoutParametersMatcher().build(); private static final List ANNOTATIONS_EXCLUDING_FIELDS = Arrays.asList( "javax.inject.Inject", + "jakarta.inject.Inject", "javax.ejb.EJB", - "javax.annotation.Resource"); + "jakarta.ejb.EJB", + "javax.annotation.Resource", + "jakarta.annotation.Resource"); @Override public List nodesToVisit() { @@ -109,10 +118,15 @@ public void visitAssignmentExpression(AssignmentExpressionTree tree) { private static boolean isOwnedByAServlet(VariableTree variable) { Symbol owner = variable.symbol().owner(); - return owner.isTypeSymbol() - && variable.parent().is(Tree.Kind.CLASS) - && (owner.type().isSubtypeOf("javax.servlet.http.HttpServlet") - || owner.type().isSubtypeOf("org.apache.struts.action.Action")); + + if (!owner.isTypeSymbol() || !variable.parent().is(Tree.Kind.CLASS)) { + return false; + } + + var ownerType = owner.type(); + return ownerType.isSubtypeOf("javax.servlet.http.HttpServlet") + || ownerType.isSubtypeOf("jakarta.servlet.http.HttpServlet") + || ownerType.isSubtypeOf("org.apache.struts.action.Action"); } private static boolean isStaticOrFinal(VariableTree variable) { diff --git a/java-checks/src/main/java/org/sonar/java/checks/security/SecureCookieCheck.java b/java-checks/src/main/java/org/sonar/java/checks/security/SecureCookieCheck.java index d7b53ceb409..4a0d9460299 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/security/SecureCookieCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/security/SecureCookieCheck.java @@ -49,14 +49,19 @@ public class SecureCookieCheck extends IssuableSubscriptionVisitor { private static final String MESSAGE = "Make sure creating this cookie without the \"secure\" flag is safe here."; private static final String JAX_RS_COOKIE = "javax.ws.rs.core.Cookie"; + private static final String JAX_RS_COOKIE_JAKARTA = "jakarta.ws.rs.core.Cookie"; private static final String JAX_RS_NEW_COOKIE = "javax.ws.rs.core.NewCookie"; + private static final String JAX_RS_NEW_COOKIE_JAKARTA = "jakarta.ws.rs.core.NewCookie"; private static final String SPRING_SAVED_COOKIE = "org.springframework.security.web.savedrequest.SavedCookie"; private static final String PLAY_COOKIE = "play.mvc.Http$Cookie"; private static final List COOKIES = Arrays.asList( "javax.servlet.http.Cookie", + "jakarta.servlet.http.Cookie", "java.net.HttpCookie", JAX_RS_COOKIE, + JAX_RS_COOKIE_JAKARTA, JAX_RS_NEW_COOKIE, + JAX_RS_NEW_COOKIE_JAKARTA, "org.apache.shiro.web.servlet.SimpleCookie", SPRING_SAVED_COOKIE, PLAY_COOKIE, @@ -68,23 +73,26 @@ public class SecureCookieCheck extends IssuableSubscriptionVisitor { * Some constructors have the 'secure' parameter and do not need a 'setSecure' call afterwards. */ private static final String JAVA_LANG_STRING = "java.lang.String"; + private static final String JAVA_UTIL_DATE = "java.util.Date"; private static final String INT = "int"; private static final String BOOLEAN = "boolean"; private static final MethodMatchers CONSTRUCTORS_WITH_SECURE_PARAM_LAST = MethodMatchers.create() - .ofTypes(JAX_RS_NEW_COOKIE) + .ofTypes(JAX_RS_NEW_COOKIE, JAX_RS_NEW_COOKIE_JAKARTA) .constructor() .addParametersMatcher(JAX_RS_COOKIE, JAVA_LANG_STRING, INT, BOOLEAN) + .addParametersMatcher(JAX_RS_COOKIE_JAKARTA, JAVA_LANG_STRING, INT, BOOLEAN) .addParametersMatcher(JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, INT, JAVA_LANG_STRING, INT, BOOLEAN) .addParametersMatcher(JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, INT, BOOLEAN) .build(); private static final MethodMatchers CONSTRUCTORS_WITH_SECURE_PARAM_BEFORE_LAST = MethodMatchers.or( MethodMatchers.create() - .ofTypes(JAX_RS_NEW_COOKIE) + .ofTypes(JAX_RS_NEW_COOKIE, JAX_RS_NEW_COOKIE_JAKARTA) .constructor() - .addParametersMatcher(JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, INT, JAVA_LANG_STRING, INT, "java.util.Date", BOOLEAN, BOOLEAN) - .addParametersMatcher(JAX_RS_COOKIE, JAVA_LANG_STRING, INT, "java.util.Date", BOOLEAN, BOOLEAN) + .addParametersMatcher(JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, INT, JAVA_LANG_STRING, INT, JAVA_UTIL_DATE, BOOLEAN, BOOLEAN) + .addParametersMatcher(JAX_RS_COOKIE, JAVA_LANG_STRING, INT, JAVA_UTIL_DATE, BOOLEAN, BOOLEAN) + .addParametersMatcher(JAX_RS_COOKIE_JAKARTA, JAVA_LANG_STRING, INT, JAVA_UTIL_DATE, BOOLEAN, BOOLEAN) .addParametersMatcher(JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, JAVA_LANG_STRING, INT, BOOLEAN, BOOLEAN) .build(), MethodMatchers.create() diff --git a/java-checks/src/main/java/org/sonar/java/checks/serialization/SerializableObjectInSessionCheck.java b/java-checks/src/main/java/org/sonar/java/checks/serialization/SerializableObjectInSessionCheck.java index aa16e38c2c3..1c537dc57bc 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/serialization/SerializableObjectInSessionCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/serialization/SerializableObjectInSessionCheck.java @@ -32,10 +32,12 @@ @Rule(key = "S2441") public class SerializableObjectInSessionCheck extends AbstractMethodDetection { + private static final String SESSION_POSTFIX = "servlet.http.HttpSession"; + @Override protected MethodMatchers getMethodInvocationMatchers() { return MethodMatchers.create() - .ofTypes("javax.servlet.http.HttpSession") + .ofTypes("javax." + SESSION_POSTFIX, "jakarta." + SESSION_POSTFIX) .names("setAttribute") .addParametersMatcher("java.lang.String", ANY) .build(); diff --git a/java-checks/src/test/java/org/sonar/java/checks/ServletInstanceFieldCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/ServletInstanceFieldCheckTest.java index 07bc9ee54e6..979999c5100 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/ServletInstanceFieldCheckTest.java +++ b/java-checks/src/test/java/org/sonar/java/checks/ServletInstanceFieldCheckTest.java @@ -39,7 +39,20 @@ void test() { .withoutSemantic() .verifyNoIssues(); } - + + @Test + void test_jakarta() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/ServletInstanceFieldCheckJakarta.java")) + .withCheck(new ServletInstanceFieldCheck()) + .verifyIssues(); + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/ServletInstanceFieldCheckJakarta.java")) + .withCheck(new ServletInstanceFieldCheck()) + .withoutSemantic() + .verifyNoIssues(); + } + @Test void test_non_compiling() { CheckVerifier.newVerifier() diff --git a/java-checks/src/test/java/org/sonar/java/checks/security/SecureCookieCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/security/SecureCookieCheckTest.java index 8c81c927617..63f43f629ac 100644 --- a/java-checks/src/test/java/org/sonar/java/checks/security/SecureCookieCheckTest.java +++ b/java-checks/src/test/java/org/sonar/java/checks/security/SecureCookieCheckTest.java @@ -38,6 +38,15 @@ void test() { .withCheck(new SecureCookieCheck()) .verifyIssues(); } + + @Test + void test_jakarta() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/security/SecureCookieCheckJakarta.java")) + .withCheck(new SecureCookieCheck()) + .verifyIssues(); + } + @Test void test_non_compiling() { CheckVerifier.newVerifier() diff --git a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SanityTest.java b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SanityTest.java index e32fe3b85d5..7665eb7624b 100644 --- a/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SanityTest.java +++ b/sonar-java-plugin/src/test/java/org/sonar/plugins/java/SanityTest.java @@ -32,6 +32,7 @@ import java.util.stream.Stream; import javax.annotation.Nullable; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.assertj.core.api.SoftAssertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.extension.RegisterExtension; @@ -138,15 +139,16 @@ void test() throws Exception { .filter(SanityTest::isTypeResolutionError) .collect(Collectors.toList()); - assertThat(errorLogs).hasSize(30); + SoftAssertions softly = new SoftAssertions(); + softly.assertThat(errorLogs).hasSize(31); List remainingErrors = new ArrayList<>(errorLogs); remainingErrors.removeAll(parsingErrors); remainingErrors.removeAll(typeResolutionErrors); - assertThat(remainingErrors).isEmpty(); + softly.assertThat(remainingErrors).isEmpty(); - assertThat(typeResolutionErrors) - .hasSize(18) + softly.assertThat(typeResolutionErrors) + .hasSize(19) .map(LogAndArguments::getFormattedMsg) .map(log -> log.substring("ECJ Unable to resolve type ".length())) // FIXME investigate root cause (seems to be a conflict of version, with classes from JDK not resolved correctly @@ -157,6 +159,7 @@ void test() throws Exception { "jakarta.ws.rs.core.Cookie", "jakarta.servlet.http.Cookie", "javax.ws.rs.core.NewCookie", + "jakarta.ws.rs.core.NewCookie", "junit.framework.TestCase", "org.apache.commons.lang.math.RandomUtils", "org.apache.commons.lang.RandomStringUtils", @@ -170,7 +173,7 @@ void test() throws Exception { "play.mvc.Http$Cookie", "play.mvc.Http$CookieBuilder"); - assertThat(parsingErrors) + softly.assertThat(parsingErrors) .hasSize(12) .map(LogAndArguments::getFormattedMsg) .allMatch(log -> @@ -186,6 +189,8 @@ void test() throws Exception { || log.contains("MockingAllMethodsCheck") || log.contains("MockitoArgumentMatchersUsedOnAllParameters") ); + + softly.assertAll(); } private static boolean isParseError(LogAndArguments log) {