Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 756db4c

Browse files
committed
Simplify the query and add more test cases
1 parent 67af9b0 commit 756db4c

4 files changed

Lines changed: 92 additions & 31 deletions

File tree

java/ql/src/experimental/Security/CWE/CWE-600/UncaughtServletException.qhelp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@
44
<qhelp>
55
<overview>
66
<p>
7-
Even though the signatures for methods in a servlet include <code>throws IOException, ServletException</code>, it's a bad idea to let such exceptions be thrown. Failure to catch exceptions in a servlet could leave a system in a vulnerable state, possibly resulting in denial-of-service attacks, or the exposure of sensitive information because when a servlet throws an exception, the servlet container typically sends debugging information back to the user. And that information could be very valuable to an attacker.
7+
Even though the request-handling methods of <code>Servlet</code> are declared <code>throws IOException, ServletException</code>, it's a bad idea to let such exceptions be thrown. Failure to catch exceptions in a servlet could leave a system in an unexpected state, possibly resulting in denial-of-service attacks, or could lead to exposure of sensitive information because when a servlet throws an exception, the servlet container typically sends debugging information back to the user. That information could be valuable to an attacker.
88
</p>
99
</overview>
1010

1111
<recommendation>
1212
<p>
13-
Handle method calls that throw IOExceptions and/or RuntimeExceptions and display custom error messages without stack traces and sensitive information, or configure an <code>error-page</code> in web.xml to display a generic user-friendly message for any uncaught exception.
13+
Catch IOExceptions and/or RuntimeExceptions and display custom error messages without stack traces and sensitive information, or configure an <code>error-page</code> in web.xml to display a generic user-friendly message for any uncaught exception.
1414
</p>
1515
</recommendation>
1616

1717
<example>
1818
<p>
19-
In the first and second examples, subclasses of IOException and RuntimeException are not caught, which disclose stack traces.
19+
In the first and second examples, subclasses of IOException and RuntimeException are not caught, which disclose stack traces. Because user-controlled data is passed to methods that throw, there is an opportunity for an attacker to provoke a stack dump.
2020
</p>
2121

2222
<p>

java/ql/src/experimental/Security/CWE/CWE-600/UncaughtServletException.ql

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
* @name Uncaught Servlet Exception
3-
* @description Uncaught exceptions in a servlet could leave a system in a vulnerable state, possibly resulting in denial-of-service attacks or the exposure of sensitive information disclosed in stack traces.
3+
* @description Uncaught exceptions in a servlet could leave a system in an unexpected state, possibly resulting in denial-of-service attacks or the exposure of sensitive information disclosed in stack traces.
44
* @kind path-problem
55
* @id java/uncaught-servlet-exception
66
* @tags security
@@ -19,12 +19,12 @@ class IOException extends RefType {
1919
IOException() { this.hasQualifiedName("java.io", "IOException") }
2020
}
2121

