OculusCyber Logo

OculusCyber

Home

Browse Topics


SAST Scanner Findings and Remediations

By oculus

October 29, 2025


How to fix SQL Injection that was found by SAST Scanner?

The core fix is really about shifting from any kind of raw string concatenation to using parameterized queries or prepared statements. Once you do that, you basically neutralize the risk because the database won't interpret the input as executable SQL. Just follow that approach wherever the SAST tool found vulnerabilities

A java code example will be like below:

So, first, here's the vulnerable version in Java:

String userId = request.getParameter("userId");Statement stmt = connection.createStatement();String query = "SELECT * FROM users WHERE id = '" + userId + "'";ResultSet rs = stmt.executeQuery(query);

And now the fixed version using a prepared statement:

String userId = request.getParameter("userId");String query = "SELECT * FROM users WHERE id = ?";PreparedStatement pstmt = connection.prepareStatement(query);pstmt.setString(1, userId);ResultSet rs = pstmt.executeQuery();

In this fixed version, using PreparedStatement with a placeholder ? ensures that the user input is safely parameterized and prevents SQL injection.

"parameterized" is essentially that instead of embedding user inputs directly into your SQL queries as raw strings, you use placeholders in the query and then supply the actual values separately.

So the database knows that these placeholders are just data and not part of the SQL command itself. This way, even if a user tries to inject malicious SQL, it won't be executed as code, just treated as a harmless parameter. It's a safe way to handle user input and a key practice in preventing SQL injection.

""
The idea is that when you use parameterization, the database recognizes that the value you're passing in is just a piece of data. It won't try to run it as part of the SQL command itself.
So yes, the same value is going in, but because it's handled as a parameter, the database won't interpret it as executable SQL. That's really the magic of it—it turns potentially dangerous input into harmless data from the database's perspective. "

That "1" in the pstmt.setString(1, userId) call is indeed the index of the parameter placeholder. In other words, it's telling the database, "Hey, replace the first ? in my query with this userId value."

If you had multiple placeholders, you'd just increment those indices. So the second ? would be 2, the third would be 3, and so on. It's basically like matching the order of the question marks in your SQL statement to the values you want to insert

Cross-site scripting, or XSS

So besides SQL injection, there are definitely a few other common true positives that SAST tools often flag. One big category is cross-site scripting, or XSS. That's when untrusted data can be injected into a web page and run as a script in someone else's browser.

Let's go with the most common type, which is reflected XSS. This happens when a web application takes user input and immediately includes it in the response without proper escaping. So, for example, in a little snippet of code in something like JavaScript on the server side, you might have something like this

const userInput = req.query.name;

res.send("<h1>Hello, " + userInput + "!</h1>");

In this example, if someone puts a script tag in as their name—like <script>alert('XSS')</script>—the browser would execute that script.

The fix would be to properly escape that user input or use a templating engine that automatically escapes it so that any HTML tags get treated as plain text rather than code. But that's the gist of how reflected XSS works!If you're doing it without a framework and just want a simple, in-code way to escape user input, you can basically replace special HTML characters with their safe equivalents.

For example, you could create a little function that replaces < with &lt;, > with &gt;, and so on. Here's a really simple JavaScript example:

