Skip to content

SONARJAVA-6783: Implement rule S9345 Classes with throwing constructors should be protected against Finalizer attacks - #5981

Draft
romainbrenguier wants to merge 4 commits into
masterfrom
romain/new-rule-s9345-sonarjava-6783
Draft

SONARJAVA-6783: Implement rule S9345 Classes with throwing constructors should be protected against Finalizer attacks#5981
romainbrenguier wants to merge 4 commits into
masterfrom
romain/new-rule-s9345-sonarjava-6783

Conversation

@romainbrenguier

Copy link
Copy Markdown
Contributor

Summary

  • Implement new rule S9345 that detects non-final, non-abstract classes with non-private constructors that can throw exceptions, making them vulnerable to Finalizer attacks
  • Non-compliant: non-final class with public/protected/package-private constructor that has a throws clause or contains throw statements
  • Compliant: final classes, abstract classes, classes with only private throwing constructors (factory pattern), enums, records, and classes without throwing constructors

Test plan

  • Unit test with CheckVerifier covering noncompliant and compliant patterns
  • CI passes
  • Ruling tests reviewed

🤖 Generated with Claude Code

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

SONARJAVA-6783

Comment on lines +45 to +48
if (ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.FINAL) ||
ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.ABSTRACT)) {
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Edge Case: Sealed classes flagged as noncompliant (false positive)

A sealed non-final class cannot be subclassed by an attacker — its permitted subclasses are fixed at compile time — so it is not exploitable by a Finalizer attack, yet the check only exempts final and abstract classes and would report a sealed class with a throwing constructor. Consider also returning early when ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.SEALED) is true (subject to RSPEC scope), to avoid false positives on sealed hierarchies.

Also skip sealed classes, which attackers cannot subclass.:

if (ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.FINAL) ||
  ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.ABSTRACT) ||
  ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.SEALED)) {
  return;
}
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

Comment on lines +49 to +63
for (Tree member : classTree.members()) {
if (member.is(Kind.CONSTRUCTOR) && isVulnerableConstructor((MethodTree) member)) {
reportIssue(classTree.simpleName(), "Make this class \"final\" or make the throwing constructors \"private\".");
return;
}
}
}

