Java: copy files from experimental

This commit is contained in:
Jami Cogswell
2023-11-20 15:12:33 -05:00
parent 67b3670d06
commit 0d38a9625e
17 changed files with 1320 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
extensions:
- addsTo:
pack: codeql/java-all
extensible: sourceModel
data:
- ["jakarta.servlet.http", "HttpServletRequest", True, "getServletPath", "", "", "ReturnValue", "remote", "manual"]
- ["javax.servlet.http", "HttpServletRequest", True, "getServletPath", "", "", "ReturnValue", "remote", "manual"]
# # ! below added by me when debugging CVEs:
# - ["org.springframework.cloud.config.server.resource", "ResourceController", True, "retrieve", "(String,String,String,ServletWebRequest,boolean)", "", "Parameter[3]", "remote", "manual"]
# - ["org.springframework.web.context.request", "ServletWebRequest", True, "getContextPath", "()", "", "ReturnValue", "remote", "manual"]
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["java.util.concurrent", "TimeUnit", True, "sleep", "", "", "Argument[0]", "thread-pause", "manual"] # ! this seems like a typo; doesn't look like it's used in the query at all
- ["org.springframework.core.io", "ClassPathResource", True, "getFilename", "", "", "Argument[this]", "get-resource", "manual"] # ! Note: `ClassPathResource` implements `Resource`, so it might make more sense to model some of these as `Resource` with subtype True.
- ["org.springframework.core.io", "ClassPathResource", True, "getPath", "", "", "Argument[this]", "get-resource", "manual"]
- ["org.springframework.core.io", "ClassPathResource", True, "getURL", "", "", "Argument[this]", "get-resource", "manual"]
- ["org.springframework.core.io", "ClassPathResource", True, "resolveURL", "", "", "Argument[this]", "get-resource", "manual"]
# # ! below added by me when debugging CVEs:
# - ["org.springframework.cloud.config.server.resource", "ResourceController", True, "retrieve", "", "", "Argument[0]", "get-resource", "manual"] # don't need
# # - ["org.springframework.cloud.config.server.resource", "ResourceController", True, "getFilePath", "", "", "Argument[0..3]", "get-resource", "manual"] # don't need
# # - ["org.springframework.cloud.config.server.resource", "ResourceRepository", True, "findOne", "", "", "Argument[0..3]", "get-resource", "manual"] # convert to summary
# # - ["org.springframework.core.io", "InputStreamSource", True, "getInputStream", "", "", "Argument[this]", "get-resource", "manual"] # convert to summary
# - ["org.springframework.util", "StreamUtils", True, "copyToString", "", "", "Argument[0]", "get-resource", "manual"] # * public class with good docs
# # - ["org.springframework.cloud.config.server.environment", "SearchPathLocator", True, "getLocations", "(String,String,String)", "", "Argument[0..2]", "get-resource", "manual"] # convert to summary
# # - ["org.springframework.cloud.config.server.environment", "SearchPathLocator$Locations", True, "getLocations", "()", "", "Argument[this]", "get-resource", "manual"] # convert to summary
# - ["org.springframework.core.io", "ResourceLoader", True, "getResource", "", "", "Argument[0]", "get-resource", "manual"] # * public interface with good docs, might be problematic for FPs based on fact that the ext contributor changed this to a taint step to avoid "exists" FPS (maybe there's another way to exclude those FPs though).
# - ["javax.servlet", "ServletContext", True, "getResource", "", "", "Argument[0]", "get-resource", "manual"]
# - ["javax.servlet", "ServletContext", True, "getResourceAsStream", "", "", "Argument[0]", "get-resource", "manual"]
# - ["javax.servlet", "ServletContext", True, "getResourcePaths", "", "", "Argument[0]", "get-resource", "manual"]
# - ["javax.servlet", "ServletContext", True, "getResource", "", "", "Argument[this]", "get-resource", "manual"]
# - ["javax.servlet", "ServletContext", True, "getResourceAsStream", "", "", "Argument[this]", "get-resource", "manual"]
# - ["javax.servlet", "ServletContext", True, "getResourcePaths", "", "", "Argument[this]", "get-resource", "manual"]
# # - ["org.apache.tomcat.util.http", "RequestUtil", True, "normalize", "", "", "Argument[0]", "get-resource", "manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["io.undertow.server.handlers.resource", "Resource", True, "getFile", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.undertow.server.handlers.resource", "Resource", True, "getFilePath", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["io.undertow.server.handlers.resource", "Resource", True, "getPath", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] # ! this as a taint step seems to contradict the fact that they did `ClassPathResource.getPath` as a sink for Spring...
- ["java.nio.file", "Path", True, "normalize", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] # ! shouldn't this be a sanitizer instead??? Or no because WEB-INF ones don't care about normalization?
- ["java.nio.file", "Path", True, "resolve", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] # ! check if this and the below are already in the default models
- ["java.nio.file", "Path", True, "resolve", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Path", True, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.nio.file", "Paths", True, "get", "", "", "Argument[0..1]", "ReturnValue", "taint", "manual"]
- ["org.springframework.core.io", "ClassPathResource", False, "ClassPathResource", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["org.springframework.core.io", "Resource", True, "createRelative", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
# # ! below added/modified by me when debugging CVEs:
- ["org.springframework.core.io", "ResourceLoader", True, "getResource", "", "", "Argument[0]", "ReturnValue", "taint", "manual"]
# - ["org.springframework.cloud.config.server.resource", "ResourceRepository", True, "findOne", "", "", "Argument[0..3]", "ReturnValue", "taint", "manual"] # * public interface, but might be too specific, no easily findable docs...
# - ["org.springframework.core.io", "InputStreamSource", True, "getInputStream", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] # * public interface with good docs, Note: other `getInputStream`s are remote source and/or taint step, so this as taint step versus sink probably is more consistent
# - ["org.springframework.cloud.config.server.environment", "SearchPathLocator", True, "getLocations", "(String,String,String)", "", "Argument[0..2]", "ReturnValue", "taint", "manual"] # * public interface with docs: https://www.javadoc.io/static/org.springframework.cloud/spring-cloud-config-server/2.1.0.RELEASE/org/springframework/cloud/config/server/environment/SearchPathLocator.html
# - ["org.springframework.cloud.config.server.environment", "SearchPathLocator$Locations", True, "getLocations", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"] # ! is the `Locations` class package-private? Or does it inherit public from it's enclosing interface?
# - ["org.springframework.cloud.config.server.resource", "ResourceController", True, "getFilePath", "", "", "Argument[0..3]", "ReturnValue", "taint", "manual"] # don't need

View File

@@ -0,0 +1,21 @@
//BAD: no path validation in Spring resource loading
@GetMapping("/file")
public String getFileContent(@RequestParam(name="fileName") String fileName) {
ClassPathResource clr = new ClassPathResource(fileName);
File file = ResourceUtils.getFile(fileName);
Resource resource = resourceLoader.getResource(fileName);
}
//GOOD: check for a trusted prefix, ensuring path traversal is not used to erase that prefix in Spring resource loading:
@GetMapping("/file")
public String getFileContent(@RequestParam(name="fileName") String fileName) {
if (!fileName.contains("..") && fileName.hasPrefix("/public-content")) {
ClassPathResource clr = new ClassPathResource(fileName);
File file = ResourceUtils.getFile(fileName);
Resource resource = resourceLoader.getResource(fileName);
}
}

View File

@@ -0,0 +1,18 @@
// BAD: no URI validation
URL url = request.getServletContext().getResource(requestUrl);
url = getClass().getResource(requestUrl);
InputStream in = url.openStream();
InputStream in = request.getServletContext().getResourceAsStream(requestPath);
in = getClass().getClassLoader().getResourceAsStream(requestPath);
// 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 (!requestPath.contains("..") && requestPath.startsWith("/trusted")) {
InputStream in = request.getServletContext().getResourceAsStream(requestPath);
}
Path path = Paths.get(requestUrl).normalize().toRealPath();
if (path.startsWith("/trusted")) {
URL url = request.getServletContext().getResource(path.toString());
}

View File

@@ -0,0 +1,11 @@
// BAD: no URI validation
String returnURL = request.getParameter("returnURL");
RequestDispatcher rd = sc.getRequestDispatcher(returnURL);
rd.forward(request, response);
// 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 (!returnURL.contains("..") && returnURL.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 (returnURL.hasPrefix("/internal") && !returnURL.contains("%")) { ... }

View File

@@ -0,0 +1,38 @@
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class UnsafeUrlForward {
@GetMapping("/bad1")
public ModelAndView bad1(String url) {
return new ModelAndView(url);
}
@GetMapping("/bad2")
public void bad2(String url, HttpServletRequest request, HttpServletResponse response) {
try {
request.getRequestDispatcher("/WEB-INF/jsp/" + url + ".jsp").include(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/good1")
public void good1(String url, HttpServletRequest request, HttpServletResponse response) {
try {
request.getRequestDispatcher("/index.jsp?token=" + url).forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,70 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Constructing a server-side redirect path with user input could allow an attacker to download application binaries
(including application classes or jar files) or view arbitrary files within protected directories.</p>
</overview>
<recommendation>
<p>Unsanitized user provided data must not be used to construct the path for URL forwarding. In order to prevent
untrusted URL forwarding, it is recommended to avoid concatenating user input directly into the forwarding URL.
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 in a URL forward
without validating the input, which may cause file leakage. In the <code>good1</code> method,
ordinary forwarding requests are shown, which will not cause file leakage.
</p>
<sample src="UnsafeUrlForward.java" />
<p>The following examples show an HTTP request parameter or request path being used directly in a
request dispatcher of Java EE without validating the input, which allows sensitive file exposure
attacks. It also shows how to remedy the problem by validating the user input.
</p>
<sample src="UnsafeServletRequestDispatch.java" />
<p>The following examples show an HTTP request parameter or request path being used directly to
retrieve a resource of a Java EE application without validating the input, which allows sensitive
file exposure attacks. It also shows how to remedy the problem by validating the user input.
</p>
<sample src="UnsafeResourceGet.java" />
<p>The following examples show an HTTP request parameter being used directly to retrieve a resource
of a Java Spring application without validating the input, which allows sensitive file exposure
attacks. It also shows how to remedy the problem by validating the user input.
</p>
<sample src="UnsafeLoadSpringResource.java" />
</example>
<references>
<li>File Disclosure:
<a href="https://vulncat.fortify.com/en/detail?id=desc.dataflow.java.file_disclosure_spring">Unsafe Url Forward</a>.
</li>
<li>Jakarta Javadoc:
<a href="https://jakarta.ee/specifications/webprofile/9/apidocs/jakarta/servlet/servletrequest#getRequestDispatcher-java.lang.String-">Security vulnerability with unsafe usage of RequestDispatcher</a>.
</li>
<li>Micro Focus:
<a href="https://vulncat.fortify.com/en/detail?id=desc.dataflow.java.file_disclosure_j2ee">File Disclosure: J2EE</a>
</li>
<li>CVE-2015-5174:
<a href="https://vuldb.com/?id.81084">Apache Tomcat 6.0/7.0/8.0/9.0 Servletcontext getResource/getResourceAsStream/getResourcePaths Path Traversal</a>
</li>
<li>CVE-2019-3799:
<a href="https://github.com/mpgn/CVE-2019-3799">CVE-2019-3799 - Spring-Cloud-Config-Server Directory Traversal &lt; 2.1.2, 2.0.4, 1.4.6</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,63 @@
/**
* @name Unsafe URL forward or include from a remote source
* @description URL forward or include based on unvalidated user-input
* may cause file information disclosure.
* @kind path-problem
* @problem.severity error
* @precision high
* @id java/unsafe-url-forward-include
* @tags security
* external/cwe-552
*/
import java
import UnsafeUrlForward
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.dataflow.TaintTracking
import experimental.semmle.code.java.frameworks.Jsf
import semmle.code.java.security.PathSanitizer
import UnsafeUrlForwardFlow::PathGraph
module UnsafeUrlForwardFlowConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) {
source instanceof ThreatModelFlowSource and
not exists(MethodCall ma, Method m | ma.getMethod() = m |
(
m instanceof HttpServletRequestGetRequestUriMethod or
m instanceof HttpServletRequestGetRequestUrlMethod or
m instanceof HttpServletRequestGetPathMethod
) and
ma = source.asExpr()
)
}
predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeUrlForwardSink }
predicate isBarrier(DataFlow::Node node) {
node instanceof UnsafeUrlForwardSanitizer or
node instanceof PathInjectionSanitizer
}
DataFlow::FlowFeature getAFeature() { result instanceof DataFlow::FeatureHasSourceCallContext }
predicate isAdditionalFlowStep(DataFlow::Node prev, DataFlow::Node succ) {
exists(MethodCall ma |
(
ma.getMethod() instanceof GetServletResourceMethod or
ma.getMethod() instanceof GetFacesResourceMethod or
ma.getMethod() instanceof GetClassResourceMethod or
ma.getMethod() instanceof GetClassLoaderResourceMethod or
ma.getMethod() instanceof GetWildflyResourceMethod
) and
ma.getArgument(0) = prev.asExpr() and
ma = succ.asExpr()
)
}
}
module UnsafeUrlForwardFlow = TaintTracking::Global<UnsafeUrlForwardFlowConfig>;
from UnsafeUrlForwardFlow::PathNode source, UnsafeUrlForwardFlow::PathNode sink
where UnsafeUrlForwardFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "Potentially untrusted URL forward due to $@.",
source.getNode(), "user-provided value"

View File

@@ -0,0 +1,163 @@
import java
private import experimental.semmle.code.java.frameworks.Jsf
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.FlowSources
private import semmle.code.java.dataflow.StringPrefixes
private import semmle.code.java.frameworks.javaee.ejb.EJBRestrictions
private import experimental.semmle.code.java.frameworks.SpringResource
/** A sink for unsafe URL forward vulnerabilities. */
abstract class UnsafeUrlForwardSink extends DataFlow::Node { }
/** A sanitizer for unsafe URL forward vulnerabilities. */
abstract class UnsafeUrlForwardSanitizer extends DataFlow::Node { }
/** An argument to `getRequestDispatcher`. */
private class RequestDispatcherSink extends UnsafeUrlForwardSink {
RequestDispatcherSink() {
exists(MethodCall ma |
ma.getMethod() instanceof GetRequestDispatcherMethod and
ma.getArgument(0) = this.asExpr()
)
}
}
/** The `getResource` method of `Class`. */
class GetClassResourceMethod extends Method {
GetClassResourceMethod() {
this.getDeclaringType() instanceof TypeClass and
this.hasName("getResource")
}
}
/** The `getResourceAsStream` method of `Class`. */
class GetClassResourceAsStreamMethod extends Method {
GetClassResourceAsStreamMethod() {
this.getDeclaringType() instanceof TypeClass and
this.hasName("getResourceAsStream")
}
}
/** The `getResource` method of `ClassLoader`. */
class GetClassLoaderResourceMethod extends Method {
GetClassLoaderResourceMethod() {
this.getDeclaringType() instanceof ClassLoaderClass and
this.hasName("getResource")
}
}
/** The `getResourceAsStream` method of `ClassLoader`. */
class GetClassLoaderResourceAsStreamMethod extends Method {
GetClassLoaderResourceAsStreamMethod() {
this.getDeclaringType() instanceof ClassLoaderClass and
this.hasName("getResourceAsStream")
}
}
/** The JBoss class `FileResourceManager`. */
class FileResourceManager extends RefType {
FileResourceManager() {
this.hasQualifiedName("io.undertow.server.handlers.resource", "FileResourceManager")
}
}
/** The JBoss method `getResource` of `FileResourceManager`. */
class GetWildflyResourceMethod extends Method {
GetWildflyResourceMethod() {
this.getDeclaringType().getASupertype*() instanceof FileResourceManager and
this.hasName("getResource")
}
}
/** The JBoss class `VirtualFile`. */
class VirtualFile extends RefType {
VirtualFile() { this.hasQualifiedName("org.jboss.vfs", "VirtualFile") }
}
/** The JBoss method `getChild` of `FileResourceManager`. */
class GetVirtualFileChildMethod extends Method {
GetVirtualFileChildMethod() {
this.getDeclaringType().getASupertype*() instanceof VirtualFile and
this.hasName("getChild")
}
}
/** An argument to `getResource()` or `getResourceAsStream()`. */
private class GetResourceSink extends UnsafeUrlForwardSink {
GetResourceSink() {
sinkNode(this, "request-forgery")
or
sinkNode(this, "get-resource")
or
exists(MethodCall ma |
(
ma.getMethod() instanceof GetServletResourceAsStreamMethod or
ma.getMethod() instanceof GetFacesResourceAsStreamMethod or
ma.getMethod() instanceof GetClassResourceAsStreamMethod or
ma.getMethod() instanceof GetClassLoaderResourceAsStreamMethod or
ma.getMethod() instanceof GetVirtualFileChildMethod
) and
ma.getArgument(0) = this.asExpr()
)
}
}
/** A sink for methods that load Spring resources. */
private class SpringResourceSink extends UnsafeUrlForwardSink {
SpringResourceSink() {
exists(MethodCall ma |
ma.getMethod() instanceof GetResourceUtilsMethod and
ma.getArgument(0) = this.asExpr()
)
}
}
/** An argument to `new ModelAndView` or `ModelAndView.setViewName`. */
private class SpringModelAndViewSink extends UnsafeUrlForwardSink {
SpringModelAndViewSink() {
exists(ClassInstanceExpr cie |
cie.getConstructedType() instanceof ModelAndView and
cie.getArgument(0) = this.asExpr()
)
or
exists(SpringModelAndViewSetViewNameCall smavsvnc | smavsvnc.getArgument(0) = this.asExpr())
}
}
private class PrimitiveSanitizer extends UnsafeUrlForwardSanitizer {
PrimitiveSanitizer() {
this.getType() instanceof PrimitiveType or
this.getType() instanceof BoxedType or
this.getType() instanceof NumberType
}
}
private class SanitizingPrefix extends InterestingPrefix {
SanitizingPrefix() {
not this.getStringValue().matches("/WEB-INF/%") and
not this.getStringValue() = "forward:"
}
override int getOffset() { result = 0 }
}
private class FollowsSanitizingPrefix extends UnsafeUrlForwardSanitizer {
FollowsSanitizingPrefix() { this.asExpr() = any(SanitizingPrefix fp).getAnAppendedExpression() }
}
private class ForwardPrefix extends InterestingPrefix {
ForwardPrefix() { this.getStringValue() = "forward:" }
override int getOffset() { result = 0 }
}
/**
* An expression appended (perhaps indirectly) to `"forward:"`, and which
* is reachable from a Spring entry point.
*/
private class SpringUrlForwardSink extends UnsafeUrlForwardSink {
SpringUrlForwardSink() {
any(SpringRequestMappingMethod sqmm).polyCalls*(this.getEnclosingCallable()) and
this.asExpr() = any(ForwardPrefix fp).getAnAppendedExpression()
}
}

View File

@@ -0,0 +1,155 @@
package com.example;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.file.Files;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/** Sample class of Spring RestController */
@RestController
public class UnsafeLoadSpringResource {
@GetMapping("/file1")
//BAD: Get resource from ClassPathResource without input validation
public String getFileContent1(@RequestParam(name="fileName") String fileName) {
// A request such as the following can disclose source code and application configuration
// fileName=/../../WEB-INF/views/page.jsp
// fileName=/com/example/package/SampleController.class
ClassPathResource clr = new ClassPathResource(fileName);
char[] buffer = new char[4096];
StringBuilder out = new StringBuilder();
try {
Reader in = new FileReader(clr.getFilename());
for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
out.append(buffer, 0, numRead);
}
} catch (IOException ie) {
ie.printStackTrace();
}
return out.toString();
}
@GetMapping("/file1a")
//GOOD: Get resource from ClassPathResource with input path validation
public String getFileContent1a(@RequestParam(name="fileName") String fileName) {
String result = null;
if (fileName.startsWith("/safe_dir") && !fileName.contains("..")) {
ClassPathResource clr = new ClassPathResource(fileName);
char[] buffer = new char[4096];
StringBuilder out = new StringBuilder();
try {
Reader in = new InputStreamReader(clr.getInputStream(), "UTF-8");
for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
out.append(buffer, 0, numRead);
}
} catch (IOException ie) {
ie.printStackTrace();
}
result = out.toString();
}
return result;
}
@GetMapping("/file2")
//BAD: Get resource from ResourceUtils without input validation
public String getFileContent2(@RequestParam(name="fileName") String fileName) {
String content = null;
try {
// A request such as the following can disclose source code and system configuration
// fileName=/etc/hosts
// fileName=file:/etc/hosts
// fileName=/opt/appdir/WEB-INF/views/page.jsp
File file = ResourceUtils.getFile(fileName);
//Read File Content
content = new String(Files.readAllBytes(file.toPath()));
} catch (IOException ie) {
ie.printStackTrace();
}
return content;
}
@GetMapping("/file2a")
//GOOD: Get resource from ResourceUtils with input path validation
public String getFileContent2a(@RequestParam(name="fileName") String fileName) {
String content = null;
if (fileName.startsWith("/safe_dir") && !fileName.contains("..")) {
try {
File file = ResourceUtils.getFile(fileName);
//Read File Content
content = new String(Files.readAllBytes(file.toPath()));
} catch (IOException ie) {
ie.printStackTrace();
}
}
return content;
}
@Autowired
ResourceLoader resourceLoader;
@GetMapping("/file3")
//BAD: Get resource from ResourceLoader (same as application context) without input validation
// Note it is not detected without the generic `resource.getInputStream()` check
public String getFileContent3(@RequestParam(name="fileName") String fileName) {
String content = null;
try {
// A request such as the following can disclose source code and system configuration
// fileName=/WEB-INF/views/page.jsp
// fileName=/WEB-INF/classes/com/example/package/SampleController.class
// fileName=file:/etc/hosts
Resource resource = resourceLoader.getResource(fileName);
char[] buffer = new char[4096];
StringBuilder out = new StringBuilder();
Reader in = new InputStreamReader(resource.getInputStream(), "UTF-8");
for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
out.append(buffer, 0, numRead);
}
content = out.toString();
} catch (IOException ie) {
ie.printStackTrace();
}
return content;
}
@GetMapping("/file3a")
//GOOD: Get resource from ResourceLoader (same as application context) with input path validation
public String getFileContent3a(@RequestParam(name="fileName") String fileName) {
String content = null;
if (fileName.startsWith("/safe_dir") && !fileName.contains("..")) {
try {
Resource resource = resourceLoader.getResource(fileName);
char[] buffer = new char[4096];
StringBuilder out = new StringBuilder();
Reader in = new InputStreamReader(resource.getInputStream(), "UTF-8");
for (int numRead; (numRead = in.read(buffer, 0, buffer.length)) > 0; ) {
out.append(buffer, 0, numRead);
}
content = out.toString();
} catch (IOException ie) {
ie.printStackTrace();
}
}
return content;
}
}

View File

@@ -0,0 +1,52 @@
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// @WebFilter("/*")
public class UnsafeRequestPath implements Filter {
private static final String BASE_PATH = "/pages";
@Override
// BAD: Request dispatcher from servlet path without check
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String path = ((HttpServletRequest) request).getServletPath();
// A sample payload "/%57EB-INF/web.xml" can bypass this `startsWith` check
if (path != null && !path.startsWith("/WEB-INF")) {
request.getRequestDispatcher(path).forward(request, response);
} else {
chain.doFilter(request, response);
}
}
// GOOD: Request dispatcher from servlet path with check
public void doFilter2(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String path = ((HttpServletRequest) request).getServletPath();
if (path.startsWith(BASE_PATH) && !path.contains("..")) {
request.getRequestDispatcher(path).forward(request, response);
} else {
chain.doFilter(request, response);
}
}
// GOOD: Request dispatcher from servlet path with whitelisted string comparison
public void doFilter3(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
String path = ((HttpServletRequest) request).getServletPath();
if (path.equals("/comaction")) {
request.getRequestDispatcher(path).forward(request, response);
} else {
chain.doFilter(request, response);
}
}
}

