Skip to content

Security: Fix CWE-89 (SQL Injection) vulnerability in src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java:77#679

Open
appsecai-app[bot] wants to merge 1 commit intomainfrom
appsecai/fix-group/69c734e2-a2970b5d-901
Open

Security: Fix CWE-89 (SQL Injection) vulnerability in src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java:77#679
appsecai-app[bot] wants to merge 1 commit intomainfrom
appsecai/fix-group/69c734e2-a2970b5d-901

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-89: SQL Injection
  • Severity: Medium
  • File: src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java:77
  • Detected By: OpenGrep
  • Detection Rule: Tainted Sql From Http Request

Description: User-controlled input from an HTTPServletRequest flows directly into a SQL query via string concatenation. The PASSWORD field is constructed by concatenating the attacker-controlled bar variable into the query string, bypassing parameterized query protection. This allows SQL injection attacks that could read, modify, or delete database contents.

Why this matters

Risk if not fixed: An attacker could execute arbitrary SQL commands, including:

  • Reading sensitive data from the database
  • Modifying or deleting records
  • Bypassing authentication logic
  • Executing administrative operations

Risk level: High — Direct SQL injection via string concatenation into an executed PreparedStatement query. Authentication bypass is trivially achievable (e.g., injecting ' OR '1'='1 into the PASSWORD field).

Why we're changing it

Status: Confirmed vulnerability

Data Flow Analysis:

  1. Source (line 43): request.getQueryString() — attacker-controlled HTTP query string
  2. Extraction (lines 56–68): param is parsed from the query string and URL-decoded
  3. doSomething() routing (lines 93–111): guess = "ABC", switchTarget = guess.charAt(2)'C' (compile-time constant). The switch falls through case 'C':bar = param (line 106). Tainted input always propagates to bar.
  4. SQL construction (line 72):
    String sql = "SELECT * from USERS where USERNAME=? and PASSWORD='" + bar + "'";
    USERNAME is parameterized; PASSWORD is string-concatenated with attacker-controlled bar.
  5. Execution (lines 77–79): PreparedStatement executes the hybrid query.

Critical finding: The query is a mixed parameterized/vulnerable construction. The USERNAME placeholder provides no protection for the PASSWORD field. An attacker supplying ' OR '1'='1 as the query parameter produces:

SELECT * from USERS where USERNAME=? and PASSWORD='' OR '1'='1'

The switch's 'C' branch is always taken (compile-time constant), so bar always carries the raw user input — no data-flow escape exists.

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 Query Parameter<br/>(attacker-controlled)"] --> B["URLDecoder.decode()<br/>param variable"]
    B --> C["doSomething() switch<br/>case 'C' - bar = param"]
    C --> D["SQL String Concatenation<br/>PASSWORD='" + bar + "'"]
    D --> E["PreparedStatement.execute()<br/>Tainted SQL sent to DB"]
    E --> F["💥 SQL Injection<br/>Arbitrary query execution"]
    
    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:#FFE5E5,stroke:#F65A5A
    style F fill:#FEF3C7,stroke:#F59E0B
Loading

How we confirmed

Manual verification steps:

  1. Trace the HTTP request parameter BenchmarkTest02632 through the code
  2. Confirm that request.getQueryString() is parsed into param without sanitization
  3. Verify that doSomething() uses a compile-time constant (guess.charAt(2) where guess="ABC") that always evaluates to 'C'
  4. Confirm that case 'C' assigns bar = param, making the tainted value reachable at the SQL sink
  5. Inspect line 72 and verify that the PASSWORD field uses string concatenation: "PASSWORD='" + bar + "'"
  6. Confirm that only setString(1, 'foo') is called, leaving the PASSWORD concatenation unparameterized
  7. Execute a test with payload ' OR '1'='1 and observe that the query structure is altered
Runnable Verification Script (click to expand)

Save this script and run with bash verify_fix.sh:

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

echo "=== Verification: SQL Injection Fix in BenchmarkTest02632.java ==="

# Step 1: Check that the vulnerable concatenation has been replaced with parameterized query
echo "Step 1: Verifying PASSWORD field is now parameterized..."
if grep -n "PASSWORD='" src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java; then
    echo "❌ FAILED: Vulnerable string concatenation still present"
    exit 1
fi
echo "✅ PASSED: No string concatenation in PASSWORD field"

# Step 2: Confirm the query now uses two bind parameters
echo ""
echo "Step 2: Verifying query uses parameterized placeholders..."
if grep -n "PASSWORD=?" src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java; then
    echo "✅ PASSED: PASSWORD field uses bind parameter (?)"
else
    echo "❌ FAILED: PASSWORD field is not parameterized"
    exit 1
fi

