mirror of
https://github.com/github/codeql.git
synced 2025-12-21 19:26:31 +01:00
Add StackTraceExposureQuery
This commit is contained in:
@@ -7,4 +7,5 @@ category: minorAnalysis
|
|||||||
* Added the `XssLocalQuery.qll` library to provide the `XssLocalFlow` taint-tracking module to reason about XSS vulnerabilities caused by local data flow.
|
* Added the `XssLocalQuery.qll` library to provide the `XssLocalFlow` taint-tracking module to reason about XSS vulnerabilities caused by local data flow.
|
||||||
* Added the `ExternallyControlledFormatStringLocalQuery.qll` library to provide the `ExternallyControlledFormatStringLocalFlow` taint-tracking module to reason about format string vulnerabilities caused by local data flow.
|
* Added the `ExternallyControlledFormatStringLocalQuery.qll` library to provide the `ExternallyControlledFormatStringLocalFlow` taint-tracking module to reason about format string vulnerabilities caused by local data flow.
|
||||||
* Added the `InsecureCookieQuery.qll` library to provide the `SecureCookieFlow` taint-tracking module to reason about insecure cookie vulnerabilities.
|
* Added the `InsecureCookieQuery.qll` library to provide the `SecureCookieFlow` taint-tracking module to reason about insecure cookie vulnerabilities.
|
||||||
* Added the `ExecTaintedLocalQuery.qll` library to provide the `LocalUserInputToArgumentToExecFlow` taint-tracking module to reason about command injection vulnerabilities caused by local data flow.
|
* Added the `ExecTaintedLocalQuery.qll` library to provide the `LocalUserInputToArgumentToExecFlow` taint-tracking module to reason about command injection vulnerabilities caused by local data flow.
|
||||||
|
* Added the `StackTraceExposureQuery.qll` library to provide the `printsStackExternally`, `stringifiedStackFlowsExternally`, and `getMessageFlowsExternally` predicates to reason about stack trace exposure vulnerabilities.
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
/** Provides predicates to reason about exposure of stack-traces. */
|
||||||
|
|
||||||
|
import java
|
||||||
|
import semmle.code.java.dataflow.DataFlow
|
||||||
|
import semmle.code.java.dataflow.TaintTracking
|
||||||
|
private import semmle.code.java.security.InformationLeak
|
||||||
|
|
||||||
|
/**
|
||||||
|
* One of the `printStackTrace()` overloads on `Throwable`.
|
||||||
|
*/
|
||||||
|
private class PrintStackTraceMethod extends Method {
|
||||||
|
PrintStackTraceMethod() {
|
||||||
|
this.getDeclaringType()
|
||||||
|
.getSourceDeclaration()
|
||||||
|
.getASourceSupertype*()
|
||||||
|
.hasQualifiedName("java.lang", "Throwable") and
|
||||||
|
this.getName() = "printStackTrace"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) {
|
||||||
|
exists(MethodAccess ma |
|
||||||
|
sink.asExpr() = ma.getAnArgument() and ma.getMethod() instanceof PrintStackTraceMethod
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private module ServletWriterSourceToPrintStackTraceMethodFlow =
|
||||||
|
TaintTracking::Global<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
|
||||||
|
* to external output.
|
||||||
|
*/
|
||||||
|
private predicate printsStackToWriter(MethodAccess call) {
|
||||||
|
exists(PrintStackTraceMethod printStackTrace |
|
||||||
|
call.getMethod() = printStackTrace and
|
||||||
|
ServletWriterSourceToPrintStackTraceMethodFlow::flowToExpr(call.getAnArgument())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A `PrintWriter` that wraps a given string writer. This pattern is used
|
||||||
|
* in the most common idiom for converting a `Throwable` to a string.
|
||||||
|
*/
|
||||||
|
private predicate printWriterOnStringWriter(Expr printWriter, Variable stringWriterVar) {
|
||||||
|
printWriter.getType().(Class).hasQualifiedName("java.io", "PrintWriter") and
|
||||||
|
stringWriterVar.getType().(Class).hasQualifiedName("java.io", "StringWriter") and
|
||||||
|
(
|
||||||
|
printWriter.(ClassInstanceExpr).getAnArgument() = stringWriterVar.getAnAccess() or
|
||||||
|
printWriterOnStringWriter(printWriter.(VarAccess).getVariable().getInitializer(),
|
||||||
|
stringWriterVar)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
|
||||||
|
exists(Expr printWriter, Variable stringWriterVar, MethodAccess printStackCall |
|
||||||
|
printWriterOnStringWriter(printWriter, stringWriterVar) and
|
||||||
|
printStackCall.getMethod() instanceof PrintStackTraceMethod and
|
||||||
|
printStackCall.getAnArgument() = printWriter and
|
||||||
|
printStackCall.getQualifier() = exception and
|
||||||
|
stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and
|
||||||
|
stackTraceString.getMethod() instanceof ToStringMethod
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||||
|
}
|
||||||
|
|
||||||
|
private module StackTraceStringToHttpResponseSinkFlow =
|
||||||
|
TaintTracking::Global<StackTraceStringToHttpResponseSinkFlowConfig>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `call` writes the data of `stackTrace` to an external stream.
|
||||||
|
*/
|
||||||
|
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
|
||||||
|
printsStackToWriter(call) and
|
||||||
|
call.getQualifier() = stackTrace and
|
||||||
|
not call.getQualifier() instanceof SuperAccess
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if `stackTrace` is a stringified stack trace which flows to an external sink.
|
||||||
|
*/
|
||||||
|
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
|
||||||
|
exists(MethodAccess stackTraceString |
|
||||||
|
stackTraceExpr(stackTrace, stackTraceString) and
|
||||||
|
StackTraceStringToHttpResponseSinkFlow::flow(DataFlow::exprNode(stackTraceString), externalExpr)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class GetMessageFlowSource extends DataFlow::Node {
|
||||||
|
GetMessageFlowSource() {
|
||||||
|
exists(Method method | this.asExpr().(MethodAccess).getMethod() = method |
|
||||||
|
method.hasName("getMessage") and
|
||||||
|
method.hasNoParameters() and
|
||||||
|
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
||||||
|
predicate isSource(DataFlow::Node src) { src instanceof GetMessageFlowSource }
|
||||||
|
|
||||||
|
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
||||||
|
}
|
||||||
|
|
||||||
|
private module GetMessageFlowSourceToHttpResponseSinkFlow =
|
||||||
|
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds if there is a call to `getMessage()` that then flows to a servlet response.
|
||||||
|
*/
|
||||||
|
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
|
||||||
|
GetMessageFlowSourceToHttpResponseSinkFlow::flow(getMessage, externalExpr)
|
||||||
|
}
|
||||||
@@ -14,130 +14,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java
|
import java
|
||||||
import semmle.code.java.dataflow.DataFlow
|
import semmle.code.java.security.StackTraceExposureQuery
|
||||||
import semmle.code.java.dataflow.TaintTracking
|
|
||||||
import semmle.code.java.security.InformationLeak
|
|
||||||
|
|
||||||
/**
|
|
||||||
* One of the `printStackTrace()` overloads on `Throwable`.
|
|
||||||
*/
|
|
||||||
class PrintStackTraceMethod extends Method {
|
|
||||||
PrintStackTraceMethod() {
|
|
||||||
this.getDeclaringType()
|
|
||||||
.getSourceDeclaration()
|
|
||||||
.getASourceSupertype*()
|
|
||||||
.hasQualifiedName("java.lang", "Throwable") and
|
|
||||||
this.getName() = "printStackTrace"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module ServletWriterSourceToPrintStackTraceMethodFlowConfig implements DataFlow::ConfigSig {
|
|
||||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof XssVulnerableWriterSource }
|
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink) {
|
|
||||||
exists(MethodAccess ma |
|
|
||||||
sink.asExpr() = ma.getAnArgument() and ma.getMethod() instanceof PrintStackTraceMethod
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module ServletWriterSourceToPrintStackTraceMethodFlow =
|
|
||||||
TaintTracking::Global<ServletWriterSourceToPrintStackTraceMethodFlowConfig>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call that uses `Throwable.printStackTrace()` on a stream that is connected
|
|
||||||
* to external output.
|
|
||||||
*/
|
|
||||||
predicate printsStackToWriter(MethodAccess call) {
|
|
||||||
exists(PrintStackTraceMethod printStackTrace |
|
|
||||||
call.getMethod() = printStackTrace and
|
|
||||||
ServletWriterSourceToPrintStackTraceMethodFlow::flowToExpr(call.getAnArgument())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A `PrintWriter` that wraps a given string writer. This pattern is used
|
|
||||||
* in the most common idiom for converting a `Throwable` to a string.
|
|
||||||
*/
|
|
||||||
predicate printWriterOnStringWriter(Expr printWriter, Variable stringWriterVar) {
|
|
||||||
printWriter.getType().(Class).hasQualifiedName("java.io", "PrintWriter") and
|
|
||||||
stringWriterVar.getType().(Class).hasQualifiedName("java.io", "StringWriter") and
|
|
||||||
(
|
|
||||||
printWriter.(ClassInstanceExpr).getAnArgument() = stringWriterVar.getAnAccess() or
|
|
||||||
printWriterOnStringWriter(printWriter.(VarAccess).getVariable().getInitializer(),
|
|
||||||
stringWriterVar)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
predicate stackTraceExpr(Expr exception, MethodAccess stackTraceString) {
|
|
||||||
exists(Expr printWriter, Variable stringWriterVar, MethodAccess printStackCall |
|
|
||||||
printWriterOnStringWriter(printWriter, stringWriterVar) and
|
|
||||||
printStackCall.getMethod() instanceof PrintStackTraceMethod and
|
|
||||||
printStackCall.getAnArgument() = printWriter and
|
|
||||||
printStackCall.getQualifier() = exception and
|
|
||||||
stackTraceString.getQualifier() = stringWriterVar.getAnAccess() and
|
|
||||||
stackTraceString.getMethod() instanceof ToStringMethod
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
module StackTraceStringToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
|
||||||
predicate isSource(DataFlow::Node src) { stackTraceExpr(_, src.asExpr()) }
|
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
|
||||||
}
|
|
||||||
|
|
||||||
module StackTraceStringToHttpResponseSinkFlow =
|
|
||||||
TaintTracking::Global<StackTraceStringToHttpResponseSinkFlowConfig>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A write of stack trace data to an external stream.
|
|
||||||
*/
|
|
||||||
predicate printsStackExternally(MethodAccess call, Expr stackTrace) {
|
|
||||||
printsStackToWriter(call) and
|
|
||||||
call.getQualifier() = stackTrace and
|
|
||||||
not call.getQualifier() instanceof SuperAccess
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A stringified stack trace flows to an external sink.
|
|
||||||
*/
|
|
||||||
predicate stringifiedStackFlowsExternally(DataFlow::Node externalExpr, Expr stackTrace) {
|
|
||||||
exists(MethodAccess stackTraceString |
|
|
||||||
stackTraceExpr(stackTrace, stackTraceString) and
|
|
||||||
StackTraceStringToHttpResponseSinkFlow::flow(DataFlow::exprNode(stackTraceString), externalExpr)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
class GetMessageFlowSource extends MethodAccess {
|
|
||||||
GetMessageFlowSource() {
|
|
||||||
exists(Method method |
|
|
||||||
method = this.getMethod() and
|
|
||||||
method.hasName("getMessage") and
|
|
||||||
method.hasNoParameters() and
|
|
||||||
method.getDeclaringType().hasQualifiedName("java.lang", "Throwable")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module GetMessageFlowSourceToHttpResponseSinkFlowConfig implements DataFlow::ConfigSig {
|
|
||||||
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof GetMessageFlowSource }
|
|
||||||
|
|
||||||
predicate isSink(DataFlow::Node sink) { sink instanceof InformationLeakSink }
|
|
||||||
}
|
|
||||||
|
|
||||||
module GetMessageFlowSourceToHttpResponseSinkFlow =
|
|
||||||
TaintTracking::Global<GetMessageFlowSourceToHttpResponseSinkFlowConfig>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A call to `getMessage()` that then flows to a servlet response.
|
|
||||||
*/
|
|
||||||
predicate getMessageFlowsExternally(DataFlow::Node externalExpr, GetMessageFlowSource getMessage) {
|
|
||||||
GetMessageFlowSourceToHttpResponseSinkFlow::flow(DataFlow::exprNode(getMessage), externalExpr)
|
|
||||||
}
|
|
||||||
|
|
||||||
from Expr externalExpr, Expr errorInformation
|
from Expr externalExpr, Expr errorInformation
|
||||||
where
|
where
|
||||||
printsStackExternally(externalExpr, errorInformation) or
|
printsStackExternally(externalExpr, errorInformation) or
|
||||||
stringifiedStackFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) or
|
stringifiedStackFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation) or
|
||||||
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), errorInformation)
|
getMessageFlowsExternally(DataFlow::exprNode(externalExpr), DataFlow::exprNode(errorInformation))
|
||||||
select externalExpr, "$@ can be exposed to an external user.", errorInformation, "Error information"
|
select externalExpr, "$@ can be exposed to an external user.", errorInformation, "Error information"
|
||||||
|
|||||||
Reference in New Issue
Block a user