function escapeHTML(str) {
  return str.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;")
            .replace(/"/g, "&quot;")
            .replace(/'/g, "&#39;");
}

const userInput = req.query.name;
const safeInput = escapeHTML(userInput);
res.send("<h1>Hello, " + safeInput + "!</h1>");

This way, any potentially dangerous characters in the user input are converted into harmless entities that the browser will just display as text rather than running as code. It's a nice and straightforward way to escape user input on your own.

Command Injection

Another pretty common SAST finding that's usually a true positive is something like command injection. So command injection is a lot like SQL injection, but instead of injecting SQL, the attacker is injecting operating system commands.

let's do that in Java. So a similar example in Java might look like this if you were using something like Runtime.getRuntime().exec() to execute a system command directly with user input.

So the vulnerable version would be something like this:

import java.io.IOException;

public class CommandInjectionExample {
    public static void main(String[] args) throws IOException {
        String userInput = "some user-provided filename"; // Imagine this comes from a request or user input
        Runtime.getRuntime().exec("cat " + userInput);
    }
}

In this example, if the userInput is something malicious, it could run unwanted commands on the system.

The safer fix would be to avoid passing user input directly into a system command. Instead, you could validate it or use a safer method, like running a predefined command that doesn't concatenate user input directly. But that's the gist of how it looks in Java!

To fix command injection in Java, you typically want to avoid directly concatenating user input into your commands. Instead, you can use something like a whitelist of allowed inputs or a safer approach that doesn't let arbitrary input become part of the command line.

Here's a simple example of a more secure function that uses a whitelist approach:

import java.io.IOException;

public class SafeCommandExecution {

    public static void safeCatCommand(String filename) throws IOException {
        // Define a whitelist of allowed filenames or a safe directory
        if (isValidFilename(filename)) {
            // Use ProcessBuilder for safer command execution
            ProcessBuilder pb = new ProcessBuilder("cat", filename);
            Process process = pb.start();

            // Handle the process output or errors as needed
            // ...
        } else {
            throw new IllegalArgumentException("Invalid filename");
        }
    }

    private static boolean isValidFilename(String filename) {
        // Implement your validation logic, for example: check against a list of known safe filenames or ensure it doesn't contain dangerous characters
        return filename.matches("[a-zA-Z0-9._-]+");
    }
}

In this example, we use a ProcessBuilder to execute the command safely and a simple validation function to ensure the filename is safe. This way, you're not directly injecting arbitrary user input into the command, and you reduce the risk of command injection.

second-order injection

Let's walk through a simple Java example of a second-order injection.

Imagine you have a web app where a user can enter some kind of data—let's say a username that gets stored in a database. Now, when you store it, you don't see any immediate problem because you just save it as text in the database.

But later, maybe you have another piece of code that reads that username from the database and uses it directly in an HTML page or another SQL query without proper escaping. That's when the malicious input that was stored earlier actually gets executed.

Here's a simple conceptual example:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class SecondOrderInjectionExample {

    public void storeUserInput(String userInput, Connection conn) throws Exception {
        // Store the user input in the database (no immediate harm)
        Statement stmt = conn.createStatement();
        String query = "INSERT INTO users (username) VALUES ('" + userInput + "')";
        stmt.executeUpdate(query);
    }

    public String retrieveAndUseUserInput(int userId, Connection conn) throws Exception {
        // Retrieve the stored input
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT username FROM users WHERE id = " + userId);
        
        if (rs.next()) {
            String storedInput = rs.getString("username");
            
            // Now we use this stored input in a new context without escaping
            // For example, directly into HTML or another query
            // If the stored input contains a malicious payload, it could execute here
            return "<html><body>Hello, " + storedInput + "!</body></html>";
        }
        return "No user found";
    }
}

In this scenario, the username is stored in the database first. Later on, when you retrieve it and use it directly in an HTML context, if that stored input had something malicious in it—like a script tag—it could execute when the page is rendered. So the injection doesn't happen at the time of storage, but when that data is used later on. And that's a classic example of a second-order injection!The mitigated version will be like

import java.sql.*;

import org.owasp.encoder.Encode; // OWASP Java Encoder library

public class SafeExample {

public void storeUserInput(String userInput, Connection conn) throws SQLException {

// 1️⃣ Use a PreparedStatement to safely insert user input

String insertQuery = "INSERT INTO users (username) VALUES (?)";

try (PreparedStatement pstmt = conn.prepareStatement(insertQuery)) {

pstmt.setString(1, userInput);

pstmt.executeUpdate();

}

}

public String retrieveAndDisplayUser(int userId, Connection conn) throws SQLException {

// 2️⃣ Use PreparedStatement again when retrieving data

String selectQuery = "SELECT username FROM users WHERE id = ?";

String safeHtmlOutput = "";

try (PreparedStatement pstmt = conn.prepareStatement(selectQuery)) {

pstmt.setInt(1, userId);

ResultSet rs = pstmt.executeQuery();

if (rs.next()) {

String storedInput = rs.getString("username");

// 3️⃣ Encode output before displaying in HTML

safeHtmlOutput = "<h1>Hello, " + Encode.forHtml(storedInput) + "!</h1>";

}

}

return safeHtmlOutput;

}

}

1. SQL InjectionWe replaced raw SQL string concatenation with PreparedStatement. This change ensures that user inputs are treated as data—not executable SQL—so malicious SQL can't be injected during inserts or queries.

2. Second-Order InjectionEven though input might look safe when stored, it could later be reused in a dangerous context (like HTML or a SQL query). To mitigate this, we added context-aware encoding right before output. This ensures that any stored payloads are neutralized before they cause harm.

3. XSS RiskWe used the OWASP Java Encoder's Encode.forHtml() method before rendering user-supplied input in HTML. This prevents scripts or HTML tags from executing in the browser by converting them into harmless, visible text.