# Step 3: Confirm setString is called for both parameters
echo ""
echo "Step 3: Verifying setString(2, bar) is called..."
if grep -n "setString(2" src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java; then
    echo "✅ PASSED: Second parameter binding found"
else
    echo "❌ FAILED: Second parameter binding not found"
    exit 1
fi

# Step 4: Compile the fixed code
echo ""
echo "Step 4: Compiling fixed code..."
if javac -d /tmp/benchmark_test src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java 2>/dev/null; then
    echo "✅ PASSED: Code compiles successfully"
else
    echo "⚠️  WARNING: Compilation check skipped (dependencies may be required)"
fi

echo ""
echo "=== All verification checks passed ==="

Vulnerable flow: src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java:77

SQL Injection

%%{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["SQL string concatenation with user input"]
        A2 --> A3["💥 Database Compromised"]
    end

    Vulnerable ~~~ Fixed

    subgraph Fixed["✅ Fixed Flow"]
        direction LR
        B1["Project"] --> B2["Parameterized queries/ORM used"]
        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: The SQL query concatenated user-controlled input (bar) directly into the PASSWORD field via string interpolation: "SELECT * from USERS where USERNAME=? and PASSWORD='" + bar + "'". Although USERNAME was parameterized, the PASSWORD value was not, allowing an attacker to inject arbitrary SQL by crafting a malicious value for the BenchmarkTest02632 query parameter (e.g., ' OR '1'='1). The doSomething() switch resolves to bar = param on case 'C', which is the active branch since switchTarget = 'C', making the user input directly reachable at the SQL sink.

Fix approach: Replacing the string-concatenated PASSWORD literal with a second positional parameter (?) and binding the value via statement.setString(2, bar) ensures the JDBC driver treats bar as data, never as executable SQL syntax. Parameterized queries are the canonical defense against SQL injection (OWASP Top 10 A03:2021, CWE-89) because the query structure is fixed at prepare-time, making injection structurally impossible regardless of input content.

Code change:

// Before (vulnerable)
String sql = "SELECT * from USERS where USERNAME=? and PASSWORD='" + bar + "'";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, "foo");

// After (fixed)
String sql = "SELECT * from USERS where USERNAME=? and PASSWORD=?";
PreparedStatement statement = connection.prepareStatement(sql);
statement.setString(1, "foo");
statement.setString(2, bar);

Alternatives considered:

  • Input sanitization/escaping — rejected because escaping is error-prone, dialect-dependent, and does not eliminate the root cause; parameterization is the correct solution
  • ORM or query builder abstraction — rejected as it would require changing the surrounding infrastructure beyond the minimal fix scope

Vulnerabilities Addressed

  • Grouped findings in scope: 1
  • Findings fixed in this PR: 1
  • Primary CWE family: CWE-89
  • Files covered: src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java
# Finding Detection Severity Location Status
1 SQL Injection
CWE-89
OpenGrep
Tainted Sql From Http Request
Medium src/main/java/org/owasp/benchmark/testcode/BenchmarkTest02632.java:77 Fixed

How we validated it

The fix was validated by:

  1. Confirming that the query structure is now fixed at prepare-time with both USERNAME and PASSWORD as bind parameters
  2. Verifying that setString(2, bar) is called to bind the PASSWORD value as data, not SQL syntax
  3. Testing with injection payloads (e.g., ' OR '1'='1) to confirm they are treated as literal strings, not SQL operators
  4. Ensuring no functionality regression — the query still correctly filters by both USERNAME and PASSWORD
  5. Confirming that the JDBC driver now sends the query structure and parameter values as separate protocol messages

How to verify

  1. Code inspection: Review the SQL query construction at line 72 to confirm both USERNAME and PASSWORD use bind parameters (?)
  2. Parameter binding: Verify that statement.setString(2, bar) is called after statement.setString(1, "foo")
  3. Compilation: Compile the fixed code to ensure no syntax errors
  4. Runtime testing: Execute the application with injection payloads and confirm they are treated as literal values
  5. Query logging: Enable SQL query logging to confirm the query structure remains constant and parameters are sent separately

Before you merge

  • Fix addresses the root cause (string concatenation replaced with parameterized query)
  • No new security vulnerabilities introduced
  • Code follows project conventions
  • Edge cases handled (null input, empty strings, special characters)
  • No functionality regression — authentication logic still works correctly
  • All SQL queries in this file use parameterized statements
  • Both USERNAME and PASSWORD fields are now bound via setString()

Learn more


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

Replace string concatenation in PASSWORD field with parameterized query.
Both USERNAME and PASSWORD now use bind parameters, preventing SQL injection
attacks via the BenchmarkTest02632 query parameter.
@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