Merge branch 'main' into thirdpartyapitelemtry

This commit is contained in:
Benjamin Muskalla
2021-09-03 14:23:31 +02:00
2136 changed files with 47113 additions and 9212 deletions

View File

@@ -0,0 +1,264 @@
import java.io.IOException;
import java.util.Hashtable;
import java.util.Properties;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.naming.CompositeName;
import javax.naming.CompoundName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.InitialDirContext;
import javax.naming.directory.SearchControls;
import javax.naming.ldap.InitialLdapContext;
import org.springframework.jndi.JndiTemplate;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.ContextMapper;
import org.springframework.ldap.core.DirContextProcessor;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.NameClassPairCallbackHandler;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class JndiInjectionTest {
@RequestMapping
public void testInitialContextBad1(@RequestParam String nameStr) throws NamingException {
Name name = new CompositeName(nameStr);
InitialContext ctx = new InitialContext();
ctx.lookup(nameStr); // $hasJndiInjection
ctx.lookupLink(nameStr); // $hasJndiInjection
InitialContext.doLookup(nameStr); // $hasJndiInjection
ctx.rename(nameStr, ""); // $hasJndiInjection
ctx.list(nameStr); // $hasJndiInjection
ctx.listBindings(nameStr); // $hasJndiInjection
ctx.lookup(name); // $hasJndiInjection
ctx.lookupLink(name); // $hasJndiInjection
InitialContext.doLookup(name); // $hasJndiInjection
ctx.rename(name, null); // $hasJndiInjection
ctx.list(name); // $hasJndiInjection
ctx.listBindings(name); // $hasJndiInjection
}
@RequestMapping
public void testDirContextBad1(@RequestParam String nameStr) throws NamingException {
Name name = new CompoundName(nameStr, new Properties());
DirContext ctx = new InitialDirContext();
ctx.lookup(nameStr); // $hasJndiInjection
ctx.lookupLink(nameStr); // $hasJndiInjection
ctx.rename(nameStr, ""); // $hasJndiInjection
ctx.list(nameStr); // $hasJndiInjection
ctx.listBindings(nameStr); // $hasJndiInjection
ctx.lookup(name); // $hasJndiInjection
ctx.lookupLink(name); // $hasJndiInjection
ctx.rename(name, null); // $hasJndiInjection
ctx.list(name); // $hasJndiInjection
ctx.listBindings(name); // $hasJndiInjection
SearchControls searchControls = new SearchControls();
searchControls.setReturningObjFlag(true);
ctx.search(nameStr, "", searchControls); // $hasJndiInjection
ctx.search(nameStr, "", new Object[] {}, searchControls); // $hasJndiInjection
SearchControls searchControls2 = new SearchControls(1, 0, 0, null, true, false);
ctx.search(nameStr, "", searchControls2); // $hasJndiInjection
ctx.search(nameStr, "", new Object[] {}, searchControls2); // $hasJndiInjection
SearchControls searchControls3 = new SearchControls(1, 0, 0, null, false, false);
ctx.search(nameStr, "", searchControls3); // Safe
ctx.search(nameStr, "", new Object[] {}, searchControls3); // Safe
}
@RequestMapping
public void testInitialLdapContextBad1(@RequestParam String nameStr) throws NamingException {
Name name = new CompositeName(nameStr);
InitialLdapContext ctx = new InitialLdapContext();
ctx.lookup(nameStr); // $hasJndiInjection
ctx.lookupLink(nameStr); // $hasJndiInjection
ctx.rename(nameStr, ""); // $hasJndiInjection
ctx.list(nameStr); // $hasJndiInjection
ctx.listBindings(nameStr); // $hasJndiInjection
ctx.lookup(name); // $hasJndiInjection
ctx.lookupLink(name); // $hasJndiInjection
ctx.rename(name, null); // $hasJndiInjection
ctx.list(name); // $hasJndiInjection
ctx.listBindings(name); // $hasJndiInjection
}
@RequestMapping
public void testSpringJndiTemplateBad1(@RequestParam String nameStr) throws NamingException {
JndiTemplate ctx = new JndiTemplate();
ctx.lookup(nameStr); // $hasJndiInjection
ctx.lookup(nameStr, null); // $hasJndiInjection
}
@RequestMapping
public void testSpringLdapTemplateBad1(@RequestParam String nameStr) throws NamingException {
LdapTemplate ctx = new LdapTemplate();
Name name = new CompositeName().add(nameStr);
ctx.lookup(nameStr); // $hasJndiInjection
ctx.lookupContext(nameStr); // $hasJndiInjection
ctx.findByDn(name, null); // $hasJndiInjection
ctx.rename(name, null); // $hasJndiInjection
ctx.list(name); // $hasJndiInjection
ctx.listBindings(name); // $hasJndiInjection
ctx.unbind(nameStr, true); // $hasJndiInjection
ctx.search(nameStr, "", 0, true, null); // $hasJndiInjection
ctx.search(nameStr, "", 0, new String[] {}, (ContextMapper<Object>) null); // $hasJndiInjection
ctx.search(nameStr, "", 0, (ContextMapper<Object>) null); // $hasJndiInjection
ctx.search(nameStr, "", (ContextMapper<Object>) null); // $hasJndiInjection
SearchControls searchControls = new SearchControls();
searchControls.setReturningObjFlag(true);
ctx.search(nameStr, "", searchControls, (AttributesMapper<Object>) null); // $hasJndiInjection
ctx.search(nameStr, "", searchControls, (AttributesMapper<Object>) null, // $hasJndiInjection
(DirContextProcessor) null);
ctx.search(nameStr, "", searchControls, (ContextMapper<Object>) null); // $hasJndiInjection
ctx.search(nameStr, "", searchControls, (ContextMapper<Object>) null, // $hasJndiInjection
(DirContextProcessor) null);
ctx.search(nameStr, "", searchControls, (NameClassPairCallbackHandler) null); // $hasJndiInjection
ctx.search(nameStr, "", searchControls, (NameClassPairCallbackHandler) null, // $hasJndiInjection
(DirContextProcessor) null);
SearchControls searchControls2 = new SearchControls(1, 0, 0, null, true, false);
ctx.search(nameStr, "", searchControls2, (AttributesMapper<Object>) null); // $hasJndiInjection
ctx.search(nameStr, "", searchControls2, (AttributesMapper<Object>) null, // $hasJndiInjection
(DirContextProcessor) null);
ctx.search(nameStr, "", searchControls2, (ContextMapper<Object>) null); // $hasJndiInjection
ctx.search(nameStr, "", searchControls2, (ContextMapper<Object>) null, // $hasJndiInjection
(DirContextProcessor) null);
ctx.search(nameStr, "", searchControls2, (NameClassPairCallbackHandler) null); // $hasJndiInjection
ctx.search(nameStr, "", searchControls2, (NameClassPairCallbackHandler) null, // $hasJndiInjection
(DirContextProcessor) null);
SearchControls searchControls3 = new SearchControls(1, 0, 0, null, false, false);
ctx.search(nameStr, "", searchControls3, (AttributesMapper<Object>) null); // Safe
ctx.search(nameStr, "", searchControls3, (AttributesMapper<Object>) null, // Safe
(DirContextProcessor) null);
ctx.search(nameStr, "", searchControls3, (ContextMapper<Object>) null); // Safe
ctx.search(nameStr, "", searchControls3, (ContextMapper<Object>) null, // Safe
(DirContextProcessor) null);
ctx.search(nameStr, "", searchControls3, (NameClassPairCallbackHandler) null); // Safe
ctx.search(nameStr, "", searchControls3, (NameClassPairCallbackHandler) null, // Safe
(DirContextProcessor) null);
ctx.searchForObject(nameStr, "", (ContextMapper<Object>) null); // $hasJndiInjection
}
@RequestMapping
public void testShiroJndiTemplateBad1(@RequestParam String nameStr) throws NamingException {
org.apache.shiro.jndi.JndiTemplate ctx = new org.apache.shiro.jndi.JndiTemplate();
ctx.lookup(nameStr); // $hasJndiInjection
ctx.lookup(nameStr, null); // $hasJndiInjection
}
@RequestMapping
public void testJMXServiceUrlBad1(@RequestParam String urlStr) throws IOException {
JMXConnectorFactory.connect(new JMXServiceURL(urlStr)); // $hasJndiInjection
JMXServiceURL url = new JMXServiceURL(urlStr);
JMXConnector connector = JMXConnectorFactory.newJMXConnector(url, null);
connector.connect(); // $hasJndiInjection
}
@RequestMapping
public void testEnvBad1(@RequestParam String urlStr) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.PROVIDER_URL, urlStr); // $hasJndiInjection
new InitialContext(env);
}
@RequestMapping
public void testEnvBad2(@RequestParam String urlStr) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put("java.naming.provider.url", urlStr); // $hasJndiInjection
new InitialDirContext(env);
}
@RequestMapping
public void testSpringJndiTemplatePropertiesBad1(@RequestParam String urlStr)
throws NamingException {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
props.put(Context.PROVIDER_URL, urlStr); // $hasJndiInjection
new JndiTemplate(props);
}
@RequestMapping
public void testSpringJndiTemplatePropertiesBad2(@RequestParam String urlStr)
throws NamingException {
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
props.setProperty("java.naming.provider.url", urlStr); // $hasJndiInjection
new JndiTemplate(props);
}
@RequestMapping
public void testSpringJndiTemplatePropertiesBad3(@RequestParam String urlStr)
throws NamingException {
Properties props = new Properties();
props.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.rmi.registry.RegistryContextFactory");
props.setProperty("java.naming.provider.url", urlStr); // $hasJndiInjection
JndiTemplate template = new JndiTemplate();
template.setEnvironment(props);
}
@RequestMapping
public void testSpringLdapTemplateOk1(@RequestParam String nameStr) throws NamingException {
LdapTemplate ctx = new LdapTemplate();
ctx.unbind(nameStr); // Safe
ctx.unbind(nameStr, false); // Safe
ctx.search(nameStr, "", 0, false, null); // Safe
ctx.search(nameStr, "", new SearchControls(), (NameClassPairCallbackHandler) new Object()); // Safe
ctx.search(nameStr, "", new SearchControls(), (NameClassPairCallbackHandler) new Object(), // Safe
null);
ctx.search(nameStr, "", (NameClassPairCallbackHandler) new Object()); // Safe
ctx.search(nameStr, "", 0, new String[] {}, (AttributesMapper<Object>) new Object()); // Safe
ctx.search(nameStr, "", 0, (AttributesMapper<Object>) new Object()); // Safe
ctx.search(nameStr, "", (AttributesMapper) new Object()); // Safe
ctx.search(nameStr, "", new SearchControls(), (ContextMapper) new Object()); // Safe
ctx.search(nameStr, "", new SearchControls(), (AttributesMapper) new Object()); // Safe
ctx.search(nameStr, "", new SearchControls(), (ContextMapper) new Object(), null); // Safe
ctx.search(nameStr, "", new SearchControls(), (AttributesMapper) new Object(), null); // Safe
ctx.searchForObject(nameStr, "", new SearchControls(), (ContextMapper) new Object()); // Safe
}
@RequestMapping
public void testEnvOk1(@RequestParam String urlStr) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put(Context.SECURITY_PRINCIPAL, urlStr); // Safe
new InitialContext(env);
}
@RequestMapping
public void testEnvOk2(@RequestParam String urlStr) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");
env.put("java.naming.security.principal", urlStr); // Safe
new InitialContext(env);
}
}