22-
/** Check whether a given exception type is caught. */
23-
private predicate catchesEx(TryStmt t, RefType exType) {
22+
/** Holds if a given exception type is caught. */
23+
private predicate exceptionIsCaught(TryStmt t, RefType exType) {
2424
exists(CatchClause cc, LocalVariableDeclExpr v |
2525
t.getACatchClause() = cc and
2626
cc.getVariable() = v and
27-
v.getType().(RefType).getASubtype*() = exType //Detect the case that a subclass exception is thrown but its parent class is declared in the catch clause.
27+
v.getType().(RefType).getASubtype*() = exType // Detect the case that a subclass exception is thrown but its parent class is declared in the catch clause.
2828
)
2929
}
3030

@@ -33,44 +33,30 @@ private predicate isServletMethod(Callable c) {
3333
c.getDeclaringType() instanceof ServletClass and
3434
c.getNumberOfParameters() = 2 and
3535
c.getParameter(1).getType() instanceof ServletResponse and
36-
(
37-
c.getName() = "doGet" or
38-
c.getName() = "doPost" or
39-
c.getName() = "doPut" or
40-
c.getName() = "doDelete" or
41-
c.getName() = "doHead" or
42-
c.getName() = "doOptions" or
43-
c.getName() = "doTrace" or
44-
c.getName() = "service"
45-
)
36+
c.getName() in ["doGet", "doPost", "doPut", "doDelete", "doHead", "doOptions", "doTrace",
37+
"service"]
4638
}
4739

4840
/** Holds if `web.xml` has an error page configured. */
4941
private predicate hasErrorPage() {
5042
exists(WebErrorPage wep | wep.getPageLocation().getValue() != "")
5143
}
5244

53-
/** Sink of uncaught IO exceptions or runtime exceptions since other exception types must be explicitly caught. */
45+
/** Sink of uncaught exceptions, which shall be IO exceptions or runtime exceptions since other exception types must be explicitly caught. */
5446
class UncaughtServletExceptionSink extends DataFlow::ExprNode {
5547
UncaughtServletExceptionSink() {
5648
exists(Method m, MethodAccess ma | ma.getMethod() = m |
5749
isServletMethod(ma.getEnclosingCallable()) and
58-
(
59-
m.getAThrownExceptionType().getASupertype*() instanceof IOException or
60-
m
61-
.getAThrownExceptionType()
62-
.getASupertype*()
63-
.hasQualifiedName("java.lang", "RuntimeException")
64-
) and
6550
ma.getAnArgument() = this.getExpr() and
6651
not exists(TryStmt t |
6752
t.getBlock() = ma.getEnclosingStmt().getEnclosingStmt*() and
68-
catchesEx(t, m.getAThrownExceptionType())
53+
exceptionIsCaught(t, m.getAThrownExceptionType())
6954
)
7055
)
7156
}
7257
}
7358

59+
/** Taint configuration of uncaught exceptions caused by user provided data from `RemoteFlowSource` */
7460
class UncaughtServletExceptionConfiguration extends TaintTracking::Configuration {
7561
UncaughtServletExceptionConfiguration() { this = "UncaughtServletException" }
7662

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
edges
22
| UncaughtServletException.java:13:15:13:43 | getParameter(...) : String | UncaughtServletException.java:14:44:14:45 | ip |
33
| UncaughtServletException.java:16:19:16:41 | getRemoteUser(...) : String | UncaughtServletException.java:17:20:17:25 | userId |
4+
| UncaughtServletException.java:75:21:75:43 | getRemoteUser(...) : String | UncaughtServletException.java:76:22:76:27 | userId |
45
nodes
56
| UncaughtServletException.java:13:15:13:43 | getParameter(...) : String | semmle.label | getParameter(...) : String |
67
| UncaughtServletException.java:14:44:14:45 | ip | semmle.label | ip |
78
| UncaughtServletException.java:16:19:16:41 | getRemoteUser(...) : String | semmle.label | getRemoteUser(...) : String |
89
| UncaughtServletException.java:17:20:17:25 | userId | semmle.label | userId |
10+
| UncaughtServletException.java:75:21:75:43 | getRemoteUser(...) : String | semmle.label | getRemoteUser(...) : String |
11+
| UncaughtServletException.java:76:22:76:27 | userId | semmle.label | userId |
912
#select
1013
| UncaughtServletException.java:14:44:14:45 | ip | UncaughtServletException.java:13:15:13:43 | getParameter(...) : String | UncaughtServletException.java:14:44:14:45 | ip | $@ flows to here and can throw uncaught exception. | UncaughtServletException.java:13:15:13:43 | getParameter(...) | User-provided value |
1114
| UncaughtServletException.java:17:20:17:25 | userId | UncaughtServletException.java:16:19:16:41 | getRemoteUser(...) : String | UncaughtServletException.java:17:20:17:25 | userId | $@ flows to here and can throw uncaught exception. | UncaughtServletException.java:16:19:16:41 | getRemoteUser(...) | User-provided value |
15+
| UncaughtServletException.java:76:22:76:27 | userId | UncaughtServletException.java:75:21:75:43 | getRemoteUser(...) : String | UncaughtServletException.java:76:22:76:27 | userId | $@ flows to here and can throw uncaught exception. | UncaughtServletException.java:75:21:75:43 | getRemoteUser(...) | User-provided value |

java/ql/test/experimental/query-tests/security/CWE-600/UncaughtServletException.java

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,99 @@
88
import javax.servlet.ServletException;
99

1010
class UncaughtServletException extends HttpServlet {
11-
// BAD - Tests `doGet` without catching exceptions
11+
// BAD - Tests `doGet` without catching exceptions.
1212
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
1313
String ip = request.getParameter("srcIP");
14-
InetAddress addr = InetAddress.getByName(ip); // BAD: getByName(String) throws UnknownHostException
14+
InetAddress addr = InetAddress.getByName(ip); // getByName(String) throws UnknownHostException
1515

1616
String userId = request.getRemoteUser();
17-
Integer.parseInt(userId); //BAD: Integer.parse(String) throws RuntimeException
17+
Integer.parseInt(userId); // Integer.parse(String) throws RuntimeException
1818
}
1919

20-
// GOOD - Tests `doPost` with catching exceptions
20+
// GOOD - Tests `doPost` with catching exceptions.
2121
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
2222
try {
2323
String ip = request.getParameter("srcIP");
2424
InetAddress addr = InetAddress.getByName(ip);
25+
26+
String userId = request.getRemoteUser();
27+
Integer.parseInt(userId); // Integer.parse(String) throws RuntimeException
2528
} catch (UnknownHostException uhex) {
2629
uhex.printStackTrace();
30+
} catch (RuntimeException re) {
31+
re.printStackTrace();
2732
}
2833
}
2934

35+
// GOOD - Tests `doPut` without user provided data and without catching exceptions.
3036
public void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
3137
String ip = "10.100.10.81";
3238
InetAddress addr = InetAddress.getByName(ip); // GOOD: hard-coded variable value or system property not controlled by attacker
3339
}
3440

