mirror of
https://github.com/github/codeql.git
synced 2025-12-20 10:46:30 +01:00
File path injection with the JFinal framework
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// BAD: no file download validation
|
||||
HttpServletRequest request = getRequest();
|
||||
String path = request.getParameter("path");
|
||||
String filePath = "/pages/" + path;
|
||||
HttpServletResponse resp = getResponse();
|
||||
File file = new File(filePath);
|
||||
resp.getOutputStream().write(file.readContent());
|
||||
|
||||
// BAD: no file upload validation
|
||||
String savePath = getPara("dir");
|
||||
File file = getFile("fileParam").getFile();
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
String filePath = "/files/" + savePath;
|
||||
FileOutputStream fos = new FileOutputStream(filePath);
|
||||
|
||||
// GOOD: check for a trusted prefix, ensuring path traversal is not used to erase that prefix:
|
||||
// (alternatively use `Path.normalize` instead of checking for `..`)
|
||||
if (!filePath.contains("..") && filePath.hasPrefix("/pages")) { ... }
|
||||
// Also GOOD: check for a forbidden prefix, ensuring URL-encoding is not used to evade the check:
|
||||
// (alternatively use `URLDecoder.decode` before `hasPrefix`)
|
||||
if (filePath.hasPrefix("/files") && !filePath.contains("%")) { ... }
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>External Control of File Name or Path, also called File Path Injection, is a vulnerability that a file path
|
||||
being accessed is composed using data from outside the application (such as the HTTP request, the database, or
|
||||
the filesystem). It allows an attacker to traverse through the filesystem and access arbitrary files.</p>
|
||||
</overview>
|
||||
|
||||
<recommendation>
|
||||
<p>Unsanitized user provided data must not be used to construct the file path. In order to prevent File
|
||||
Path Injection, it is recommended to avoid concatenating user input directly into the file path. Instead,
|
||||
user input should be checked against allowed (e.g., must come within <code>user_content/</code>) or disallowed
|
||||
(e.g. must not come within <code>/internal</code>) paths, ensuring that neither path traversal using <code>../</code>
|
||||
or URL encoding are used to evade these checks.
|
||||
</p>
|
||||
</recommendation>
|
||||
|
||||
<example>
|
||||
<p>The following examples show the bad case and the good case respectively.
|
||||
The <code>BAD</code> methods show an HTTP request parameter being used directly to construct a file path
|
||||
without validating the input, which may cause file leakage. In the <code>GOOD</code> method, file path
|
||||
is validated.
|
||||
</p>
|
||||
<sample src="FilePathInjection.java" />
|
||||
</example>
|
||||
|
||||
<references>
|
||||
<li>OWASP:
|
||||
<a href="https://owasp.org/www-community/attacks/Path_Traversal">Path Traversal</a>.
|
||||
</li>
|
||||
<li>Veracode:
|
||||
<a href="https://www.veracode.com/security/dotnet/cwe-73">External Control of File Name or Path Flaw</a>.
|
||||
</li>
|
||||
</references>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* @name File Path Injection
|
||||
* @description Loading files based on unvalidated user-input may cause file information disclosure
|
||||
* and uploading files with unvalidated file types to an arbitrary directory may lead to
|
||||
* Remote Command Execution (RCE).
|
||||
* @kind path-problem
|
||||
* @problem.severity error
|
||||
* @precision high
|
||||
* @id java/file-path-injection
|
||||
* @tags security
|
||||
* external/cwe-073
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import JFinalController
|
||||
import PathSanitizer
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A sink that represents a file read operation.
|
||||
*/
|
||||
private class ReadFileSinkModels extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"java.io;FileInputStream;false;FileInputStream;;;Argument[0];read-file",
|
||||
"java.io;File;false;File;;;Argument[0];read-file"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink that represents a file creation or access, such as a file read, write, copy or move operation.
|
||||
*/
|
||||
private class FileAccessSink extends DataFlow::Node {
|
||||
FileAccessSink() { sinkNode(this, "create-file") or sinkNode(this, "read-file") }
|
||||
}
|
||||
|
||||
class InjectFilePathConfig extends TaintTracking::Configuration {
|
||||
InjectFilePathConfig() { this = "InjectFilePathConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof FileAccessSink }
|
||||
|
||||
override predicate isSanitizerGuard(DataFlow::BarrierGuard guard) {
|
||||
guard instanceof PathTraversalBarrierGuard
|
||||
}
|
||||
}
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, InjectFilePathConfig conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "External control of file name or path due to $@.",
|
||||
source.getNode(), "user-provided value"
|
||||
@@ -0,0 +1,44 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/** The class `com.jfinal.config.Routes`. */
|
||||
class JFinalRoutes extends RefType {
|
||||
JFinalRoutes() { this.hasQualifiedName("com.jfinal.config", "Routes") }
|
||||
}
|
||||
|
||||
/** The method `add` of the class `Routes`. */
|
||||
class AddJFinalRoutes extends Method {
|
||||
AddJFinalRoutes() {
|
||||
this.getDeclaringType() instanceof JFinalRoutes and
|
||||
this.getName() = "add"
|
||||
}
|
||||
}
|
||||
|
||||
/** The class `com.jfinal.core.Controller`. */
|
||||
class JFinalController extends RefType {
|
||||
JFinalController() { this.hasQualifiedName("com.jfinal.core", "Controller") }
|
||||
}
|
||||
|
||||
/** Source model of remote flow source with `JFinal`. */
|
||||
private class JFinalControllerSource extends SourceModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"com.jfinal.core;Controller;true;getAttr" + ["", "ForInt", "ForStr"] +
|
||||
";;;ReturnValue;remote",
|
||||
"com.jfinal.core;Controller;true;getCookie" + ["", "Object", "Objects", "ToInt", "ToLong"] +
|
||||
";;;ReturnValue;remote",
|
||||
"com.jfinal.core;Controller;true;getFile" + ["", "s"] + ";;;ReturnValue;remote",
|
||||
"com.jfinal.core;Controller;true;getHeader;;;ReturnValue;remote",
|
||||
"com.jfinal.core;Controller;true;getKv;;;ReturnValue;remote",
|
||||
"com.jfinal.core;Controller;true;getPara" +
|
||||
[
|
||||
"", "Map", "ToBoolean", "ToDate", "ToInt", "ToLong", "Values", "ValuesToInt",
|
||||
"ValuesToLong"
|
||||
] + ";;;ReturnValue;remote",
|
||||
"com.jfinal.core;Controller;true;getSession" + ["", "Attr"] + ";;;ReturnValue;remote",
|
||||
"com.jfinal.core;Controller;true;get" + ["", "Int", "Long", "Boolean", "Date"] +
|
||||
";;;ReturnValue;remote"
|
||||
]
|
||||
}
|
||||
}
|
||||
175
java/ql/src/experimental/Security/CWE/CWE-073/PathSanitizer.qll
Normal file
175
java/ql/src/experimental/Security/CWE/CWE-073/PathSanitizer.qll
Normal file
@@ -0,0 +1,175 @@
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
|
||||
/** A barrier guard that protects against path traversal vulnerabilities. */
|
||||
abstract class PathTraversalBarrierGuard extends DataFlow::BarrierGuard { }
|
||||
|
||||
/**
|
||||
* A guard that considers safe a string being exactly compared to a trusted value.
|
||||
*/
|
||||
private class ExactStringPathMatchGuard extends PathTraversalBarrierGuard instanceof MethodAccess {
|
||||
ExactStringPathMatchGuard() {
|
||||
super.getMethod().getDeclaringType() instanceof TypeString and
|
||||
super.getMethod().getName() = ["equals", "equalsIgnoreCase"]
|
||||
}
|
||||
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = super.getQualifier() and
|
||||
branch = true
|
||||
}
|
||||
}
|
||||
|
||||
private class AllowListGuard extends Guard instanceof MethodAccess {
|
||||
AllowListGuard() {
|
||||
(isStringPartialMatch(this) or isPathPartialMatch(this)) and
|
||||
not isDisallowedWord(super.getAnArgument())
|
||||
}
|
||||
|
||||
Expr getCheckedExpr() { result = super.getQualifier() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard that considers a path safe because it is checked against an allowlist of partial trusted values.
|
||||
* This requires additional protection against path traversal, either another guard (`PathTraversalGuard`)
|
||||
* or a sanitizer (`PathNormalizeSanitizer`), to ensure any internal `..` components are removed from the path.
|
||||
*/
|
||||
private class AllowListBarrierGuard extends PathTraversalBarrierGuard instanceof AllowListGuard {
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = super.getCheckedExpr() and
|
||||
branch = true and
|
||||
(
|
||||
// Either a path normalization sanitizer comes before the guard,
|
||||
exists(PathNormalizeSanitizer sanitizer | DataFlow::localExprFlow(sanitizer, e))
|
||||
or
|
||||
// or a check like `!path.contains("..")` comes before the guard
|
||||
exists(PathTraversalGuard previousGuard |
|
||||
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
|
||||
previousGuard.controls(this.getBasicBlock().(ConditionBlock), false)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard that considers a path safe because it is checked for `..` components, having previously
|
||||
* been checked for a trusted prefix.
|
||||
*/
|
||||
private class DotDotCheckBarrierGuard extends PathTraversalBarrierGuard instanceof PathTraversalGuard {
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = super.getCheckedExpr() and
|
||||
branch = false and
|
||||
// The same value has previously been checked against a list of allowed prefixes:
|
||||
exists(AllowListGuard previousGuard |
|
||||
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
|
||||
previousGuard.controls(this.getBasicBlock().(ConditionBlock), true)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class BlockListGuard extends Guard instanceof MethodAccess {
|
||||
BlockListGuard() {
|
||||
(isStringPartialMatch(this) or isPathPartialMatch(this)) and
|
||||
isDisallowedWord(super.getAnArgument())
|
||||
}
|
||||
|
||||
Expr getCheckedExpr() { result = super.getQualifier() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard that considers a string safe because it is checked against a blocklist of known dangerous values.
|
||||
* This requires a prior check for URL encoding concealing a forbidden value, either a guard (`UrlEncodingGuard`)
|
||||
* or a sanitizer (`UrlDecodeSanitizer`).
|
||||
*/
|
||||
private class BlockListBarrierGuard extends PathTraversalBarrierGuard instanceof BlockListGuard {
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = super.getCheckedExpr() and
|
||||
branch = false and
|
||||
(
|
||||
// Either `e` has been URL decoded:
|
||||
exists(UrlDecodeSanitizer sanitizer | DataFlow::localExprFlow(sanitizer, e))
|
||||
or
|
||||
// or `e` has previously been checked for URL encoding sequences:
|
||||
exists(UrlEncodingGuard previousGuard |
|
||||
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
|
||||
previousGuard.controls(this.getBasicBlock(), false)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A guard that considers a string safe because it is checked for URL encoding sequences,
|
||||
* having previously been checked against a block-list of forbidden values.
|
||||
*/
|
||||
private class URLEncodingBarrierGuard extends PathTraversalBarrierGuard instanceof UrlEncodingGuard {
|
||||
override predicate checks(Expr e, boolean branch) {
|
||||
e = super.getCheckedExpr() and
|
||||
branch = false and
|
||||
exists(BlockListGuard previousGuard |
|
||||
DataFlow::localExprFlow(previousGuard.getCheckedExpr(), e) and
|
||||
previousGuard.controls(this.getBasicBlock(), false)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call to a method that checks a partial string match.
|
||||
*/
|
||||
private predicate isStringPartialMatch(MethodAccess ma) {
|
||||
ma.getMethod().getDeclaringType() instanceof TypeString and
|
||||
ma.getMethod().getName() =
|
||||
["contains", "startsWith", "matches", "regionMatches", "indexOf", "lastIndexOf"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `ma` is a call to a method of `java.nio.file.Path` that checks a partial path match.
|
||||
*/
|
||||
private predicate isPathPartialMatch(MethodAccess ma) {
|
||||
ma.getMethod().getDeclaringType() instanceof TypePath and
|
||||
ma.getMethod().getName() = "startsWith"
|
||||
}
|
||||
|
||||
private predicate isDisallowedWord(CompileTimeConstantExpr word) {
|
||||
word.getStringValue().matches(["%WEB-INF%", "%META-INF%", "%..%"])
|
||||
}
|
||||
|
||||
/** A complementary guard that protects against path traversal, by looking for the literal `..`. */
|
||||
class PathTraversalGuard extends Guard instanceof MethodAccess {
|
||||
Expr checked;
|
||||
|
||||
PathTraversalGuard() {
|
||||
super.getMethod().getDeclaringType() instanceof TypeString and
|
||||
super.getMethod().hasName(["contains", "indexOf"]) and
|
||||
super.getAnArgument().(CompileTimeConstantExpr).getStringValue() = ".."
|
||||
}
|
||||
|
||||
Expr getCheckedExpr() { result = super.getQualifier() }
|
||||
}
|
||||
|
||||
/** A complementary sanitizer that protects against path traversal using path normalization. */
|
||||
private class PathNormalizeSanitizer extends MethodAccess {
|
||||
PathNormalizeSanitizer() {
|
||||
this.getMethod().getDeclaringType().hasQualifiedName("java.nio.file", "Path") and
|
||||
this.getMethod().hasName("normalize")
|
||||
}
|
||||
}
|
||||
|
||||
/** A complementary guard that protects against double URL encoding, by looking for the literal `%`. */
|
||||
private class UrlEncodingGuard extends Guard instanceof MethodAccess {
|
||||
UrlEncodingGuard() {
|
||||
super.getMethod().getDeclaringType() instanceof TypeString and
|
||||
super.getMethod().hasName(["contains", "indexOf"]) and
|
||||
super.getAnArgument().(CompileTimeConstantExpr).getStringValue() = "%"
|
||||
}
|
||||
|
||||
Expr getCheckedExpr() { result = super.getQualifier() }
|
||||
}
|
||||
|
||||
/** A complementary sanitizer that protects against double URL encoding using URL decoding. */
|
||||
private class UrlDecodeSanitizer extends MethodAccess {
|
||||
UrlDecodeSanitizer() {
|
||||
this.getMethod().getDeclaringType().hasQualifiedName("java.net", "URLDecoder") and
|
||||
this.getMethod().hasName("decode")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
edges
|
||||
| FilePathInjection.java:20:21:20:34 | getPara(...) : String | FilePathInjection.java:25:47:25:59 | finalFilePath |
|
||||
| FilePathInjection.java:61:50:61:58 | file : File | FilePathInjection.java:66:30:66:33 | file |
|
||||
| FilePathInjection.java:88:17:88:44 | getParameter(...) : String | FilePathInjection.java:92:24:92:31 | filePath |
|
||||
| FilePathInjection.java:88:17:88:44 | getParameter(...) : String | FilePathInjection.java:92:24:92:31 | filePath : String |
|
||||
| FilePathInjection.java:92:15:92:32 | new File(...) : File | FilePathInjection.java:100:19:100:22 | file : File |
|
||||
| FilePathInjection.java:92:24:92:31 | filePath : String | FilePathInjection.java:92:15:92:32 | new File(...) : File |
|
||||
| FilePathInjection.java:100:19:100:22 | file : File | FilePathInjection.java:61:50:61:58 | file : File |
|
||||
nodes
|
||||
| FilePathInjection.java:20:21:20:34 | getPara(...) : String | semmle.label | getPara(...) : String |
|
||||
| FilePathInjection.java:25:47:25:59 | finalFilePath | semmle.label | finalFilePath |
|
||||
| FilePathInjection.java:61:50:61:58 | file : File | semmle.label | file : File |
|
||||
| FilePathInjection.java:66:30:66:33 | file | semmle.label | file |
|
||||
| FilePathInjection.java:88:17:88:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
|
||||
| FilePathInjection.java:92:15:92:32 | new File(...) : File | semmle.label | new File(...) : File |
|
||||
| FilePathInjection.java:92:24:92:31 | filePath | semmle.label | filePath |
|
||||
| FilePathInjection.java:92:24:92:31 | filePath : String | semmle.label | filePath : String |
|
||||
| FilePathInjection.java:100:19:100:22 | file : File | semmle.label | file : File |
|
||||
subpaths
|
||||
#select
|
||||
| FilePathInjection.java:25:47:25:59 | finalFilePath | FilePathInjection.java:20:21:20:34 | getPara(...) : String | FilePathInjection.java:25:47:25:59 | finalFilePath | External control of file name or path due to $@. | FilePathInjection.java:20:21:20:34 | getPara(...) | user-provided value |
|
||||
| FilePathInjection.java:66:30:66:33 | file | FilePathInjection.java:88:17:88:44 | getParameter(...) : String | FilePathInjection.java:66:30:66:33 | file | External control of file name or path due to $@. | FilePathInjection.java:88:17:88:44 | getParameter(...) | user-provided value |
|
||||
| FilePathInjection.java:92:24:92:31 | filePath | FilePathInjection.java:88:17:88:44 | getParameter(...) : String | FilePathInjection.java:92:24:92:31 | filePath | External control of file name or path due to $@. | FilePathInjection.java:88:17:88:44 | getParameter(...) | user-provided value |
|
||||
@@ -0,0 +1,127 @@
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.jfinal.core.Controller;
|
||||
|
||||
public class FilePathInjection extends Controller {
|
||||
private static final String BASE_PATH = "/pages";
|
||||
|
||||
// BAD: Upload file to user specified path without validation
|
||||
public void uploadFile() throws IOException {
|
||||
String savePath = getPara("dir");
|
||||
File file = getFile("fileParam").getFile();
|
||||
String finalFilePath = BASE_PATH + savePath;
|
||||
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
FileOutputStream fos = new FileOutputStream(finalFilePath);
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
byte[] buf = new byte[1024];
|
||||
i = fis.read(buf);
|
||||
fos.write(buf);
|
||||
} while (i != -1);
|
||||
|
||||
fis.close();
|
||||
fos.close();
|
||||
}
|
||||
|
||||
// GOOD: Upload file to user specified path with path normalization and validation
|
||||
public void uploadFile2() throws IOException {
|
||||
String savePath = getPara("dir");
|
||||
File file = getFile("fileParam").getFile();
|
||||
String finalFilePath = BASE_PATH + savePath;
|
||||
Path path = Paths.get(finalFilePath).normalize();
|
||||
|
||||
if (path.startsWith(BASE_PATH)) {
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
FileOutputStream fos = new FileOutputStream(path.toFile());
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
byte[] buf = new byte[1024];
|
||||
i = fis.read(buf);
|
||||
fos.write(buf);
|
||||
} while (i != -1);
|
||||
|
||||
fis.close();
|
||||
fos.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void readFile(HttpServletResponse resp, File file) {
|
||||
OutputStream os = null;
|
||||
FileInputStream fis = null;
|
||||
try {
|
||||
os = resp.getOutputStream();
|
||||
fis = new FileInputStream(file);
|
||||
byte fileContent[] = new byte[(int) file.length()];
|
||||
fis.read(fileContent);
|
||||
os.write(fileContent);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Invalid directory or file " + file.getName());
|
||||
} finally {
|
||||
try {
|
||||
if (os != null)
|
||||
os.close();
|
||||
} catch (Exception e2) {
|
||||
}
|
||||
try {
|
||||
if (fis != null)
|
||||
fis.close();
|
||||
} catch (Exception e2) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void downloadFile() throws FileNotFoundException, IOException {
|
||||
HttpServletRequest request = getRequest();
|
||||
String path = request.getParameter("path");
|
||||
String filePath = BASE_PATH + path;
|
||||
|
||||
HttpServletResponse resp = getResponse();
|
||||
File file = new File(filePath);
|
||||
if (path != null && file.exists()) {
|
||||
resp.setHeader("Content-type", "application/force-download");
|
||||
resp.setHeader("Content-Disposition", "inline;filename=\"" + filePath + "\"");
|
||||
resp.setHeader("Content-Transfer-Encoding", "Binary");
|
||||
resp.setHeader("Content-length", "" + file.length());
|
||||
resp.setHeader("Content-Type", "application/octet-stream");
|
||||
resp.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
|
||||
readFile(resp, file);
|
||||
} else {
|
||||
System.err.println("File does not exist " + path);
|
||||
}
|
||||
}
|
||||
|
||||
public void downloadFile2() throws FileNotFoundException, IOException {
|
||||
HttpServletRequest request = getRequest();
|
||||
String path = request.getParameter("path");
|
||||
String filePath = BASE_PATH + path;
|
||||
|
||||
HttpServletResponse resp = getResponse();
|
||||
if (!filePath.contains("..") && filePath.startsWith(BASE_PATH)) {
|
||||
File file = new File(filePath);
|
||||
if (file.exists()) {
|
||||
resp.setHeader("Content-type", "application/force-download");
|
||||
resp.setHeader("Content-Disposition", "inline;filename=\"" + filePath + "\"");
|
||||
resp.setHeader("Content-Transfer-Encoding", "Binary");
|
||||
resp.setHeader("Content-length", "" + file.length());
|
||||
resp.setHeader("Content-Type", "application/octet-stream");
|
||||
resp.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
|
||||
readFile(resp, file);
|
||||
} else {
|
||||
System.err.println("File does not exist " + path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
experimental/Security/CWE/CWE-073/FilePathInjection.ql
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../../stubs/servlet-api-2.4:${testdir}/../../../../stubs/jfinal-4.9.15
|
||||
331
java/ql/test/stubs/jfinal-4.9.15/com/jfinal/core/Controller.java
generated
Normal file
331
java/ql/test/stubs/jfinal-4.9.15/com/jfinal/core/Controller.java
generated
Normal file
@@ -0,0 +1,331 @@
|
||||
package com.jfinal.core;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import com.jfinal.kit.Kv;
|
||||
import com.jfinal.upload.UploadFile;
|
||||
|
||||
public abstract class Controller {
|
||||
public String getPara(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPara(String name, String defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<String, String[]> getParaMap() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Enumeration<String> getParaNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String[] getParaValues(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer[] getParaValuesToInt(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long[] getParaValuesToLong(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Enumeration<String> getAttrNames() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getAttr(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getAttr(String name, T defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getAttrForStr(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getAttrForInt(String name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public String getHeader(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getParaToInt(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getParaToInt(String name, Integer defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getParaToLong(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getParaToLong(String name, Long defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Boolean getParaToBoolean(String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean getParaToBoolean(String name, Boolean defaultValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean getParaToBoolean() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean getParaToBoolean(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean getParaToBoolean(int index, Boolean defaultValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Date getParaToDate(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getParaToDate(String name, Date defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getParaToDate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpServletRequest getRequest() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpServletResponse getResponse() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpSession getSession() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public HttpSession getSession(boolean create) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getSessionAttr(String key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public <T> T getSessionAttr(String key, T defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCookie(String name, String defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getCookie(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getCookieToInt(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getCookieToInt(String name, Integer defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getCookieToLong(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getCookieToLong(String name, Long defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Cookie getCookieObject(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Cookie[] getCookieObjects() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPara() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPara(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getPara(int index, String defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getParaToInt(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getParaToInt(int index, Integer defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getParaToLong(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getParaToLong(int index, Long defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getParaToInt() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getParaToLong() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Kv getKv() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<UploadFile> getFiles(String uploadPath, Integer maxPostSize, String encoding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public UploadFile getFile(String parameterName, String uploadPath, Integer maxPostSize, String encoding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<UploadFile> getFiles(String uploadPath, int maxPostSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public UploadFile getFile(String parameterName, String uploadPath, int maxPostSize) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<UploadFile> getFiles(String uploadPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public UploadFile getFile(String parameterName, String uploadPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<UploadFile> getFiles() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public UploadFile getFile() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public UploadFile getFile(String parameterName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String get(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String get(String name, String defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getInt(String name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Integer getInt(String name, Integer defaultValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Long getLong(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getLong(String name, Long defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean getBoolean(String name, Boolean defaultValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Date getDate(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getDate(String name, Date defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String get(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String get(int index, String defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Integer getInt() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Integer getInt(int index) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Integer getInt(int index, Integer defaultValue) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Long getLong() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getLong(int index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Long getLong(int index, Long defaultValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Boolean getBoolean() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean getBoolean(int index, Boolean defaultValue) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
6
java/ql/test/stubs/jfinal-4.9.15/com/jfinal/kit/Kv.java
generated
Normal file
6
java/ql/test/stubs/jfinal-4.9.15/com/jfinal/kit/Kv.java
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
package com.jfinal.kit;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Kv extends HashMap {
|
||||
}
|
||||
32
java/ql/test/stubs/jfinal-4.9.15/com/jfinal/upload/UploadFile.java
generated
Normal file
32
java/ql/test/stubs/jfinal-4.9.15/com/jfinal/upload/UploadFile.java
generated
Normal file
@@ -0,0 +1,32 @@
|
||||
package com.jfinal.upload;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class UploadFile {
|
||||
public UploadFile(String parameterName, String uploadPath, String filesystemName, String originalFileName, String contentType) {
|
||||
}
|
||||
|
||||
public String getParameterName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getOriginalFileName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getUploadPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user