View File

@@ -0,0 +1,270 @@
package com.example;
import java.io.InputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.net.URI;
import java.net.URL;
import java.net.URISyntaxException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import io.undertow.server.handlers.resource.FileResourceManager;
import io.undertow.server.handlers.resource.Resource;
import org.jboss.vfs.VFS;
import org.jboss.vfs.VirtualFile;
public class UnsafeResourceGet extends HttpServlet {
private static final String BASE_PATH = "/pages";
@Override
// BAD: getResource constructed from `ServletContext` without input validation
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestUrl = request.getParameter("requestURL");
ServletOutputStream out = response.getOutputStream();
ServletConfig cfg = getServletConfig();
ServletContext sc = cfg.getServletContext();
// A sample request /fake.jsp/../WEB-INF/web.xml can load the web.xml file
URL url = sc.getResource(requestUrl);
InputStream in = url.openStream();
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
// GOOD: getResource constructed from `ServletContext` with input validation
protected void doGetGood(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestUrl = request.getParameter("requestURL");
ServletOutputStream out = response.getOutputStream();
ServletConfig cfg = getServletConfig();
ServletContext sc = cfg.getServletContext();
Path path = Paths.get(requestUrl).normalize().toRealPath();
if (path.startsWith(BASE_PATH)) {
URL url = sc.getResource(path.toString());
InputStream in = url.openStream();
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
}
// GOOD: getResource constructed from `ServletContext` with null check only
protected void doGetGood2(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestUrl = request.getParameter("requestURL");
PrintWriter writer = response.getWriter();
ServletConfig cfg = getServletConfig();
ServletContext sc = cfg.getServletContext();
// A sample request /fake.jsp/../WEB-INF/web.xml can load the web.xml file
URL url = sc.getResource(requestUrl);
if (url == null) {
writer.println("Requested source not found");
}
}
// GOOD: getResource constructed from `ServletContext` with `equals` check
protected void doGetGood3(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestUrl = request.getParameter("requestURL");
ServletOutputStream out = response.getOutputStream();
ServletContext sc = request.getServletContext();
if (requestUrl.equals("/public/crossdomain.xml")) {
URL url = sc.getResource(requestUrl);
InputStream in = url.openStream();
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
}
@Override
// BAD: getResourceAsStream constructed from `ServletContext` without input validation
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestPath = request.getParameter("requestPath");
ServletOutputStream out = response.getOutputStream();
// A sample request /fake.jsp/../WEB-INF/web.xml can load the web.xml file
InputStream in = request.getServletContext().getResourceAsStream(requestPath);
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
// GOOD: getResourceAsStream constructed from `ServletContext` with input validation
protected void doPostGood(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestPath = request.getParameter("requestPath");
ServletOutputStream out = response.getOutputStream();
if (!requestPath.contains("..") && requestPath.startsWith("/trusted")) {
InputStream in = request.getServletContext().getResourceAsStream(requestPath);
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
}
@Override
// BAD: getResource constructed from `Class` without input validation
protected void doHead(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestUrl = request.getParameter("requestURL");
ServletOutputStream out = response.getOutputStream();
// A sample request /fake.jsp/../../../WEB-INF/web.xml can load the web.xml file
// Note the class is in two levels of subpackages and `Class.getResource` starts from its own directory
URL url = getClass().getResource(requestUrl);
InputStream in = url.openStream();
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
// GOOD: getResource constructed from `Class` with input validation
protected void doHeadGood(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestUrl = request.getParameter("requestURL");
ServletOutputStream out = response.getOutputStream();
Path path = Paths.get(requestUrl).normalize().toRealPath();
if (path.startsWith(BASE_PATH)) {
URL url = getClass().getResource(path.toString());
InputStream in = url.openStream();
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
}
@Override
// BAD: getResourceAsStream constructed from `ClassLoader` without input validation
protected void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestPath = request.getParameter("requestPath");
ServletOutputStream out = response.getOutputStream();
ServletConfig cfg = getServletConfig();
ServletContext sc = cfg.getServletContext();
// A sample request /fake.jsp/../../../WEB-INF/web.xml can load the web.xml file
// Note the class is in two levels of subpackages and `ClassLoader.getResourceAsStream` starts from its own directory
InputStream in = getClass().getClassLoader().getResourceAsStream(requestPath);
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
// GOOD: getResourceAsStream constructed from `ClassLoader` with input validation
protected void doPutGood(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestPath = request.getParameter("requestPath");
ServletOutputStream out = response.getOutputStream();
ServletConfig cfg = getServletConfig();
ServletContext sc = cfg.getServletContext();
if (!requestPath.contains("..") && requestPath.startsWith("/trusted")) {
InputStream in = getClass().getClassLoader().getResourceAsStream(requestPath);
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
}
// BAD: getResource constructed from `ClassLoader` without input validation
protected void doPutBad(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestUrl = request.getParameter("requestURL");
ServletOutputStream out = response.getOutputStream();
// A sample request /fake.jsp/../../../WEB-INF/web.xml can load the web.xml file
// Note the class is in two levels of subpackages and `ClassLoader.getResource` starts from its own directory
URL url = getClass().getClassLoader().getResource(requestUrl);
InputStream in = url.openStream();
byte[] buf = new byte[4 * 1024]; // 4K buffer
int bytesRead;
while ((bytesRead = in.read(buf)) != -1) {
out.write(buf, 0, bytesRead);
}
}
// BAD: getResource constructed using Undertow IO without input validation
protected void doPutBad2(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestPath = request.getParameter("requestPath");
try {
FileResourceManager rm = new FileResourceManager(VFS.getChild(new URI("/usr/share")).getPhysicalFile());
Resource rs = rm.getResource(requestPath);
VirtualFile overlay = VFS.getChild(new URI("EAP_HOME/modules/"));
// Do file operations
overlay.getChild(rs.getPath());
} catch (URISyntaxException ue) {
throw new IOException("Cannot parse the URI");
}
}
// GOOD: getResource constructed using Undertow IO with input validation
protected void doPutGood2(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestPath = request.getParameter("requestPath");
try {
FileResourceManager rm = new FileResourceManager(VFS.getChild(new URI("/usr/share")).getPhysicalFile());
Resource rs = rm.getResource(requestPath);
VirtualFile overlay = VFS.getChild(new URI("EAP_HOME/modules/"));
String path = rs.getPath();
if (path.startsWith("/trusted_path") && !path.contains("..")) {
// Do file operations
overlay.getChild(path);
}
} catch (URISyntaxException ue) {
throw new IOException("Cannot parse the URI");
}
}
}

View File

@@ -0,0 +1,58 @@
package com.example;
import javax.faces.context.FacesContext;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
/** Sample class of JSF managed bean */
public class UnsafeResourceGet2 {
// BAD: getResourceAsStream constructed from `ExternalContext` without input validation
public String parameterActionBad1() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
String loadUrl = params.get("loadUrl");
InputStreamReader isr = new InputStreamReader(fc.getExternalContext().getResourceAsStream(loadUrl));
BufferedReader br = new BufferedReader(isr);
if(br.ready()) {
//Do Stuff
return "result";
}
return "home";
}
// BAD: getResource constructed from `ExternalContext` without input validation
public String parameterActionBad2() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
String loadUrl = params.get("loadUrl");
URL url = fc.getExternalContext().getResource(loadUrl);
InputStream in = url.openStream();
//Do Stuff
return "result";
}
// GOOD: getResource constructed from `ExternalContext` with input validation
public String parameterActionGood1() throws IOException {
FacesContext fc = FacesContext.getCurrentInstance();
Map<String, String> params = fc.getExternalContext().getRequestParameterMap();
String loadUrl = params.get("loadUrl");
if (loadUrl.equals("/public/crossdomain.xml")) {
URL url = fc.getExternalContext().getResource(loadUrl);
InputStream in = url.openStream();
//Do Stuff
return "result";
}
return "home";
}
}

View File

@@ -0,0 +1,131 @@
import java.io.IOException;
import java.net.URLDecoder;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
public class UnsafeServletRequestDispatch extends HttpServlet {
private static final String BASE_PATH = "/pages";
@Override
// BAD: Request dispatcher constructed from `ServletContext` without input validation
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
String returnURL = request.getParameter("returnURL");
ServletConfig cfg = getServletConfig();
if (action.equals("Login")) {
ServletContext sc = cfg.getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher("/Login.jsp");
rd.forward(request, response);
} else {
ServletContext sc = cfg.getServletContext();
RequestDispatcher rd = sc.getRequestDispatcher(returnURL);
rd.forward(request, response);
}
}
@Override
// BAD: Request dispatcher constructed from `HttpServletRequest` without input validation
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
String returnURL = request.getParameter("returnURL");
if (action.equals("Login")) {
RequestDispatcher rd = request.getRequestDispatcher("/Login.jsp");
rd.forward(request, response);
} else {
RequestDispatcher rd = request.getRequestDispatcher(returnURL);
rd.forward(request, response);
}
}
@Override
// GOOD: Request dispatcher with a whitelisted URI
protected void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
if (action.equals("Login")) {
RequestDispatcher rd = request.getRequestDispatcher("/Login.jsp");
rd.forward(request, response);
} else if (action.equals("Register")) {
RequestDispatcher rd = request.getRequestDispatcher("/Register.jsp");
rd.forward(request, response);
}
}
// BAD: Request dispatcher without path traversal check
protected void doHead2(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = request.getParameter("path");
// A sample payload "/pages/welcome.jsp/../WEB-INF/web.xml" can bypass the `startsWith` check
// The payload "/pages/welcome.jsp/../../%57EB-INF/web.xml" can bypass the check as well since RequestDispatcher will decode `%57` as `W`
if (path.startsWith(BASE_PATH)) {
request.getServletContext().getRequestDispatcher(path).include(request, response);
}
}
// GOOD: Request dispatcher with path traversal check
protected void doHead3(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = request.getParameter("path");
if (path.startsWith(BASE_PATH) && !path.contains("..")) {
request.getServletContext().getRequestDispatcher(path).include(request, response);
}
}
// GOOD: Request dispatcher with path normalization and comparison
protected void doHead4(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = request.getParameter("path");
Path requestedPath = Paths.get(BASE_PATH).resolve(path).normalize();
// /pages/welcome.jsp/../../WEB-INF/web.xml becomes /WEB-INF/web.xml
// /pages/welcome.jsp/../../%57EB-INF/web.xml becomes /%57EB-INF/web.xml
if (requestedPath.startsWith(BASE_PATH)) {
request.getServletContext().getRequestDispatcher(requestedPath.toString()).forward(request, response);
}
}
// FN: Request dispatcher with negation check and path normalization, but without URL decoding
// When promoting this query, consider using FlowStates to make `getRequestDispatcher` a sink
// only if a URL-decoding step has NOT been crossed (i.e. make URLDecoder.decode change the
// state to a different value than the one required at the sink).
protected void doHead5(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = request.getParameter("path");
Path requestedPath = Paths.get(BASE_PATH).resolve(path).normalize();
if (!requestedPath.startsWith("/WEB-INF") && !requestedPath.startsWith("/META-INF")) {
request.getServletContext().getRequestDispatcher(requestedPath.toString()).forward(request, response);
}
}
// GOOD: Request dispatcher with path traversal check and URL decoding
protected void doHead6(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path = request.getParameter("path");
boolean hasEncoding = path.contains("%");
while (hasEncoding) {
path = URLDecoder.decode(path, "UTF-8");
hasEncoding = path.contains("%");
}
if (!path.startsWith("/WEB-INF/") && !path.contains("..")) {
request.getServletContext().getRequestDispatcher(path).include(request, response);
}
}
}

View File

@@ -0,0 +1,129 @@
edges
| UnsafeLoadSpringResource.java:27:32:27:77 | fileName : String | UnsafeLoadSpringResource.java:31:49:31:56 | fileName : String |
| UnsafeLoadSpringResource.java:31:27:31:57 | new ClassPathResource(...) : ClassPathResource | UnsafeLoadSpringResource.java:35:31:35:33 | clr |
| UnsafeLoadSpringResource.java:31:49:31:56 | fileName : String | UnsafeLoadSpringResource.java:31:27:31:57 | new ClassPathResource(...) : ClassPathResource |
| UnsafeLoadSpringResource.java:68:32:68:77 | fileName : String | UnsafeLoadSpringResource.java:76:38:76:45 | fileName |
| UnsafeLoadSpringResource.java:108:32:108:77 | fileName : String | UnsafeLoadSpringResource.java:116:51:116:58 | fileName |
| UnsafeRequestPath.java:20:17:20:63 | getServletPath(...) : String | UnsafeRequestPath.java:23:33:23:36 | path |
| UnsafeResourceGet2.java:16:32:16:79 | getRequestParameterMap(...) : Map | UnsafeResourceGet2.java:17:20:17:25 | params : Map |
| UnsafeResourceGet2.java:17:20:17:25 | params : Map | UnsafeResourceGet2.java:17:20:17:40 | get(...) : String |
| UnsafeResourceGet2.java:17:20:17:40 | get(...) : String | UnsafeResourceGet2.java:19:93:19:99 | loadUrl |
| UnsafeResourceGet2.java:32:32:32:79 | getRequestParameterMap(...) : Map | UnsafeResourceGet2.java:33:20:33:25 | params : Map |
| UnsafeResourceGet2.java:33:20:33:25 | params : Map | UnsafeResourceGet2.java:33:20:33:40 | get(...) : String |
| UnsafeResourceGet2.java:33:20:33:40 | get(...) : String | UnsafeResourceGet2.java:35:49:35:55 | loadUrl : String |
| UnsafeResourceGet2.java:35:13:35:56 | getResource(...) : URL | UnsafeResourceGet2.java:37:20:37:22 | url |
| UnsafeResourceGet2.java:35:49:35:55 | loadUrl : String | UnsafeResourceGet2.java:35:13:35:56 | getResource(...) : URL |
| UnsafeResourceGet.java:32:23:32:56 | getParameter(...) : String | UnsafeResourceGet.java:39:28:39:37 | requestUrl : String |
| UnsafeResourceGet.java:39:13:39:38 | getResource(...) : URL | UnsafeResourceGet.java:41:20:41:22 | url |
| UnsafeResourceGet.java:39:28:39:37 | requestUrl : String | UnsafeResourceGet.java:39:13:39:38 | getResource(...) : URL |
| UnsafeResourceGet.java:111:24:111:58 | getParameter(...) : String | UnsafeResourceGet.java:115:68:115:78 | requestPath |
| UnsafeResourceGet.java:143:23:143:56 | getParameter(...) : String | UnsafeResourceGet.java:148:36:148:45 | requestUrl : String |
| UnsafeResourceGet.java:148:13:148:46 | getResource(...) : URL | UnsafeResourceGet.java:150:20:150:22 | url |
| UnsafeResourceGet.java:148:36:148:45 | requestUrl : String | UnsafeResourceGet.java:148:13:148:46 | getResource(...) : URL |
| UnsafeResourceGet.java:181:24:181:58 | getParameter(...) : String | UnsafeResourceGet.java:189:68:189:78 | requestPath |
| UnsafeResourceGet.java:219:23:219:56 | getParameter(...) : String | UnsafeResourceGet.java:224:53:224:62 | requestUrl : String |
| UnsafeResourceGet.java:224:13:224:63 | getResource(...) : URL | UnsafeResourceGet.java:226:20:226:22 | url |
| UnsafeResourceGet.java:224:53:224:62 | requestUrl : String | UnsafeResourceGet.java:224:13:224:63 | getResource(...) : URL |
| UnsafeResourceGet.java:237:24:237:58 | getParameter(...) : String | UnsafeResourceGet.java:241:33:241:43 | requestPath : String |
| UnsafeResourceGet.java:241:18:241:44 | getResource(...) : Resource | UnsafeResourceGet.java:245:21:245:22 | rs : Resource |
| UnsafeResourceGet.java:241:33:241:43 | requestPath : String | UnsafeResourceGet.java:241:18:241:44 | getResource(...) : Resource |
| UnsafeResourceGet.java:245:21:245:22 | rs : Resource | UnsafeResourceGet.java:245:21:245:32 | getPath(...) |
| UnsafeServletRequestDispatch.java:23:22:23:54 | getParameter(...) : String | UnsafeServletRequestDispatch.java:32:51:32:59 | returnURL |
| UnsafeServletRequestDispatch.java:42:22:42:54 | getParameter(...) : String | UnsafeServletRequestDispatch.java:48:56:48:64 | returnURL |
| UnsafeServletRequestDispatch.java:71:17:71:44 | getParameter(...) : String | UnsafeServletRequestDispatch.java:76:53:76:56 | path |
| UnsafeUrlForward.java:13:27:13:36 | url : String | UnsafeUrlForward.java:14:27:14:29 | url |
| UnsafeUrlForward.java:18:27:18:36 | url : String | UnsafeUrlForward.java:20:28:20:30 | url |
| UnsafeUrlForward.java:25:21:25:30 | url : String | UnsafeUrlForward.java:26:23:26:25 | url |
| UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:48:31:63 | ... + ... |
| UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:61:31:63 | url |
| UnsafeUrlForward.java:36:19:36:28 | url : String | UnsafeUrlForward.java:38:33:38:35 | url |
| UnsafeUrlForward.java:47:19:47:28 | url : String | UnsafeUrlForward.java:49:33:49:62 | ... + ... |
| UnsafeUrlForward.java:58:19:58:28 | url : String | UnsafeUrlForward.java:60:33:60:62 | ... + ... |
nodes
| UnsafeLoadSpringResource.java:27:32:27:77 | fileName : String | semmle.label | fileName : String |
| UnsafeLoadSpringResource.java:31:27:31:57 | new ClassPathResource(...) : ClassPathResource | semmle.label | new ClassPathResource(...) : ClassPathResource |
| UnsafeLoadSpringResource.java:31:49:31:56 | fileName : String | semmle.label | fileName : String |
| UnsafeLoadSpringResource.java:35:31:35:33 | clr | semmle.label | clr |
| UnsafeLoadSpringResource.java:68:32:68:77 | fileName : String | semmle.label | fileName : String |
| UnsafeLoadSpringResource.java:76:38:76:45 | fileName | semmle.label | fileName |
| UnsafeLoadSpringResource.java:108:32:108:77 | fileName : String | semmle.label | fileName : String |
| UnsafeLoadSpringResource.java:116:51:116:58 | fileName | semmle.label | fileName |
| UnsafeRequestPath.java:20:17:20:63 | getServletPath(...) : String | semmle.label | getServletPath(...) : String |
| UnsafeRequestPath.java:23:33:23:36 | path | semmle.label | path |
| UnsafeResourceGet2.java:16:32:16:79 | getRequestParameterMap(...) : Map | semmle.label | getRequestParameterMap(...) : Map |
| UnsafeResourceGet2.java:17:20:17:25 | params : Map | semmle.label | params : Map |
| UnsafeResourceGet2.java:17:20:17:40 | get(...) : String | semmle.label | get(...) : String |
| UnsafeResourceGet2.java:19:93:19:99 | loadUrl | semmle.label | loadUrl |
| UnsafeResourceGet2.java:32:32:32:79 | getRequestParameterMap(...) : Map | semmle.label | getRequestParameterMap(...) : Map |
| UnsafeResourceGet2.java:33:20:33:25 | params : Map | semmle.label | params : Map |
| UnsafeResourceGet2.java:33:20:33:40 | get(...) : String | semmle.label | get(...) : String |
| UnsafeResourceGet2.java:35:13:35:56 | getResource(...) : URL | semmle.label | getResource(...) : URL |
| UnsafeResourceGet2.java:35:49:35:55 | loadUrl : String | semmle.label | loadUrl : String |
| UnsafeResourceGet2.java:37:20:37:22 | url | semmle.label | url |
| UnsafeResourceGet.java:32:23:32:56 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeResourceGet.java:39:13:39:38 | getResource(...) : URL | semmle.label | getResource(...) : URL |
| UnsafeResourceGet.java:39:28:39:37 | requestUrl : String | semmle.label | requestUrl : String |
| UnsafeResourceGet.java:41:20:41:22 | url | semmle.label | url |
| UnsafeResourceGet.java:111:24:111:58 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeResourceGet.java:115:68:115:78 | requestPath | semmle.label | requestPath |
| UnsafeResourceGet.java:143:23:143:56 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeResourceGet.java:148:13:148:46 | getResource(...) : URL | semmle.label | getResource(...) : URL |
| UnsafeResourceGet.java:148:36:148:45 | requestUrl : String | semmle.label | requestUrl : String |
| UnsafeResourceGet.java:150:20:150:22 | url | semmle.label | url |
| UnsafeResourceGet.java:181:24:181:58 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeResourceGet.java:189:68:189:78 | requestPath | semmle.label | requestPath |
| UnsafeResourceGet.java:219:23:219:56 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeResourceGet.java:224:13:224:63 | getResource(...) : URL | semmle.label | getResource(...) : URL |
| UnsafeResourceGet.java:224:53:224:62 | requestUrl : String | semmle.label | requestUrl : String |
| UnsafeResourceGet.java:226:20:226:22 | url | semmle.label | url |
| UnsafeResourceGet.java:237:24:237:58 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeResourceGet.java:241:18:241:44 | getResource(...) : Resource | semmle.label | getResource(...) : Resource |
| UnsafeResourceGet.java:241:33:241:43 | requestPath : String | semmle.label | requestPath : String |
| UnsafeResourceGet.java:245:21:245:22 | rs : Resource | semmle.label | rs : Resource |
| UnsafeResourceGet.java:245:21:245:32 | getPath(...) | semmle.label | getPath(...) |
| UnsafeServletRequestDispatch.java:23:22:23:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeServletRequestDispatch.java:32:51:32:59 | returnURL | semmle.label | returnURL |
| UnsafeServletRequestDispatch.java:42:22:42:54 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeServletRequestDispatch.java:48:56:48:64 | returnURL | semmle.label | returnURL |
| UnsafeServletRequestDispatch.java:71:17:71:44 | getParameter(...) : String | semmle.label | getParameter(...) : String |
| UnsafeServletRequestDispatch.java:76:53:76:56 | path | semmle.label | path |
| UnsafeUrlForward.java:13:27:13:36 | url : String | semmle.label | url : String |
| UnsafeUrlForward.java:14:27:14:29 | url | semmle.label | url |
| UnsafeUrlForward.java:18:27:18:36 | url : String | semmle.label | url : String |
| UnsafeUrlForward.java:20:28:20:30 | url | semmle.label | url |
| UnsafeUrlForward.java:25:21:25:30 | url : String | semmle.label | url : String |
| UnsafeUrlForward.java:26:23:26:25 | url | semmle.label | url |
| UnsafeUrlForward.java:30:27:30:36 | url : String | semmle.label | url : String |
| UnsafeUrlForward.java:31:48:31:63 | ... + ... | semmle.label | ... + ... |
| UnsafeUrlForward.java:31:61:31:63 | url | semmle.label | url |
| UnsafeUrlForward.java:36:19:36:28 | url : String | semmle.label | url : String |
| UnsafeUrlForward.java:38:33:38:35 | url | semmle.label | url |
| UnsafeUrlForward.java:47:19:47:28 | url : String | semmle.label | url : String |
| UnsafeUrlForward.java:49:33:49:62 | ... + ... | semmle.label | ... + ... |
| UnsafeUrlForward.java:58:19:58:28 | url : String | semmle.label | url : String |
| UnsafeUrlForward.java:60:33:60:62 | ... + ... | semmle.label | ... + ... |
subpaths
#select
| UnsafeLoadSpringResource.java:35:31:35:33 | clr | UnsafeLoadSpringResource.java:27:32:27:77 | fileName : String | UnsafeLoadSpringResource.java:35:31:35:33 | clr | Potentially untrusted URL forward due to $@. | UnsafeLoadSpringResource.java:27:32:27:77 | fileName | user-provided value |
| UnsafeLoadSpringResource.java:76:38:76:45 | fileName | UnsafeLoadSpringResource.java:68:32:68:77 | fileName : String | UnsafeLoadSpringResource.java:76:38:76:45 | fileName | Potentially untrusted URL forward due to $@. | UnsafeLoadSpringResource.java:68:32:68:77 | fileName | user-provided value |
| UnsafeLoadSpringResource.java:116:51:116:58 | fileName | UnsafeLoadSpringResource.java:108:32:108:77 | fileName : String | UnsafeLoadSpringResource.java:116:51:116:58 | fileName | Potentially untrusted URL forward due to $@. | UnsafeLoadSpringResource.java:108:32:108:77 | fileName | user-provided value |
| UnsafeRequestPath.java:23:33:23:36 | path | UnsafeRequestPath.java:20:17:20:63 | getServletPath(...) : String | UnsafeRequestPath.java:23:33:23:36 | path | Potentially untrusted URL forward due to $@. | UnsafeRequestPath.java:20:17:20:63 | getServletPath(...) | user-provided value |
| UnsafeResourceGet2.java:19:93:19:99 | loadUrl | UnsafeResourceGet2.java:16:32:16:79 | getRequestParameterMap(...) : Map | UnsafeResourceGet2.java:19:93:19:99 | loadUrl | Potentially untrusted URL forward due to $@. | UnsafeResourceGet2.java:16:32:16:79 | getRequestParameterMap(...) | user-provided value |
| UnsafeResourceGet2.java:37:20:37:22 | url | UnsafeResourceGet2.java:32:32:32:79 | getRequestParameterMap(...) : Map | UnsafeResourceGet2.java:37:20:37:22 | url | Potentially untrusted URL forward due to $@. | UnsafeResourceGet2.java:32:32:32:79 | getRequestParameterMap(...) | user-provided value |
| UnsafeResourceGet.java:41:20:41:22 | url | UnsafeResourceGet.java:32:23:32:56 | getParameter(...) : String | UnsafeResourceGet.java:41:20:41:22 | url | Potentially untrusted URL forward due to $@. | UnsafeResourceGet.java:32:23:32:56 | getParameter(...) | user-provided value |
| UnsafeResourceGet.java:115:68:115:78 | requestPath | UnsafeResourceGet.java:111:24:111:58 | getParameter(...) : String | UnsafeResourceGet.java:115:68:115:78 | requestPath | Potentially untrusted URL forward due to $@. | UnsafeResourceGet.java:111:24:111:58 | getParameter(...) | user-provided value |
| UnsafeResourceGet.java:150:20:150:22 | url | UnsafeResourceGet.java:143:23:143:56 | getParameter(...) : String | UnsafeResourceGet.java:150:20:150:22 | url | Potentially untrusted URL forward due to $@. | UnsafeResourceGet.java:143:23:143:56 | getParameter(...) | user-provided value |
| UnsafeResourceGet.java:189:68:189:78 | requestPath | UnsafeResourceGet.java:181:24:181:58 | getParameter(...) : String | UnsafeResourceGet.java:189:68:189:78 | requestPath | Potentially untrusted URL forward due to $@. | UnsafeResourceGet.java:181:24:181:58 | getParameter(...) | user-provided value |
| UnsafeResourceGet.java:226:20:226:22 | url | UnsafeResourceGet.java:219:23:219:56 | getParameter(...) : String | UnsafeResourceGet.java:226:20:226:22 | url | Potentially untrusted URL forward due to $@. | UnsafeResourceGet.java:219:23:219:56 | getParameter(...) | user-provided value |
| UnsafeResourceGet.java:245:21:245:32 | getPath(...) | UnsafeResourceGet.java:237:24:237:58 | getParameter(...) : String | UnsafeResourceGet.java:245:21:245:32 | getPath(...) | Potentially untrusted URL forward due to $@. | UnsafeResourceGet.java:237:24:237:58 | getParameter(...) | user-provided value |
| UnsafeServletRequestDispatch.java:32:51:32:59 | returnURL | UnsafeServletRequestDispatch.java:23:22:23:54 | getParameter(...) : String | UnsafeServletRequestDispatch.java:32:51:32:59 | returnURL | Potentially untrusted URL forward due to $@. | UnsafeServletRequestDispatch.java:23:22:23:54 | getParameter(...) | user-provided value |
| UnsafeServletRequestDispatch.java:48:56:48:64 | returnURL | UnsafeServletRequestDispatch.java:42:22:42:54 | getParameter(...) : String | UnsafeServletRequestDispatch.java:48:56:48:64 | returnURL | Potentially untrusted URL forward due to $@. | UnsafeServletRequestDispatch.java:42:22:42:54 | getParameter(...) | user-provided value |
| UnsafeServletRequestDispatch.java:76:53:76:56 | path | UnsafeServletRequestDispatch.java:71:17:71:44 | getParameter(...) : String | UnsafeServletRequestDispatch.java:76:53:76:56 | path | Potentially untrusted URL forward due to $@. | UnsafeServletRequestDispatch.java:71:17:71:44 | getParameter(...) | user-provided value |
| UnsafeUrlForward.java:14:27:14:29 | url | UnsafeUrlForward.java:13:27:13:36 | url : String | UnsafeUrlForward.java:14:27:14:29 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:13:27:13:36 | url | user-provided value |
| UnsafeUrlForward.java:20:28:20:30 | url | UnsafeUrlForward.java:18:27:18:36 | url : String | UnsafeUrlForward.java:20:28:20:30 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:18:27:18:36 | url | user-provided value |
| UnsafeUrlForward.java:26:23:26:25 | url | UnsafeUrlForward.java:25:21:25:30 | url : String | UnsafeUrlForward.java:26:23:26:25 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:25:21:25:30 | url | user-provided value |
| UnsafeUrlForward.java:31:48:31:63 | ... + ... | UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:48:31:63 | ... + ... | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:30:27:30:36 | url | user-provided value |
| UnsafeUrlForward.java:31:61:31:63 | url | UnsafeUrlForward.java:30:27:30:36 | url : String | UnsafeUrlForward.java:31:61:31:63 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:30:27:30:36 | url | user-provided value |
| UnsafeUrlForward.java:38:33:38:35 | url | UnsafeUrlForward.java:36:19:36:28 | url : String | UnsafeUrlForward.java:38:33:38:35 | url | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:36:19:36:28 | url | user-provided value |
| UnsafeUrlForward.java:49:33:49:62 | ... + ... | UnsafeUrlForward.java:47:19:47:28 | url : String | UnsafeUrlForward.java:49:33:49:62 | ... + ... | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:47:19:47:28 | url | user-provided value |
| UnsafeUrlForward.java:60:33:60:62 | ... + ... | UnsafeUrlForward.java:58:19:58:28 | url : String | UnsafeUrlForward.java:60:33:60:62 | ... + ... | Potentially untrusted URL forward due to $@. | UnsafeUrlForward.java:58:19:58:28 | url | user-provided value |

View File

@@ -0,0 +1,78 @@
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class UnsafeUrlForward {
@GetMapping("/bad1")
public ModelAndView bad1(String url) {
return new ModelAndView(url);
}
@GetMapping("/bad2")
public ModelAndView bad2(String url) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName(url);
return modelAndView;
}
@GetMapping("/bad3")
public String bad3(String url) {
return "forward:" + url + "/swagger-ui/index.html";
}
@GetMapping("/bad4")
public ModelAndView bad4(String url) {
ModelAndView modelAndView = new ModelAndView("forward:" + url);
return modelAndView;
}
@GetMapping("/bad5")
public void bad5(String url, HttpServletRequest request, HttpServletResponse response) {
try {
request.getRequestDispatcher(url).include(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/bad6")
public void bad6(String url, HttpServletRequest request, HttpServletResponse response) {
try {
request.getRequestDispatcher("/WEB-INF/jsp/" + url + ".jsp").include(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/bad7")
public void bad7(String url, HttpServletRequest request, HttpServletResponse response) {
try {
request.getRequestDispatcher("/WEB-INF/jsp/" + url + ".jsp").forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
@GetMapping("/good1")
public void good1(String url, HttpServletRequest request, HttpServletResponse response) {
try {
request.getRequestDispatcher("/index.jsp?token=" + url).forward(request, response);
} catch (ServletException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1 @@
experimental/Security/CWE/CWE-552/UnsafeUrlForward.ql

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/springframework-5.3.8/:${testdir}/../../../stubs/javax-faces-2.3/:${testdir}/../../../stubs/undertow-io-2.2/:${testdir}/../../../stubs/jboss-vfs-3.2/:${testdir}/../../../stubs/springframework-5.3.8/