Skip to content

Commit 0ccf4ce

Browse files
committed
Fix XSS FPs when content type is safe
1 parent 9f3572d commit 0ccf4ce

File tree

3 files changed

+108
-4
lines changed

3 files changed

+108
-4
lines changed

java/ql/lib/semmle/code/java/frameworks/Servlets.qll

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,16 @@ class ResponseSetHeaderMethod extends Method {
315315
}
316316
}
317317

318+
/**
319+
* The method `setContentType` declared in `javax.servlet.http.HttpServletResponse`.
320+
*/
321+
class ResponseSetContentTypeMethod extends Method {
322+
ResponseSetContentTypeMethod() {
323+
this.getDeclaringType() instanceof ServletResponse and
324+
this.hasName("setContentType")
325+
}
326+
}
327+
318328
/**
319329
* A class that has `javax.servlet.Servlet` as an ancestor.
320330
*/

java/ql/lib/semmle/code/java/security/XSS.qll

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,25 @@ private class WritingMethod extends Method {
9292
/** An output stream or writer that writes to a servlet, JSP or JSF response. */
9393
class XssVulnerableWriterSource extends MethodCall {
9494
XssVulnerableWriterSource() {
95-
this.getMethod() instanceof ServletResponseGetWriterMethod
96-
or
97-
this.getMethod() instanceof ServletResponseGetOutputStreamMethod
95+
(
96+
this.getMethod() instanceof ServletResponseGetWriterMethod
97+
or
98+
this.getMethod() instanceof ServletResponseGetOutputStreamMethod
99+
) and
100+
not exists(MethodCall mc, Expr contentType |
101+
mc.getMethod() instanceof ResponseSetContentTypeMethod and
102+
contentType = mc.getArgument(0)
103+
or
104+
(
105+
mc.getMethod() instanceof ResponseAddHeaderMethod or
106+
mc.getMethod() instanceof ResponseSetHeaderMethod
107+
) and
108+
mc.getArgument(0).(CompileTimeConstantExpr).getStringValue().toLowerCase() = "content-type" and
109+
contentType = mc.getArgument(1)
110+
|
111+
isXssSafeContentTypeString(contentType.(CompileTimeConstantExpr).getStringValue()) and
112+
DataFlow::localExprFlow(mc.getQualifier(), this.getQualifier())
113+
)
98114
or
99115
exists(Method m | m = this.getMethod() |
100116
m.hasQualifiedName("javax.servlet.jsp", "JspContext", "getOut")
@@ -106,6 +122,11 @@ class XssVulnerableWriterSource extends MethodCall {
106122
}
107123
}
108124

125+
pragma[nomagic]
126+
private predicate isXssSafeContentTypeString(string s) {
127+
s = any(CompileTimeConstantExpr cte).getStringValue() and isXssSafeContentType(s)
128+
}
129+
109130
/**
110131
* A xss vulnerable writer source node.
111132
*/

java/ql/test/query-tests/security/CWE-079/semmle/tests/XSS.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import javax.servlet.http.HttpServletResponse;
1313

1414
public class XSS extends HttpServlet {
15-
protected void doGet(HttpServletRequest request, HttpServletResponse response)
15+
protected void doGet(HttpServletRequest request, HttpServletResponse response, boolean safeContentType, boolean getWriter, int setContentMethod)
1616
throws ServletException, IOException {
1717
// BAD: a request parameter is written directly to the Servlet response stream
1818
response.getWriter()
@@ -38,6 +38,79 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response)
3838

3939
// GOOD: sanitizer
4040
response.getOutputStream().write(hudson.Util.escape(request.getPathInfo()).getBytes()); // safe
41+
42+
if(safeContentType) {
43+
if(getWriter) {
44+
if(setContentMethod == 0) {
45+
// GOOD: set content-type to something safe
46+
response.setContentType("text/plain");
47+
response.getWriter().print(request.getPathInfo());
48+
}
49+
else if(setContentMethod == 1) {
50+
// GOOD: set content-type to something safe
51+
response.setHeader("Content-Type", "text/plain");
52+
response.getWriter().print(request.getPathInfo());
53+
}
54+
else {
55+
// GOOD: set content-type to something safe
56+
response.addHeader("Content-Type", "text/plain");
57+
response.getWriter().print(request.getPathInfo());
58+
}
59+
}
60+
else {
61+
if(setContentMethod == 0) {
62+
// GOOD: set content-type to something safe
63+
response.setContentType("text/plain");
64+
response.getOutputStream().write(request.getPathInfo().getBytes());
65+
}
66+
else if(setContentMethod == 1) {
67+
// GOOD: set content-type to something safe
68+
response.setHeader("Content-Type", "text/plain");
69+
response.getOutputStream().write(request.getPathInfo().getBytes());
70+
}
71+
else {
72+
// GOOD: set content-type to something safe
73+
response.addHeader("Content-Type", "text/plain");
74+
response.getOutputStream().write(request.getPathInfo().getBytes());
75+
}
76+
}
77+
}
78+
else {
79+
if(getWriter) {
80+
if(setContentMethod == 0) {
81+
// BAD: set content-type to something that is not safe
82+
response.setContentType("text/html");
83+
response.getWriter().print(request.getPathInfo()); // $ xss
84+
}
85+
else if(setContentMethod == 1) {
86+
// BAD: set content-type to something that is not safe
87+
response.setHeader("Content-Type", "text/html");
88+
response.getWriter().print(request.getPathInfo()); // $ xss
89+
}
90+
else {
91+
// BAD: set content-type to something that is not safe
92+
response.addHeader("Content-Type", "text/html");
93+
response.getWriter().print(request.getPathInfo()); // $ xss
94+
}
95+
}
96+
else {
97+
if(setContentMethod == 0) {
98+
// BAD: set content-type to something that is not safe
99+
response.setContentType("text/html");
100+
response.getOutputStream().write(request.getPathInfo().getBytes()); // $ xss
101+
}
102+
else if(setContentMethod == 1) {
103+
// BAD: set content-type to something that is not safe
104+
response.setHeader("Content-Type", "text/html");
105+
response.getOutputStream().write(request.getPathInfo().getBytes()); // $ xss
106+
}
107+
else {
108+
// BAD: set content-type to something that is not safe
109+
response.addHeader("Content-Type", "text/html");
110+
response.getOutputStream().write(request.getPathInfo().getBytes()); // $ xss
111+
}
112+
}
113+
}
41114
}
42115

43116
/**

0 commit comments

Comments
 (0)