From 325a5095449dc73d0ae35327b8e9bd3ba98c9749 Mon Sep 17 00:00:00 2001
From: Romain Brenguier
Date: Fri, 26 Jun 2026 16:55:16 +0200
Subject: [PATCH 1/4] Add S8913 metadata and SonarWay profile entry
---
.../org/sonar/l10n/java/rules/java/S8913.html | 31 ++++++++++++++++
.../org/sonar/l10n/java/rules/java/S8913.json | 28 +++++++++++++++
.../java/rules/java/Sonar_way_profile.json | 35 ++++++++++---------
3 files changed, 77 insertions(+), 17 deletions(-)
create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html
create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html
new file mode 100644
index 00000000000..1dccdd01c95
--- /dev/null
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html
@@ -0,0 +1,31 @@
+=== Documentation
+
+* Quarkus - Generating Jakarta REST resources with Panache - https://quarkus.io/guides/rest-data-panache[Official Quarkus documentation on REST Data with Panache, including how to properly use resource interfaces and add custom methods]
+
+* Quarkus - Adding additional methods to the generated resource - https://quarkus.io/guides/rest-data-panache#adding-additional-methods-to-the-generated-resource[Specific section on how to correctly add custom methods using default interface methods]
+
+
+=== What is the potential impact?
+
+This issue leads to wasted development effort and potential confusion when debugging. Developers may spend time writing and maintaining implementation code that has no effect on the application's behavior. The silent nature of this problem makes it particularly problematic, as there are no build-time errors or runtime warnings to indicate that the custom implementation is being ignored.
+
+While this is not a security or data integrity issue, it can delay development and lead to frustration when custom business logic doesn't execute as expected.
+
+This rule raises an issue when a class implements an interface that extends one of the framework's provided base resource interfaces for REST endpoints with built-in database entity operations.
+
+== Why is this an issue?
+
+Certain frameworks use code generation mechanisms to automatically create complete implementations from interface definitions or contract specifications. When you define an interface that extends framework-specific base interfaces designed for resource management, the framework generates the complete implementation at build time.
+
+The framework processes these interfaces during the build phase and creates resource handlers with standard operations (create, read, update, delete, list). This generation happens automatically without requiring any manual implementation.
+
+If you create a class that implements such an interface, the framework will ignore your implementation entirely. The code generator doesn't check for existing implementations -- it simply creates its own implementation class. This means:
+
+* Your custom implementation code will never be executed
+* Any method overrides you write will have no effect
+* The application will use the auto-generated implementation instead
+* You'll waste development time writing code that serves no purpose
+
+This silent failure can be particularly confusing because the application will compile and run without errors, but your custom logic won't execute. Developers may spend significant time debugging why their code changes aren't taking effect.
+
+The only supported way to add custom functionality is through extension mechanisms that are explicitly recognized by the code generation process, such as inline method implementations within the interface definition itself. These inline implementations are respected by the code generation mechanism and will be included in the final generated resource alongside the standard operations.
\ No newline at end of file
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json
new file mode 100644
index 00000000000..a39e10b92c1
--- /dev/null
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json
@@ -0,0 +1,28 @@
+{
+ "title": "REST Data with Panache resource interfaces should not have implementation classes",
+ "type": "CODE_SMELL",
+ "status": "ready",
+ "remediation": {
+ "func": "Constant/Issue",
+ "constantCost": "10min"
+ },
+ "tags": [
+ "quarkus",
+ "rest",
+ "confusing"
+ ],
+ "defaultSeverity": "Major",
+ "ruleSpecification": "RSPEC-8913",
+ "sqKey": "S8913",
+ "scope": "All",
+ "defaultQualityProfiles": [
+ "Sonar way"
+ ],
+ "quickfix": "unknown",
+ "code": {
+ "impacts": {
+ "MAINTAINABILITY": "MEDIUM"
+ },
+ "attribute": "CLEAR"
+ }
+}
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json
index 823ca6f2864..45f2b97b390 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json
@@ -4,26 +4,13 @@
"S100",
"S101",
"S106",
- "S107",
- "S108",
- "S110",
- "S112",
- "S114",
- "S115",
- "S116",
- "S117",
- "S119",
- "S120",
- "S125",
- "S127",
- "S128",
- "S131",
- "S135",
- "S899",
"S1065",
"S1066",
"S1068",
+ "S107",
"S1075",
+ "S108",
+ "S110",
"S1110",
"S1111",
"S1113",
@@ -31,6 +18,7 @@
"S1117",
"S1118",
"S1119",
+ "S112",
"S1121",
"S1123",
"S1124",
@@ -41,18 +29,22 @@
"S1133",
"S1134",
"S1135",
+ "S114",
"S1141",
"S1143",
"S1144",
+ "S115",
"S1150",
"S1153",
"S1155",
"S1157",
"S1158",
+ "S116",
"S1161",
"S1163",
"S1165",
"S1168",
+ "S117",
"S1170",
"S1171",
"S1172",
@@ -62,6 +54,7 @@
"S1182",
"S1185",
"S1186",
+ "S119",
"S1190",
"S1191",
"S1192",
@@ -69,6 +62,7 @@
"S1195",
"S1197",
"S1199",
+ "S120",
"S1201",
"S1206",
"S1210",
@@ -80,11 +74,16 @@
"S1221",
"S1223",
"S1226",
+ "S125",
"S1264",
+ "S127",
+ "S128",
"S1301",
+ "S131",
"S1313",
"S1317",
"S1319",
+ "S135",
"S1450",
"S1452",
"S1479",
@@ -535,6 +534,8 @@
"S8786",
"S8909",
"S8911",
- "S8924"
+ "S8913",
+ "S8924",
+ "S899"
]
}
From d74b754ad719dfb331a8a6b0156c314ee951f55c Mon Sep 17 00:00:00 2001
From: Romain Brenguier
Date: Fri, 26 Jun 2026 17:04:45 +0200
Subject: [PATCH 2/4] Implement new rule S8913
Detect when classes implement interfaces extending Quarkus REST Data with
Panache resource interfaces (PanacheEntityResource, PanacheRepositoryResource,
PanacheMongoEntityResource, PanacheMongoRepositoryResource). These implementations
are silently ignored by Quarkus's code generation mechanism, leading to wasted
development effort and confusion.
---
.../resources/autoscan/diffs/diff_S8913.json | 6 ++
...acheResourceImplementationCheckSample.java | 95 ++++++++++++++++++
.../orm/panache/PanacheEntityBase.java | 4 +
.../orm/panache/PanacheRepositoryBase.java | 4 +
.../data/panache/PanacheEntityResource.java | 4 +
.../panache/PanacheRepositoryResource.java | 4 +
.../panache/PanacheMongoEntityBase.java | 4 +
.../panache/PanacheMongoRepositoryBase.java | 4 +
.../panache/PanacheMongoEntityResource.java | 4 +
.../PanacheMongoRepositoryResource.java | 4 +
...ataPanacheResourceImplementationCheck.java | 62 ++++++++++++
...anacheResourceImplementationCheckTest.java | 42 ++++++++
.../org/sonar/l10n/java/rules/java/S8913.html | 97 +++++++++++++------
.../org/sonar/l10n/java/rules/java/S8913.json | 5 +-
.../java/rules/java/Sonar_way_profile.json | 34 +++----
15 files changed, 324 insertions(+), 49 deletions(-)
create mode 100644 its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json
create mode 100644 java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheEntityResource.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheRepositoryResource.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoEntityResource.java
create mode 100644 java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoRepositoryResource.java
create mode 100644 java-checks/src/main/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheck.java
create mode 100644 java-checks/src/test/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheckTest.java
diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json
new file mode 100644
index 00000000000..8369876f975
--- /dev/null
+++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json
@@ -0,0 +1,6 @@
+{
+ "ruleKey": "S8913",
+ "hasTruePositives": false,
+ "falseNegatives": 6,
+ "falsePositives": 0
+}
\ No newline at end of file
diff --git a/java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java
new file mode 100644
index 00000000000..c0305b887ac
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java
@@ -0,0 +1,95 @@
+package checks;
+
+import io.quarkus.hibernate.orm.panache.PanacheEntityBase;
+import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
+import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource;
+import io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource;
+import io.quarkus.mongodb.panache.PanacheMongoEntityBase;
+import io.quarkus.mongodb.panache.PanacheMongoRepositoryBase;
+import io.quarkus.mongodb.rest.data.panache.PanacheMongoEntityResource;
+import io.quarkus.mongodb.rest.data.panache.PanacheMongoRepositoryResource;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import java.util.Collections;
+import java.util.List;
+
+class Person extends PanacheEntityBase {
+ public Long id;
+ public String name;
+}
+
+class PersonRepository implements PanacheRepositoryBase {
+}
+
+class MongoPerson extends PanacheMongoEntityBase {
+ public Long id;
+ public String name;
+}
+
+class MongoPersonRepository implements PanacheMongoRepositoryBase {
+}
+
+interface PeopleResource extends PanacheEntityResource {
+}
+
+class PeopleResourceImpl implements PeopleResource { // Noncompliant {{Remove this implementation class; Quarkus generates the resource implementation automatically.}}
+ @Override
+ public Person get(Long id) {
+ return null;
+ }
+}
+
+interface PersonRepositoryResource extends PanacheRepositoryResource {
+}
+
+class PersonRepositoryResourceImpl implements PersonRepositoryResource { // Noncompliant
+}
+
+interface MongoPersonResource extends PanacheMongoEntityResource {
+}
+
+class MongoPersonResourceImpl implements MongoPersonResource { // Noncompliant
+}
+
+interface MongoPersonRepositoryResource extends PanacheMongoRepositoryResource {
+}
+
+class MongoPersonRepositoryResourceImpl implements MongoPersonRepositoryResource { // Noncompliant
+}
+
+abstract class AbstractPersonResource implements PeopleResource { // Noncompliant
+}
+
+class ConcretePersonResource extends AbstractPersonResource { // Noncompliant
+}
+
+interface CompliantResource extends PanacheEntityResource {
+}
+
+interface ResourceWithDefaults extends PanacheEntityResource {
+ @GET
+ @Path("/name/{name}")
+ @Produces("application/json")
+ default List findByName(@PathParam("name") String name) {
+ return Collections.emptyList();
+ }
+}
+
+interface RegularRestInterface {
+ Person get(Long id);
+}
+
+class RegularRestImpl implements RegularRestInterface {
+ @Override
+ public Person get(Long id) {
+ return null;
+ }
+}
+
+class PersonService {
+ public Person findById(Long id) {
+ return null;
+ }
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java
new file mode 100644
index 00000000000..38b78403cb7
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheEntityBase.java
@@ -0,0 +1,4 @@
+package io.quarkus.hibernate.orm.panache;
+
+public abstract class PanacheEntityBase {
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java
new file mode 100644
index 00000000000..9c52ec5abaf
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/panache/PanacheRepositoryBase.java
@@ -0,0 +1,4 @@
+package io.quarkus.hibernate.orm.panache;
+
+public interface PanacheRepositoryBase {
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheEntityResource.java b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheEntityResource.java
new file mode 100644
index 00000000000..5725d137eea
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheEntityResource.java
@@ -0,0 +1,4 @@
+package io.quarkus.hibernate.orm.rest.data.panache;
+
+public interface PanacheEntityResource {
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheRepositoryResource.java b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheRepositoryResource.java
new file mode 100644
index 00000000000..1f9cfebdb1a
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/hibernate/orm/rest/data/panache/PanacheRepositoryResource.java
@@ -0,0 +1,4 @@
+package io.quarkus.hibernate.orm.rest.data.panache;
+
+public interface PanacheRepositoryResource {
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java
new file mode 100644
index 00000000000..03824cd350d
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoEntityBase.java
@@ -0,0 +1,4 @@
+package io.quarkus.mongodb.panache;
+
+public abstract class PanacheMongoEntityBase {
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java
new file mode 100644
index 00000000000..de0a9e2098b
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/panache/PanacheMongoRepositoryBase.java
@@ -0,0 +1,4 @@
+package io.quarkus.mongodb.panache;
+
+public interface PanacheMongoRepositoryBase {
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoEntityResource.java b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoEntityResource.java
new file mode 100644
index 00000000000..9dc5bb856dc
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoEntityResource.java
@@ -0,0 +1,4 @@
+package io.quarkus.mongodb.rest.data.panache;
+
+public interface PanacheMongoEntityResource {
+}
diff --git a/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoRepositoryResource.java b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoRepositoryResource.java
new file mode 100644
index 00000000000..e5688d42f17
--- /dev/null
+++ b/java-checks-test-sources/default/src/main/java/io/quarkus/mongodb/rest/data/panache/PanacheMongoRepositoryResource.java
@@ -0,0 +1,4 @@
+package io.quarkus.mongodb.rest.data.panache;
+
+public interface PanacheMongoRepositoryResource {
+}
diff --git a/java-checks/src/main/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheck.java b/java-checks/src/main/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheck.java
new file mode 100644
index 00000000000..e7ff96b3161
--- /dev/null
+++ b/java-checks/src/main/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheck.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube Java
+ * Copyright (C) SonarSource Sàrl
+ * mailto:info AT sonarsource DOT com
+ *
+ * You can redistribute and/or modify this program under the terms of
+ * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the Sonar Source-Available License for more details.
+ *
+ * You should have received a copy of the Sonar Source-Available License
+ * along with this program; if not, see https://sonarsource.com/license/ssal/
+ */
+package org.sonar.java.checks;
+
+import java.util.Collections;
+import java.util.List;
+import org.sonar.check.Rule;
+import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
+import org.sonar.plugins.java.api.semantic.Type;
+import org.sonar.plugins.java.api.tree.ClassTree;
+import org.sonar.plugins.java.api.tree.Tree;
+
+@Rule(key = "S8913")
+public class RestDataPanacheResourceImplementationCheck extends IssuableSubscriptionVisitor {
+
+ private static final String PANACHE_ENTITY_RESOURCE = "io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource";
+ private static final String PANACHE_REPOSITORY_RESOURCE = "io.quarkus.hibernate.orm.rest.data.panache.PanacheRepositoryResource";
+ private static final String PANACHE_MONGO_ENTITY_RESOURCE = "io.quarkus.mongodb.rest.data.panache.PanacheMongoEntityResource";
+ private static final String PANACHE_MONGO_REPOSITORY_RESOURCE = "io.quarkus.mongodb.rest.data.panache.PanacheMongoRepositoryResource";
+
+ @Override
+ public List nodesToVisit() {
+ return Collections.singletonList(Tree.Kind.CLASS);
+ }
+
+ @Override
+ public void visitNode(Tree tree) {
+ ClassTree classTree = (ClassTree) tree;
+ if (classTree.symbol().isUnknown()) {
+ return;
+ }
+ if (implementsPanacheResourceInterface(classTree)) {
+ reportIssue(classTree.simpleName(), "Remove this implementation class; Quarkus generates the resource implementation automatically.");
+ }
+ }
+
+ private static boolean implementsPanacheResourceInterface(ClassTree classTree) {
+ Type classType = classTree.symbol().type();
+ return isPanacheResourceType(classType);
+ }
+
+ private static boolean isPanacheResourceType(Type type) {
+ return type.isSubtypeOf(PANACHE_ENTITY_RESOURCE)
+ || type.isSubtypeOf(PANACHE_REPOSITORY_RESOURCE)
+ || type.isSubtypeOf(PANACHE_MONGO_ENTITY_RESOURCE)
+ || type.isSubtypeOf(PANACHE_MONGO_REPOSITORY_RESOURCE);
+ }
+}
diff --git a/java-checks/src/test/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheckTest.java
new file mode 100644
index 00000000000..f33adf1df87
--- /dev/null
+++ b/java-checks/src/test/java/org/sonar/java/checks/RestDataPanacheResourceImplementationCheckTest.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube Java
+ * Copyright (C) SonarSource Sàrl
+ * mailto:info AT sonarsource DOT com
+ *
+ * You can redistribute and/or modify this program under the terms of
+ * the Sonar Source-Available License Version 1, as published by SonarSource Sàrl.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the Sonar Source-Available License for more details.
+ *
+ * You should have received a copy of the Sonar Source-Available License
+ * along with this program; if not, see https://sonarsource.com/license/ssal/
+ */
+package org.sonar.java.checks;
+
+import org.junit.jupiter.api.Test;
+import org.sonar.java.checks.verifier.CheckVerifier;
+
+import static org.sonar.java.checks.verifier.TestUtils.mainCodeSourcesPath;
+
+class RestDataPanacheResourceImplementationCheckTest {
+
+ @Test
+ void test() {
+ CheckVerifier.newVerifier()
+ .onFile(mainCodeSourcesPath("checks/RestDataPanacheResourceImplementationCheckSample.java"))
+ .withCheck(new RestDataPanacheResourceImplementationCheck())
+ .verifyIssues();
+ }
+
+ @Test
+ void testWithoutSemantic() {
+ CheckVerifier.newVerifier()
+ .onFile(mainCodeSourcesPath("checks/RestDataPanacheResourceImplementationCheckSample.java"))
+ .withCheck(new RestDataPanacheResourceImplementationCheck())
+ .withoutSemantic()
+ .verifyNoIssues();
+ }
+}
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html
index 1dccdd01c95..e798bed821a 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.html
@@ -1,31 +1,72 @@
-=== Documentation
+
This rule raises an issue when a class implements an interface that extends one of the framework’s provided base resource interfaces for REST
+endpoints with built-in database entity operations.
+In Java with Quarkus, these interfaces are specifically: PanacheEntityResource, PanacheRepositoryResource,
+PanacheMongoEntityResource, and PanacheMongoRepositoryResource.
+Why is this an issue?
+Certain frameworks use code generation mechanisms to automatically create complete implementations from interface definitions or contract
+specifications. When you define an interface that extends framework-specific base interfaces designed for resource management, the framework generates
+the complete implementation at build time.
+The framework processes these interfaces during the build phase and creates resource handlers with standard operations (create, read, update,
+delete, list). This generation happens automatically without requiring any manual implementation.
+If you create a class that implements such an interface, the framework will ignore your implementation entirely. The code generator doesn’t check
+for existing implementations — it simply creates its own implementation class. This means:
+
+ - Your custom implementation code will never be executed
+ - Any method overrides you write will have no effect
+ - The application will use the auto-generated implementation instead
+ - You’ll waste development time writing code that serves no purpose
+
+This silent failure can be particularly confusing because the application will compile and run without errors, but your custom logic won’t execute.
+Developers may spend significant time debugging why their code changes aren’t taking effect.
+The only supported way to add custom functionality is through extension mechanisms that are explicitly recognized by the code generation process,
+such as inline method implementations within the interface definition itself. These inline implementations are respected by the code generation
+mechanism and will be included in the final generated resource alongside the standard operations.
+In Quarkus REST Data with Panache, this applies to interfaces extending PanacheEntityResource or
+PanacheRepositoryResource. The framework generates Jakarta REST resources at build time. The supported extension mechanism is
+default methods defined directly in the resource interface.
+What is the potential impact?
+This issue leads to wasted development effort and potential confusion when debugging. Developers may spend time writing and maintaining
+implementation code that has no effect on the application’s behavior. The silent nature of this problem makes it particularly problematic, as there
+are no build-time errors or runtime warnings to indicate that the custom implementation is being ignored.
+While this is not a security or data integrity issue, it can delay development and lead to frustration when custom business logic doesn’t execute
+as expected.
+How to fix it
+Remove the implementation class entirely. If you need custom methods, add them as default methods directly in the resource interface. Default
+methods will be included in the generated resource alongside the standard CRUD operations.
+Code examples
+Noncompliant code example
+
+public interface PeopleResource extends PanacheEntityResource<Person, Long> {
+}
-* Quarkus - Generating Jakarta REST resources with Panache - https://quarkus.io/guides/rest-data-panache[Official Quarkus documentation on REST Data with Panache, including how to properly use resource interfaces and add custom methods]
+// Noncompliant: this implementation will be completely ignored
+public class PeopleResourceImpl implements PeopleResource {
+ @Override
+ public Person get(Long id) {
+ return Person.findById(id);
+ }
+}
+
+Compliant solution
+
+// Compliant: only the interface is needed, with custom methods as defaults
+public interface PeopleResource extends PanacheEntityResource<Person, Long> {
-* Quarkus - Adding additional methods to the generated resource - https://quarkus.io/guides/rest-data-panache#adding-additional-methods-to-the-generated-resource[Specific section on how to correctly add custom methods using default interface methods]
+ @GET
+ @Path("/name/{name}")
+ @Produces("application/json")
+ default List<Person> findByName(@PathParam("name") String name) {
+ return Person.find("name = :name", Collections.singletonMap("name", name)).list();
+ }
+}
+
+Resources
+Documentation
+
-
-=== What is the potential impact?
-
-This issue leads to wasted development effort and potential confusion when debugging. Developers may spend time writing and maintaining implementation code that has no effect on the application's behavior. The silent nature of this problem makes it particularly problematic, as there are no build-time errors or runtime warnings to indicate that the custom implementation is being ignored.
-
-While this is not a security or data integrity issue, it can delay development and lead to frustration when custom business logic doesn't execute as expected.
-
-This rule raises an issue when a class implements an interface that extends one of the framework's provided base resource interfaces for REST endpoints with built-in database entity operations.
-
-== Why is this an issue?
-
-Certain frameworks use code generation mechanisms to automatically create complete implementations from interface definitions or contract specifications. When you define an interface that extends framework-specific base interfaces designed for resource management, the framework generates the complete implementation at build time.
-
-The framework processes these interfaces during the build phase and creates resource handlers with standard operations (create, read, update, delete, list). This generation happens automatically without requiring any manual implementation.
-
-If you create a class that implements such an interface, the framework will ignore your implementation entirely. The code generator doesn't check for existing implementations -- it simply creates its own implementation class. This means:
-
-* Your custom implementation code will never be executed
-* Any method overrides you write will have no effect
-* The application will use the auto-generated implementation instead
-* You'll waste development time writing code that serves no purpose
-
-This silent failure can be particularly confusing because the application will compile and run without errors, but your custom logic won't execute. Developers may spend significant time debugging why their code changes aren't taking effect.
-
-The only supported way to add custom functionality is through extension mechanisms that are explicitly recognized by the code generation process, such as inline method implementations within the interface definition itself. These inline implementations are respected by the code generation mechanism and will be included in the final generated resource alongside the standard operations.
\ No newline at end of file
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json
index a39e10b92c1..b97ed1820b8 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8913.json
@@ -3,7 +3,7 @@
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
- "func": "Constant/Issue",
+ "func": "Constant\/Issue",
"constantCost": "10min"
},
"tags": [
@@ -15,9 +15,6 @@
"ruleSpecification": "RSPEC-8913",
"sqKey": "S8913",
"scope": "All",
- "defaultQualityProfiles": [
- "Sonar way"
- ],
"quickfix": "unknown",
"code": {
"impacts": {
diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json
index 45f2b97b390..8e0e6914c84 100644
--- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json
+++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/Sonar_way_profile.json
@@ -4,13 +4,26 @@
"S100",
"S101",
"S106",
+ "S107",
+ "S108",
+ "S110",
+ "S112",
+ "S114",
+ "S115",
+ "S116",
+ "S117",
+ "S119",
+ "S120",
+ "S125",
+ "S127",
+ "S128",
+ "S131",
+ "S135",
+ "S899",
"S1065",
"S1066",
"S1068",
- "S107",
"S1075",
- "S108",
- "S110",
"S1110",
"S1111",
"S1113",
@@ -18,7 +31,6 @@
"S1117",
"S1118",
"S1119",
- "S112",
"S1121",
"S1123",
"S1124",
@@ -29,22 +41,18 @@
"S1133",
"S1134",
"S1135",
- "S114",
"S1141",
"S1143",
"S1144",
- "S115",
"S1150",
"S1153",
"S1155",
"S1157",
"S1158",
- "S116",
"S1161",
"S1163",
"S1165",
"S1168",
- "S117",
"S1170",
"S1171",
"S1172",
@@ -54,7 +62,6 @@
"S1182",
"S1185",
"S1186",
- "S119",
"S1190",
"S1191",
"S1192",
@@ -62,7 +69,6 @@
"S1195",
"S1197",
"S1199",
- "S120",
"S1201",
"S1206",
"S1210",
@@ -74,16 +80,11 @@
"S1221",
"S1223",
"S1226",
- "S125",
"S1264",
- "S127",
- "S128",
"S1301",
- "S131",
"S1313",
"S1317",
"S1319",
- "S135",
"S1450",
"S1452",
"S1479",
@@ -535,7 +536,6 @@
"S8909",
"S8911",
"S8913",
- "S8924",
- "S899"
+ "S8924"
]
}
From d6cd45f226d8552dba92fe6be3f30e0473c6b281 Mon Sep 17 00:00:00 2001
From: Romain Brenguier
Date: Wed, 1 Jul 2026 09:56:15 +0200
Subject: [PATCH 3/4] Fix CI failures
- Removed @Override method from PeopleResourceImpl in test sample because PanacheEntityResource stub has no methods to override, causing compilation error
- Removed @Override get(Long) method from PeopleResourceImpl in test sample because the stub PanacheEntityResource interface has no get method to override
- Removed @Override get(Long) method from PeopleResourceImpl in test sample file because PanacheEntityResource does not declare that method, causing a compilation error
- Removed @Override get(Long) method from PeopleResourceImpl in test sample because PanacheEntityResource stub is an empty interface with no methods to override, causing compilation failure
---
.../RestDataPanacheResourceImplementationCheckSample.java | 4 ----
1 file changed, 4 deletions(-)
diff --git a/java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java
index c0305b887ac..56ec4a21e9e 100644
--- a/java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java
+++ b/java-checks-test-sources/default/src/main/java/checks/RestDataPanacheResourceImplementationCheckSample.java
@@ -35,10 +35,6 @@ interface PeopleResource extends PanacheEntityResource {
}
class PeopleResourceImpl implements PeopleResource { // Noncompliant {{Remove this implementation class; Quarkus generates the resource implementation automatically.}}
- @Override
- public Person get(Long id) {
- return null;
- }
}
interface PersonRepositoryResource extends PanacheRepositoryResource {
From d8e570dccbf084864a8367ad438572929b7aeb31 Mon Sep 17 00:00:00 2001
From: Romain Brenguier
Date: Wed, 1 Jul 2026 10:07:16 +0200
Subject: [PATCH 4/4] Fix CI: Updated diff_S8913.json to reflect that S8913
reports the same issues with and without bytecode (hasTruePositives=true,
falseNegatives=0)
---
.../src/test/resources/autoscan/diffs/diff_S8913.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json b/its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json
index 8369876f975..0eee55e9f3a 100644
--- a/its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json
+++ b/its/autoscan/src/test/resources/autoscan/diffs/diff_S8913.json
@@ -1,6 +1,6 @@
{
"ruleKey": "S8913",
- "hasTruePositives": false,
- "falseNegatives": 6,
+ "hasTruePositives": true,
+ "falseNegatives": 0,
"falsePositives": 0
}
\ No newline at end of file