Skip to content

Security: Fix CWE-79 (Cross-Site Scripting) vulnerability in src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java:64#667

Open
appsecai-app[bot] wants to merge 1 commit intomainfrom
appsecai/fix-group/69c734e2-20dbc70c-709
Open

Security: Fix CWE-79 (Cross-Site Scripting) vulnerability in src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java:64#667
appsecai-app[bot] wants to merge 1 commit intomainfrom
appsecai/fix-group/69c734e2-20dbc70c-709

Conversation

@appsecai-app
Copy link
Copy Markdown

@appsecai-app appsecai-app bot commented Mar 28, 2026

What we found

  • AppSecAI Vulnerability ID: 69c734eb
  • Vulnerability: CWE-79: Cross-site Scripting (XSS)
  • Severity: Medium
  • File: src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java:64
  • Detected By: OpenGrep
  • Detection Rule: No Direct Response Writer

Description: Detected user-controlled input flowing into a response writer without HTML encoding. This bypasses any view or template environments that would normally escape HTML, exposing the application to cross-site scripting (XSS) vulnerabilities. Direct writes to response output streams circumvent standard web framework protections.

Why this matters

Risk if not fixed: An attacker could inject malicious JavaScript that executes in victims' browsers, potentially stealing session cookies, redirecting users to phishing sites, or performing actions on behalf of authenticated users.

Risk level: Medium to High — depends on sensitivity of data accessible through the compromised session or the actions an attacker can perform.

Attack surface: Any HTTP request parameter name can be crafted to contain script payloads. The vulnerability requires no special authentication or preconditions.

Why we're changing it

Status: Confirmed vulnerability with clear exploitation path.

Vulnerability Flow Diagram

%%{init: {'theme':'base','themeVariables':{'fontFamily':'ui-sans-serif, Inter, system-ui, sans-serif','primaryColor':'#EDE9FE','primaryTextColor':'#1A1A2E','primaryBorderColor':'#7C3AED','lineColor':'#5B21B6','secondaryColor':'#FEF3C7','tertiaryColor':'#DCFCE7'}}}%%
flowchart TD
    A["HTTP Request Parameter Name<br/>(Attacker-Controlled)"] --> B["request.getParameterNames()<br/>Line 53 - param = name"]
    B --> C["doSomething(param)<br/>Line 74 - 500/42 + 196 ) 200<br/>Always TRUE - returns param unmodified"]
    C --> D["response.getWriter().format(bar, obj)<br/>Line 64 - No HTML Encoding"]
    D --> E["❌ Unencoded Script Injected<br/>into HTML Response Body"]
    E --> F["🔴 Browser Executes Malicious<br/>JavaScript in Victim Context"]
    
    style A fill:#EDE9FE,stroke:#7C3AED
    style B fill:#EDE9FE,stroke:#7C3AED
    style C fill:#EDE9FE,stroke:#7C3AED
    style D fill:#FFE5E5,stroke:#F65A5A
    style E fill:#FEF3C7,stroke:#F59E0B
    style F fill:#FEF3C7,stroke:#F59E0B
Loading

Data Flow Analysis

Input source (lines 45–58): The code iterates request.getParameterNames(), looking for any parameter whose value equals "BenchmarkTest02317". When found, param is assigned the parameter name — fully attacker-controlled. An HTTP request can contain arbitrary parameter names.

Propagation (lines 67–78, doSomething): The arithmetic branch (500/42) + 196 > 200 evaluates to 207 > 200 — a compile-time constant true. Therefore bar = param unconditionally; no sanitization occurs on this path. The else branch is dead code.

Sink (line 64):

response.getWriter().format(bar, obj);

PrintWriter.format() writes bar as a format string directly to the HTTP response. The response content type is text/html;charset=UTF-8, and the X-XSS-Protection: 0 header is explicitly set to disable browser-side XSS filtering. There is no HTML encoding at any point in the flow.

Exploitation: An attacker crafts a request with a parameter named <script>alert(1)</script> (value BenchmarkTest02317). That name flows unmodified through doSomething, then is written verbatim into the HTML response body, executing in any browser that renders the response.

How we confirmed

Manual Verification Steps

  1. Trace parameter flow: Confirm request.getParameterNames() returns attacker-controlled parameter names (line 53).
  2. Verify constant condition: Confirm the arithmetic expression (500/42) + 196 > 200 always evaluates to true (line 74), making the else branch unreachable.
  3. Identify sink: Confirm response.getWriter().format(bar, obj) writes bar directly to the response without encoding (line 64).
  4. Check response context: Confirm response content type is text/html and X-XSS-Protection header is set to 0 (lines 62, 63).
  5. Verify no encoding: Confirm no HTML encoding, escaping, or sanitization exists anywhere in the call chain.
Runnable Verification Script (click to expand)

Save this script and run with bash verify_fix.sh:

#!/bin/bash
# Verification script for CWE-79 fix in BenchmarkTest02317.java
set -e

echo "=== Verification: CWE-79 Cross-Site Scripting Fix ==="

# Step 1: Check that the vulnerable line has been replaced
echo "Step 1: Verifying vulnerable format() call has been replaced..."
if grep -n "response.getWriter().format(bar, obj)" src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java; then
    echo "❌ FAILED: Vulnerable format() call still present"
    exit 1
fi
echo "✅ Vulnerable format() call removed"

# Step 2: Check that OWASP Java Encoder is being used
echo "Step 2: Verifying OWASP Java Encoder is applied..."
if grep -n "Encode.forHtml(bar)" src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java; then
    echo "✅ HTML encoding with Encode.forHtml() found"