View File

@@ -0,0 +1,20 @@
import java
import semmle.code.java.security.JndiInjectionQuery
import TestUtilities.InlineExpectationsTest
class HasJndiInjectionTest extends InlineExpectationsTest {
HasJndiInjectionTest() { this = "HasJndiInjectionTest" }
override string getARelevantTag() { result = "hasJndiInjection" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasJndiInjection" and
exists(DataFlow::Node src, DataFlow::Node sink, JndiInjectionFlowConfig conf |
conf.hasFlow(src, sink)
|
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/shiro-core-1.5.2:${testdir}/../../../stubs/spring-ldap-2.3.2

View File

@@ -37,18 +37,18 @@ public class JaxXSS {
else {
if(chainDirectly) {
if(contentTypeFirst)
return builder.type(MediaType.APPLICATION_JSON).entity(userControlled).build(); // $SPURIOUS: xss
return builder.type(MediaType.APPLICATION_JSON).entity(userControlled).build();
else
return builder.entity(userControlled).type(MediaType.APPLICATION_JSON).build(); // $SPURIOUS: xss
return builder.entity(userControlled).type(MediaType.APPLICATION_JSON).build();
}
else {
if(contentTypeFirst) {
Response.ResponseBuilder builder2 = builder.type(MediaType.APPLICATION_JSON);
return builder2.entity(userControlled).build(); // $SPURIOUS: xss
return builder2.entity(userControlled).build();
}
else {
Response.ResponseBuilder builder2 = builder.entity(userControlled);
return builder2.type(MediaType.APPLICATION_JSON).build(); // $SPURIOUS: xss
return builder2.type(MediaType.APPLICATION_JSON).build();
}
}
}
@@ -63,39 +63,43 @@ public class JaxXSS {
if(safeContentType) {
if(route == 0) {
// via ok, as a string literal:
return Response.ok(userControlled, "application/json").build(); // $SPURIOUS: xss
return Response.ok(userControlled, "application/json").build();
}
else if(route == 1) {
// via ok, as a string constant:
return Response.ok(userControlled, MediaType.APPLICATION_JSON).build(); // $SPURIOUS: xss
return Response.ok(userControlled, MediaType.APPLICATION_JSON).build();
}
else if(route == 2) {
// via ok, as a MediaType constant:
return Response.ok(userControlled, MediaType.APPLICATION_JSON_TYPE).build(); // $SPURIOUS: xss
return Response.ok(userControlled, MediaType.APPLICATION_JSON_TYPE).build();
}
else if(route == 3) {
// via ok, as a Variant, via constructor:
return Response.ok(userControlled, new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).build(); // $SPURIOUS: xss
return Response.ok(userControlled, new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).build();
}
else if(route == 4) {
// via ok, as a Variant, via static method:
return Response.ok(userControlled, Variant.mediaTypes(MediaType.APPLICATION_JSON_TYPE).build().get(0)).build(); // $SPURIOUS: xss
return Response.ok(userControlled, Variant.mediaTypes(MediaType.APPLICATION_JSON_TYPE).build().get(0)).build();
}
else if(route == -4) {
// via ok, as a Variant, via static method (testing multiple media types):
return Response.ok(userControlled, Variant.mediaTypes(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_OCTET_STREAM_TYPE).build().get(0)).build();
}
else if(route == 5) {
// via ok, as a Variant, via instance method:
return Response.ok(userControlled, Variant.languages(Locale.UK).mediaTypes(MediaType.APPLICATION_JSON_TYPE).build().get(0)).build(); // $SPURIOUS: xss
return Response.ok(userControlled, Variant.languages(Locale.UK).mediaTypes(MediaType.APPLICATION_JSON_TYPE).build().get(0)).build();
}
else if(route == 6) {
// via builder variant, before entity:
return Response.ok().variant(new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).entity(userControlled).build(); // $SPURIOUS: xss
return Response.ok().variant(new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).entity(userControlled).build();
}
else if(route == 7) {
// via builder variant, after entity:
return Response.ok().entity(userControlled).variant(new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).build(); // $SPURIOUS: xss
return Response.ok().entity(userControlled).variant(new Variant(MediaType.APPLICATION_JSON_TYPE, "language", "encoding")).build();
}
else if(route == 8) {
// provide entity via ok, then content-type via builder:
return Response.ok(userControlled).type(MediaType.APPLICATION_JSON_TYPE).build(); // $SPURIOUS: xss
return Response.ok(userControlled).type(MediaType.APPLICATION_JSON_TYPE).build();
}
}
else {
@@ -158,27 +162,27 @@ public class JaxXSS {
@GET @Produces(MediaType.TEXT_HTML)
public static Response methodContentTypeUnsafe(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
return Response.ok(userControlled).build(); // $xss
}
@POST @Produces(MediaType.TEXT_HTML)
public static Response methodContentTypeUnsafePost(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
return Response.ok(userControlled).build(); // $xss
}
@GET @Produces("text/html")
public static Response methodContentTypeUnsafeStringLiteral(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
return Response.ok(userControlled).build(); // $xss
}
@GET @Produces({MediaType.TEXT_HTML, MediaType.APPLICATION_JSON})
public static Response methodContentTypeMaybeSafe(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
return Response.ok(userControlled).build(); // $xss
}
@GET @Produces(MediaType.APPLICATION_JSON)
public static Response methodContentTypeSafeOverriddenWithUnsafe(String userControlled) {
return Response.ok().type(MediaType.TEXT_HTML).entity(userControlled).build(); // $MISSING: xss
return Response.ok().type(MediaType.TEXT_HTML).entity(userControlled).build(); // $xss
}
@GET @Produces(MediaType.TEXT_HTML)
@@ -201,12 +205,12 @@ public class JaxXSS {
@GET @Produces({"text/html"})
public Response overridesWithUnsafe(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
return Response.ok(userControlled).build(); // $xss
}
@GET
public Response overridesWithUnsafe2(String userControlled) {
return Response.ok().type(MediaType.TEXT_HTML).entity(userControlled).build(); // $MISSING: xss
return Response.ok().type(MediaType.TEXT_HTML).entity(userControlled).build(); // $xss
}
}
@@ -215,12 +219,12 @@ public class JaxXSS {
public static class ClassContentTypeUnsafe {
@GET
public Response test(String userControlled) {
return Response.ok(userControlled).build(); // $MISSING: xss
return Response.ok(userControlled).build(); // $xss
}
@GET
public String testDirectReturn(String userControlled) {
return userControlled; // $MISSING: xss
return userControlled; // $xss
}
@GET @Produces({"application/json"})

View File

@@ -5,6 +5,7 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.util.HtmlUtils;
import java.util.Optional;
@@ -157,4 +158,9 @@ public class SpringXSS {
return userControlled; // $xss
}
}
@GetMapping(value = "/abc")
public static String sanitizedString(String userControlled) {
return HtmlUtils.htmlEscape(userControlled);
}
}

View File

@@ -0,0 +1,55 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringReader;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
public class GroovyClassLoaderTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// "groovy.lang;GroovyClassLoader;false;parseClass;(GroovyCodeSource);;Argument[0];groovy",
{
String script = request.getParameter("script");
final GroovyClassLoader classLoader = new GroovyClassLoader();
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
classLoader.parseClass(gcs); // $hasGroovyInjection
}
// "groovy.lang;GroovyClassLoader;false;parseClass;(GroovyCodeSource,boolean);;Argument[0];groovy",
{
String script = request.getParameter("script");
final GroovyClassLoader classLoader = new GroovyClassLoader();
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
classLoader.parseClass(gcs, true); // $hasGroovyInjection
}
// "groovy.lang;GroovyClassLoader;false;parseClass;(InputStream,String);;Argument[0];groovy",
{
String script = request.getParameter("script");
final GroovyClassLoader classLoader = new GroovyClassLoader();
classLoader.parseClass(new ByteArrayInputStream(script.getBytes()), "test"); // $hasGroovyInjection
}
// "groovy.lang;GroovyClassLoader;false;parseClass;(Reader,String);;Argument[0];groovy",
{
String script = request.getParameter("script");
final GroovyClassLoader classLoader = new GroovyClassLoader();
classLoader.parseClass(new StringReader(script), "test"); // $hasGroovyInjection
}
// "groovy.lang;GroovyClassLoader;false;parseClass;(String);;Argument[0];groovy",
{
String script = request.getParameter("script");
final GroovyClassLoader classLoader = new GroovyClassLoader();
classLoader.parseClass(script); // $hasGroovyInjection
}
// "groovy.lang;GroovyClassLoader;false;parseClass;(String,String);;Argument[0];groovy",
{
String script = request.getParameter("script");
final GroovyClassLoader classLoader = new GroovyClassLoader();
classLoader.parseClass(script, "test"); // $hasGroovyInjection
}
}
}

View File

@@ -0,0 +1,97 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.codehaus.groovy.control.CompilationUnit;
import org.codehaus.groovy.control.SourceUnit;
import org.codehaus.groovy.control.io.ReaderSource;
import org.codehaus.groovy.control.io.StringReaderSource;
import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
import org.codehaus.groovy.tools.javac.JavaStubCompilationUnit;
public class GroovyCompilationUnitTest extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// "org.codehaus.groovy.control;CompilationUnit;false;compile;;;Argument[-1];groovy"
{
CompilationUnit cu = new CompilationUnit();
cu.addSource("test", request.getParameter("source"));
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
cu.addSource(request.getParameter("source"), "safe");
cu.compile(); // Safe
}
{
CompilationUnit cu = new CompilationUnit();
cu.addSource("test",
new ByteArrayInputStream(request.getParameter("source").getBytes()));
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
cu.addSource(new URL(request.getParameter("source")));
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
SourceUnit su =
new SourceUnit("test", request.getParameter("source"), null, null, null);
cu.addSource(su);
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
SourceUnit su =
new SourceUnit(request.getParameter("source"), "safe", null, null, null);
cu.addSource(su);
cu.compile(); // Safe
}
{
CompilationUnit cu = new CompilationUnit();
StringReaderSource rs = new StringReaderSource(request.getParameter("source"), null);
SourceUnit su = new SourceUnit("test", rs, null, null, null);
cu.addSource(su);
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
SourceUnit su =
new SourceUnit(new URL(request.getParameter("source")), null, null, null);
cu.addSource(su);
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
SourceUnit su = SourceUnit.create("test", request.getParameter("source"));
cu.addSource(su);
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
SourceUnit su = SourceUnit.create("test", request.getParameter("source"), 0);
cu.addSource(su);
cu.compile(); // $hasGroovyInjection
}
{
CompilationUnit cu = new CompilationUnit();
SourceUnit su = SourceUnit.create(request.getParameter("source"), "safe", 0);
cu.addSource(su);
cu.compile(); // Safe
}
{
JavaAwareCompilationUnit cu = new JavaAwareCompilationUnit();
cu.addSource("test", request.getParameter("source"));
cu.compile(); // $hasGroovyInjection
}
{
JavaStubCompilationUnit cu = new JavaStubCompilationUnit(null, null);
cu.addSource("test", request.getParameter("source"));
cu.compile(); // Safe - JavaStubCompilationUnit only creates stubs
}
}
}

View File

@@ -0,0 +1,40 @@
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import groovy.util.Eval;
public class GroovyEvalTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// "groovy.util;Eval;false;me;(String);;Argument[0];groovy",
{
String script = request.getParameter("script");
Eval.me(script); // $hasGroovyInjection
}
// "groovy.util;Eval;false;me;(String,Object,String);;Argument[2];groovy",
{
String script = request.getParameter("script");
Eval.me("test", "result", script); // $hasGroovyInjection
}
// "groovy.util;Eval;false;x;(Object,String);;Argument[1];groovy",
{
String script = request.getParameter("script");
Eval.x("result2", script); // $hasGroovyInjection
}
// "groovy.util;Eval;false;xy;(Object,Object,String);;Argument[2];groovy",
{
String script = request.getParameter("script");
Eval.xy("result3", "result4", script); // $hasGroovyInjection
}
// "groovy.util;Eval;false;xyz;(Object,Object,Object,String);;Argument[3];groovy",
{
String script = request.getParameter("script");
Eval.xyz("result3", "result4", "aaa", script); // $hasGroovyInjection
}
}
}

View File

@@ -0,0 +1,22 @@
import java
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.dataflow.FlowSources
import semmle.code.java.security.GroovyInjectionQuery
import TestUtilities.InlineExpectationsTest
class HasGroovyInjectionTest extends InlineExpectationsTest {
HasGroovyInjectionTest() { this = "HasGroovyInjectionTest" }
override string getARelevantTag() { result = "hasGroovyInjection" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasGroovyInjection" and
exists(DataFlow::Node src, DataFlow::Node sink, GroovyInjectionConfig conf |
conf.hasFlow(src, sink)
|
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -0,0 +1,154 @@
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyShell;
public class GroovyShellTest extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// "groovy.lang;GroovyShell;false;evaluate;(GroovyCodeSource);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
shell.evaluate(gcs); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;evaluate;(Reader);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
Reader reader = new StringReader(script);
shell.evaluate(reader); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;evaluate;(Reader,String);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
Reader reader = new StringReader(script);
shell.evaluate(reader, "_"); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;evaluate;(String);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.evaluate(script); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;evaluate;(String,String);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.evaluate(script, "test"); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;evaluate;(String,String,String);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.evaluate(script, "test", "test2"); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;evaluate;(URI);;Argument[0];groovy",
try {
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.parse(new URI(script)); // $hasGroovyInjection
} catch (URISyntaxException e) {
}
// "groovy.lang;GroovyShell;false;parse;(Reader);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
Reader reader = new StringReader(script);
shell.parse(reader); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;parse;(Reader,String);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
Reader reader = new StringReader(script);
shell.parse(reader, "_"); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;parse;(String);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.parse(script); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;parse;(String,String);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.parse(script, "_"); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;parse;(URI);;Argument[0];groovy",
try {
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.parse(new URI(script)); // $hasGroovyInjection
} catch (URISyntaxException e) {
}
// "groovy.lang;GroovyShell;false;run;(GroovyCodeSource,String[]);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
shell.run(gcs, new String[] {}); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;run;(GroovyCodeSource,List);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
GroovyCodeSource gcs = new GroovyCodeSource(script, "test", "Test");
shell.run(gcs, new ArrayList<String>()); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;run;(Reader,String,String[]);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
Reader reader = new StringReader(script);
shell.run(reader, "test", new String[] {}); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;run;(Reader,String,List);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
Reader reader = new StringReader(script);
shell.run(reader, "test", new ArrayList<String>()); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;run;(String,String,String[]);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.run(script, "_", new String[] {}); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;run;(String,String,List);;Argument[0];groovy",
{
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.run(script, "_", new ArrayList<String>()); // $hasGroovyInjection
}
// "groovy.lang;GroovyShell;false;run;(URI,String[]);;Argument[0];groovy",
try {
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.run(new URI(script), new String[] {}); // $hasGroovyInjection
} catch (URISyntaxException e) {
}
// "groovy.lang;GroovyShell;false;run;(URI,List);;Argument[0];groovy",
try {
GroovyShell shell = new GroovyShell();
String script = request.getParameter("script");
shell.run(new URI(script), new ArrayList<String>()); // $hasGroovyInjection
} catch (URISyntaxException e) {
}
}
}

View File

@@ -1 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/validation-api-2.0.1.Final:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../stubs/apache-commons-logging-1.2:${testdir}/../../../stubs/mvel2-2.4.7:${testdir}/../../../stubs/scriptengine:${testdir}/../../../stubs/jsr223-api
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/validation-api-2.0.1.Final:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/apache-commons-jexl-2.1.1:${testdir}/../../../stubs/apache-commons-jexl-3.1:${testdir}/../../../stubs/apache-commons-logging-1.2:${testdir}/../../../stubs/mvel2-2.4.7:${testdir}/../../../stubs/groovy-all-3.0.7:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/scriptengine:${testdir}/../../../stubs/jsr223-api

View File

@@ -1,10 +1,15 @@
edges
| HttpsUrlsTest.java:23:23:23:31 | "http://" : String | HttpsUrlsTest.java:24:21:24:56 | ... + ... : String |
| HttpsUrlsTest.java:23:23:23:31 | "http://" : String | HttpsUrlsTest.java:28:50:28:50 | u |
| HttpsUrlsTest.java:24:13:24:57 | new URL(...) : URL | HttpsUrlsTest.java:28:50:28:50 | u |
| HttpsUrlsTest.java:24:21:24:56 | ... + ... : String | HttpsUrlsTest.java:24:13:24:57 | new URL(...) : URL |
| HttpsUrlsTest.java:36:23:36:28 | "http" : String | HttpsUrlsTest.java:41:50:41:50 | u |
| HttpsUrlsTest.java:49:23:49:31 | "http://" : String | HttpsUrlsTest.java:55:50:55:50 | u |
| HttpsUrlsTest.java:87:23:87:28 | "http" : String | HttpsUrlsTest.java:92:50:92:50 | u |
nodes
| HttpsUrlsTest.java:23:23:23:31 | "http://" : String | semmle.label | "http://" : String |
| HttpsUrlsTest.java:24:13:24:57 | new URL(...) : URL | semmle.label | new URL(...) : URL |
| HttpsUrlsTest.java:24:21:24:56 | ... + ... : String | semmle.label | ... + ... : String |
| HttpsUrlsTest.java:28:50:28:50 | u | semmle.label | u |
| HttpsUrlsTest.java:36:23:36:28 | "http" : String | semmle.label | "http" : String |
| HttpsUrlsTest.java:41:50:41:50 | u | semmle.label | u |

View File

@@ -0,0 +1,141 @@
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwt;
import io.jsonwebtoken.JwtHandlerAdapter;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.impl.DefaultJwtParser;
import io.jsonwebtoken.impl.DefaultJwtParserBuilder;
public class MissingJWTSignatureCheckTest {
private JwtParser getASignedParser() {
return Jwts.parser().setSigningKey("someBase64EncodedKey");
}
private JwtParser getASignedParserFromParserBuilder() {
return Jwts.parserBuilder().setSigningKey("someBase64EncodedKey").build();
}
private JwtParser getASignedNewParser() {
return new DefaultJwtParser().setSigningKey("someBase64EncodedKey");
}
private void callSignedParsers() {
JwtParser parser1 = getASignedParser();
badJwtOnParserBuilder(parser1, "");
badJwtHandlerOnParserBuilder(parser1, "");
goodJwtOnParserBuilder(parser1, "");
goodJwtHandler(parser1, "");
JwtParser parser2 = getASignedParserFromParserBuilder();
badJwtOnParserBuilder(parser2, "");
badJwtHandlerOnParserBuilder(parser2, "");
goodJwtOnParserBuilder(parser2, "");
goodJwtHandler(parser2, "");
JwtParser parser3 = getASignedNewParser();
badJwtOnParserBuilder(parser3, "");
badJwtHandlerOnParserBuilder(parser3, "");
goodJwtOnParserBuilder(parser3, "");
goodJwtHandler(parser3, "");
}
private JwtParser getAnUnsignedParser() {
return Jwts.parser();
}
private JwtParser getAnUnsignedParserFromParserBuilder() {
return Jwts.parserBuilder().build();
}
private JwtParser getAnUnsignedNewParser() {
return new DefaultJwtParser();
}
private void callUnsignedParsers() {
JwtParser parser1 = getAnUnsignedParser();
badJwtOnParserBuilder(parser1, "");
badJwtHandlerOnParserBuilder(parser1, "");
goodJwtOnParserBuilder(parser1, "");
goodJwtHandler(parser1, "");
JwtParser parser2 = getAnUnsignedParserFromParserBuilder();
badJwtOnParserBuilder(parser2, "");
badJwtHandlerOnParserBuilder(parser2, "");
goodJwtOnParserBuilder(parser2, "");
goodJwtHandler(parser2, "");
JwtParser parser3 = getAnUnsignedNewParser();
badJwtOnParserBuilder(parser3, "");
badJwtHandlerOnParserBuilder(parser3, "");
goodJwtOnParserBuilder(parser3, "");
goodJwtHandler(parser3, "");
}
private void signParserAfterParseCall() {
JwtParser parser = getAnUnsignedParser();
parser.parse(""); // Safe
parser.setSigningKey("someBase64EncodedKey");
}
private void badJwtOnParserBuilder(JwtParser parser, String token) {
parser.parse(token); // $hasMissingJwtSignatureCheck
}
private void badJwtHandlerOnParserBuilder(JwtParser parser, String token) {
parser.parse(token, new JwtHandlerAdapter<Jwt<Header, String>>() { // $hasMissingJwtSignatureCheck
@Override
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
return jwt;
}
});
}
private void goodJwtOnParserBuilder(JwtParser parser, String token) {
parser.parseClaimsJws(token) // Safe
.getBody();
}
private void goodJwtHandler(JwtParser parser, String token) {
parser.parse(token, new JwtHandlerAdapter<Jws<String>>() { // Safe
@Override
public Jws<String> onPlaintextJws(Jws<String> jws) {
return jws;
}
});
}
private void badJwtOnParserBuilder(String token) {
Jwts.parserBuilder().setSigningKey("someBase64EncodedKey").build().parse(token); // $hasMissingJwtSignatureCheck
}
private void badJwtOnDefaultParserBuilder(String token) {
new DefaultJwtParserBuilder().setSigningKey("someBase64EncodedKey").build().parse(token); // $hasMissingJwtSignatureCheck
}
private void badJwtHandlerOnParser(String token) {
Jwts.parser().setSigningKey("someBase64EncodedKey").parse(token, // $hasMissingJwtSignatureCheck
new JwtHandlerAdapter<Jwt<Header, String>>() {
@Override
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
return jwt;
}
});
}
private void goodJwtOnParser(String token) {
Jwts.parser().setSigningKey("someBase64EncodedKey").parseClaimsJws(token) // Safe
.getBody();
}
private void goodJwtHandlerOnParserBuilder(String token) {
Jwts.parserBuilder().setSigningKey("someBase64EncodedKey").build().parse(token, // Safe
new JwtHandlerAdapter<Jws<String>>() {
@Override
public Jws<String> onPlaintextJws(Jws<String> jws) {
return jws;
}
});
}
}

