Files
codeql/java/ql/test/query-tests/security/CWE-918/SanitizationTests.java
2026-02-12 16:57:04 +00:00

228 lines
11 KiB
Java

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class SanitizationTests extends HttpServlet {
private static final String VALID_URI = "http://lgtm.com";
private HttpClient client = HttpClient.newHttpClient();
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
URI uri = new URI(request.getParameter("uri")); // $ Source
// BAD: a request parameter is incorporated without validation into a Http
// request
HttpRequest r = HttpRequest.newBuilder(uri).build(); // $ Alert
client.send(r, null); // $ Alert
// GOOD: sanitisation by concatenation with a prefix that prevents targeting an arbitrary host.
// We test a few different ways of sanitisation: via string conctentation (perhaps nested),
// via a stringbuilder (for which we consider appends done in the constructor, chained onto
// the constructor and applied in subsequent statements) and via String.format.
String safeUri3 = "https://example.com/" + request.getParameter("uri3");
HttpRequest r3 = HttpRequest.newBuilder(new URI(safeUri3)).build();
client.send(r3, null);
String safeUri4 = "https://example.com/" + ("someprefix" + request.getParameter("uri4"));
HttpRequest r4 = HttpRequest.newBuilder(new URI(safeUri4)).build();
client.send(r4, null);
StringBuilder safeUri5 = new StringBuilder();
safeUri5.append("https://example.com/").append(request.getParameter("uri5"));
HttpRequest r5 = HttpRequest.newBuilder(new URI(safeUri5.toString())).build();
client.send(r5, null);
StringBuilder safeUri5a = new StringBuilder("https://example.com/");
safeUri5a.append(request.getParameter("uri5a"));
HttpRequest r5a = HttpRequest.newBuilder(new URI(safeUri5a.toString())).build();
client.send(r5a, null);
StringBuilder safeUri5b = (new StringBuilder("https://example.com/")).append("dir/");
safeUri5b.append(request.getParameter("uri5b"));
HttpRequest r5b = HttpRequest.newBuilder(new URI(safeUri5b.toString())).build();
client.send(r5b, null);
StringBuilder safeUri5c = (new StringBuilder("prefix")).append("https://example.com/dir/");
safeUri5c.append(request.getParameter("uri5c"));
HttpRequest r5c = HttpRequest.newBuilder(new URI(safeUri5c.toString())).build();
client.send(r5c, null);
String safeUri6 = String.format("https://example.com/%s", request.getParameter("uri6"));
HttpRequest r6 = HttpRequest.newBuilder(new URI(safeUri6)).build();
client.send(r6, null);
String safeUri7 = String.format("%s/%s", "https://example.com", request.getParameter("uri7"));
HttpRequest r7 = HttpRequest.newBuilder(new URI(safeUri7)).build();
client.send(r7, null);
String safeUri8 = String.format("%s%s", "https://example.com/", request.getParameter("uri8"));
HttpRequest r8 = HttpRequest.newBuilder(new URI(safeUri8)).build();
client.send(r8, null);
String safeUri9 = String.format("http://%s", "myserver.com") + "/" + request.getParameter("uri9");
HttpRequest r9 = HttpRequest.newBuilder(new URI(safeUri9)).build();
client.send(r9, null);
// BAD: cases where a string that would sanitise is used, but occurs in the wrong
// place to sanitise user input:
String unsafeUri3 = request.getParameter("baduri3") + "https://example.com/"; // $ Source
HttpRequest unsafer3 = HttpRequest.newBuilder(new URI(unsafeUri3)).build(); // $ Alert
client.send(unsafer3, null); // $ Alert
String unsafeUri4 = ("someprefix" + request.getParameter("baduri4")) + "https://example.com/"; // $ Source
HttpRequest unsafer4 = HttpRequest.newBuilder(new URI(unsafeUri4)).build(); // $ Alert
client.send(unsafer4, null); // $ Alert
StringBuilder unsafeUri5 = new StringBuilder();
unsafeUri5.append(request.getParameter("baduri5")).append("https://example.com/"); // $ Source
HttpRequest unsafer5 = HttpRequest.newBuilder(new URI(unsafeUri5.toString())).build(); // $ Alert
client.send(unsafer5, null); // $ Alert
StringBuilder unafeUri5a = new StringBuilder(request.getParameter("uri5a")); // $ Source
unafeUri5a.append("https://example.com/");
HttpRequest unsafer5a = HttpRequest.newBuilder(new URI(unafeUri5a.toString())).build(); // $ Alert
client.send(unsafer5a, null); // $ Alert
StringBuilder unsafeUri5b = (new StringBuilder(request.getParameter("uri5b"))).append("dir/"); // $ Source
unsafeUri5b.append("https://example.com/");
HttpRequest unsafer5b = HttpRequest.newBuilder(new URI(unsafeUri5b.toString())).build(); // $ Alert
client.send(unsafer5b, null); // $ Alert
StringBuilder unsafeUri5c = (new StringBuilder("https")).append(request.getParameter("uri5c")); // $ Source
unsafeUri5c.append("://example.com/dir/");
HttpRequest unsafer5c = HttpRequest.newBuilder(new URI(unsafeUri5c.toString())).build(); // $ Alert
client.send(unsafer5c, null); // $ Alert
String unsafeUri6 = String.format("%shttps://example.com/", request.getParameter("baduri6")); // $ Source
HttpRequest unsafer6 = HttpRequest.newBuilder(new URI(unsafeUri6)).build(); // $ Alert
client.send(unsafer6, null); // $ Alert
String unsafeUri7 = String.format("%s/%s", request.getParameter("baduri7"), "https://example.com"); // $ Source
HttpRequest unsafer7 = HttpRequest.newBuilder(new URI(unsafeUri7)).build(); // $ Alert
client.send(unsafer7, null); // $ Alert
String unsafeUri8 = String.format("%s%s", request.getParameter("baduri8"), "https://example.com/"); // $ Source
HttpRequest unsafer8 = HttpRequest.newBuilder(new URI(unsafeUri8)).build(); // $ Alert
client.send(unsafer8, null); // $ Alert
String unsafeUri9 = request.getParameter("baduri9") + "/" + String.format("http://%s", "myserver.com"); // $ Source
HttpRequest unsafer9 = HttpRequest.newBuilder(new URI(unsafeUri9)).build(); // $ Alert
client.send(unsafer9, null); // $ Alert
String unsafeUri10 = String.format("%s://%s:%s%s", "http", "myserver.com", "80", request.getParameter("baduri10")); // $ Source
HttpRequest unsafer10 = HttpRequest.newBuilder(new URI(unsafeUri10)).build(); // $ Alert
client.send(unsafer10, null); // $ Alert
// GOOD: sanitisation by regexp validation
String param10 = request.getParameter("uri10");
if (param10.matches("[a-zA-Z0-9_-]+")) {
HttpRequest r10 = HttpRequest.newBuilder(new URI(param10)).build();
client.send(r10, null);
}
String param11 = request.getParameter("uri11");
validate(param11);
HttpRequest r11 = HttpRequest.newBuilder(new URI(param11)).build();
client.send(r11, null);
String param12 = request.getParameter("uri12");
if (Pattern.matches("[a-zA-Z0-9_-]+", param12)) {
HttpRequest r12 = HttpRequest.newBuilder(new URI(param12)).build();
client.send(r12, null);
}
Pattern pattern = Pattern.compile("[a-zA-Z0-9_-]+");
String param13 = request.getParameter("uri13");
Matcher matcher = pattern.matcher(param13);
if (matcher.matches()) {
HttpRequest r13 = HttpRequest.newBuilder(new URI(param13)).build();
client.send(r13, null);
}
// GOOD: sanitisation by @Pattern annotation on a field
AnnotatedFieldObject obj14 = new AnnotatedFieldObject(request.getParameter("uri14"));
HttpRequest r14a = HttpRequest.newBuilder(new URI(obj14.uri)).build();
client.send(r14a, null);
HttpRequest r14b = HttpRequest.newBuilder(new URI(obj14.getUri())).build();
client.send(r14b, null);
// GOOD: sanitisation by @Pattern annotation on a parameter of a constructor
AnnotatedParameterObject obj15 = new AnnotatedParameterObject(request.getParameter("uri15"));
HttpRequest r15a = HttpRequest.newBuilder(new URI(obj15.uri)).build();
client.send(r15a, null);
HttpRequest r15b = HttpRequest.newBuilder(new URI(obj15.getUri())).build();
client.send(r15b, null);
// GOOD: sanitisation by @Pattern annotation on a parameter of a method
HttpRequest r16 = HttpRequest.newBuilder(new URI(identity1(request.getParameter("uri16")))).build();
client.send(r16, null);
// GOOD: sanitisation by @Pattern annotation on a method (which constrains the return value)
HttpRequest r17 = HttpRequest.newBuilder(new URI(identity2(request.getParameter("uri17")))).build();
client.send(r17, null);
// GOOD: sanitisation by @Pattern annotation on a type (we do not recognise this, so we get an FP)
HttpRequest r18 = HttpRequest.newBuilder(new URI(getFromList(List.of(request.getParameter("uri18"))))).build(); // $ SPURIOUS: Source Alert
client.send(r18, null); // $ SPURIOUS: Alert
} catch (Exception e) {
// TODO: handle exception
}
}
private void validate(String s) {
if (!s.matches("[a-zA-Z0-9_-]+")) {
throw new IllegalArgumentException("Invalid ID");
}
}
public String identity1(@javax.validation.constraints.Pattern(regexp = "[a-zA-Z0-9_-]+") String uri) {
return uri;
}
@javax.validation.constraints.Pattern(regexp = "[a-zA-Z0-9_-]+")
public String identity2(String uri) {
return uri;
}
public String getFromList(List<@javax.validation.constraints.Pattern(regexp = "[a-zA-Z0-9_-]+") String> list) {
return list.get(0);
}
public class AnnotatedFieldObject {
@javax.validation.constraints.Pattern(regexp = "[a-zA-Z0-9_-]+")
String uri;
String otherField;
public AnnotatedFieldObject(String uri) {
this.uri = uri;
}
public String getUri() {
return uri;
}
}
public class AnnotatedParameterObject {
String uri;
public AnnotatedParameterObject(@javax.validation.constraints.Pattern(regexp = "[a-zA-Z0-9_-]+") String uri) {
this.uri = uri;
}
public String getUri() {
return uri;
}
}
}