else
    echo "❌ FAILED: HTML encoding not found"
    exit 1
fi

# Step 3: Check that write() method is used instead of format()
echo "Step 3: Verifying write() method is used for output..."
if grep -n "response.getWriter().write(Encode.forHtml(bar))" src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java; then
    echo "✅ Safe write() method with encoding found"
else
    echo "❌ FAILED: Safe write() method not found"
    exit 1
fi

# Step 4: Verify OWASP Java Encoder import
echo "Step 4: Verifying OWASP Java Encoder import..."
if grep -n "import org.owasp.encoder.Encode" src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java; then
    echo "✅ OWASP Java Encoder import present"
else
    echo "❌ FAILED: OWASP Java Encoder import missing"
    exit 1
fi

echo ""
echo "=== All verification checks passed ==="
exit 0

Vulnerable flow: src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java:64

Cross-Site Scripting

%%{init: {'theme':'base','themeVariables':{'fontFamily':'ui-sans-serif, Inter, system-ui, sans-serif','primaryColor':'#EDE9FE','primaryTextColor':'#1A1A2E','primaryBorderColor':'#7C3AED','lineColor':'#5B21B6','secondaryColor':'#FEF3C7','tertiaryColor':'#DCFCE7'}}}%%
flowchart TD
    subgraph Vulnerable["❌ Vulnerable Flow"]
        direction LR
        A1["Project"] --> A2["Unescaped user input in HTML output"]
        A2 --> A3["💥 XSS Script Executed"]
    end

    Vulnerable ~~~ Fixed

    subgraph Fixed["✅ Fixed Flow"]
        direction LR
        B1["Project"] --> B2["Context-aware output encoding"]
        B3["🛡️ Attack Blocked"]
        B2 --> B3
    end

    style A2 fill:#FFE5E5,color:#000
    style A3 fill:#ffa94d,color:#000
    style B2 fill:#74c0fc,color:#000
    style B3 fill:#DCFCE7,color:#000
Loading

How we fixed it

Root Cause

User-controlled input (HTTP request parameter name) flowed into response.getWriter().format(bar, obj) without HTML encoding. The parameter name is assigned to bar through doSomething(), which unconditionally returns param because the arithmetic condition (500/42 + 196 = 207 > 200) is always true. Writing unencoded user input directly to an HTML response allows an attacker to inject arbitrary HTML and JavaScript.

Fix Approach

The OWASP Java Encoder's Encode.forHtml() contextually encodes all HTML metacharacters (<, >, &, ", ') before the string reaches the response writer, neutralizing any injected markup or script. The write() call replaces format() to eliminate the secondary format-string injection risk (user input used as a printf-style format pattern). This is the idiomatic defense recommended by OWASP for output encoding in HTML context.

Code Change

Before:

response.getWriter().format(bar, obj);

After:

response.getWriter().write(Encode.forHtml(bar));

Alternatives Considered

  • StringEscapeUtils.escapeHtml4() from Apache Commons Text — rejected in favor of OWASP Java Encoder, which is already a project dependency and provides context-aware encoding.
  • Allowlist validation of the parameter name before use — rejected as a sole defense because allowlist logic is error-prone and encoding provides a more robust, defense-in-depth control at the output boundary.
  • JSP/template layer for rendering — rejected as it would require architectural changes beyond minimal fix scope.

Vulnerabilities Addressed

  • Grouped findings in scope: 1
  • Findings fixed in this PR: 1
  • Primary CWE family: CWE-79
  • Files covered: src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java
# Finding Detection Severity Location Status
1 Cross-Site Scripting
CWE-79
OpenGrep
No Direct Response Writer
Medium src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02317.java:64 Fixed

How we validated it

Validation approach:

  1. Confirmed the vulnerable format() call has been replaced with write(Encode.forHtml()).
  2. Verified OWASP Java Encoder is imported and available in the project dependencies.
  3. Confirmed the encoding is applied at the output boundary, before any data reaches the response stream.
  4. Tested with payloads containing HTML metacharacters to ensure they are properly escaped.
  5. Verified no functionality regression — the parameter name is still written to the response, but safely encoded.

How to verify

Reviewers can verify this fix by:

  1. Code inspection: Check that line 64 now calls response.getWriter().write(Encode.forHtml(bar)) instead of format(bar, obj).
  2. Import verification: Confirm org.owasp.encoder.Encode is imported at the top of the file.
  3. Dependency check: Verify OWASP Java Encoder is in the project's pom.xml or build.gradle.
  4. Manual testing: Submit a request with a parameter name containing <script>alert(1)</script> and verify the response contains HTML-encoded entities (&lt;script&gt;...&lt;/script&gt;) instead of executable script.
  5. Automated testing: Run the verification script provided above to confirm all checks pass.

Before you merge

  • Fix addresses the root cause (output encoding at the sink), not just the symptom
  • No new security vulnerabilities introduced (no secondary injection vectors)
  • Code follows project conventions (matches existing encoding patterns in codebase)
  • Edge cases handled (null input, empty strings, special characters all properly encoded)
  • No functionality regression (parameter name still appears in response, safely encoded)
  • Output encoding applied to all contexts (HTML context confirmed; no mixed contexts)
  • OWASP Java Encoder dependency is available and correctly imported

Learn more


This fix was generated by AppSecAI. Please review before merging.

Apply OWASP Java Encoder's Encode.forHtml() to prevent cross-site
scripting vulnerability where user-controlled HTTP request parameter
names were written directly to HTML response without encoding.

Fixes: 1 CWE-79 vulnerability
@kevinfealey kevinfealey added the 1.0.3 Version 1.0.3 label Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.0.3 Version 1.0.3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants