diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
deleted file mode 100644
index 53ee6182511..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.ql
+++ /dev/null
@@ -1,68 +0,0 @@
-/**
- * @name JSONP Injection
- * @description User-controlled callback function names that are not verified are vulnerable
- * to jsonp injection attacks.
- * @kind path-problem
- * @problem.severity error
- * @precision high
- * @id java/JSONP-Injection
- * @tags security
- * external/cwe/cwe-352
- */
-
-import java
-import JsonpInjectionLib
-import JsonpInjectionFilterLib
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.deadcode.WebEntryPoints
-import DataFlow::PathGraph
-
-
-/** If there is a method to verify `token`, `auth`, `referer`, and `origin`, it will not pass. */
-class ServletVerifAuth extends DataFlow::BarrierGuard {
- ServletVerifAuth() {
- exists(MethodAccess ma, Node prod, Node succ |
- ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- prod instanceof RemoteFlowSource and
- succ.asExpr() = ma.getAnArgument() and
- ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- localFlowStep*(prod, succ) and
- this = ma
- )
- }
-
- override predicate checks(Expr e, boolean branch) {
- exists(Node node |
- node instanceof JsonpInjectionSink and
- e = node.asExpr() and
- branch = true
- )
- }
-}
-
-/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
-class JsonpInjectionConfig extends TaintTracking::Configuration {
- JsonpInjectionConfig() { this = "JsonpInjectionConfig" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof GetHttpRequestSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof JsonpInjectionSink }
-
- override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
- guard instanceof ServletVerifAuth
- }
-
- override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
- exists(MethodAccess ma |
- isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
- )
- }
-}
-
-from DataFlow::PathNode source, DataFlow::PathNode sink, JsonpInjectionConfig conf
-where
- not checks() = false and
- conf.hasFlowPath(source, sink) and
- exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
-select sink.getNode(), source, sink, "Jsonp Injection query might include code from $@.",
- source.getNode(), "this user input"
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
deleted file mode 100644
index b349bed2641..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionFilterLib.qll
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
- * @name JSONP Injection
- * @description User-controlled callback function names that are not verified are vulnerable
- * to json hijacking attacks.
- * @kind path-problem
- */
-
-import java
-import DataFlow
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.dataflow.TaintTracking2
-import DataFlow::PathGraph
-
-class FilterVerifAuth extends DataFlow::BarrierGuard {
- FilterVerifAuth() {
- exists(MethodAccess ma, Node prod, Node succ |
- ma.getMethod().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- prod instanceof RemoteFlowSource and
- succ.asExpr() = ma.getAnArgument() and
- ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
- localFlowStep*(prod, succ) and
- this = ma
- )
- }
-
- override predicate checks(Expr e, boolean branch) {
- exists(Node node |
- node instanceof DoFilterMethodSink and
- e = node.asExpr() and
- branch = true
- )
- }
-}
-
-/** A data flow source for `Filter.doFilter` method paramters. */
-private class DoFilterMethodSource extends DataFlow::Node {
- DoFilterMethodSource() {
- exists(Method m |
- isDoFilterMethod(m) and
- m.getAParameter().getAnAccess() = this.asExpr()
- )
- }
-}
-
-/** A data flow sink for `FilterChain.doFilter` method qualifying expression. */
-private class DoFilterMethodSink extends DataFlow::Node {
- DoFilterMethodSink() {
- exists(MethodAccess ma, Method m | ma.getMethod() = m |
- m.hasName("doFilter") and
- m.getDeclaringType*().hasQualifiedName("javax.servlet", "FilterChain") and
- ma.getQualifier() = this.asExpr()
- )
- }
-}
-
-/** Taint-tracking configuration tracing flow from `doFilter` method paramter source to output
- * `FilterChain.doFilter` method qualifying expression.
- * */
-class DoFilterMethodConfig extends TaintTracking::Configuration {
- DoFilterMethodConfig() { this = "DoFilterMethodConfig" }
-
- override predicate isSource(DataFlow::Node source) { source instanceof DoFilterMethodSource }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof DoFilterMethodSink }
-
- override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
- guard instanceof FilterVerifAuth
- }
-}
-
-/** Implement class modeling verification for `Filter.doFilter`, return false if it fails. */
-boolean checks() {
- exists(DataFlow::PathNode source, DataFlow::PathNode sink, DoFilterMethodConfig conf |
- conf.hasFlowPath(source, sink) and
- result = false
- )
-}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
deleted file mode 100644
index 3f730425823..00000000000
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionLib.qll
+++ /dev/null
@@ -1,155 +0,0 @@
-import java
-import DataFlow
-import JsonStringLib
-import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.FlowSources
-import semmle.code.java.frameworks.spring.SpringController
-
-/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
-private predicate isGetServletMethod(Method m) {
- isServletRequestMethod(m) and m.getName() = "doGet"
-}
-
-/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
-private predicate isGetSpringControllerMethod(Method m) {
- exists(Annotation a |
- a = m.getAnAnnotation() and
- a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
- )
- or
- exists(Annotation a |
- a = m.getAnAnnotation() and
- a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
- a.getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}")
- )
-}
-
-/** Method parameters use the annotation `@RequestParam` or the parameter type is `ServletRequest`, `String`, `Object` */
-predicate checkSpringMethodParameterType(Method m, int i) {
- m.getParameter(i).getType() instanceof ServletRequest
- or
- exists(Parameter p |
- p = m.getParameter(i) and
- p.hasAnnotation() and
- p.getAnAnnotation()
- .getType()
- .hasQualifiedName("org.springframework.web.bind.annotation", "RequestParam") and
- p.getType().getName().regexpMatch("String|Object")
- )
- or
- exists(Parameter p |
- p = m.getParameter(i) and
- not p.hasAnnotation() and
- p.getType().getName().regexpMatch("String|Object")
- )
-}
-
-/** A data flow source for get method request parameters. */
-abstract class GetHttpRequestSource extends DataFlow::Node { }
-
-/** A data flow source for servlet get method request parameters. */
-private class ServletGetHttpRequestSource extends GetHttpRequestSource {
- ServletGetHttpRequestSource() {
- exists(Method m |
- isGetServletMethod(m) and
- m.getParameter(0).getAnAccess() = this.asExpr()
- )
- }
-}
-
-/** A data flow source for spring controller get method request parameters. */
-private class SpringGetHttpRequestSource extends GetHttpRequestSource {
- SpringGetHttpRequestSource() {
- exists(SpringControllerMethod scm, int i |
- isGetSpringControllerMethod(scm) and
- checkSpringMethodParameterType(scm, i) and
- scm.getParameter(i).getAnAccess() = this.asExpr()
- )
- }
-}
-
-/** A data flow sink for unvalidated user input that is used to jsonp. */
-abstract class JsonpInjectionSink extends DataFlow::Node { }
-
-/** Use ```print```, ```println```, ```write``` to output result. */
-private class WriterPrintln extends JsonpInjectionSink {
- WriterPrintln() {
- exists(MethodAccess ma |
- ma.getMethod().getName().regexpMatch("print|println|write") and
- ma.getMethod()
- .getDeclaringType()
- .getASourceSupertype*()
- .hasQualifiedName("java.io", "PrintWriter") and
- ma.getArgument(0) = this.asExpr()
- )
- }
-}
-
-/** Spring Request Method return result. */
-private class SpringReturn extends JsonpInjectionSink {
- SpringReturn() {
- exists(ReturnStmt rs, Method m | m = rs.getEnclosingCallable() |
- isGetSpringControllerMethod(m) and
- rs.getResult() = this.asExpr()
- )
- }
-}
-
-/** A concatenate expression using `(` and `)` or `);`. */
-class JsonpInjectionExpr extends AddExpr {
- JsonpInjectionExpr() {
- getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
- getLeftOperand()
- .(AddExpr)
- .getLeftOperand()
- .(AddExpr)
- .getRightOperand()
- .toString()
- .regexpMatch("\"\\(\"")
- }
-
- /** Get the jsonp function name of this expression */
- Expr getFunctionName() {
- result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
- }
-
- /** Get the json data of this expression */
- Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
-}
-
-/** A data flow configuration tracing flow from remote sources to jsonp function name. */
-class RemoteFlowConfig extends DataFlow2::Configuration {
- RemoteFlowConfig() { this = "RemoteFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(JsonpInjectionExpr jhe | jhe.getFunctionName() = sink.asExpr())
- }
-}
-
-/** A data flow configuration tracing flow from json data to splicing jsonp data. */
-class JsonDataFlowConfig extends DataFlow2::Configuration {
- JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
-
- override predicate isSink(DataFlow::Node sink) {
- exists(JsonpInjectionExpr jhe | jhe.getJsonExpr() = sink.asExpr())
- }
-}
-
-/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
-class JsonpInjectionFlowConfig extends DataFlow::Configuration {
- JsonpInjectionFlowConfig() { this = "JsonpInjectionFlowConfig" }
-
- override predicate isSource(DataFlow::Node src) {
- exists(JsonpInjectionExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
- jhe = src.asExpr() and
- jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
- rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
- )
- }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof JsonpInjectionSink }
-}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonStringLib.qll
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonStringLib.qll
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
similarity index 72%
rename from java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
index 9f079513a8b..84a172a7aeb 100644
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.java
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpController.java
@@ -3,17 +3,14 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import java.io.PrintWriter;
import java.util.HashMap;
-import java.util.Random;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
-public class JsonpInjection {
+public class JsonpController {
private static HashMap hashMap = new HashMap();
static {
@@ -96,54 +93,13 @@ public class JsonpInjection {
@GetMapping(value = "jsonp7")
@ResponseBody
- public String good(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- String val = "";
- Random random = new Random();
- for (int i = 0; i < 10; i++) {
- val += String.valueOf(random.nextInt(10));
- }
- // good
- jsonpCallback = jsonpCallback + "_" + val;
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- @GetMapping(value = "jsonp8")
- @ResponseBody
public String good1(HttpServletRequest request) {
String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
String token = request.getParameter("token");
- // good
if (verifToken(token)){
- System.out.println(token);
- String jsonStr = getJsonStr(hashMap);
- resultStr = jsonpCallback + "(" + jsonStr + ")";
- return resultStr;
- }
-
- return "error";
- }
-
- @GetMapping(value = "jsonp9")
- @ResponseBody
- public String good2(HttpServletRequest request) {
- String resultStr = null;
- String jsonpCallback = request.getParameter("jsonpCallback");
-
- String referer = request.getHeader("Referer");
-
- boolean result = verifReferer(referer);
-
- boolean test = result;
- // good
- if (test){
+ String jsonpCallback = request.getParameter("jsonpCallback");
String jsonStr = getJsonStr(hashMap);
resultStr = jsonpCallback + "(" + jsonStr + ")";
return resultStr;
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjection.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.java
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
similarity index 96%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
index b063b409d3a..bb5d628ac0b 100644
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjection.qhelp
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.qhelp
@@ -16,7 +16,7 @@ there is a problem of sensitive information leakage.
The following example shows the case of no verification processing and verification processing for the external input function name.
-
+
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
new file mode 100644
index 00000000000..f3ae25daa03
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjection.ql
@@ -0,0 +1,64 @@
+/**
+ * @name JSONP Injection
+ * @description User-controlled callback function names that are not verified are vulnerable
+ * to jsonp injection attacks.
+ * @kind path-problem
+ * @problem.severity error
+ * @precision high
+ * @id java/JSONP-Injection
+ * @tags security
+ * external/cwe/cwe-352
+ */
+
+import java
+import JsonpInjectionLib
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.deadcode.WebEntryPoints
+import semmle.code.java.security.XSS
+import DataFlow::PathGraph
+
+/** Determine whether there is a verification method for the remote streaming source data flow path method. */
+predicate existsFilterVerificationMethod() {
+ exists(MethodAccess ma,Node existsNode, Method m|
+ ma.getMethod() instanceof VerificationMethodClass and
+ existsNode.asExpr() = ma and
+ m = getAnMethod(existsNode.getEnclosingCallable()) and
+ isDoFilterMethod(m)
+ )
+}
+
+/** Determine whether there is a verification method for the remote streaming source data flow path method. */
+predicate existsServletVerificationMethod(Node checkNode) {
+ exists(MethodAccess ma,Node existsNode|
+ ma.getMethod() instanceof VerificationMethodClass and
+ existsNode.asExpr() = ma and
+ getAnMethod(existsNode.getEnclosingCallable()) = getAnMethod(checkNode.getEnclosingCallable())
+ )
+}
+
+/** Taint-tracking configuration tracing flow from get method request sources to output jsonp data. */
+class RequestResponseFlowConfig extends TaintTracking::Configuration {
+ RequestResponseFlowConfig() { this = "RequestResponseFlowConfig" }
+
+ override predicate isSource(DataFlow::Node source) {
+ source instanceof RemoteFlowSource and
+ getAnMethod(source.getEnclosingCallable()) instanceof RequestGetMethod
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
+
+ override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(MethodAccess ma |
+ isRequestGetParamMethod(ma) and pred.asExpr() = ma.getQualifier() and succ.asExpr() = ma
+ )
+ }
+}
+
+from DataFlow::PathNode source, DataFlow::PathNode sink, RequestResponseFlowConfig conf
+where
+ not existsServletVerificationMethod(source.getNode()) and
+ not existsFilterVerificationMethod() and
+ conf.hasFlowPath(source, sink) and
+ exists(JsonpInjectionFlowConfig jhfc | jhfc.hasFlowTo(sink.getNode()))
+select sink.getNode(), source, sink, "Jsonp Injection query might include code from $@.",
+ source.getNode(), "this user input"
\ No newline at end of file
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
new file mode 100644
index 00000000000..b8964524a9f
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionLib.qll
@@ -0,0 +1,130 @@
+import java
+import DataFlow
+import JsonStringLib
+import semmle.code.java.security.XSS
+import semmle.code.java.dataflow.DataFlow
+import semmle.code.java.dataflow.FlowSources
+import semmle.code.java.frameworks.spring.SpringController
+
+/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
+class VerificationMethodFlowConfig extends TaintTracking::Configuration {
+ VerificationMethodFlowConfig() { this = "VerificationMethodFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(MethodAccess ma, BarrierGuard bg |
+ ma.getMethod().getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
+ bg = ma and
+ sink.asExpr() = ma.getAnArgument()
+ )
+ }
+}
+
+/** The parameter name of the method is `token`, `auth`, `referer`, `origin`. */
+class VerificationMethodClass extends Method {
+ VerificationMethodClass() {
+ exists(MethodAccess ma, BarrierGuard bg, VerificationMethodFlowConfig vmfc, Node node |
+ this = ma.getMethod() and
+ this.getAParameter().getName().regexpMatch("(?i).*(token|auth|referer|origin).*") and
+ bg = ma and
+ node.asExpr() = ma.getAnArgument() and
+ vmfc.hasFlowTo(node)
+ )
+ }
+}
+
+/** Get Callable by recursive method. */
+Callable getAnMethod(Callable call) {
+ result = call
+ or
+ result = getAnMethod(call.getAReference().getEnclosingCallable())
+}
+
+abstract class RequestGetMethod extends Method { }
+
+/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+private class ServletGetMethod extends RequestGetMethod {
+ ServletGetMethod() {
+ exists(Method m |
+ m = this and
+ isServletRequestMethod(m) and
+ m.getName() = "doGet"
+ )
+ }
+}
+
+/** Holds if `m` is a method of some override of `HttpServlet.doGet`. */
+private class SpringControllerGetMethod extends RequestGetMethod {
+ SpringControllerGetMethod() {
+ exists(Annotation a |
+ a = this.getAnAnnotation() and
+ a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "GetMapping")
+ )
+ or
+ exists(Annotation a |
+ a = this.getAnAnnotation() and
+ a.getType().hasQualifiedName("org.springframework.web.bind.annotation", "RequestMapping") and
+ a.getValue("method").toString().regexpMatch("RequestMethod.GET|\\{...\\}")
+ )
+ }
+}
+
+/** A concatenate expression using `(` and `)` or `);`. */
+class JsonpInjectionExpr extends AddExpr {
+ JsonpInjectionExpr() {
+ getRightOperand().toString().regexpMatch("\"\\)\"|\"\\);\"") and
+ getLeftOperand()
+ .(AddExpr)
+ .getLeftOperand()
+ .(AddExpr)
+ .getRightOperand()
+ .toString()
+ .regexpMatch("\"\\(\"")
+ }
+
+ /** Get the jsonp function name of this expression */
+ Expr getFunctionName() {
+ result = getLeftOperand().(AddExpr).getLeftOperand().(AddExpr).getLeftOperand()
+ }
+
+ /** Get the json data of this expression */
+ Expr getJsonExpr() { result = getLeftOperand().(AddExpr).getRightOperand() }
+}
+
+/** A data flow configuration tracing flow from remote sources to jsonp function name. */
+class RemoteFlowConfig extends DataFlow2::Configuration {
+ RemoteFlowConfig() { this = "RemoteFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof RemoteFlowSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonpInjectionExpr jhe | jhe.getFunctionName() = sink.asExpr())
+ }
+}
+
+/** A data flow configuration tracing flow from json data to splicing jsonp data. */
+class JsonDataFlowConfig extends DataFlow2::Configuration {
+ JsonDataFlowConfig() { this = "JsonDataFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) { src instanceof JsonpStringSource }
+
+ override predicate isSink(DataFlow::Node sink) {
+ exists(JsonpInjectionExpr jhe | jhe.getJsonExpr() = sink.asExpr())
+ }
+}
+
+/** Taint-tracking configuration tracing flow from user-controllable function name jsonp data to output jsonp data. */
+class JsonpInjectionFlowConfig extends TaintTracking::Configuration {
+ JsonpInjectionFlowConfig() { this = "JsonpInjectionFlowConfig" }
+
+ override predicate isSource(DataFlow::Node src) {
+ exists(JsonpInjectionExpr jhe, JsonDataFlowConfig jdfc, RemoteFlowConfig rfc |
+ jhe = src.asExpr() and
+ jdfc.hasFlowTo(DataFlow::exprNode(jhe.getJsonExpr())) and
+ rfc.hasFlowTo(DataFlow::exprNode(jhe.getFunctionName()))
+ )
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof XssSink }
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet1.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet1.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet1.java
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java b/java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet2.java
similarity index 100%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet2.java
rename to java/ql/src/experimental/Security/CWE/CWE-352/JsonpInjectionServlet2.java
diff --git a/java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java b/java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java
new file mode 100644
index 00000000000..97444932ae1
--- /dev/null
+++ b/java/ql/src/experimental/Security/CWE/CWE-352/RefererFilter.java
@@ -0,0 +1,43 @@
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.util.StringUtils;
+
+public class RefererFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ String refefer = request.getHeader("Referer");
+ boolean result = verifReferer(refefer);
+ if (result){
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ response.sendError(444, "Referer xxx.");
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ public static boolean verifReferer(String referer){
+ if (StringUtils.isEmpty(referer)){
+ return false;
+ }
+ if (referer.startsWith("http://www.baidu.com/")){
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/java/ql/src/semmle/code/java/frameworks/Servlets.qll b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
index b2054dc30cb..5cccf62122f 100644
--- a/java/ql/src/semmle/code/java/frameworks/Servlets.qll
+++ b/java/ql/src/semmle/code/java/frameworks/Servlets.qll
@@ -338,7 +338,6 @@ predicate isRequestGetParamMethod(MethodAccess ma) {
ma.getMethod() instanceof HttpServletRequestGetQueryStringMethod
}
-
/**
* A class that has `javax.servlet.Filter` as an ancestor.
*/
@@ -346,21 +345,18 @@ class FilterClass extends Class {
FilterClass() { getAnAncestor().hasQualifiedName("javax.servlet", "Filter") }
}
-
/**
* The interface `javax.servlet.FilterChain`
*/
-class FilterChain extends RefType {
- FilterChain() {
- hasQualifiedName("javax.servlet", "FilterChain")
- }
+class FilterChain extends Interface {
+ FilterChain() { hasQualifiedName("javax.servlet", "FilterChain") }
}
-/** Holds if `m` is a request handler method (for example `doGet` or `doPost`). */
+/** Holds if `m` is a filter handler method (for example `doFilter`). */
predicate isDoFilterMethod(Method m) {
m.getDeclaringType() instanceof FilterClass and
m.getNumberOfParameters() = 3 and
m.getParameter(0).getType() instanceof ServletRequest and
m.getParameter(1).getType() instanceof ServletResponse and
m.getParameter(2).getType() instanceof FilterChain
-}
\ No newline at end of file
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java
new file mode 100644
index 00000000000..cf860c75640
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpController.java
@@ -0,0 +1,128 @@
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.gson.Gson;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+public class JsonpController {
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+
+ @GetMapping(value = "jsonp1", produces="text/javascript")
+ @ResponseBody
+ public String bad1(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+ resultStr = jsonpCallback + "(" + result + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp2")
+ @ResponseBody
+ public String bad2(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+
+ resultStr = jsonpCallback + "(" + JSONObject.toJSONString(hashMap) + ")";
+
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp3")
+ @ResponseBody
+ public String bad3(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp4")
+ @ResponseBody
+ public String bad4(HttpServletRequest request) {
+ String resultStr = null;
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String restr = JSONObject.toJSONString(hashMap);
+ resultStr = jsonpCallback + "(" + restr + ");";
+ return resultStr;
+ }
+
+ @GetMapping(value = "jsonp5")
+ @ResponseBody
+ public void bad5(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String result = gson.toJson(hashMap);
+
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp6")
+ @ResponseBody
+ public void bad6(HttpServletRequest request,
+ HttpServletResponse response) throws Exception {
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ ObjectMapper mapper = new ObjectMapper();
+ String result = mapper.writeValueAsString(hashMap);
+ String resultStr = null;
+ pw = response.getWriter();
+ resultStr = jsonpCallback + "(" + result + ")";
+ pw.println(resultStr);
+ }
+
+ @GetMapping(value = "jsonp7")
+ @ResponseBody
+ public String good1(HttpServletRequest request) {
+ String resultStr = null;
+
+ String token = request.getParameter("token");
+
+ if (verifToken(token)){
+ String jsonpCallback = request.getParameter("jsonpCallback");
+ String jsonStr = getJsonStr(hashMap);
+ resultStr = jsonpCallback + "(" + jsonStr + ")";
+ return resultStr;
+ }
+
+ return "error";
+ }
+
+ public static String getJsonStr(Object result) {
+ return JSONObject.toJSONString(result);
+ }
+
+ public static boolean verifToken(String token){
+ if (token != "xxxx"){
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean verifReferer(String referer){
+ if (!referer.startsWith("http://test.com/")){
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
deleted file mode 100644
index 7e3069cf1d9..00000000000
--- a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection.expected
+++ /dev/null
@@ -1,60 +0,0 @@
-edges
-| JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | JsonpInjection.java:34:16:34:24 | resultStr |
-| JsonpInjection.java:33:21:33:54 | ... + ... : String | JsonpInjection.java:34:16:34:24 | resultStr |
-| JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | JsonpInjection.java:45:16:45:24 | resultStr |
-| JsonpInjection.java:43:21:43:80 | ... + ... : String | JsonpInjection.java:45:16:45:24 | resultStr |
-| JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | JsonpInjection.java:55:16:55:24 | resultStr |
-| JsonpInjection.java:54:21:54:55 | ... + ... : String | JsonpInjection.java:55:16:55:24 | resultStr |
-| JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | JsonpInjection.java:65:16:65:24 | resultStr |
-| JsonpInjection.java:64:21:64:54 | ... + ... : String | JsonpInjection.java:65:16:65:24 | resultStr |
-| JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | JsonpInjection.java:80:20:80:28 | resultStr |
-| JsonpInjection.java:79:21:79:54 | ... + ... : String | JsonpInjection.java:80:20:80:28 | resultStr |
-| JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | JsonpInjection.java:94:20:94:28 | resultStr |
-| JsonpInjection.java:93:21:93:54 | ... + ... : String | JsonpInjection.java:94:20:94:28 | resultStr |
-| JsonpInjection.java:101:32:101:38 | request : HttpServletRequest | JsonpInjection.java:112:16:112:24 | resultStr |
-| JsonpInjection.java:127:25:127:59 | ... + ... : String | JsonpInjection.java:128:20:128:28 | resultStr |
-| JsonpInjection.java:148:25:148:59 | ... + ... : String | JsonpInjection.java:149:20:149:28 | resultStr |
-nodes
-| JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:33:21:33:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:34:16:34:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:34:16:34:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:43:21:43:80 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:45:16:45:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:45:16:45:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:54:21:54:55 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:55:16:55:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:55:16:55:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:64:21:64:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:65:16:65:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:65:16:65:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:79:21:79:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:80:20:80:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:93:21:93:54 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:94:20:94:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:94:20:94:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:101:32:101:38 | request : HttpServletRequest | semmle.label | request : HttpServletRequest |
-| JsonpInjection.java:112:16:112:24 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:127:25:127:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:128:20:128:28 | resultStr | semmle.label | resultStr |
-| JsonpInjection.java:148:25:148:59 | ... + ... : String | semmle.label | ... + ... : String |
-| JsonpInjection.java:149:20:149:28 | resultStr | semmle.label | resultStr |
-#select
-| JsonpInjection.java:34:16:34:24 | resultStr | JsonpInjection.java:29:32:29:38 | request : HttpServletRequest | JsonpInjection.java:34:16:34:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:29:32:29:38 | request | this user input |
-| JsonpInjection.java:45:16:45:24 | resultStr | JsonpInjection.java:41:32:41:38 | request : HttpServletRequest | JsonpInjection.java:45:16:45:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:41:32:41:38 | request | this user input |
-| JsonpInjection.java:55:16:55:24 | resultStr | JsonpInjection.java:52:32:52:38 | request : HttpServletRequest | JsonpInjection.java:55:16:55:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:52:32:52:38 | request | this user input |
-| JsonpInjection.java:65:16:65:24 | resultStr | JsonpInjection.java:62:32:62:38 | request : HttpServletRequest | JsonpInjection.java:65:16:65:24 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:62:32:62:38 | request | this user input |
-| JsonpInjection.java:80:20:80:28 | resultStr | JsonpInjection.java:72:32:72:38 | request : HttpServletRequest | JsonpInjection.java:80:20:80:28 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:72:32:72:38 | request | this user input |
-| JsonpInjection.java:94:20:94:28 | resultStr | JsonpInjection.java:87:32:87:38 | request : HttpServletRequest | JsonpInjection.java:94:20:94:28 |
-resultStr | Jsonp Injection query might include code from $@. | JsonpInjection.java:87:32:87:38 | request | this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java
new file mode 100644
index 00000000000..14ef76275b1
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet1.java
@@ -0,0 +1,64 @@
+import com.google.gson.Gson;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class JsonpInjectionServlet1 extends HttpServlet {
+
+ private static HashMap hashMap = new HashMap();
+
+ static {
+ hashMap.put("username","admin");
+ hashMap.put("password","123456");
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ private String key = "test";
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ doPost(req, resp);
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("application/json");
+ String jsonpCallback = req.getParameter("jsonpCallback");
+ PrintWriter pw = null;
+ Gson gson = new Gson();
+ String jsonResult = gson.toJson(hashMap);
+
+ String referer = req.getHeader("Referer");
+
+ boolean result = verifReferer(referer);
+
+ // good
+ if (result){
+ String resultStr = null;
+ pw = resp.getWriter();
+ resultStr = jsonpCallback + "(" + jsonResult + ")";
+ pw.println(resultStr);
+ pw.flush();
+ }
+ }
+
+ public static boolean verifReferer(String referer){
+ if (!referer.startsWith("http://test.com/")){
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void init(ServletConfig config) throws ServletException {
+ this.key = config.getInitParameter("key");
+ System.out.println("初始化" + this.key);
+ super.init(config);
+ }
+
+}
diff --git a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java
similarity index 75%
rename from java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java
rename to java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java
index 916cd9bf676..bbfbc2dc436 100644
--- a/java/ql/src/Security/CWE/CWE-352/JsonpInjectionServlet.java
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjectionServlet2.java
@@ -4,12 +4,11 @@ import java.io.PrintWriter;
import java.util.HashMap;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
-import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-public class JsonpInjectionServlet extends HttpServlet {
+public class JsonpInjectionServlet2 extends HttpServlet {
private static HashMap hashMap = new HashMap();
@@ -23,21 +22,12 @@ public class JsonpInjectionServlet extends HttpServlet {
private String key = "test";
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
- String jsonpCallback = req.getParameter("jsonpCallback");
-
- PrintWriter pw = null;
- Gson gson = new Gson();
- String result = gson.toJson(hashMap);
-
- String resultStr = null;
- pw = resp.getWriter();
- resultStr = jsonpCallback + "(" + result + ")";
- pw.println(resultStr);
- pw.flush();
+ doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("application/json");
String jsonpCallback = req.getParameter("jsonpCallback");
PrintWriter pw = null;
Gson gson = new Gson();
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
new file mode 100644
index 00000000000..a89d03b67a7
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_1.expected
@@ -0,0 +1,60 @@
+edges
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
+nodes
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:101:24:101:28 | token | semmle.label | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+#select
+| JsonpController.java:31:16:31:24 | resultStr | JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:26:32:26:68 | getParameter(...) | this user input |
+| JsonpController.java:42:16:42:24 | resultStr | JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:38:32:38:68 | getParameter(...) | this user input |
+| JsonpController.java:52:16:52:24 | resultStr | JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:49:32:49:68 | getParameter(...) | this user input |
+| JsonpController.java:62:16:62:24 | resultStr | JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:59:32:59:68 | getParameter(...) | this user input |
+| JsonpController.java:77:20:77:28 | resultStr | JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:69:32:69:68 | getParameter(...) | this user input |
+| JsonpController.java:91:20:91:28 | resultStr | JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:84:32:84:68 | getParameter(...) | this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
new file mode 100644
index 00000000000..4b12308a212
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_2.expected
@@ -0,0 +1,78 @@
+edges
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+nodes
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:101:24:101:28 | token | semmle.label | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+#select
+| JsonpController.java:31:16:31:24 | resultStr | JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:26:32:26:68 | getParameter(...) | this user input |
+| JsonpController.java:42:16:42:24 | resultStr | JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:38:32:38:68 | getParameter(...) | this user input |
+| JsonpController.java:52:16:52:24 | resultStr | JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:49:32:49:68 | getParameter(...) | this user input |
+| JsonpController.java:62:16:62:24 | resultStr | JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:59:32:59:68 | getParameter(...) | this user input |
+| JsonpController.java:77:20:77:28 | resultStr | JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:69:32:69:68 | getParameter(...) | this user input |
+| JsonpController.java:91:20:91:28 | resultStr | JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 |
+ resultStr | Jsonp Injection query might include code from $@. | JsonpController.java:84:32:84:68 | getParameter(...) | this user input |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServle
+t2.java:39:20:39:28 | resultStr | Jsonp Injection query might include code from $@. | JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) |
+ this user input |
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
new file mode 100644
index 00000000000..8e33ca6984c
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/JsonpInjection_3.expected
@@ -0,0 +1,66 @@
+edges
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:30:21:30:54 | ... + ... : String | JsonpController.java:31:16:31:24 | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:40:21:40:80 | ... + ... : String | JsonpController.java:42:16:42:24 | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:51:21:51:55 | ... + ... : String | JsonpController.java:52:16:52:24 | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:61:21:61:54 | ... + ... : String | JsonpController.java:62:16:62:24 | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:76:21:76:54 | ... + ... : String | JsonpController.java:77:20:77:28 | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:90:21:90:54 | ... + ... : String | JsonpController.java:91:20:91:28 | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | JsonpController.java:101:24:101:28 | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpController.java:104:25:104:59 | ... + ... : String | JsonpController.java:105:20:105:28 | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | JsonpInjectionServlet1.java:38:39:38:45 | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | JsonpInjectionServlet1.java:45:24:45:32 | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | JsonpInjectionServlet2.java:39:20:39:28 | resultStr |
+| RefererFilter.java:22:26:22:53 | getHeader(...) : String | RefererFilter.java:23:39:23:45 | refefer |
+nodes
+| JsonpController.java:26:32:26:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:30:21:30:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:31:16:31:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:38:32:38:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:40:21:40:80 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:42:16:42:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:49:32:49:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:51:21:51:55 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:52:16:52:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:59:32:59:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:61:21:61:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:62:16:62:24 | resultStr | semmle.label | resultStr |
+| JsonpController.java:69:32:69:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:76:21:76:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:77:20:77:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:84:32:84:68 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:90:21:90:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:91:20:91:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:99:24:99:52 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:101:24:101:28 | token | semmle.label | token |
+| JsonpController.java:102:36:102:72 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpController.java:104:25:104:59 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpController.java:105:20:105:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet1.java:36:26:36:49 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| JsonpInjectionServlet1.java:38:39:38:45 | referer | semmle.label | referer |
+| JsonpInjectionServlet1.java:44:25:44:62 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet1.java:45:24:45:32 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:31:32:31:64 | getParameter(...) : String | semmle.label | getParameter(...) : String |
+| JsonpInjectionServlet2.java:38:21:38:54 | ... + ... : String | semmle.label | ... + ... : String |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| JsonpInjectionServlet2.java:39:20:39:28 | resultStr | semmle.label | resultStr |
+| RefererFilter.java:22:26:22:53 | getHeader(...) : String | semmle.label | getHeader(...) : String |
+| RefererFilter.java:23:39:23:45 | refefer | semmle.label | refefer |
+#select
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/Readme b/java/ql/test/experimental/query-tests/security/CWE-352/Readme
new file mode 100644
index 00000000000..15715d6187c
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/Readme
@@ -0,0 +1,3 @@
+1. The JsonpInjection_1.expected result is obtained through the test of `JsonpController.java`.
+2. The JsonpInjection_2.expected result is obtained through the test of `JsonpController.java`, `JsonpInjectionServlet1.java`, `JsonpInjectionServlet2.java`.
+3. The JsonpInjection_3.expected result is obtained through the test of `JsonpController.java`, `JsonpInjectionServlet1.java`, `JsonpInjectionServlet2.java`, `RefererFilter.java`.
\ No newline at end of file
diff --git a/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java b/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
new file mode 100644
index 00000000000..97444932ae1
--- /dev/null
+++ b/java/ql/test/experimental/query-tests/security/CWE-352/RefererFilter.java
@@ -0,0 +1,43 @@
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.util.StringUtils;
+
+public class RefererFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ @Override
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) servletRequest;
+ HttpServletResponse response = (HttpServletResponse) servletResponse;
+ String refefer = request.getHeader("Referer");
+ boolean result = verifReferer(refefer);
+ if (result){
+ filterChain.doFilter(servletRequest, servletResponse);
+ }
+ response.sendError(444, "Referer xxx.");
+ }
+
+ @Override
+ public void destroy() {
+ }
+
+ public static boolean verifReferer(String referer){
+ if (StringUtils.isEmpty(referer)){
+ return false;
+ }
+ if (referer.startsWith("http://www.baidu.com/")){
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
new file mode 100644
index 00000000000..bbe53dc2a5f
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
@@ -0,0 +1,7 @@
+package com.google.gson;
+
+public final class Gson {
+ public String toJson(Object src) {
+ return null;
+ }
+}
diff --git a/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
new file mode 100644
index 00000000000..6ee07f84593
--- /dev/null
+++ b/java/ql/test/stubs/spring-core-5.3.2/org/springframework/util/StringUtils.java
@@ -0,0 +1,8 @@
+package org.springframework.util;
+
+public abstract class StringUtils {
+
+ public static boolean isEmpty(Object str) {
+ return str == null || "".equals(str);
+ }
+}
\ No newline at end of file
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/Filter.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/Filter.java
new file mode 100644
index 00000000000..5833e3c909d
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/Filter.java
@@ -0,0 +1,13 @@
+package javax.servlet;
+
+import java.io.IOException;
+
+public interface Filter {
+ default void init(FilterConfig filterConfig) throws ServletException {
+ }
+
+ void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException;
+
+ default void destroy() {
+ }
+}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterChain.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterChain.java
new file mode 100644
index 00000000000..6a1dfc588b6
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterChain.java
@@ -0,0 +1,8 @@
+package javax.servlet;
+
+import java.io.IOException;
+
+public interface FilterChain {
+ void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
+}
+
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterConfig.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterConfig.java
new file mode 100644
index 00000000000..66c13eb54f0
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/FilterConfig.java
@@ -0,0 +1,3 @@
+package javax.servlet;
+
+public interface FilterConfig {}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletException.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletException.java
new file mode 100644
index 00000000000..ce5f7c4465a
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletException.java
@@ -0,0 +1,8 @@
+package javax.servlet;
+
+public class ServletException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ServletException() {
+ }
+}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletRequest.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletRequest.java
new file mode 100644
index 00000000000..4ee0026d066
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletRequest.java
@@ -0,0 +1,87 @@
+package javax.servlet;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Map;
+
+public interface ServletRequest {
+ Object getAttribute(String var1);
+
+ Enumeration getAttributeNames();
+
+ String getCharacterEncoding();
+
+ void setCharacterEncoding(String var1) throws UnsupportedEncodingException;
+
+ int getContentLength();
+
+ long getContentLengthLong();
+
+ String getContentType();
+
+ ServletInputStream getInputStream() throws IOException;
+
+ String getParameter(String var1);
+
+ Enumeration getParameterNames();
+
+ String[] getParameterValues(String var1);
+
+ Map getParameterMap();
+
+ String getProtocol();
+
+ String getScheme();
+
+ String getServerName();
+
+ int getServerPort();
+
+ BufferedReader getReader() throws IOException;
+
+ String getRemoteAddr();
+
+ String getRemoteHost();
+
+ void setAttribute(String var1, Object var2);
+
+ void removeAttribute(String var1);
+
+ Locale getLocale();
+
+ Enumeration getLocales();
+
+ boolean isSecure();
+
+ RequestDispatcher getRequestDispatcher(String var1);
+
+ /** @deprecated */
+ @Deprecated
+ String getRealPath(String var1);
+
+ int getRemotePort();
+
+ String getLocalName();
+
+ String getLocalAddr();
+
+ int getLocalPort();
+
+ ServletContext getServletContext();
+
+ AsyncContext startAsync() throws IllegalStateException;
+
+ AsyncContext startAsync(ServletRequest var1, ServletResponse var2) throws IllegalStateException;
+
+ boolean isAsyncStarted();
+
+ boolean isAsyncSupported();
+
+ AsyncContext getAsyncContext();
+
+ DispatcherType getDispatcherType();
+}
+
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletResponse.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletResponse.java
new file mode 100644
index 00000000000..0aa6121e686
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/ServletResponse.java
@@ -0,0 +1,39 @@
+package javax.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+
+public interface ServletResponse {
+ String getCharacterEncoding();
+
+ String getContentType();
+
+ ServletOutputStream getOutputStream() throws IOException;
+
+ PrintWriter getWriter() throws IOException;
+
+ void setCharacterEncoding(String var1);
+
+ void setContentLength(int var1);
+
+ void setContentLengthLong(long var1);
+
+ void setContentType(String var1);
+
+ void setBufferSize(int var1);
+
+ int getBufferSize();
+
+ void flushBuffer() throws IOException;
+
+ void resetBuffer();
+
+ boolean isCommitted();
+
+ void reset();
+
+ void setLocale(Locale var1);
+
+ Locale getLocale();
+}
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletRequest.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletRequest.java
new file mode 100644
index 00000000000..02d53a96a33
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletRequest.java
@@ -0,0 +1,116 @@
+package javax.servlet.http;
+
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Map;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+
+public interface HttpServletRequest extends ServletRequest {
+ String BASIC_AUTH = "BASIC";
+ String FORM_AUTH = "FORM";
+ String CLIENT_CERT_AUTH = "CLIENT_CERT";
+ String DIGEST_AUTH = "DIGEST";
+
+ String getAuthType();
+
+ Cookie[] getCookies();
+
+ long getDateHeader(String var1);
+
+ String getHeader(String var1);
+
+ Enumeration getHeaders(String var1);
+
+ Enumeration getHeaderNames();
+
+ int getIntHeader(String var1);
+
+ default HttpServletMapping getHttpServletMapping() {
+ return new HttpServletMapping() {
+ public String getMatchValue() {
+ return "";
+ }
+
+ public String getPattern() {
+ return "";
+ }
+
+ public String getServletName() {
+ return "";
+ }
+
+ public MappingMatch getMappingMatch() {
+ return null;
+ }
+ };
+ }
+
+ String getMethod();
+
+ String getPathInfo();
+
+ String getPathTranslated();
+
+ default PushBuilder newPushBuilder() {
+ return null;
+ }
+
+ String getContextPath();
+
+ String getQueryString();
+
+ String getRemoteUser();
+
+ boolean isUserInRole(String var1);
+
+ Principal getUserPrincipal();
+
+ String getRequestedSessionId();
+
+ String getRequestURI();
+
+ StringBuffer getRequestURL();
+
+ String getServletPath();
+
+ HttpSession getSession(boolean var1);
+
+ HttpSession getSession();
+
+ String changeSessionId();
+
+ boolean isRequestedSessionIdValid();
+
+ boolean isRequestedSessionIdFromCookie();
+
+ boolean isRequestedSessionIdFromURL();
+
+ /** @deprecated */
+ @Deprecated
+ boolean isRequestedSessionIdFromUrl();
+
+ boolean authenticate(HttpServletResponse var1) throws IOException, ServletException;
+
+ void login(String var1, String var2) throws ServletException;
+
+ void logout() throws ServletException;
+
+ Collection getParts() throws IOException, ServletException;
+
+ Part getPart(String var1) throws IOException, ServletException;
+
+ T upgrade(Class var1) throws IOException, ServletException;
+
+ default Map getTrailerFields() {
+ return Collections.emptyMap();
+ }
+
+ default boolean isTrailerFieldsReady() {
+ return false;
+ }
+}
+
diff --git a/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletResponse.java b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletResponse.java
new file mode 100644
index 00000000000..0a2c6af0913
--- /dev/null
+++ b/java/ql/test/stubs/tomcat-embed-core-9.0.41/javax/servlet/http/HttpServletResponse.java
@@ -0,0 +1,106 @@
+package javax.servlet.http;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.function.Supplier;
+import javax.servlet.ServletResponse;
+
+public interface HttpServletResponse extends ServletResponse {
+ int SC_CONTINUE = 100;
+ int SC_SWITCHING_PROTOCOLS = 101;
+ int SC_OK = 200;
+ int SC_CREATED = 201;
+ int SC_ACCEPTED = 202;
+ int SC_NON_AUTHORITATIVE_INFORMATION = 203;
+ int SC_NO_CONTENT = 204;
+ int SC_RESET_CONTENT = 205;
+ int SC_PARTIAL_CONTENT = 206;
+ int SC_MULTIPLE_CHOICES = 300;
+ int SC_MOVED_PERMANENTLY = 301;
+ int SC_MOVED_TEMPORARILY = 302;
+ int SC_FOUND = 302;
+ int SC_SEE_OTHER = 303;
+ int SC_NOT_MODIFIED = 304;
+ int SC_USE_PROXY = 305;
+ int SC_TEMPORARY_REDIRECT = 307;
+ int SC_BAD_REQUEST = 400;
+ int SC_UNAUTHORIZED = 401;
+ int SC_PAYMENT_REQUIRED = 402;
+ int SC_FORBIDDEN = 403;
+ int SC_NOT_FOUND = 404;
+ int SC_METHOD_NOT_ALLOWED = 405;
+ int SC_NOT_ACCEPTABLE = 406;
+ int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+ int SC_REQUEST_TIMEOUT = 408;
+ int SC_CONFLICT = 409;
+ int SC_GONE = 410;
+ int SC_LENGTH_REQUIRED = 411;
+ int SC_PRECONDITION_FAILED = 412;
+ int SC_REQUEST_ENTITY_TOO_LARGE = 413;
+ int SC_REQUEST_URI_TOO_LONG = 414;
+ int SC_UNSUPPORTED_MEDIA_TYPE = 415;
+ int SC_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+ int SC_EXPECTATION_FAILED = 417;
+ int SC_INTERNAL_SERVER_ERROR = 500;
+ int SC_NOT_IMPLEMENTED = 501;
+ int SC_BAD_GATEWAY = 502;
+ int SC_SERVICE_UNAVAILABLE = 503;
+ int SC_GATEWAY_TIMEOUT = 504;
+ int SC_HTTP_VERSION_NOT_SUPPORTED = 505;
+
+ void addCookie(Cookie var1);
+
+ boolean containsHeader(String var1);
+
+ String encodeURL(String var1);
+
+ String encodeRedirectURL(String var1);
+
+ /** @deprecated */
+ @Deprecated
+ String encodeUrl(String var1);
+
+ /** @deprecated */
+ @Deprecated
+ String encodeRedirectUrl(String var1);
+
+ void sendError(int var1, String var2) throws IOException;
+
+ void sendError(int var1) throws IOException;
+
+ void sendRedirect(String var1) throws IOException;
+
+ void setDateHeader(String var1, long var2);
+
+ void addDateHeader(String var1, long var2);
+
+ void setHeader(String var1, String var2);
+
+ void addHeader(String var1, String var2);
+
+ void setIntHeader(String var1, int var2);
+
+ void addIntHeader(String var1, int var2);
+
+ void setStatus(int var1);
+
+ /** @deprecated */
+ @Deprecated
+ void setStatus(int var1, String var2);
+
+ int getStatus();
+
+ String getHeader(String var1);
+
+ Collection getHeaders(String var1);
+
+ Collection getHeaderNames();
+
+ default void setTrailerFields(Supplier