Merge pull request #3881 from intrigus-lgtm/more-pathcreations

Java: Centralize and model additional path creations.
This commit is contained in:
Anders Schack-Mulligen
2020-08-06 11:21:39 +02:00
committed by GitHub
8 changed files with 285 additions and 80 deletions

View File

@@ -1,74 +0,0 @@
import java
import semmle.code.java.controlflow.Guards
abstract class PathCreation extends Expr {
abstract Expr getInput();
}
class PathsGet extends PathCreation, MethodAccess {
PathsGet() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePaths and
m.getName() = "get"
)
}
override Expr getInput() { result = this.getAnArgument() }
}
class FileSystemGetPath extends PathCreation, MethodAccess {
FileSystemGetPath() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypeFileSystem and
m.getName() = "getPath"
)
}
override Expr getInput() { result = this.getAnArgument() }
}
class FileCreation extends PathCreation, ClassInstanceExpr {
FileCreation() { this.getConstructedType() instanceof TypeFile }
override Expr getInput() {
result = this.getAnArgument() and
// Relevant arguments include those that are not a `File`.
not result.getType() instanceof TypeFile
}
}
class FileWriterCreation extends PathCreation, ClassInstanceExpr {
FileWriterCreation() { this.getConstructedType().getQualifiedName() = "java.io.FileWriter" }
override Expr getInput() {
result = this.getAnArgument() and
// Relevant arguments are those of type `String`.
result.getType() instanceof TypeString
}
}
predicate inWeakCheck(Expr e) {
// None of these are sufficient to guarantee that a string is safe.
exists(MethodAccess m, Method def | m.getQualifier() = e and m.getMethod() = def |
def.getName() = "startsWith" or
def.getName() = "endsWith" or
def.getName() = "isEmpty" or
def.getName() = "equals"
)
or
// Checking against `null` has no bearing on path traversal.
exists(EqualityTest b | b.getAnOperand() = e | b.getAnOperand() instanceof NullLiteral)
}
// Ignore cases where the variable has been checked somehow,
// but allow some particularly obviously bad cases.
predicate guarded(VarAccess e) {
exists(PathCreation p | e = p.getInput()) and
exists(ConditionBlock cb, Expr c |
cb.getCondition().getAChildExpr*() = c and
c = e.getVariable().getAnAccess() and
cb.controls(e.getBasicBlock(), true) and
// Disallow a few obviously bad checks.
not inWeakCheck(c)
)
}

View File

@@ -14,8 +14,9 @@
import java
import semmle.code.java.dataflow.FlowSources
import PathsCommon
import semmle.code.java.security.PathCreation
import DataFlow::PathGraph
import TaintedPathCommon
class ContainsDotDotSanitizer extends DataFlow::BarrierGuard {
ContainsDotDotSanitizer() {
@@ -34,7 +35,7 @@ class TaintedPathConfig extends TaintTracking::Configuration {
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
override predicate isSink(DataFlow::Node sink) {
exists(Expr e | e = sink.asExpr() | e = any(PathCreation p).getInput() and not guarded(e))
exists(Expr e | e = sink.asExpr() | e = any(PathCreation p).getAnInput() and not guarded(e))
}
override predicate isSanitizer(DataFlow::Node node) {
@@ -48,7 +49,7 @@ class TaintedPathConfig extends TaintTracking::Configuration {
from DataFlow::PathNode source, DataFlow::PathNode sink, PathCreation p, TaintedPathConfig conf
where
sink.getNode().asExpr() = p.getInput() and
sink.getNode().asExpr() = p.getAnInput() and
conf.hasFlowPath(source, sink)
select p, source, sink, "$@ flows to here and is used in a path.", source.getNode(),
"User-provided value"

View File

@@ -0,0 +1,33 @@
/**
* Models a very basic guard for the tainted path queries.
*/
import java
import semmle.code.java.controlflow.Guards
import semmle.code.java.security.PathCreation
private predicate inWeakCheck(Expr e) {
// None of these are sufficient to guarantee that a string is safe.
exists(MethodAccess m, Method def | m.getQualifier() = e and m.getMethod() = def |
def.getName() = "startsWith" or
def.getName() = "endsWith" or
def.getName() = "isEmpty" or
def.getName() = "equals"
)
or
// Checking against `null` has no bearing on path traversal.
exists(EqualityTest b | b.getAnOperand() = e | b.getAnOperand() instanceof NullLiteral)
}
// Ignore cases where the variable has been checked somehow,
// but allow some particularly obviously bad cases.
predicate guarded(VarAccess e) {
exists(PathCreation p | e = p.getAnInput()) and
exists(ConditionBlock cb, Expr c |
cb.getCondition().getAChildExpr*() = c and
c = e.getVariable().getAnAccess() and
cb.controls(e.getBasicBlock(), true) and
// Disallow a few obviously bad checks.
not inWeakCheck(c)
)
}

View File

@@ -14,15 +14,18 @@
import java
import semmle.code.java.dataflow.FlowSources
import PathsCommon
import semmle.code.java.security.PathCreation
import DataFlow::PathGraph
import TaintedPathCommon
class TaintedPathLocalConfig extends TaintTracking::Configuration {
TaintedPathLocalConfig() { this = "TaintedPathLocalConfig" }
override predicate isSource(DataFlow::Node source) { source instanceof LocalUserInput }
override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(PathCreation p).getInput() }
override predicate isSink(DataFlow::Node sink) {
sink.asExpr() = any(PathCreation p).getAnInput()
}
}
from
@@ -30,7 +33,7 @@ from
TaintedPathLocalConfig conf
where
e = sink.getNode().asExpr() and
e = p.getInput() and
e = p.getAnInput() and
conf.hasFlowPath(source, sink) and
not guarded(e)
select p, source, sink, "$@ flows to here and is used in a path.", source.getNode(),