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

Skip to content

Java: Centralize and model additional path creations. #3881

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 0 additions & 74 deletions java/ql/src/Security/CWE/CWE-022/PathsCommon.qll

This file was deleted.

7 changes: 4 additions & 3 deletions java/ql/src/Security/CWE/CWE-022/TaintedPath.ql
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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) {
Expand All @@ -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"
33 changes: 33 additions & 0 deletions java/ql/src/Security/CWE/CWE-022/TaintedPathCommon.qll
Original file line number Diff line number Diff line change
@@ -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)
)
}
9 changes: 6 additions & 3 deletions java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,26 @@

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
DataFlow::PathNode source, DataFlow::PathNode sink, PathCreation p, Expr e,
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(),
Expand Down
141 changes: 141 additions & 0 deletions java/ql/src/semmle/code/java/security/PathCreation.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/**
* Models the different ways to create paths. Either by using `java.io.File`-related APIs or `java.nio.file.Path`-related APIs.
*/

import java

/** Models the creation of a path. */
abstract class PathCreation extends Expr {
/**
* Gets an input that is used in the creation of this path.
* This excludes inputs of type `File` and `Path`.
*/
abstract Expr getAnInput();
}

/** Models the `java.nio.file.Paths.get` method. */
private class PathsGet extends PathCreation, MethodAccess {
PathsGet() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePaths and
m.getName() = "get"
)
}

override Expr getAnInput() { result = this.getAnArgument() }
}

/** Models the `java.nio.file.FileSystem.getPath` method. */
private class FileSystemGetPath extends PathCreation, MethodAccess {
FileSystemGetPath() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypeFileSystem and
m.getName() = "getPath"
)
}

override Expr getAnInput() { result = this.getAnArgument() }
}

/** Models the `new java.io.File(...)` constructor. */
private class FileCreation extends PathCreation, ClassInstanceExpr {
FileCreation() { this.getConstructedType() instanceof TypeFile }

override Expr getAnInput() {
result = this.getAnArgument() and
// Relevant arguments include those that are not a `File`.
not result.getType() instanceof TypeFile
}
}

/** Models the `java.nio.file.Path.resolveSibling` method. */
private class PathResolveSiblingCreation extends PathCreation, MethodAccess {
PathResolveSiblingCreation() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePath and
m.getName() = "resolveSibling"
)
}

override Expr getAnInput() {
result = this.getAnArgument() and
// Relevant arguments are those of type `String`.
result.getType() instanceof TypeString
}
}

/** Models the `java.nio.file.Path.resolve` method. */
private class PathResolveCreation extends PathCreation, MethodAccess {
PathResolveCreation() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePath and
m.getName() = "resolve"
)
}

override Expr getAnInput() {
result = this.getAnArgument() and
// Relevant arguments are those of type `String`.
result.getType() instanceof TypeString
}
}

/** Models the `java.nio.file.Path.of` method. */
private class PathOfCreation extends PathCreation, MethodAccess {
PathOfCreation() {
exists(Method m | m = this.getMethod() |
m.getDeclaringType() instanceof TypePath and
m.getName() = "of"
)
}

override Expr getAnInput() { result = this.getAnArgument() }
}

/** Models the `new java.io.FileWriter(...)` constructor. */
private class FileWriterCreation extends PathCreation, ClassInstanceExpr {
FileWriterCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileWriter") }

override Expr getAnInput() {
result = this.getAnArgument() and
// Relevant arguments are those of type `String`.
result.getType() instanceof TypeString
}
}

/** Models the `new java.io.FileReader(...)` constructor. */
private class FileReaderCreation extends PathCreation, ClassInstanceExpr {
FileReaderCreation() { this.getConstructedType().hasQualifiedName("java.io", "FileReader") }

override Expr getAnInput() {
result = this.getAnArgument() and
// Relevant arguments are those of type `String`.
result.getType() instanceof TypeString
}
}

