Creation of Immutable Text Using String Concatenation

Description

Creation of Immutable Text Using String Concatenation occurs when software creates immutable text strings using repeated string concatenation operations, typically within loops. In languages where strings are immutable (like Java, C#, Python), each concatenation operation creates a new string object in memory, copying all existing content plus the new content. When performed repeatedly, especially in loops, this results in O(n²) time complexity and excessive memory allocation, creating both performance and potential security concerns.

Risk

While primarily a performance issue, this weakness can have security implications. Attackers who can trigger code paths with inefficient string concatenation may exploit this for denial-of-service attacks by causing excessive CPU usage and memory allocation. Processing large inputs through inefficient string building can exhaust server resources. In web applications, this can lead to slow response times making the system vulnerable to slowloris-style attacks. Memory pressure from excessive string objects can trigger garbage collection pauses, further degrading performance.

Solution

Use mutable string builder classes instead of concatenation: StringBuilder in Java/C#, StringBuffer for thread-safe scenarios, list joining in Python, or string streams in C++. Pre-allocate capacity when the final size is known or can be estimated. For simple cases with few concatenations, the performance impact is negligible, but always use builders in loops or when processing variable-length input. Profile string-heavy code paths to identify bottlenecks. Consider using string formatting or interpolation where appropriate.

Common Consequences

ImpactDetails
AvailabilityScope: Availability

DoS: Resource Consumption (CPU) - Repeated string concatenation in loops causes O(n²) time complexity, consuming excessive CPU cycles.
AvailabilityScope: Availability

DoS: Resource Consumption (Memory) - Each concatenation creates new string objects, potentially exhausting memory.
OtherScope: Other

Reduce Performance - General performance degradation that may enable denial-of-service attacks.

Example Code

Vulnerable Code

// Vulnerable: String concatenation in loop (Java)
public class VulnerableStringBuilder {

    public String vulnerableBuildHTML(List<String> items) {
        String html = "<ul>";

        // Vulnerable: Each += creates a new String object
        // O(n²) complexity: for n items, copies approximately n²/2 characters
        for (String item : items) {
            html += "<li>" + item + "</li>";  // Creates multiple new strings
        }

        html += "</ul>";
        return html;
    }

    // With 10,000 items, this creates ~50 million character copies
    // Memory usage spikes during execution
}
// Vulnerable: String concatenation in C#
public class VulnerableProcessor
{
    public string VulnerableBuildReport(DataTable data)
    {
        string report = "Report:\n";

        // Vulnerable: Immutable string concatenation
        foreach (DataRow row in data.Rows)
        {
            // Each line creates multiple new string objects
            report += "ID: " + row["id"] + ", ";
            report += "Name: " + row["name"] + ", ";
            report += "Value: " + row["value"] + "\n";
        }

        return report;
    }
}
# Vulnerable: String concatenation in Python
def vulnerable_build_csv(records):
    csv_data = ""

    # Vulnerable: Python strings are immutable
    # Each += creates a new string object
    for record in records:
        line = ""
        for field in record:
            line += str(field) + ","  # Multiple allocations per field
        line = line[:-1]  # Remove trailing comma
        csv_data += line + "\n"  # More allocations

    return csv_data

# Processing 100,000 records becomes extremely slow
// Vulnerable: String concatenation in JavaScript
function vulnerableBuildJSON(items) {
    let json = '{"items":[';

    // Vulnerable: Inefficient in tight loops with many items
    for (let i = 0; i < items.length; i++) {
        json += '{"id":' + items[i].id + ',';
        json += '"name":"' + items[i].name + '"}';
        if (i < items.length - 1) {
            json += ',';
        }
    }

    json += ']}';
    return json;
}
// Vulnerable: String concatenation in Go
func vulnerableBuildLog(entries []LogEntry) string {
    result := ""

    // Vulnerable: Go strings are immutable
    // Each concatenation allocates new memory
    for _, entry := range entries {
        result += fmt.Sprintf("[%s] %s: %s\n",
            entry.Timestamp, entry.Level, entry.Message)
    }

    return result
}

Fixed Code

// Fixed: Using StringBuilder in Java
public class FixedStringBuilder {

    public String fixedBuildHTML(List<String> items) {
        // Fixed: StringBuilder is mutable, O(n) complexity
        StringBuilder html = new StringBuilder();

        // Pre-allocate capacity if size is known
        // Estimate: ~20 chars per item + wrapper
        html.ensureCapacity(items.size() * 25 + 10);

        html.append("<ul>");

        for (String item : items) {
            html.append("<li>")
                .append(escapeHtml(item))  // Also fix XSS!
                .append("</li>");
        }

        html.append("</ul>");
        return html.toString();
    }

    // Thread-safe version using StringBuffer
    public String fixedBuildHTMLThreadSafe(List<String> items) {
        StringBuffer html = new StringBuffer();
        html.append("<ul>");

        for (String item : items) {
            synchronized (html) {
                html.append("<li>")
                    .append(escapeHtml(item))
                    .append("</li>");
            }
        }

        html.append("</ul>");
        return html.toString();
    }

    // Modern Java: String.join for simple cases
    public String fixedBuildList(List<String> items) {
        return String.join(", ", items);
    }
}
// Fixed: Using StringBuilder in C#
public class FixedProcessor
{
    public string FixedBuildReport(DataTable data)
    {
        // Fixed: StringBuilder with estimated capacity
        var report = new StringBuilder(data.Rows.Count * 100);

        report.AppendLine("Report:");

        foreach (DataRow row in data.Rows)
        {
            report.Append("ID: ").Append(row["id"]).Append(", ");
            report.Append("Name: ").Append(row["name"]).Append(", ");
            report.Append("Value: ").Append(row["value"]).AppendLine();
        }

        return report.ToString();
    }

    // Alternative: Using string interpolation with StringBuilder
    public string FixedBuildReportInterpolated(DataTable data)
    {
        var report = new StringBuilder();

        foreach (DataRow row in data.Rows)
        {
            report.AppendLine($"ID: {row["id"]}, Name: {row["name"]}, Value: {row["value"]}");
        }

        return report.ToString();
    }
}
# Fixed: Using list join in Python
def fixed_build_csv(records):
    # Fixed: Build list of lines, then join once
    lines = []

    for record in records:
        # Use join for fields within each line
        line = ",".join(str(field) for field in record)
        lines.append(line)

    # Single join operation at the end
    return "\n".join(lines)


# Alternative: Using StringIO for stream-like building
from io import StringIO

def fixed_build_csv_stringio(records):
    buffer = StringIO()

    for record in records:
        buffer.write(",".join(str(field) for field in record))
        buffer.write("\n")

    return buffer.getvalue()


# Best for CSV: Use the csv module
import csv
from io import StringIO

def fixed_build_csv_proper(records):
    buffer = StringIO()
    writer = csv.writer(buffer)
    writer.writerows(records)
    return buffer.getvalue()
// Fixed: Using Array.join in JavaScript
function fixedBuildJSON(items) {
    // Fixed: Build array, join once
    const parts = items.map(item =>
        `{"id":${item.id},"name":"${escapeJson(item.name)}"}`
    );

    return `{"items":[${parts.join(',')}]}`;
}

// Better: Use JSON.stringify
function fixedBuildJSONProper(items) {
    return JSON.stringify({ items: items });
}

// For HTML building
function fixedBuildHTML(items) {
    const listItems = items.map(item =>
        `<li>${escapeHtml(item)}</li>`
    );

    return `<ul>${listItems.join('')}</ul>`;
}

// Template literals for complex building
function fixedBuildReport(data) {
    const rows = data.map(row => `
        <tr>
            <td>${escapeHtml(row.id)}</td>
            <td>${escapeHtml(row.name)}</td>
        </tr>
    `);

    return `<table>${rows.join('')}</table>`;
}
// Fixed: Using strings.Builder in Go
import (
    "strings"
    "fmt"
)

func fixedBuildLog(entries []LogEntry) string {
    // Fixed: strings.Builder is efficient for string building
    var builder strings.Builder

    // Pre-grow buffer if size is known
    builder.Grow(len(entries) * 100)

    for _, entry := range entries {
        fmt.Fprintf(&builder, "[%s] %s: %s\n",
            entry.Timestamp, entry.Level, entry.Message)
    }

    return builder.String()
}

// Alternative: Using bytes.Buffer
import "bytes"

func fixedBuildLogBuffer(entries []LogEntry) string {
    var buffer bytes.Buffer

    for _, entry := range entries {
        fmt.Fprintf(&buffer, "[%s] %s: %s\n",
            entry.Timestamp, entry.Level, entry.Message)
    }

    return buffer.String()
}

// For simple joining
func fixedJoinStrings(parts []string) string {
    return strings.Join(parts, ", ")
}
// Fixed: Using String capacity in Rust
fn fixed_build_html(items: &[String]) -> String {
    // Pre-allocate estimated capacity
    let mut html = String::with_capacity(items.len() * 25 + 10);

    html.push_str("<ul>");

    for item in items {
        html.push_str("<li>");
        html.push_str(&escape_html(item));
        html.push_str("</li>");
    }

    html.push_str("</ul>");
    html
}

// Using iterators and collect
fn fixed_build_csv(records: &[Vec<String>]) -> String {
    records
        .iter()
        .map(|record| record.join(","))
        .collect::<Vec<_>>()
        .join("\n")
}

CVE Examples

This CWE is primarily a performance/quality issue. While no direct CVEs are attributed specifically to string concatenation, performance vulnerabilities caused by inefficient string handling have contributed to denial-of-service conditions in various applications.


  • CWE-1176: Inefficient CPU Computation (parent)
  • CWE-1006: Bad Coding Practices (category member)
  • CWE-400: Uncontrolled Resource Consumption (can lead to)

References

  1. MITRE Corporation. "CWE-1046: Creation of Immutable Text Using String Concatenation." https://cwe.mitre.org/data/definitions/1046.html
  2. Oracle. "Java StringBuilder Class Documentation."
  3. Microsoft. "StringBuilder Class (System.Text)."