From 8f1cc61cf6f62bf84031c60a3ee9c70165afd1df Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 24 Aug 2026 12:58:44 +0200 Subject: [PATCH 1/6] SONARJAVA-6826: Implemented rule S9359 (Octal escape sequences should not be followed by digits) This rule detects octal escape sequences in string literals that are followed by additional digits, which can create ambiguity about the intended character sequence. The rule identifies patterns like '\128' where it's unclear whether this represents octal escape '\12' followed by literal '8', or a malformed escape sequence. Files added: - OctalEscapeSequenceFollowedByDigitCheck.java: Rule implementation - OctalEscapeSequenceFollowedByDigitCheckTest.java: Unit tests - OctalEscapeSequenceFollowedByDigitCheckSample.java: Test samples - S9359.html, S9359.json: Rule metadata --- ...apeSequenceFollowedByDigitCheckSample.java | 40 +++++++++ ...talEscapeSequenceFollowedByDigitCheck.java | 90 +++++++++++++++++++ ...scapeSequenceFollowedByDigitCheckTest.java | 42 +++++++++ .../org/sonar/l10n/java/rules/java/S9359.html | 56 ++++++++++++ .../org/sonar/l10n/java/rules/java/S9359.json | 24 +++++ .../main/resources/profiles/Sonar_way/S9359 | 0 6 files changed, 252 insertions(+) create mode 100644 java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java create mode 100644 java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java create mode 100644 java-checks/src/test/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheckTest.java create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html create mode 100644 sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json create mode 100644 sonar-java-plugin/src/main/resources/profiles/Sonar_way/S9359 diff --git a/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java new file mode 100644 index 00000000000..990af8816cc --- /dev/null +++ b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java @@ -0,0 +1,40 @@ +package checks; + +class OctalEscapeSequenceFollowedByDigitCheckSample { + void testNoncompliant() { + String s1 = "\128"; // Noncompliant {{Remove this octal escape sequence or separate it from the following digit.}} +// ^^^^^ + String s2 = "\09"; // Noncompliant +// ^^^ + String s3 = "\7778"; // Noncompliant +// ^^^^^ + String s4 = "\1234"; // Noncompliant +// ^^^^^ + String s5 = "\789"; // Noncompliant +// ^^^ + String s6 = "\0000"; // Noncompliant +// ^^^^^ + String s7 = "\7777"; // Noncompliant +// ^^^^^ + String s8 = "a\128b"; // Noncompliant +// ^^^^^ + String s9 = "\12\3456"; // Noncompliant +// ^^^^^ + } + + void testCompliant() { + String s1 = "\12"; // Compliant + String s2 = "\12a"; // Compliant + String s3 = "\\128"; // Compliant + String s4 = "128"; // Compliant + String s5 = "\u0041"; // Compliant + String s6 = "\n"; // Compliant + String s7 = "\\08"; // Compliant + String s8 = "\12" + "8"; // Compliant + } + + void testCharacterLiteral() { + char c1 = '\12'; // Compliant + char c2 = '\1'; // Compliant + } +} diff --git a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java new file mode 100644 index 00000000000..2fde7c98870 --- /dev/null +++ b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java @@ -0,0 +1,90 @@ +/* + * 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.Arrays; +import java.util.List; +import org.sonar.check.Rule; +import org.sonar.java.model.LiteralUtils; +import org.sonar.plugins.java.api.IssuableSubscriptionVisitor; +import org.sonar.plugins.java.api.tree.LiteralTree; +import org.sonar.plugins.java.api.tree.Tree; +import org.sonar.plugins.java.api.tree.Tree.Kind; + +@Rule(key = "S9359") +public class OctalEscapeSequenceFollowedByDigitCheck extends IssuableSubscriptionVisitor { + + @Override + public List nodesToVisit() { + return Arrays.asList(Kind.STRING_LITERAL, Kind.TEXT_BLOCK); + } + + @Override + public void visitNode(Tree node) { + if (LiteralUtils.isEmptyString(node)) { + return; + } + String value = LiteralUtils.trimQuotes(((LiteralTree) node).value()); + if (node.is(Kind.TEXT_BLOCK)) { + value = value.replaceAll("(\\r?\\n|\\r)\\s*", ""); + } + + int i = 0; + while (i < value.length()) { + char c = value.charAt(i); + if (c == '\\') { + // Skip escaped backslash + if (i + 1 < value.length() && value.charAt(i + 1) == '\\') { + i += 2; + continue; + } + // Check for octal escape followed by digit or another escape + if (i + 1 < value.length()) { + char next = value.charAt(i + 1); + if (isOctalDigit(next)) { + int escapeEnd = findEscapeEnd(value, i); + if (escapeEnd < value.length() && isAmbiguousFollowUp(value.charAt(escapeEnd))) { + reportIssue(node, "Remove this octal escape sequence or separate it from the following digit."); + return; + } + i = escapeEnd; + continue; + } + } + } + i++; + } + } + + private static boolean isOctalDigit(char c) { + return c >= '0' && c <= '7'; + } + + private static int findEscapeEnd(String value, int start) { + int escapeEnd = start + 2; + while (escapeEnd < value.length() + && isOctalDigit(value.charAt(escapeEnd)) + && escapeEnd - start < 4) { + escapeEnd++; + } + return escapeEnd; + } + + private static boolean isAmbiguousFollowUp(char c) { + return c >= '0' && c <= '9' || c == '\\'; + } +} diff --git a/java-checks/src/test/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheckTest.java b/java-checks/src/test/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheckTest.java new file mode 100644 index 00000000000..1cc1a261c89 --- /dev/null +++ b/java-checks/src/test/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheckTest.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 OctalEscapeSequenceFollowedByDigitCheckTest { + + @Test + void test() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/OctalEscapeSequenceFollowedByDigitCheckSample.java")) + .withCheck(new OctalEscapeSequenceFollowedByDigitCheck()) + .verifyIssues(); + } + + @Test + void test_without_semantic() { + CheckVerifier.newVerifier() + .onFile(mainCodeSourcesPath("checks/OctalEscapeSequenceFollowedByDigitCheckSample.java")) + .withCheck(new OctalEscapeSequenceFollowedByDigitCheck()) + .withoutSemantic() + .verifyIssues(); + } +} diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html new file mode 100644 index 00000000000..22c23fbea9c --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html @@ -0,0 +1,56 @@ +

