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.
| 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
- Accumulate into a
StringBuilder(Java and C#) and calltoString()/ToString()once. - Use
String.join,Collectors.joiningorstring.Joinwhen joining a collection. - For secrets, use a
char[]orbyte[]buffer that you can overwrite when done, not a String.
References
- CWE-1046: Creation of Immutable Text Using String Concatenation
- Java SE: StringBuilder
- .NET: StringBuilder
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.