35-
}
41+
// GOOD - Tests rethrowing caught exceptions without stack trace, which the typical programming practice.
42+
public void doDelete(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
43+
try {
44+
String ip = request.getParameter("srcIP");
45+
InetAddress addr = InetAddress.getByName(ip);
46+
} catch (UnknownHostException uhex) {
47+
throw new IOException("Host not found "+uhex.getMessage());
48+
}
49+
}
50+
51+
// BAD - Tests rethrowing caught exceptions with stack trace.
52+
// Note this case is not yet detected by this query.
53+
public void doOptions(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
54+
try {
55+
String ip = request.getParameter("srcIP");
56+
InetAddress addr = InetAddress.getByName(ip);
57+
} catch (UnknownHostException uhex) {
58+
throw new IOException(uhex);
59+
}
60+
}
61+
62+
// GOOD - Tests invoking another top-level method.
63+
public void doHead(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
64+
doGet(request, response);
65+
}
66+
67+
// BAD - Tests nested try-blocks without catching runtime exceptions.
68+
public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
69+
try {
70+
String ip = request.getParameter("srcIP");
71+
InetAddress addr = null;
72+
try {
73+
addr = InetAddress.getByName(ip);
74+
75+
String userId = request.getRemoteUser();
76+
Integer.parseInt(userId); // Integer.parse(String) throws RuntimeException
77+
} catch (UnknownHostException uhex) {
78+
throw new UnknownHostException("Got exception "+uhex.getMessage());
79+
}
80+
} catch (IOException ie) {
81+
ie.printStackTrace();
82+
}
83+
}
84+
85+
// GOOD - Tests nested try-blocks with catching all exceptions.
86+
public void doTrace(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
87+
try {
88+
try {
89+
String ip = request.getParameter("srcIP");
90+
InetAddress addr = null;
91+
try {
92+
addr = InetAddress.getByName(ip);
93+
94+
String userId = request.getRemoteUser();
95+
Integer.parseInt(userId); // Integer.parse(String) throws RuntimeException
96+
} catch (UnknownHostException uhex) {
97+
throw new UnknownHostException("Got exception "+uhex.getMessage());
98+
}
99+
} catch (IOException ie) {
100+
ie.printStackTrace();
101+
}
102+
} catch (RuntimeException re) {
103+
re.printStackTrace();
104+
}
105+
}
106+
}

0 commit comments

Comments
 (0)