This is an issue when an octal escape sequence in a string literal is immediately followed by another digit, creating ambiguity about which digits +are part of the escape sequence.

+

Why is this an issue?

+

Some programming languages support numeric escape sequences in string and character literals where the escape sequence consists of a backslash +followed by a variable number of digits in a specific number base (such as octal or other bases). For example, an escape sequence might consist of a +backslash followed by one to three digits representing a character code.

+

The problem arises when such a variable-length escape sequence is immediately followed by another digit that could be part of the sequence. +Consider a string containing a backslash, followed by digits that could be interpreted as either a single long escape sequence or a shorter escape +sequence followed by a literal digit character. This creates confusion because:

+ +

This ambiguity can lead to bugs where the string contains different characters than the developer intended. The issue is particularly problematic +because:

+ +

In Java, these are octal escape sequences consisting of a backslash followed by one to three octal digits (0-7). For example, "\12" +represents a line feed character (decimal 10), and "\128" is actually the octal escape \12 (line feed) followed by the +literal character 8.

+

What is the potential impact?

+

This issue affects code maintainability and can lead to subtle bugs. When developers misunderstand what characters are in a string, it can +cause:

+ +

While the impact is typically low severity, it can waste significant debugging time when the actual characters in a string don’t match +expectations.

+

How to fix it

+

Replace the octal escape sequence with a Unicode escape sequence. Unicode escapes use the format \uXXXX where XXXX is the four-digit +hexadecimal code point. This makes it clear exactly which character is intended and eliminates ambiguity.

+

Code examples

+

Noncompliant code example

+
+String message = "Error code: \128"; // Noncompliant
+
+

Compliant solution

+
+String message = "Error code: \u000A" + "8";
+
+

Resources

+

Documentation

+ + diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json new file mode 100644 index 00000000000..c895943891a --- /dev/null +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json @@ -0,0 +1,24 @@ +{ + "title": "Octal escape sequences should not be followed by digits", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5 min" + }, + "tags": [ + "pitfall", + "confusing" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-9359", + "sqKey": "S9359", + "scope": "All", + "quickfix": "unknown", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "CLEAR" + } +} diff --git a/sonar-java-plugin/src/main/resources/profiles/Sonar_way/S9359 b/sonar-java-plugin/src/main/resources/profiles/Sonar_way/S9359 new file mode 100644 index 00000000000..e69de29bb2d From 3165e2b096ea6e4d69afee197a641cc94432e52c Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 24 Aug 2026 13:19:36 +0200 Subject: [PATCH 2/6] SONARJAVA-6826: Fix S9359 false positives and CI failures - Remove backslash from isAmbiguousFollowUp to avoid false positive on consecutive octal escapes like "\1\2" - Replace newlines with space instead of empty string in text block handling to avoid false positives when octal escapes span lines - Remove misaligned column markers from test sample to match full-node issue reporting - Add compliant test case for consecutive octal escapes - Set quickfix metadata to "infeasible" instead of "unknown" Co-Authored-By: Claude Opus 4.6 --- .../OctalEscapeSequenceFollowedByDigitCheckSample.java | 10 +--------- .../OctalEscapeSequenceFollowedByDigitCheck.java | 4 ++-- .../org/sonar/l10n/java/rules/java/S9359.json | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java index 990af8816cc..703092f8861 100644 --- a/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java @@ -3,23 +3,14 @@ class OctalEscapeSequenceFollowedByDigitCheckSample { void testNoncompliant() { String s1 = "\128"; // Noncompliant {{Remove this octal escape sequence or separate it from the following digit.}} -// ^^^^^ String s2 = "\09"; // Noncompliant -// ^^^ String s3 = "\7778"; // Noncompliant -// ^^^^^ String s4 = "\1234"; // Noncompliant -// ^^^^^ String s5 = "\789"; // Noncompliant -// ^^^ String s6 = "\0000"; // Noncompliant -// ^^^^^ String s7 = "\7777"; // Noncompliant -// ^^^^^ String s8 = "a\128b"; // Noncompliant -// ^^^^^ String s9 = "\12\3456"; // Noncompliant -// ^^^^^ } void testCompliant() { @@ -31,6 +22,7 @@ void testCompliant() { String s6 = "\n"; // Compliant String s7 = "\\08"; // Compliant String s8 = "\12" + "8"; // Compliant + String s9 = "\1\2"; // Compliant } void testCharacterLiteral() { diff --git a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java index 2fde7c98870..0082d922190 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java @@ -40,7 +40,7 @@ public void visitNode(Tree node) { } String value = LiteralUtils.trimQuotes(((LiteralTree) node).value()); if (node.is(Kind.TEXT_BLOCK)) { - value = value.replaceAll("(\\r?\\n|\\r)\\s*", ""); + value = value.replaceAll("(\\r?\\n|\\r)\\s*", " "); } int i = 0; @@ -85,6 +85,6 @@ && isOctalDigit(value.charAt(escapeEnd)) } private static boolean isAmbiguousFollowUp(char c) { - return c >= '0' && c <= '9' || c == '\\'; + return c >= '0' && c <= '9'; } } diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json index c895943891a..29f30700a68 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json @@ -14,7 +14,7 @@ "ruleSpecification": "RSPEC-9359", "sqKey": "S9359", "scope": "All", - "quickfix": "unknown", + "quickfix": "infeasible", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM" From b630e14eac95d11dda5eff2b5cc19bb1ccfeb7ba Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 24 Aug 2026 13:45:06 +0200 Subject: [PATCH 3/6] SONARJAVA-6826: Refactor S9359 to fix SonarQube quality gate - Reduce cognitive complexity of visitNode by extracting containsOctalFollowedByDigit and processBackslash helper methods - Eliminate multiple continue statements by using if/else-if/else - Add test cases for text blocks, edge cases, and improved coverage Co-Authored-By: Claude Opus 4.6 --- ...apeSequenceFollowedByDigitCheckSample.java | 30 +++++++++++++ ...talEscapeSequenceFollowedByDigitCheck.java | 45 ++++++++++--------- 2 files changed, 55 insertions(+), 20 deletions(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java index 703092f8861..980535cddaa 100644 --- a/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java @@ -23,10 +23,40 @@ void testCompliant() { String s7 = "\\08"; // Compliant String s8 = "\12" + "8"; // Compliant String s9 = "\1\2"; // Compliant + String s10 = ""; // Compliant - empty string + String s11 = "\377"; // Compliant - max octal at end of string + String s12 = "\377a"; // Compliant - max octal followed by non-digit + String s13 = "\t9"; // Compliant - non-octal escape followed by digit + String s14 = "\n0"; // Compliant - non-octal escape followed by digit + String s15 = "\\\\8"; // Compliant - double escaped backslash followed by digit + String s16 = "\1"; // Compliant - single octal at end + String s17 = "abc"; // Compliant - no escapes + } + + void testNoncompliantTextBlock() { + String tb1 = """ + \128"""; // Noncompliant@-1 + String tb2 = """ + \09"""; // Noncompliant@-1 + } + + void testCompliantTextBlock() { + String tb1 = """ + \12"""; + String tb2 = """ + \12a"""; + String tb3 = """ + \\128"""; + String tb4 = """ + \n0"""; } void testCharacterLiteral() { char c1 = '\12'; // Compliant char c2 = '\1'; // Compliant } + + void testNoncompliantMaxOctal() { + String s1 = "\3778"; // Noncompliant + } } diff --git a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java index 0082d922190..097f11ab48d 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java @@ -42,32 +42,37 @@ public void visitNode(Tree node) { if (node.is(Kind.TEXT_BLOCK)) { value = value.replaceAll("(\\r?\\n|\\r)\\s*", " "); } + if (containsOctalFollowedByDigit(value)) { + reportIssue(node, "Remove this octal escape sequence or separate it from the following digit."); + } + } + private static boolean containsOctalFollowedByDigit(String value) { int i = 0; while (i < value.length()) { - char c = value.charAt(i); - if (c == '\\') { - // Skip escaped backslash - if (i + 1 < value.length() && value.charAt(i + 1) == '\\') { - i += 2; - continue; - } - // Check for octal escape followed by digit or another escape - if (i + 1 < value.length()) { - char next = value.charAt(i + 1); - if (isOctalDigit(next)) { - int escapeEnd = findEscapeEnd(value, i); - if (escapeEnd < value.length() && isAmbiguousFollowUp(value.charAt(escapeEnd))) { - reportIssue(node, "Remove this octal escape sequence or separate it from the following digit."); - return; - } - i = escapeEnd; - continue; - } + if (value.charAt(i) != '\\') { + i++; + } else if (i + 1 < value.length() && value.charAt(i + 1) == '\\') { + i += 2; + } else { + i = processBackslash(value, i); + if (i < 0) { + return true; } } - i++; } + return false; + } + + private static int processBackslash(String value, int i) { + if (i + 1 < value.length() && isOctalDigit(value.charAt(i + 1))) { + int escapeEnd = findEscapeEnd(value, i); + if (escapeEnd < value.length() && isAmbiguousFollowUp(value.charAt(escapeEnd))) { + return -1; + } + return escapeEnd; + } + return i + 1; } private static boolean isOctalDigit(char c) { From 80fffa9a4c2300bfb2261e4f675719e53c0832bf Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 24 Aug 2026 14:14:33 +0200 Subject: [PATCH 4/6] SONARJAVA-6826: Fix S9359 false negative for octal escapes starting with digits 4-7 Java only allows 2-digit octal escapes when the first digit is 4-7 (e.g., \45), but the rule was treating them as 3-digit escapes. This caused false negatives like "\456" not being flagged (Java parses it as \45 followed by '6'). Co-Authored-By: Claude Opus 4.6 --- .../checks/OctalEscapeSequenceFollowedByDigitCheckSample.java | 3 +++ .../java/checks/OctalEscapeSequenceFollowedByDigitCheck.java | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java index 980535cddaa..c577c329108 100644 --- a/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java +++ b/java-checks-test-sources/default/src/main/java/checks/OctalEscapeSequenceFollowedByDigitCheckSample.java @@ -11,6 +11,7 @@ void testNoncompliant() { String s7 = "\7777"; // Noncompliant String s8 = "a\128b"; // Noncompliant String s9 = "\12\3456"; // Noncompliant + String s10 = "\456"; // Noncompliant } void testCompliant() { @@ -31,6 +32,8 @@ void testCompliant() { String s15 = "\\\\8"; // Compliant - double escaped backslash followed by digit String s16 = "\1"; // Compliant - single octal at end String s17 = "abc"; // Compliant - no escapes + String s18 = "\45"; // Compliant + String s19 = "\45a"; // Compliant } void testNoncompliantTextBlock() { diff --git a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java index 097f11ab48d..52b6f31084b 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java @@ -81,9 +81,10 @@ private static boolean isOctalDigit(char c) { private static int findEscapeEnd(String value, int start) { int escapeEnd = start + 2; + int maxEnd = value.charAt(start + 1) <= '3' ? start + 4 : start + 3; while (escapeEnd < value.length() && isOctalDigit(value.charAt(escapeEnd)) - && escapeEnd - start < 4) { + && escapeEnd < maxEnd) { escapeEnd++; } return escapeEnd; From f984604d4626f67e890cb29d59b32d1b17953c1d Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Mon, 24 Aug 2026 14:35:55 +0200 Subject: [PATCH 5/6] SONARJAVA-6826: Add explicit parentheses to fix S864 quality gate issue Co-Authored-By: Claude Opus 4.6 --- .../java/checks/OctalEscapeSequenceFollowedByDigitCheck.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java index 52b6f31084b..86dd04d7d16 100644 --- a/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java +++ b/java-checks/src/main/java/org/sonar/java/checks/OctalEscapeSequenceFollowedByDigitCheck.java @@ -81,7 +81,7 @@ private static boolean isOctalDigit(char c) { private static int findEscapeEnd(String value, int start) { int escapeEnd = start + 2; - int maxEnd = value.charAt(start + 1) <= '3' ? start + 4 : start + 3; + int maxEnd = (value.charAt(start + 1) <= '3') ? (start + 4) : (start + 3); while (escapeEnd < value.length() && isOctalDigit(value.charAt(escapeEnd)) && escapeEnd < maxEnd) { From 16d87f5278fbfc6ffe1378753ebed17c0121f2fb Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 25 Aug 2026 10:31:06 +0200 Subject: [PATCH 6/6] SONARJAVA-6826: Update S9359 rule metadata and documentation Prefer named escape sequences over Unicode escapes in fix guidance, and update quickfix status to unknown. Co-Authored-By: Claude Opus 4.6 --- .../resources/org/sonar/l10n/java/rules/java/S9359.html | 7 ++++--- .../resources/org/sonar/l10n/java/rules/java/S9359.json | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html index 22c23fbea9c..41e6b730168 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.html @@ -34,8 +34,9 @@

What is the potential impact?

While the impact is typically low severity, it can waste significant debugging time when the actual characters in a string don’t match expectations.

How to fix it

-

Replace the octal escape sequence with a Unicode escape sequence. Unicode escapes use the format \uXXXX where XXXX is the four-digit -hexadecimal code point. This makes it clear exactly which character is intended and eliminates ambiguity.

+

Replace the octal escape sequence with the equivalent standard escape sequence or separate the escape from the trailing digit to remove ambiguity. +For characters that have a named escape (such as \n for newline or \t for tab), prefer using that form. Alternatively, split +the string so the escape sequence and the following literal digit are in separate concatenated strings.

Code examples

Noncompliant code example

@@ -43,7 +44,7 @@ 

Noncompliant code example

Compliant solution

-String message = "Error code: \u000A" + "8";
+String message = "Error code: \n" + "8";
 

Resources

Documentation

diff --git a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json index 29f30700a68..c895943891a 100644 --- a/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json +++ b/sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S9359.json @@ -14,7 +14,7 @@ "ruleSpecification": "RSPEC-9359", "sqKey": "S9359", "scope": "All", - "quickfix": "infeasible", + "quickfix": "unknown", "code": { "impacts": { "MAINTAINABILITY": "MEDIUM"