View File

@@ -0,0 +1,20 @@
import java
import semmle.code.java.security.MissingJWTSignatureCheckQuery
import TestUtilities.InlineExpectationsTest
class HasMissingJwtSignatureCheckTest extends InlineExpectationsTest {
HasMissingJwtSignatureCheckTest() { this = "HasMissingJwtSignatureCheckTest" }
override string getARelevantTag() { result = "hasMissingJwtSignatureCheck" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasMissingJwtSignatureCheck" and
exists(DataFlow::Node source, DataFlow::Node sink, MissingJwtSignatureCheckConf conf |
conf.hasFlow(source, sink)
|
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -0,0 +1 @@
semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/jwtk-jjwt-0.11.2

View File

@@ -0,0 +1,121 @@
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.json.JSONObject;
import org.jabsorb.JSONSerializer;
import org.jabsorb.serializer.SerializerState;
import org.jabsorb.serializer.ObjectMatch;
import com.example.User;
import com.thirdparty.Person;
public class JabsorbServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
// GOOD: final class type specified
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
try {
Object jsonObject = new JSONObject(json);
JSONSerializer serializer = new JSONSerializer();
serializer.registerDefaultSerializers();
serializer.setMarshallClassHints(true);
serializer.setMarshallNullAttributes(true);
SerializerState state = new SerializerState();
User user = (User) serializer.unmarshall(state, User.class, jsonObject);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
// GOOD: concrete class type specified even if it has vulnerable subclasses
public void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
try {
Object jsonObject = new JSONObject(json);
JSONSerializer serializer = new JSONSerializer();
serializer.registerDefaultSerializers();
serializer.setMarshallClassHints(true);
serializer.setMarshallNullAttributes(true);
SerializerState state = new SerializerState();
Person person = (Person) serializer.unmarshall(state, Person.class, jsonObject);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
@Override
// GOOD: try unmarshall but doesn't actually marshall the object
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
try {
Object jsonObject = new JSONObject(json);
JSONSerializer serializer = new JSONSerializer();
serializer.registerDefaultSerializers();
serializer.setMarshallClassHints(true);
serializer.setMarshallNullAttributes(true);
SerializerState state = new SerializerState();
ObjectMatch objMatch = serializer.tryUnmarshall(state, Class.forName(clazz), jsonObject);
User obj = new User();
boolean result = objMatch.equals(obj);
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
@Override
// BAD: allow class name to be controlled by remote source
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
try {
Object jsonObject = new JSONObject(json);
JSONSerializer serializer = new JSONSerializer();
serializer.registerDefaultSerializers();
serializer.setMarshallClassHints(true);
serializer.setMarshallNullAttributes(true);
SerializerState state = new SerializerState();
User user = (User) serializer.unmarshall(state, Class.forName(clazz), jsonObject); // $unsafeDeserialization
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
// BAD: allow explicit class type controlled by remote source in the format of "json={\"javaClass\":\"com.thirdparty.Attacker\", ...}"
public void doPut2(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
try {
JSONSerializer serializer = new JSONSerializer();
serializer.registerDefaultSerializers();
User user = (User) serializer.fromJSON(json); // $unsafeDeserialization
} catch (Exception e) {
throw new IOException(e.getMessage());
}
}
}

View File

@@ -0,0 +1,102 @@
import java.io.IOException;
import java.io.Reader;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jodd.json.JsonParser;
import com.example.User;
import com.thirdparty.Person;
public class JoddJsonServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
// GOOD: class type specified (despite a dangerous configuration)
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
JsonParser parser = new JsonParser();
parser.setClassMetadataName("class");
Person person = parser.parse(json, Person.class);
}
@Override
// BAD: dangerously configured parser with no class restriction passed to `parse`,
// using a few different possible call sequences.
public void doHead(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
int callOrder;
try {
callOrder = Integer.parseInt(req.getParameter("callOrder"));
}
catch(NumberFormatException e) {
throw new RuntimeException(e);
}
JsonParser parser = new JsonParser();
if(callOrder == 0) {
parser.setClassMetadataName("class");
User obj = parser.parse(json, null); // $unsafeDeserialization
} else if(callOrder == 1) {
parser.setClassMetadataName("class").parse(json, null); // $unsafeDeserialization
} else if(callOrder == 2) {
parser.setClassMetadataName("class").lazy(true).parse(json, null); // $unsafeDeserialization
} else if(callOrder == 3) {
parser.withClassMetadata(true).lazy(true).parse(json, null); // $unsafeDeserialization
}
}
@Override
// BAD: allow class name to be controlled by remote source
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
try {
JsonParser parser = new JsonParser();
Object obj = parser.parse(json, Class.forName(clazz)); // $unsafeDeserialization
} catch (ClassNotFoundException cne) {
throw new IOException(cne.getMessage());
}
}
@Override
// GOOD: dangerously configured parser is ameliorated by setting a list of allowed classes, using various call orders,
// or by explicitly disabling the class metadata option.
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String json = req.getParameter("json");
String clazz = req.getParameter("class");
int callOrder;
try {
callOrder = Integer.parseInt(req.getParameter("callOrder"));
}
catch(NumberFormatException e) {
throw new RuntimeException(e);
}
JsonParser parser = new JsonParser();
if(callOrder == 0) {
parser.setClassMetadataName("class");
parser.allowClass("example.Class");
User obj = parser.parse(json, null);
} else if(callOrder == 1) {
parser.allowClass("example.Class");
parser.setClassMetadataName("class");
User obj = parser.parse(json, null);
} else if(callOrder == 2) {
parser.setClassMetadataName("class").allowClass("example.Class").parse(json, null);
} else if(callOrder == 3) {
parser.allowClass("example.Class").setClassMetadataName("class").parse(json, null);
} else if(callOrder == 4) {
parser.setClassMetadataName("class").withClassMetadata(false).parse(json, null);
} else if(callOrder == 5) {
parser.withClassMetadata(true).setClassMetadataName(null).parse(json, null);
}
}
}

View File

@@ -0,0 +1,29 @@
package com.example;
public final class User {
private String uid;
private String name;
public User() {
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "User[ name = "+name+", uid: "+uid+ "]";
}
}

View File

@@ -0,0 +1,29 @@
package com.thirdparty;
public class Person {
private int snum;
private String name;
public Person() {
}
public int getSnum() {
return snum;
}
public void setSnum(int snum) {
this.snum = snum;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Person[ name = "+name+", snum: "+snum+ "]";
}
}

View File

@@ -1 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.10
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/snakeyaml-1.21:${testdir}/../../../stubs/xstream-1.4.10:${testdir}/../../../stubs/kryo-4.0.2:${testdir}/../../../stubs/jsr311-api-1.1.1:${testdir}/../../../stubs/fastjson-1.2.74:${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/servlet-api-2.4:${testdir}/../../../stubs/jyaml-1.3:${testdir}/../../../stubs/json-io-4.10.0:${testdir}/../../../stubs/yamlbeans-1.09:${testdir}/../../../stubs/hessian-4.0.38:${testdir}/../../../stubs/castor-1.4.1:${testdir}/../../../stubs/jackson-databind-2.12:${testdir}/../../../stubs/jackson-core-2.12:${testdir}/../../../stubs/jabsorb-1.3.2:${testdir}/../../../stubs/json-java-20210307:${testdir}/../../../stubs/joddjson-6.0.3

View File

@@ -0,0 +1,51 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app"
android:installLocation="auto"
android:versionCode="1"
android:versionName="0.1" >
<uses-permission android:name="android.permission.INTERNET" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".UnsafeAndroidAccess"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".UnsafeActivity1" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
</intent-filter>
</activity>
<activity android:name=".UnsafeActivity2">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
</intent-filter>
</activity>
<activity android:name=".SafeActivity1" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
</intent-filter>
</activity>
<activity android:name=".SafeActivity2" android:exported="false" />
<activity android:name=".SafeActivity3" />
<activity android:name=".UnsafeActivity3" android:exported="true" />
<activity android:name=".UnsafeActivity4" android:exported="true" />
<receiver android:name=".UnsafeAndroidBroadcastReceiver" android:exported="true" />
</application>
</manifest>

View File

@@ -0,0 +1,24 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/** A utility program for getting intent extra information from Android activity */
public class IntentUtils {
/** Get intent extra */
public static String getIntentUrl(Activity a) {
String thisUrl = a.getIntent().getStringExtra("url");
return thisUrl;
}
/** Get bundle extra */
public static String getBundleUrl(Activity a) {
String thisUrl = a.getIntent().getExtras().getString("url");
return thisUrl;
}
}

View File

@@ -0,0 +1,36 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class SafeActivity1 extends Activity {
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from bundle extras.
// The Activity is explicitly not exported, even though it has an intent-filter.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl); // Safe
}
}

View File

@@ -0,0 +1,36 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class SafeActivity2 extends Activity {
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from bundle extras.
// The Activity is explicitly not exported.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl); // Safe
}
}

View File

@@ -0,0 +1,36 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class SafeActivity3 extends Activity {
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from bundle extras.
// The Activity is implicitly not exported.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl); // Safe
}
}

