mirror of
https://github.com/github/codeql.git
synced 2026-04-26 17:25:19 +02:00
Merge branch 'main' into atorralba/android-implicit-pending-intents
This commit is contained in:
@@ -115,6 +115,7 @@ private module Frameworks {
|
||||
private import semmle.code.java.security.AndroidIntentRedirection
|
||||
private import semmle.code.java.security.ResponseSplitting
|
||||
private import semmle.code.java.security.InformationLeak
|
||||
private import semmle.code.java.security.Files
|
||||
private import semmle.code.java.security.GroovyInjection
|
||||
private import semmle.code.java.security.ImplicitPendingIntents
|
||||
private import semmle.code.java.security.JexlInjectionSinkModels
|
||||
@@ -259,20 +260,6 @@ private predicate sinkModelCsv(string row) {
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(String,URL[],ClassLoader);;Argument[1];open-url",
|
||||
"java.net;URLClassLoader;false;URLClassLoader;(String,URL[],ClassLoader,URLStreamHandlerFactory);;Argument[1];open-url",
|
||||
"java.net;URLClassLoader;false;newInstance;;;Argument[0];open-url",
|
||||
// Create file
|
||||
"java.io;FileOutputStream;false;FileOutputStream;;;Argument[0];create-file",
|
||||
"java.io;RandomAccessFile;false;RandomAccessFile;;;Argument[0];create-file",
|
||||
"java.io;FileWriter;false;FileWriter;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;move;;;Argument[1];create-file",
|
||||
"java.nio.file;Files;false;copy;;;Argument[1];create-file",
|
||||
"java.nio.file;Files;false;newOutputStream;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;newBufferedReader;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createDirectory;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createFile;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createLink;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createSymbolicLink;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createTempDirectory;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createTempFile;;;Argument[0];create-file",
|
||||
// Bean validation
|
||||
"javax.validation;ConstraintValidatorContext;true;buildConstraintViolationWithTemplate;;;Argument[0];bean-validation",
|
||||
// Set hostname
|
||||
|
||||
@@ -921,13 +921,15 @@ module Private {
|
||||
|
||||
private predicate inputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _)
|
||||
parseArg(c, _) or
|
||||
inputNeedsReferenceSpecific(c)
|
||||
}
|
||||
|
||||
private predicate outputNeedsReference(string c) {
|
||||
c = "Argument" or
|
||||
parseArg(c, _) or
|
||||
c = "ReturnValue"
|
||||
c = "ReturnValue" or
|
||||
outputNeedsReferenceSpecific(c)
|
||||
}
|
||||
|
||||
private predicate sourceElementRef(InterpretNode ref, string output, string kind) {
|
||||
|
||||
@@ -102,6 +102,12 @@ string getParameterPositionCsv(ParameterPosition pos) { result = pos.toString()
|
||||
/** Gets the textual representation of an argument position in the format used for flow summaries. */
|
||||
string getArgumentPositionCsv(ArgumentPosition pos) { result = pos.toString() }
|
||||
|
||||
/** Holds if input specification component `c` needs a reference. */
|
||||
predicate inputNeedsReferenceSpecific(string c) { none() }
|
||||
|
||||
/** Holds if output specification component `c` needs a reference. */
|
||||
predicate outputNeedsReferenceSpecific(string c) { none() }
|
||||
|
||||
class SourceOrSinkElement = Top;
|
||||
|
||||
/**
|
||||
|
||||
16
java/ql/lib/semmle/code/java/frameworks/android/Fragment.qll
Normal file
16
java/ql/lib/semmle/code/java/frameworks/android/Fragment.qll
Normal file
@@ -0,0 +1,16 @@
|
||||
/** Provides classes and predicates to track Android fragments. */
|
||||
|
||||
import java
|
||||
|
||||
/** The class `android.app.Fragment` */
|
||||
class Fragment extends Class {
|
||||
Fragment() { this.hasQualifiedName("android.app", "Fragment") }
|
||||
}
|
||||
|
||||
/** The method `instantiate` of the class `android.app.Fragment`. */
|
||||
class FragmentInstantiateMethod extends Method {
|
||||
FragmentInstantiateMethod() {
|
||||
this.getDeclaringType() instanceof Fragment and
|
||||
this.hasName("instantiate")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Provides classes and predicates to reason about cleartext storage in the Android filesystem
|
||||
* (external or internal storage).
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
import semmle.code.java.security.CleartextStorageQuery
|
||||
import semmle.code.java.security.Files
|
||||
import semmle.code.xml.AndroidManifest
|
||||
|
||||
private class AndroidFilesystemCleartextStorageSink extends CleartextStorageSink {
|
||||
AndroidFilesystemCleartextStorageSink() {
|
||||
filesystemInput(_, this.asExpr()) and
|
||||
// Make sure we are in an Android application.
|
||||
exists(AndroidManifestXmlFile manifest)
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to a method or constructor that may write to files to the local filesystem. */
|
||||
class LocalFileOpenCall extends Storable {
|
||||
LocalFileOpenCall() {
|
||||
this = any(DataFlow::Node sink | sinkNode(sink, "create-file")).asExpr().(Argument).getCall()
|
||||
}
|
||||
|
||||
override Expr getAnInput() {
|
||||
exists(FilesystemFlowConfig conf, DataFlow::Node n |
|
||||
filesystemInput(n, result) and
|
||||
conf.hasFlow(DataFlow::exprNode(this), n)
|
||||
)
|
||||
}
|
||||
|
||||
override Expr getAStore() {
|
||||
exists(FilesystemFlowConfig conf, DataFlow::Node n |
|
||||
closesFile(n, result) and
|
||||
conf.hasFlow(DataFlow::exprNode(this), n)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** Holds if `input` is written into `file`. */
|
||||
private predicate filesystemInput(DataFlow::Node file, Argument input) {
|
||||
exists(DataFlow::Node write | sinkNode(write, "write-file") |
|
||||
input = write.asExpr() or
|
||||
isVarargs(input, write)
|
||||
) and
|
||||
if input.getCall().getCallee().isStatic()
|
||||
then file.asExpr() = input.getCall()
|
||||
else file.asExpr() = input.getCall().getQualifier()
|
||||
}
|
||||
|
||||
/** Holds if `arg` is part of `varargs`. */
|
||||
private predicate isVarargs(Argument arg, DataFlow::ImplicitVarargsArray varargs) {
|
||||
arg.isVararg() and arg.getCall() = varargs.getCall()
|
||||
}
|
||||
|
||||
/** Holds if `store` closes `file`. */
|
||||
private predicate closesFile(DataFlow::Node file, Call closeCall) {
|
||||
closeCall.getCallee() instanceof CloseFileMethod and
|
||||
if closeCall.getCallee().isStatic()
|
||||
then file.asExpr() = closeCall
|
||||
else file.asExpr() = closeCall.getQualifier()
|
||||
or
|
||||
// try-with-resources automatically closes the file
|
||||
any(TryStmt try).getAResource() = closeCall.(LocalFileOpenCall).getEnclosingStmt() and
|
||||
closeCall = file.asExpr()
|
||||
}
|
||||
|
||||
/** A method that closes a file, perhaps after writing some data. */
|
||||
private class CloseFileMethod extends Method {
|
||||
CloseFileMethod() {
|
||||
this.hasQualifiedName("java.io", ["RandomAccessFile", "FileOutputStream", "PrintStream"],
|
||||
"close")
|
||||
or
|
||||
this.getDeclaringType().getASupertype*().hasQualifiedName("java.io", "Writer") and
|
||||
this.hasName("close")
|
||||
or
|
||||
this.hasQualifiedName("java.nio.file", "Files", ["write", "writeString"])
|
||||
}
|
||||
}
|
||||
|
||||
private class FilesystemFlowConfig extends DataFlow::Configuration {
|
||||
FilesystemFlowConfig() { this = "FilesystemFlowConfig" }
|
||||
|
||||
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof LocalFileOpenCall }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
filesystemInput(sink, _) or
|
||||
closesFile(sink, _)
|
||||
}
|
||||
|
||||
override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) {
|
||||
// Add nested Writer constructors as extra data flow steps
|
||||
exists(ClassInstanceExpr cie |
|
||||
cie.getConstructedType().getASupertype*().hasQualifiedName("java.io", "Writer") and
|
||||
node1.asExpr() = cie.getArgument(0) and
|
||||
node2.asExpr() = cie
|
||||
)
|
||||
}
|
||||
}
|
||||
72
java/ql/lib/semmle/code/java/security/Files.qll
Normal file
72
java/ql/lib/semmle/code/java/security/Files.qll
Normal file
@@ -0,0 +1,72 @@
|
||||
/** Provides classes and predicates to work with File objects. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.ExternalFlow
|
||||
|
||||
private class CreateFileSinkModels extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"java.io;FileOutputStream;false;FileOutputStream;;;Argument[0];create-file",
|
||||
"java.io;RandomAccessFile;false;RandomAccessFile;;;Argument[0];create-file",
|
||||
"java.io;FileWriter;false;FileWriter;;;Argument[0];create-file",
|
||||
"java.io;PrintStream;false;PrintStream;(File);;Argument[0];create-file",
|
||||
"java.io;PrintStream;false;PrintStream;(File,String);;Argument[0];create-file",
|
||||
"java.io;PrintStream;false;PrintStream;(File,Charset);;Argument[0];create-file",
|
||||
"java.io;PrintStream;false;PrintStream;(String);;Argument[0];create-file",
|
||||
"java.io;PrintStream;false;PrintStream;(String,String);;Argument[0];create-file",
|
||||
"java.io;PrintStream;false;PrintStream;(String,Charset);;Argument[0];create-file",
|
||||
"java.io;PrintWriter;false;PrintWriter;(File);;Argument[0];create-file",
|
||||
"java.io;PrintWriter;false;PrintWriter;(File,String);;Argument[0];create-file",
|
||||
"java.io;PrintWriter;false;PrintWriter;(File,Charset);;Argument[0];create-file",
|
||||
"java.io;PrintWriter;false;PrintWriter;(String);;Argument[0];create-file",
|
||||
"java.io;PrintWriter;false;PrintWriter;(String,String);;Argument[0];create-file",
|
||||
"java.io;PrintWriter;false;PrintWriter;(String,Charset);;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;copy;;;Argument[1];create-file",
|
||||
"java.nio.file;Files;false;createDirectories;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createDirectory;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createFile;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createLink;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createSymbolicLink;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createTempDirectory;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;createTempFile;(Path,String,String,FileAttribute[]);;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;move;;;Argument[1];create-file",
|
||||
"java.nio.file;Files;false;newBufferedWriter;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;newOutputStream;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;write;;;Argument[0];create-file",
|
||||
"java.nio.file;Files;false;writeString;;;Argument[0];create-file"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
private class WriteFileSinkModels extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
[
|
||||
"java.io;FileOutputStream;false;write;;;Argument[0];write-file",
|
||||
"java.io;RandomAccessFile;false;write;;;Argument[0];write-file",
|
||||
"java.io;RandomAccessFile;false;writeBytes;;;Argument[0];write-file",
|
||||
"java.io;RandomAccessFile;false;writeChars;;;Argument[0];write-file",
|
||||
"java.io;RandomAccessFile;false;writeUTF;;;Argument[0];write-file",
|
||||
"java.io;Writer;true;append;;;Argument[0];write-file",
|
||||
"java.io;Writer;true;write;;;Argument[0];write-file",
|
||||
"java.io;PrintStream;true;append;;;Argument[0];write-file",
|
||||
"java.io;PrintStream;true;format;(String,Object[]);;Argument[0..1];write-file",
|
||||
"java.io;PrintStream;true;format;(Locale,String,Object[]);;Argument[1..2];write-file",
|
||||
"java.io;PrintStream;true;print;;;Argument[0];write-file",
|
||||
"java.io;PrintStream;true;printf;(String,Object[]);;Argument[0..1];write-file",
|
||||
"java.io;PrintStream;true;printf;(Locale,String,Object[]);;Argument[1..2];write-file",
|
||||
"java.io;PrintStream;true;println;;;Argument[0];write-file",
|
||||
"java.io;PrintStream;true;write;;;Argument[0];write-file",
|
||||
"java.io;PrintStream;true;writeBytes;;;Argument[0];write-file",
|
||||
"java.io;PrintWriter;false;format;(String,Object[]);;Argument[0..1];write-file",
|
||||
"java.io;PrintWriter;false;format;(Locale,String,Object[]);;Argument[1..2];write-file",
|
||||
"java.io;PrintWriter;false;print;;;Argument[0];write-file",
|
||||
"java.io;PrintWriter;false;printf;(String,Object[]);;Argument[0..1];write-file",
|
||||
"java.io;PrintWriter;false;printf;(Locale,String,Object[]);;Argument[1..2];write-file",
|
||||
"java.io;PrintWriter;false;println;;;Argument[0];write-file",
|
||||
"java.nio.file;Files;false;write;;;Argument[1];write-file",
|
||||
"java.nio.file;Files;false;writeString;;;Argument[1];write-file"
|
||||
]
|
||||
}
|
||||
}
|
||||
83
java/ql/lib/semmle/code/java/security/FragmentInjection.qll
Normal file
83
java/ql/lib/semmle/code/java/security/FragmentInjection.qll
Normal file
@@ -0,0 +1,83 @@
|
||||
/** Provides classes and predicates to reason about Android Fragment injection vulnerabilities. */
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.TaintTracking
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.frameworks.android.Android
|
||||
private import semmle.code.java.frameworks.android.Fragment
|
||||
private import semmle.code.java.Reflection
|
||||
|
||||
/** The method `isValidFragment` of the class `android.preference.PreferenceActivity`. */
|
||||
class IsValidFragmentMethod extends Method {
|
||||
IsValidFragmentMethod() {
|
||||
this.getDeclaringType()
|
||||
.getASupertype*()
|
||||
.hasQualifiedName("android.preference", "PreferenceActivity") and
|
||||
this.hasName("isValidFragment")
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this method makes the Activity it is declared in vulnerable to Fragment injection,
|
||||
* that is, all code paths in this method return `true` and the Activity is exported.
|
||||
*/
|
||||
predicate isUnsafe() {
|
||||
this.getDeclaringType().(AndroidActivity).isExported() and
|
||||
forex(ReturnStmt retStmt | retStmt.getEnclosingCallable() = this |
|
||||
retStmt.getResult().(BooleanLiteral).getBooleanValue() = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Fragment injection vulnerabilities,
|
||||
* that is, method calls that dynamically add fragments to activities.
|
||||
*/
|
||||
abstract class FragmentInjectionSink extends DataFlow::Node { }
|
||||
|
||||
/** An additional taint step for flows related to Fragment injection vulnerabilites. */
|
||||
class FragmentInjectionAdditionalTaintStep extends Unit {
|
||||
/**
|
||||
* Holds if the step from `node1` to `node2` should be considered a taint
|
||||
* step in flows related to Fragment injection vulnerabilites.
|
||||
*/
|
||||
abstract predicate step(DataFlow::Node n1, DataFlow::Node n2);
|
||||
}
|
||||
|
||||
private class FragmentInjectionSinkModels extends SinkModelCsv {
|
||||
override predicate row(string row) {
|
||||
row =
|
||||
["android.app", "android.support.v4.app", "androidx.fragment.app"] +
|
||||
";FragmentTransaction;true;" +
|
||||
[
|
||||
"add;(Class,Bundle,String);;Argument[0]", "add;(Fragment,String);;Argument[0]",
|
||||
"add;(int,Class,Bundle);;Argument[1]", "add;(int,Fragment);;Argument[1]",
|
||||
"add;(int,Class,Bundle,String);;Argument[1]", "add;(int,Fragment,String);;Argument[1]",
|
||||
"attach;(Fragment);;Argument[0]", "replace;(int,Class,Bundle);;Argument[1]",
|
||||
"replace;(int,Fragment);;Argument[1]", "replace;(int,Class,Bundle,String);;Argument[1]",
|
||||
"replace;(int,Fragment,String);;Argument[1]",
|
||||
] + ";fragment-injection"
|
||||
}
|
||||
}
|
||||
|
||||
private class DefaultFragmentInjectionSink extends FragmentInjectionSink {
|
||||
DefaultFragmentInjectionSink() { sinkNode(this, "fragment-injection") }
|
||||
}
|
||||
|
||||
private class DefaultFragmentInjectionAdditionalTaintStep extends FragmentInjectionAdditionalTaintStep {
|
||||
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
exists(ReflectiveClassIdentifierMethodAccess ma |
|
||||
ma.getArgument(0) = n1.asExpr() and ma = n2.asExpr()
|
||||
)
|
||||
or
|
||||
exists(NewInstance ni |
|
||||
ni.getQualifier() = n1.asExpr() and
|
||||
ni = n2.asExpr()
|
||||
)
|
||||
or
|
||||
exists(MethodAccess ma |
|
||||
ma.getMethod() instanceof FragmentInstantiateMethod and
|
||||
ma.getArgument(1) = n1.asExpr() and
|
||||
ma = n2.asExpr()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/** Provides classes and predicates to be used in queries related to Android Fragment injection. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.dataflow.TaintTracking
|
||||
import semmle.code.java.security.FragmentInjection
|
||||
|
||||
/**
|
||||
* A taint-tracking configuration for unsafe user input
|
||||
* that is used to create Android fragments dynamically.
|
||||
*/
|
||||
class FragmentInjectionTaintConf extends TaintTracking::Configuration {
|
||||
FragmentInjectionTaintConf() { this = "FragmentInjectionTaintConf" }
|
||||
|
||||
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
|
||||
|
||||
override predicate isSink(DataFlow::Node sink) { sink instanceof FragmentInjectionSink }
|
||||
|
||||
override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) {
|
||||
any(FragmentInjectionAdditionalTaintStep c).step(n1, n2)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/** Provides clases and methods shared by randomness-related queries. */
|
||||
/** Provides classes and methods shared by randomness-related queries. */
|
||||
|
||||
import java
|
||||
import semmle.code.java.dataflow.DefUse
|
||||
|
||||
Reference in New Issue
Block a user