Skip to content

VNX-1046: Creation of Immutable Text Using String Concatenation

Reports a Java or C# String accumulated with +=, s = s + ... or s = s.concat(...) inside a for, foreach, while or do loop, where the variable is a String local declared before the loop.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-1046
Severity Low
SARIF level note
Kind sast
Languages Java, C#
CWE CWE-1046
CAPEC CAPEC-204
CWSS vector CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags java csharp string-concatenation stringbuilder memory cwe-1046
Source internal/sast/rules/vnx-cwe1046-auto.rego

Overview

VNX-1046 reports CWE-1046: Creation of Immutable Text Using String Concatenation in Java and C#. The finding is an s += ..., s = s + ... or Java s = s.concat(...) statement inside the body of a for, foreach, while or do loop, where s is a local declared as String/string (or a C# var initialised with a string literal or string.Empty) before the loop. Braced bodies, Allman-style braces, a single unbraced statement and a statement on the loop’s own line are all covered.

Not reported: StringBuilder, StringBuffer and StringJoiner appends, String.join and Collectors.joining, concatenation after the loop ends, numeric += on int/long/double variables (including a name that an earlier method declared as a String), a String declared inside the loop body (rebuilt each iteration rather than accumulated), fields and variables declared more than 100 lines above, Kotlin and other JVM languages, test code, comments and strings.

Why This Matters

Strings in Java and C# are immutable. Every += allocates a new string and copies the whole text built so far into it, so building a string of n pieces in a loop costs time proportional to n squared. When the loop count comes from a request (rows returned, items posted, lines uploaded), the request controls how much CPU the server spends. Every intermediate copy also stays on the heap until it is collected. When the text is a password, a token or key material, that leaves many copies of the secret in memory, where a heap dump or a memory-disclosure bug can find them.

What Gets Flagged

String csv = "";
for (Column column : columns) {
    csv += column.getName() + ",";            // FLAGGED
}
String hex = "";
for (byte b : digest) hex += String.format("%02x", b);   // FLAGGED
string list = "";
foreach (var id in ids)
{
    list += id + ",";                          // FLAGGED
}

What Is Not Flagged

StringBuilder csv = new StringBuilder();
for (Column column : columns) {
    csv.append(column.getName()).append(',');
}

for (Order order : orders) {
    String line = order.getId() + ": ";        // a new String each iteration
    line += order.getAmount();
}

Remediation

  1. Accumulate into a StringBuilder (Java and C#) and call toString()/ToString() once.
  2. Use String.join, Collectors.joining or string.Join when joining a collection.
  3. For secrets, use a char[] or byte[] buffer that you can overwrite when done, not a String.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-1046
// nosec vnx-1046

Or record an ignore rule: vulnetix ignore add --rule vnx-1046 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-cwe1046-auto.rego, cli/internal/sast/docs/vnx-cwe1046-auto.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.