Security: Fix CWE-79 (Cross-Site Scripting) vulnerability in src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02316.java:64#665
Open
appsecai-app[bot] wants to merge 1 commit intomainfrom
Conversation
Replace unescaped response.getWriter().format() with HTML-encoded Encode.forHtml() to prevent reflected XSS attacks via HTTP parameter names. Fixes 1 CWE-79 vulnerability detected by OpenGrep.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What we found
69c734ebsrc/main/java/org/owasp/benchmark/testcode/BenchmarkTest02316.java:64Description: Detected user-controlled input flowing into a response OutputStream without HTML encoding. This bypasses view-layer escaping and exposes the application to reflected cross-site scripting (XSS) vulnerabilities.
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.
Attack vector: An attacker crafts a URL with a malicious parameter name (e.g.,
?<script>alert(1)</script>=BenchmarkTest02316) and tricks a user into clicking it. The parameter name is extracted, passed through the application unchanged, and written directly into the HTML response body without encoding.Risk level: Medium to High — Depends on sensitivity of data accessible through the victim's session.
Why we're changing it
Data Flow Analysis
Source (lines 45–58): The servlet iterates
request.getParameterNames(). When a parameter's value equals"BenchmarkTest02316", its name is assigned toparam. HTTP parameter names are fully attacker-controlled — an attacker submits?<script>alert(1)</script>=BenchmarkTest02316.Propagation (lines 60, 70–74):
doSomething()passesparamtothing.doSomething(param):Thing1.doSomething:return i— identity, no transformation.Thing2.doSomething:return new StringBuilder(i).toString()— identity, no transformation.Neither implementation applies HTML encoding or any sanitization.
Sink (line 64):
response.getWriter().format(bar, obj)—bar(the attacker-controlled parameter name) serves as the format template passed directly toPrintWriter.format(). The response content-type istext/html;charset=UTF-8(line 41). TheX-XSS-Protection: 0header is explicitly set on line 62, disabling browser-side mitigation.The attacker payload
<script>alert(1)</script>as a parameter name passes through Thing1/Thing2 unchanged and is written verbatim into the HTML response body viaformat().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["param = request.getParameterName()"] B --> C["bar = doSomething(param)<br/>(Thing1/Thing2 - No Encoding)"] C --> D["response.getWriter().format(bar, obj)<br/>(Unescaped HTML Sink)"] D --> E["❌ XSS Payload Executed<br/>in Victim Browser"] F["✅ Fix - Encode.forHtml(bar)"] -.-> G["HTML Special Chars Encoded<br/>((, ), and , etc.)"] G -.-> H["🛡️ Payload Rendered as Text<br/>Attack Blocked"] style A fill:#EDE9FE,stroke:#7C3AED style B fill:#EDE9FE,stroke:#7C3AED style C fill:#FFE5E5,stroke:#F65A5A style D fill:#FFE5E5,stroke:#F65A5A style E fill:#FEF3C7,stroke:#F59E0B style F fill:#DCFCE7,stroke:#16A34A style G fill:#DCFCE7,stroke:#16A34A style H fill:#DCFCE7,stroke:#16A34AHow we confirmed
Manual Verification Steps
request.getParameterNames()returns attacker-controlled names from the HTTP request.Thing1.doSomething()andThing2.doSomething()perform identity operations without HTML encoding.response.getWriter().format(bar, obj)writesbardirectly into an HTML response without encoding.text/htmland thatX-XSS-Protection: 0is set, confirming the response is rendered as HTML in browsers.?<img src=x onerror=alert(1)>=BenchmarkTest02316) and confirm the script executes in the response.Runnable Verification Script (click to expand)
Save this script and run with
bash verify_fix.sh:Vulnerable flow: src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02316.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:#000How we fixed it
Fix Description
Root cause: User-controlled input flows from request parameters through ThingFactory.doSomething() into the 'bar' variable, which is then passed directly as the format string argument to response.getWriter().format(). This bypasses any view-layer HTML escaping, allowing an attacker to inject arbitrary HTML and JavaScript into the HTTP response, resulting in reflected XSS.
Fix approach: The fix replaces the unescaped format() call with response.getWriter().write() wrapped by org.owasp.encoder.Encode.forHtml(), which contextually encodes HTML special characters (<, >, &, ", ') into their safe entity equivalents. This ensures that even if 'bar' contains attacker-supplied markup, it is rendered as literal text rather than executable HTML/JS. The OWASP Java Encoder library is the established, context-aware encoding solution for this pattern.
Alternatives considered:
Code Changes
Before:
After:
Import added:
Vulnerabilities Addressed
CWE-79
No Direct Response Writer
How we validated it
format()call has been replaced withwrite()wrapped byEncode.forHtml()How to verify
Reviewers can verify this fix by:
Encode.forHtml(bar)instead offormat(bar, obj)owasp-java-encoderis in the project'spom.xmlorbuild.gradlemvn clean compileto ensure the fix compiles without errors?<img src=x onerror=alert(1)>=BenchmarkTest02316). The payload should be rendered as literal text in the response, not executed.Before you merge
Learn more
This fix was generated by AppSecAI. Please review before merging.