/** Models the `new java.io.FileInputStream(...)` constructor. */
private class FileInputStreamCreation extends PathCreation, ClassInstanceExpr {
FileInputStreamCreation() {
this.getConstructedType().hasQualifiedName("java.io", "FileInputStream")
}

override Expr getAnInput() {
result = this.getAnArgument() and
// Relevant arguments are those of type `String`.
result.getType() instanceof TypeString
}
}

/** Models the `new java.io.FileOutputStream(...)` constructor. */
private class FileOutputStreamCreation extends PathCreation, ClassInstanceExpr {
FileOutputStreamCreation() {
this.getConstructedType().hasQualifiedName("java.io", "FileOutputStream")
}

override Expr getAnInput() {
result = this.getAnArgument() and
// Relevant arguments are those of type `String`.
result.getType() instanceof TypeString
}
}
25 changes: 25 additions & 0 deletions java/ql/test/library-tests/pathcreation/PathCreation.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
| PathCreation.java:13:18:13:32 | new File(...) | PathCreation.java:13:27:13:31 | "dir" |
| PathCreation.java:14:19:14:40 | new File(...) | PathCreation.java:14:28:14:32 | "dir" |
| PathCreation.java:14:19:14:40 | new File(...) | PathCreation.java:14:35:14:39 | "sub" |
| PathCreation.java:18:18:18:49 | new File(...) | PathCreation.java:18:44:18:48 | "sub" |
| PathCreation.java:18:27:18:41 | new File(...) | PathCreation.java:18:36:18:40 | "dir" |
| PathCreation.java:22:18:22:41 | new File(...) | PathCreation.java:22:27:22:40 | new URI(...) |
| PathCreation.java:26:18:26:31 | of(...) | PathCreation.java:26:26:26:30 | "dir" |
| PathCreation.java:27:19:27:39 | of(...) | PathCreation.java:27:27:27:31 | "dir" |
| PathCreation.java:27:19:27:39 | of(...) | PathCreation.java:27:34:27:38 | "sub" |
| PathCreation.java:31:18:31:40 | of(...) | PathCreation.java:31:26:31:39 | new URI(...) |
| PathCreation.java:35:18:35:33 | get(...) | PathCreation.java:35:28:35:32 | "dir" |
| PathCreation.java:36:19:36:41 | get(...) | PathCreation.java:36:29:36:33 | "dir" |
| PathCreation.java:36:19:36:41 | get(...) | PathCreation.java:36:36:36:40 | "sub" |
| PathCreation.java:40:18:40:42 | get(...) | PathCreation.java:40:28:40:41 | new URI(...) |
| PathCreation.java:44:18:44:56 | getPath(...) | PathCreation.java:44:51:44:55 | "dir" |
| PathCreation.java:45:19:45:64 | getPath(...) | PathCreation.java:45:52:45:56 | "dir" |
| PathCreation.java:45:19:45:64 | getPath(...) | PathCreation.java:45:59:45:63 | "sub" |
| PathCreation.java:49:18:49:31 | of(...) | PathCreation.java:49:26:49:30 | "dir" |
| PathCreation.java:49:18:49:53 | resolveSibling(...) | PathCreation.java:49:48:49:52 | "sub" |
| PathCreation.java:53:18:53:31 | of(...) | PathCreation.java:53:26:53:30 | "dir" |
| PathCreation.java:53:18:53:46 | resolve(...) | PathCreation.java:53:41:53:45 | "sub" |
| PathCreation.java:57:25:57:45 | new FileWriter(...) | PathCreation.java:57:40:57:44 | "dir" |
| PathCreation.java:61:25:61:45 | new FileReader(...) | PathCreation.java:61:40:61:44 | "dir" |
| PathCreation.java:65:32:65:58 | new FileOutputStream(...) | PathCreation.java:65:53:65:57 | "dir" |
| PathCreation.java:69:31:69:56 | new FileInputStream(...) | PathCreation.java:69:51:69:55 | "dir" |
Loading