private static boolean isVulnerableConstructor(MethodTree constructor) {
if (ModifiersUtils.hasModifier(constructor.modifiers(), Modifier.PRIVATE)) {
return false;
}
return !constructor.throwsClauses().isEmpty() || containsThrowStatement(constructor);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Edge Case: Throwing field/instance initializers not detected

The check only inspects CONSTRUCTOR members and scans each constructor's own block, so a throw that occurs in an instance initializer block or a field initializer (e.g. private final X x = compute(); where compute() throws) is missed even though those run during construction. A class with no explicit constructor but a throwing field/instance initializer has no CONSTRUCTOR member at all and is never flagged, a false negative for the same finalizer-attack vector. Consider also examining instance initializer blocks and field initializers, and accounting for the implicit default constructor.

Was this helpful? React with 👍 / 👎

@datadog-sonarsource

datadog-sonarsource Bot commented Aug 21, 2026

Copy link
Copy Markdown

Pipelines

⚠️ Warnings

🚦 6 Pipeline jobs failed

Build | Ruling QA (warp-custom-ubuntu-24-04, only-sonarqube-project, LATEST_RELEASE)

View in Datadog · View in GitHub Actions

NullPointerException during analysis caused by inability to run check class 'org.sonar.java.checks.FinalizerAttackCheck' on file 'src/main/java/org/sonar/server/computation/task/projectanalysis/filemove/FileMoveDetectionStep.java'.

Build | Ruling QA (warp-custom-ubuntu-24-04, without-sonarqube-project, LATEST_RELEASE)

View in Datadog · View in GitHub Actions

Unable to analyze file: 'src/main/java/org/apache/commons/beanutils2/locale/LocaleBeanUtilsBean.java' due to UnsupportedOperationException: Trying to save symbol table twice for the same file.

Build | Ruling QA (warp-custom-windows-2022-l, without-sonarqube-project, LATEST_RELEASE)

View in Datadog · View in GitHub Actions

Unable to analyze file 'mall-admin/src/main/java/com/macro/mall/config/MallSecurityConfig.java': Trying to save symbol table twice is not supported.

View all 6 failed jobs.

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: b04aa3b | Docs | View more details | Give us feedback!

void test() {
CheckVerifier.newVerifier()
.onFile(mainCodeSourcesPath("checks/FinalizerAttackCheckSample.java"))
.withCheck(new FinalizerAttackCheck())

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We should add a test withoutSemantic

@github-actions

Copy link
Copy Markdown
Contributor

Ruling needs updating. A fix PR has been created: #5982

Please review and merge it into your branch.

"sqKey": "S9345",
"scope": "Main",
"defaultQualityProfiles": [
"Sonar way"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The rule should have been added to the quality profiles. rule-api needs to be re-run.

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Ruling Diff Summary

Detected changes in 4 rule files: 0 issues removed, 56 issues added.

S9345 (java) on commons-beanutils - 0 issues removed, 10 issues added - new ruling file

Added src/main/java/org/apache/commons/beanutils2/BeanPropertyValueChangeClosure.java (line 79)

        74 |  * </ul>
        75 |  *
        76 |  * @see org.apache.commons.beanutils2.PropertyUtils
        77 |  * @see org.apache.commons.collections4.Closure
        78 |  */
>>>     79 | public class BeanPropertyValueChangeClosure implements Closure {
        80 | 
        81 |     /** For logging. */
        82 |     private final Log log = LogFactory.getLog(this.getClass());
        83 | 
        84 |     /**

Added src/main/java/org/apache/commons/beanutils2/BeanPropertyValueEqualsPredicate.java (line 110)

       105 |  * </ul>
       106 |  *
       107 |  * @see org.apache.commons.beanutils2.PropertyUtils
       108 |  * @see org.apache.commons.collections4.Predicate
       109 |  */
>>>    110 | public class BeanPropertyValueEqualsPredicate implements Predicate {
       111 | 
       112 |     /** For logging. */
       113 |     private final Log log = LogFactory.getLog(this.getClass());
       114 | 
       115 |     /**

Added src/main/java/org/apache/commons/beanutils2/BeanToPropertyValueTransformer.java (line 71)

        66 |  * </ul>
        67 |  *
        68 |  * @see org.apache.commons.beanutils2.PropertyUtils
        69 |  * @see org.apache.commons.collections4.Transformer
        70 |  */
>>>     71 | public class BeanToPropertyValueTransformer implements Transformer {
        72 | 
        73 |     /** For logging. */
        74 |     private final Log log = LogFactory.getLog(this.getClass());
        75 | 
        76 |     /** The name of the property that will be used in the transformation of the object. */

Added src/main/java/org/apache/commons/beanutils2/FluentPropertyBeanIntrospector.java (line 78)

        73 |  * method.
        74 |  * </p>
        75 |  *
        76 |  * @since 1.9
        77 |  */
>>>     78 | public class FluentPropertyBeanIntrospector implements BeanIntrospector {
        79 |     /** The default prefix for write methods. */
        80 |     public static final String DEFAULT_WRITE_METHOD_PREFIX = "set";
        81 | 
        82 |     /** The logger. */
        83 |     private final Log log = LogFactory.getLog(getClass());

Added src/main/java/org/apache/commons/beanutils2/MappedPropertyDescriptor.java (line 44)

        39 |  * <p>where <code><strong>Property</strong></code> must be replaced
        40 |  * by the name of the property.
        41 |  * @see java.beans.PropertyDescriptor
        42 |  *
        43 |  */
>>>     44 | public class MappedPropertyDescriptor extends PropertyDescriptor {
        45 |     // ----------------------------------------------------- Instance Variables
        46 | 
        47 |     /**
        48 |      * The underlying data type of the property we are describing.
        49 |      */

Added src/main/java/org/apache/commons/beanutils2/MethodUtils.java (line 1304)

      1299 |     }
      1300 | 
      1301 |     /**
      1302 |      * Represents the key to looking up a Method by reflection.
      1303 |      */
>>>   1304 |     private static class MethodDescriptor {
      1305 |         private final Class<?> cls;
      1306 |         private final String methodName;
      1307 |         private final Class<?>[] paramTypes;
      1308 |         private final boolean exact;
      1309 |         private final int hashCode;

Added src/main/java/org/apache/commons/beanutils2/ResultSetDynaClass.java (line 82)

        77 |  *   }
        78 |  * </pre>
        79 |  *
        80 |  */
        81 | 
>>>     82 | public class ResultSetDynaClass extends JDBCDynaClass {
        83 | 
        84 |     private static final long serialVersionUID = 1L;
        85 | 
        86 |     // ----------------------------------------------------------- Constructors
        87 | 

Added src/main/java/org/apache/commons/beanutils2/RowSetDynaClass.java (line 66)

        61 |  * convenient mechanism for transporting data sets to remote Java-based
        62 |  * application components.</p>
        63 |  *
        64 |  */
        65 | 
>>>     66 | public class RowSetDynaClass extends JDBCDynaClass {
        67 | 
        68 |     private static final long serialVersionUID = 1L;
        69 | 
        70 |     // ----------------------------------------------------- Instance variables
        71 | 

Added src/main/java/org/apache/commons/beanutils2/SuppressPropertiesBeanIntrospector.java (line 38)

        33 |  * {@code BeanIntrospector} are removed again.
        34 |  * </p>
        35 |  *
        36 |  * @since 1.9.2
        37 |  */
>>>     38 | public class SuppressPropertiesBeanIntrospector implements BeanIntrospector {
        39 |     /**
        40 |      * A specialized instance which is configured to suppress the special {@code class}
        41 |      * properties of Java beans. Unintended access to the property {@code class} (which is
        42 |      * common to all Java objects) can be a security risk because it also allows access to
        43 |      * the class loader. Adding this instance as {@code BeanIntrospector} to an instance

Added src/main/java/org/apache/commons/beanutils2/converters/ArrayConverter.java (line 129)

       124 |  *    int[][] result = (int[][])matrixConverter.convert(int[][].class, matrixString);
       125 |  * </pre>
       126 |  *
       127 |  * @since 1.8.0
       128 |  */
>>>    129 | public class ArrayConverter extends AbstractConverter {
       130 | 
       131 |     private final Class<?> defaultType;
       132 |     private final Converter elementConverter;
       133 |     private int defaultSize;
       134 |     private char delimiter    = ',';
S9345 (java) on eclipse-jetty - 0 issues removed, 37 issues added - new ruling file

Added jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java (line 28)

(source file not found at this revision: jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java)

Added jetty-http/src/main/java/org/eclipse/jetty/http/HttpCookie.java (line 32)

(source file not found at this revision: jetty-http/src/main/java/org/eclipse/jetty/http/HttpCookie.java)

Added jetty-http/src/main/java/org/eclipse/jetty/http/PrecompressedHttpContent.java (line 30)

(source file not found at this revision: jetty-http/src/main/java/org/eclipse/jetty/http/PrecompressedHttpContent.java)

Added jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/UriTemplatePathSpec.java (line 41)

(source file not found at this revision: jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/UriTemplatePathSpec.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java (line 39)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ArrayByteBufferPool.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/CustomRequestLog.java (line 273)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/CustomRequestLog.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java (line 41)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/EncodingHttpWriter.java (line 29)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/EncodingHttpWriter.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelListeners.java (line 33)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelListeners.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java (line 83)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/MultiPartFormInputStream.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/ServletPathMapping.java (line 38)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/ServletPathMapping.java)

Added jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java (line 29)

(source file not found at this revision: jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONPojoConvertorFactory.java)

Added jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java (line 49)

(source file not found at this revision: jetty-util/src/main/java/org/eclipse/jetty/util/BlockingArrayQueue.java)

Added jetty-util/src/main/java/org/eclipse/jetty/util/ClassLoadingObjectInputStream.java (line 32)

(source file not found at this revision: jetty-util/src/main/java/org/eclipse/jetty/util/ClassLoadingObjectInputStream.java)

Added jetty-util/src/main/java/org/eclipse/jetty/util/CountingCallback.java (line 41)

(source file not found at this revision: jetty-util/src/main/java/org/eclipse/jetty/util/CountingCallback.java)
S9345 (java) on guava - 0 issues removed, 2 issues added - new ruling file

Added src/com/google/common/base/FinalizableReferenceQueue.java (line 94)

        89 |  * </pre>
        90 |  *
        91 |  * @author Bob Lee
        92 |  * @since 2.0
        93 |  */
>>>     94 | public class FinalizableReferenceQueue implements Closeable {
        95 |   /*
        96 |    * The Finalizer thread keeps a phantom reference to this object. When the client (for example, a
        97 |    * map built by MapMaker) no longer has a strong reference to this object, the garbage collector
        98 |    * will reclaim it and enqueue the phantom reference. The enqueued reference will trigger the
        99 |    * Finalizer to stop.

Added src/com/google/common/io/MultiReader.java (line 33)

        28 |  * A {@link Reader} that concatenates multiple readers.
        29 |  *
        30 |  * @author Bin Zhu
        31 |  * @since 1.0
        32 |  */
>>>     33 | class MultiReader extends Reader {
        34 |   private final Iterator<? extends CharSource> it;
        35 |   private Reader current;
        36 | 
        37 |   MultiReader(Iterator<? extends CharSource> readers) throws IOException {
        38 |     this.it = readers;
S9345 (java) on sonar-server - 0 issues removed, 7 issues added - new ruling file

Added src/main/java/org/sonar/server/computation/task/projectanalysis/source/ReportIterator.java (line 33)

(source file not found at this revision: src/main/java/org/sonar/server/computation/task/projectanalysis/source/ReportIterator.java)

Added src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java (line 53)

(source file not found at this revision: src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java)

Added src/main/java/org/sonar/server/platform/web/MasterServletFilter.java (line 42)

(source file not found at this revision: src/main/java/org/sonar/server/platform/web/MasterServletFilter.java)

Added src/main/java/org/sonar/server/plugins/UpdateCenterClient.java (line 64)

(source file not found at this revision: src/main/java/org/sonar/server/plugins/UpdateCenterClient.java)

Added src/main/java/org/sonar/server/user/SecurityRealmFactory.java (line 38)

(source file not found at this revision: src/main/java/org/sonar/server/user/SecurityRealmFactory.java)

Added src/main/java/org/sonar/server/util/ObjectInputStreamIterator.java (line 31)

(source file not found at this revision: src/main/java/org/sonar/server/util/ObjectInputStreamIterator.java)

Added src/main/java/org/sonar/server/util/cache/DiskCache.java (line 37)

(source file not found at this revision: src/main/java/org/sonar/server/util/cache/DiskCache.java)

Comment on lines +1 to +4
{
"com.macro.mall:mall:mall-admin/src/main/java/com/macro/mall/dto/PmsProductResult.java": [
11
]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Quality: Unrelated ruling results (S2160, S6212) bundled into new-rule PR

This PR adds rule S9345, but it also introduces a new S2160 expected-results file and adds line 91 to the S6212 results for the mall project. These rules are unrelated to the Finalizer-attack rule and the ruling projects (mall, guava, etc.) are unaffected by the test-source and check additions here, so these diffs likely reflect stale/regenerated results being swept in rather than an intended change. Bundling unrelated rule-result churn into a feature PR obscures whether it hides a real regression; either split these out into a dedicated ruling-update PR or confirm they are expected and intentional.

Was this helpful? React with 👍 / 👎

@github-actions

Copy link
Copy Markdown
Contributor

Ruling needs updating. A fix PR has been created: #5983

Please review and merge it into your branch.

romainbrenguier and others added 3 commits August 21, 2026 14:07
…rs should be protected against Finalizer attacks

Detect non-final, non-abstract classes whose non-private constructors
can throw exceptions (via throws clause or throw statements in the body),
making them vulnerable to Finalizer attacks through malicious subclasses.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
🤖 Generated with GitHub Actions
…houtSemantic test

- Make inner classes static in FinalizerAttackCheckSample to fix
  "non-static variable this cannot be referenced from a static context"
  compilation error caused by FactoryService's static factory method
- Add S9345 placeholder to Sonar way quality profile
- Add withoutSemantic test since the check only uses syntactic analysis

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nathsou
nathsou force-pushed the romain/new-rule-s9345-sonarjava-6783 branch from d5edb3f to ea06a0f Compare August 21, 2026 12:08
@github-actions

Copy link
Copy Markdown
Contributor

Ruling needs updating. A fix PR has been created: #5983

Please review and merge it into your branch.

…ass as secondary

The main issue location is now on the throwing constructor (primary)
with the class declaration as a secondary location, instead of the
other way around. Each vulnerable constructor gets its own issue.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Aug 21, 2026

Copy link
Copy Markdown
CI failed: CI failures caused by a compilation error in a test sample (referencing 'this' from a static context) and integration test ruling mismatches from the new S9345 rule implementation.

Overview

Two distinct change-related failure patterns were identified across the CI logs: a compilation failure due to an invalid static reference in FinalizerAttackCheckSample.java, and integration/ruling test failures caused by unexpected rule behavior or ruling expectation mismatches for the newly introduced rule S9345.

Failures

Compilation Error in FinalizerAttackCheckSample (confidence: high)

  • Type: build
  • Affected jobs: 96709352806
  • Related to change: yes
  • Root cause: In java-checks-test-sources/default/src/main/java/checks/FinalizerAttackCheckSample.java at line 78, a non-static variable this is being referenced from a static context.
  • Suggested fix: Fix line 78 in FinalizerAttackCheckSample.java by removing the reference to this from the static context or making the context/variable static appropriately.

Java Ruling Integration Test Failure / Rule Exception (confidence: high)

  • Type: test
  • Affected jobs: 96708954686
  • Related to change: yes
  • Root cause: The newly introduced rule S9345 (FinalizerAttackCheck) encountered NullPointerExceptions and caused differences in issues during the integration ruling tests (JavaRulingTest.sonarqube_server).
  • Suggested fix: Review FinalizerAttackCheck.java for null safety when visiting nodes, ensure proper symbol table handling, and update the expected ruling test dumps if the generated issues are correct.

Summary

  • Change-related failures: 2 failures (one compilation error in the test sample, and ruling/integration test failures related to rule S9345).
  • Infrastructure/flaky failures: 0 infrastructure or flaky failures.
  • Recommended action: Fix the non-static reference in FinalizerAttackCheckSample.java, ensure FinalizerAttackCheck.java handles nodes without throwing NullPointerExceptions, and update the ruling expectations accordingly.
Code Review 👍 Approved with suggestions 0 resolved / 3 findings

Implements rule S9345 to detect classes with throwing constructors vulnerable to Finalizer attacks. Consider addressing false positives on sealed classes, handling throwing initializers, and separating unrelated ruling changes.

💡 Edge Case: Sealed classes flagged as noncompliant (false positive)

📄 java-checks/src/main/java/org/sonar/java/checks/FinalizerAttackCheck.java:45-48

A sealed non-final class cannot be subclassed by an attacker — its permitted subclasses are fixed at compile time — so it is not exploitable by a Finalizer attack, yet the check only exempts final and abstract classes and would report a sealed class with a throwing constructor. Consider also returning early when ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.SEALED) is true (subject to RSPEC scope), to avoid false positives on sealed hierarchies.

Also skip sealed classes, which attackers cannot subclass.
if (ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.FINAL) ||
  ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.ABSTRACT) ||
  ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.SEALED)) {
  return;
}
💡 Edge Case: Throwing field/instance initializers not detected

📄 java-checks/src/main/java/org/sonar/java/checks/FinalizerAttackCheck.java:49-63

The check only inspects CONSTRUCTOR members and scans each constructor's own block, so a throw that occurs in an instance initializer block or a field initializer (e.g. private final X x = compute(); where compute() throws) is missed even though those run during construction. A class with no explicit constructor but a throwing field/instance initializer has no CONSTRUCTOR member at all and is never flagged, a false negative for the same finalizer-attack vector. Consider also examining instance initializer blocks and field initializers, and accounting for the implicit default constructor.

💡 Quality: Unrelated ruling results (S2160, S6212) bundled into new-rule PR

📄 its/ruling/src/test/resources/mall/java-S2160.json:1-4 📄 its/ruling/src/test/resources/mall/java-S6212.json:13

This PR adds rule S9345, but it also introduces a new S2160 expected-results file and adds line 91 to the S6212 results for the mall project. These rules are unrelated to the Finalizer-attack rule and the ruling projects (mall, guava, etc.) are unaffected by the test-source and check additions here, so these diffs likely reflect stale/regenerated results being swept in rather than an intended change. Bundling unrelated rule-result churn into a feature PR obscures whether it hides a real regression; either split these out into a dedicated ruling-update PR or confirm they are expected and intentional.

🤖 Prompt for agents
Code Review: Implements rule S9345 to detect classes with throwing constructors vulnerable to Finalizer attacks. Consider addressing false positives on sealed classes, handling throwing initializers, and separating unrelated ruling changes.

1. 💡 Edge Case: Sealed classes flagged as noncompliant (false positive)
   Files: java-checks/src/main/java/org/sonar/java/checks/FinalizerAttackCheck.java:45-48

   A `sealed` non-final class cannot be subclassed by an attacker — its permitted subclasses are fixed at compile time — so it is not exploitable by a Finalizer attack, yet the check only exempts `final` and `abstract` classes and would report a sealed class with a throwing constructor. Consider also returning early when `ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.SEALED)` is true (subject to RSPEC scope), to avoid false positives on sealed hierarchies.

   Fix (Also skip sealed classes, which attackers cannot subclass.):
   if (ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.FINAL) ||
     ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.ABSTRACT) ||
     ModifiersUtils.hasModifier(classTree.modifiers(), Modifier.SEALED)) {
     return;
   }

2. 💡 Edge Case: Throwing field/instance initializers not detected
   Files: java-checks/src/main/java/org/sonar/java/checks/FinalizerAttackCheck.java:49-63

   The check only inspects `CONSTRUCTOR` members and scans each constructor's own block, so a throw that occurs in an instance initializer block or a field initializer (e.g. `private final X x = compute();` where `compute()` throws) is missed even though those run during construction. A class with no explicit constructor but a throwing field/instance initializer has no `CONSTRUCTOR` member at all and is never flagged, a false negative for the same finalizer-attack vector. Consider also examining instance initializer blocks and field initializers, and accounting for the implicit default constructor.

3. 💡 Quality: Unrelated ruling results (S2160, S6212) bundled into new-rule PR
   Files: its/ruling/src/test/resources/mall/java-S2160.json:1-4, its/ruling/src/test/resources/mall/java-S6212.json:13

   This PR adds rule S9345, but it also introduces a new S2160 expected-results file and adds line 91 to the S6212 results for the `mall` project. These rules are unrelated to the Finalizer-attack rule and the ruling projects (mall, guava, etc.) are unaffected by the test-source and check additions here, so these diffs likely reflect stale/regenerated results being swept in rather than an intended change. Bundling unrelated rule-result churn into a feature PR obscures whether it hides a real regression; either split these out into a dedicated ruling-update PR or confirm they are expected and intentional.

Implementation Status ✅ 1 / 1 issues implemented
SONARJAVA-6783 — 1 / 1 objectives

The PR successfully implements rule S9345 to detect non-final classes with throwing constructors and protect them against Finalizer attacks.

✅ 1 complete
  • ✅ Implement rule S9345 to protect classes with throwing constructors against Finalizer attacks

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

Copy link
Copy Markdown
Contributor

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.

1 participant