View File

@@ -0,0 +1,36 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class UnsafeActivity1 extends Activity {
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from bundle extras.
// The Activity is exported and has an intent-filter.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
}

View File

@@ -0,0 +1,36 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class UnsafeActivity2 extends Activity {
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from bundle extras.
// The Activity is implicitly exported because it has an intent-filter.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
}

View File

@@ -0,0 +1,36 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class UnsafeActivity3 extends Activity {
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from bundle extras.
// The Activity is explicitly exported.
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
}

View File

@@ -0,0 +1,44 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class UnsafeActivity4 extends Activity {
/*
* Test onCreate with both JavaScript and cross-origin resource access enabled while taking
* remote user inputs from bundle extras.
*
* The Activity is explicitly exported.
*
* Note this case of invoking a utility method that takes an Activity and then calls
* `a.getIntent().getStringExtra(...)` is not yet detected thus is beyond what the query is
* capable of.
*/
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = IntentUtils.getIntentUrl(this);
thisUrl = IntentUtils.getBundleUrl(this);
wv.loadUrl(thisUrl); // $ MISSING: hasUnsafeAndroidAccess
}
}

View File

@@ -0,0 +1,151 @@
package com.example.app;
import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
// The Activity is implicitly exported because it has an intent-filter.
public class UnsafeAndroidAccess extends Activity {
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from bundle extras
public void testOnCreate1(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getExtras().getString("url");
wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs from string extra
public void testOnCreate2(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getStringExtra("url");
wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
// Test onCreate with both JavaScript and cross-origin resource access disabled by default while
// taking remote user inputs
public void testOnCreate3(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getStringExtra("url");
wv.loadUrl(thisUrl); // Safe
}
// Test onCreate with JavaScript enabled but cross-origin resource access disabled while taking
// remote user inputs
public void testOnCreate4(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getStringExtra("url");
wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
// Test onCreate with both JavaScript and cross-origin resource access enabled while not taking
// remote user inputs
public void testOnCreate5(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
wv.loadUrl("https://www.mycorp.com"); // Safe
}
// Test onCreate with both JavaScript and cross-origin resource access enabled while taking
// remote user inputs and concatenating them to a safe URL.
public void testOnCreate6(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(-1);
WebView wv = (WebView) findViewById(-1);
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
String thisUrl = getIntent().getStringExtra("url");
wv.loadUrl("https://www.mycorp.com/" + thisUrl); // Safe
}
}

View File

@@ -0,0 +1,20 @@
import java
import semmle.code.java.security.UnsafeAndroidAccessQuery
import TestUtilities.InlineExpectationsTest
class UnsafeAndroidAccessTest extends InlineExpectationsTest {
UnsafeAndroidAccessTest() { this = "HasUnsafeAndroidAccess" }
override string getARelevantTag() { result = "hasUnsafeAndroidAccess" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasUnsafeAndroidAccess" and
exists(DataFlow::Node src, DataFlow::Node sink, FetchUntrustedResourceConfiguration conf |
conf.hasFlow(src, sink)
|
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -0,0 +1,35 @@
package com.example.app;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.BroadcastReceiver;
import android.os.Bundle;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class UnsafeAndroidBroadcastReceiver extends BroadcastReceiver {
// Test onCreate with JavaScript enabled but cross-origin resource access disabled while taking
// remote user inputs
@Override
public void onReceive(Context context, Intent intent) {
String thisUrl = intent.getStringExtra("url");
WebView wv = null;
WebSettings webSettings = wv.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccessFromFileURLs(true);
wv.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
wv.loadUrl(thisUrl); // $hasUnsafeAndroidAccess
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/android

View File

@@ -0,0 +1,73 @@
import ognl.Node;
import ognl.Ognl;
import ognl.enhance.ExpressionAccessor;
import java.util.HashMap;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class OgnlInjection {
@RequestMapping
public void testOgnlParseExpression(@RequestParam String expr) throws Exception {
Object tree = Ognl.parseExpression(expr);
Ognl.getValue(tree, new HashMap<>(), new Object()); // $hasOgnlInjection
Ognl.setValue(tree, new HashMap<>(), new Object()); // $hasOgnlInjection
Node node = (Node) tree;
node.getValue(null, new Object()); // $hasOgnlInjection
node.setValue(null, new Object(), new Object()); // $hasOgnlInjection
}
@RequestMapping
public void testOgnlCompileExpression(@RequestParam String expr) throws Exception {
Node tree = Ognl.compileExpression(null, new Object(), expr);
Ognl.getValue(tree, new HashMap<>(), new Object()); // $hasOgnlInjection
Ognl.setValue(tree, new HashMap<>(), new Object()); // $hasOgnlInjection
tree.getValue(null, new Object()); // $hasOgnlInjection
tree.setValue(null, new Object(), new Object()); // $hasOgnlInjection
}
@RequestMapping
public void testOgnlDirectlyToGetSet(@RequestParam String expr) throws Exception {
Ognl.getValue(expr, new Object()); // $hasOgnlInjection
Ognl.setValue(expr, new Object(), new Object()); // $hasOgnlInjection
}
@RequestMapping
public void testStruts(@RequestParam String expr) throws Exception {
OgnlUtil ognl = new OgnlUtil();
ognl.getValue(expr, new HashMap<>(), new Object()); // $hasOgnlInjection
ognl.setValue(expr, new HashMap<>(), new Object(), new Object()); // $hasOgnlInjection
new OgnlUtil().callMethod(expr, new HashMap<>(), new Object()); // $hasOgnlInjection
}
@RequestMapping
public void testExpressionAccessor(@RequestParam String expr) throws Exception {
Node tree = Ognl.compileExpression(null, new Object(), expr);
ExpressionAccessor accessor = tree.getAccessor();
accessor.get(null, new Object()); // $hasOgnlInjection
accessor.set(null, new Object(), new Object()); // $hasOgnlInjection
Ognl.getValue(accessor, null, new Object()); // $hasOgnlInjection
Ognl.setValue(accessor, null, new Object()); // $hasOgnlInjection
}
@RequestMapping
public void testExpressionAccessorSetExpression(@RequestParam String expr) throws Exception {
Node tree = Ognl.compileExpression(null, new Object(), "\"some safe expression\".toString()");
ExpressionAccessor accessor = tree.getAccessor();
Node taintedTree = Ognl.compileExpression(null, new Object(), expr);
accessor.setExpression(taintedTree);
accessor.get(null, new Object()); // $hasOgnlInjection
accessor.set(null, new Object(), new Object()); // $hasOgnlInjection
Ognl.getValue(accessor, null, new Object()); // $hasOgnlInjection
Ognl.setValue(accessor, null, new Object()); // $hasOgnlInjection
}
}

View File

@@ -0,0 +1,20 @@
import java
import semmle.code.java.security.OgnlInjectionQuery
import TestUtilities.InlineExpectationsTest
class OgnlInjectionTest extends InlineExpectationsTest {
OgnlInjectionTest() { this = "HasOgnlInjection" }
override string getARelevantTag() { result = "hasOgnlInjection" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasOgnlInjection" and
exists(DataFlow::Node src, DataFlow::Node sink, OgnlInjectionFlowConfig conf |
conf.hasFlow(src, sink)
|
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -0,0 +1 @@
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/springframework-5.3.8:${testdir}/../../../stubs/ognl-3.2.14:${testdir}/../../../stubs/struts2-core-2.5.22