mirror of
https://github.com/github/codeql.git
synced 2026-04-29 18:55:14 +02:00
Move common code to a library and add more test cases
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/**
|
||||
* @name Local Android DoS Caused By NumberFormatException
|
||||
* @id java/android/nfe-local-android-dos
|
||||
* @description NumberFormatException thrown but not caught by an Android application that allows external inputs can crash the application, which is a local Denial of Service (Dos) attack.
|
||||
* @description NumberFormatException thrown but not caught by an Android application that allows external inputs can crash the application, constituting a local Denial of Service (DoS) attack.
|
||||
* @kind path-problem
|
||||
* @tags security
|
||||
* external/cwe/cwe-755
|
||||
@@ -10,74 +10,9 @@
|
||||
import java
|
||||
import semmle.code.java.frameworks.android.Intent
|
||||
import semmle.code.java.dataflow.FlowSources
|
||||
import semmle.code.java.NumberFormatException
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/** Code from java/ql/src/Violations of Best Practice/Exception Handling/NumberFormatException.ql */
|
||||
private class SpecialMethodAccess extends MethodAccess {
|
||||
predicate isValueOfMethod(string klass) {
|
||||
this.getMethod().getName() = "valueOf" and
|
||||
this.getQualifier().getType().(RefType).hasQualifiedName("java.lang", klass) and
|
||||
this.getAnArgument().getType().(RefType).hasQualifiedName("java.lang", "String")
|
||||
}
|
||||
|
||||
predicate isParseMethod(string klass, string name) {
|
||||
this.getMethod().getName() = name and
|
||||
this.getQualifier().getType().(RefType).hasQualifiedName("java.lang", klass)
|
||||
}
|
||||
|
||||
predicate throwsNFE() {
|
||||
this.isParseMethod("Byte", "parseByte") or
|
||||
this.isParseMethod("Short", "parseShort") or
|
||||
this.isParseMethod("Integer", "parseInt") or
|
||||
this.isParseMethod("Long", "parseLong") or
|
||||
this.isParseMethod("Float", "parseFloat") or
|
||||
this.isParseMethod("Double", "parseDouble") or
|
||||
this.isParseMethod("Byte", "decode") or
|
||||
this.isParseMethod("Short", "decode") or
|
||||
this.isParseMethod("Integer", "decode") or
|
||||
this.isParseMethod("Long", "decode") or
|
||||
this.isValueOfMethod("Byte") or
|
||||
this.isValueOfMethod("Short") or
|
||||
this.isValueOfMethod("Integer") or
|
||||
this.isValueOfMethod("Long") or
|
||||
this.isValueOfMethod("Float") or
|
||||
this.isValueOfMethod("Double")
|
||||
}
|
||||
}
|
||||
|
||||
private class SpecialClassInstanceExpr extends ClassInstanceExpr {
|
||||
predicate isStringConstructor(string klass) {
|
||||
this.getType().(RefType).hasQualifiedName("java.lang", klass) and
|
||||
this.getAnArgument().getType().(RefType).hasQualifiedName("java.lang", "String") and
|
||||
this.getNumArgument() = 1
|
||||
}
|
||||
|
||||
predicate throwsNFE() {
|
||||
this.isStringConstructor("Byte") or
|
||||
this.isStringConstructor("Short") or
|
||||
this.isStringConstructor("Integer") or
|
||||
this.isStringConstructor("Long") or
|
||||
this.isStringConstructor("Float") or
|
||||
this.isStringConstructor("Double")
|
||||
}
|
||||
}
|
||||
|
||||
class NumberFormatException extends RefType {
|
||||
NumberFormatException() { this.hasQualifiedName("java.lang", "NumberFormatException") }
|
||||
}
|
||||
|
||||
private predicate catchesNFE(TryStmt t) {
|
||||
exists(CatchClause cc, LocalVariableDeclExpr v |
|
||||
t.getACatchClause() = cc and
|
||||
cc.getVariable() = v and
|
||||
v.getType().(RefType).getASubtype*() instanceof NumberFormatException
|
||||
)
|
||||
}
|
||||
|
||||
private predicate throwsNFE(Expr e) {
|
||||
e.(SpecialClassInstanceExpr).throwsNFE() or e.(SpecialMethodAccess).throwsNFE()
|
||||
}
|
||||
|
||||
/**
|
||||
* Taint configuration tracking flow from untrusted inputs to number conversion calls in exported Android compononents.
|
||||
*/
|
||||
@@ -90,7 +25,7 @@ class NFELocalDoSConfiguration extends TaintTracking::Configuration {
|
||||
/** Holds if NFE is thrown but not caught */
|
||||
override predicate isSink(DataFlow::Node sink) {
|
||||
exists(Expr e |
|
||||
e.getEnclosingCallable().getDeclaringType() instanceof ExportableAndroidComponent and
|
||||
e.getEnclosingCallable().getDeclaringType().(ExportableAndroidComponent).isExported() and
|
||||
throwsNFE(e) and
|
||||
not exists(TryStmt t |
|
||||
t.getBlock() = e.getEnclosingStmt().getEnclosingStmt*() and
|
||||
@@ -103,5 +38,6 @@ class NFELocalDoSConfiguration extends TaintTracking::Configuration {
|
||||
|
||||
from DataFlow::PathNode source, DataFlow::PathNode sink, NFELocalDoSConfiguration conf
|
||||
where conf.hasFlowPath(source, sink)
|
||||
select sink.getNode(), source, sink, "Local Android Denial of Service due to $@.", source.getNode(),
|
||||
select sink.getNode(), source, sink,
|
||||
"Uncaught NumberFormatException in an exported Android component due to $@.", source.getNode(),
|
||||
"user-provided value"
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name=".SafeActivity" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.example.app;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
|
||||
/** A utility program for getting intent extra information from Android activity */
|
||||
public class IntentUtils {
|
||||
|
||||
/** Get double extra */
|
||||
public static double getDoubleExtra(Activity a, String key) {
|
||||
String value = a.getIntent().getStringExtra(key);
|
||||
return Double.parseDouble(value);
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ nodes
|
||||
| NFEAndroidDoS.java:44:21:44:43 | new Double(...) | semmle.label | new Double(...) |
|
||||
| NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | semmle.label | valueOf(...) |
|
||||
#select
|
||||
| NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) | NFEAndroidDoS.java:13:24:13:34 | getIntent(...) : Intent | NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) | Local Android Denial of Service due to $@. | NFEAndroidDoS.java:13:24:13:34 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | Local Android Denial of Service due to $@. | NFEAndroidDoS.java:22:21:22:31 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | Local Android Denial of Service due to $@. | NFEAndroidDoS.java:25:22:25:32 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:44:21:44:43 | new Double(...) | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:44:21:44:43 | new Double(...) | Local Android Denial of Service due to $@. | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | Local Android Denial of Service due to $@. | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) | NFEAndroidDoS.java:13:24:13:34 | getIntent(...) : Intent | NFEAndroidDoS.java:14:21:14:51 | parseDouble(...) | Uncaught NumberFormatException in an exported Android component due to $@. | NFEAndroidDoS.java:13:24:13:34 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | NFEAndroidDoS.java:22:21:22:31 | getIntent(...) : Intent | NFEAndroidDoS.java:23:15:23:40 | parseInt(...) | Uncaught NumberFormatException in an exported Android component due to $@. | NFEAndroidDoS.java:22:21:22:31 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | NFEAndroidDoS.java:25:22:25:32 | getIntent(...) : Intent | NFEAndroidDoS.java:26:16:26:42 | parseInt(...) | Uncaught NumberFormatException in an exported Android component due to $@. | NFEAndroidDoS.java:25:22:25:32 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:44:21:44:43 | new Double(...) | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:44:21:44:43 | new Double(...) | Uncaught NumberFormatException in an exported Android component due to $@. | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) | user-provided value |
|
||||
| NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) : Intent | NFEAndroidDoS.java:47:21:47:47 | valueOf(...) | Uncaught NumberFormatException in an exported Android component due to $@. | NFEAndroidDoS.java:43:24:43:34 | getIntent(...) | user-provided value |
|
||||
|
||||
@@ -46,4 +46,41 @@ public class NFEAndroidDoS extends Activity {
|
||||
String maxPriceStr = getIntent().getStringExtra("priceMax");
|
||||
double maxPrice = Double.valueOf(minPriceStr);
|
||||
}
|
||||
|
||||
// GOOD - parse string extra to double with caught NFE
|
||||
public void testOnCreate5(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(-1);
|
||||
|
||||
double minPrice = 0;
|
||||
try {
|
||||
String minPriceStr = getIntent().getStringExtra("priceMin");
|
||||
minPrice = Double.parseDouble(minPriceStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
nfe.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// GOOD - parse string extra to double with caught NFE as the supertype Throwable
|
||||
public void testOnCreate6(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(-1);
|
||||
|
||||
double minPrice = 0;
|
||||
try {
|
||||
String minPriceStr = getIntent().getStringExtra("priceMin");
|
||||
minPrice = Double.parseDouble(minPriceStr);
|
||||
} catch (Throwable te) {
|
||||
te.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// BAD - parse string extra to double
|
||||
// Note this case of invoking utility method that takes an Activity a then calls `a.getIntent().getStringExtra(...)` is not yet detected thus is beyond what the query is capable of.
|
||||
public void testOnCreate7(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(-1);
|
||||
|
||||
double priceMin = IntentUtils.getDoubleExtra(this, "priceMin");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.example.app;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
/** Android activity that tests app crash by NumberFormatException, which is not exported in `AndroidManifest.xml` */
|
||||
public class SafeActivity extends Activity {
|
||||
// BAD - parse string extra to double
|
||||
public void testOnCreate1(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(-1);
|
||||
|
||||
String minPriceStr = getIntent().getStringExtra("priceMin");
|
||||
double minPrice = Double.parseDouble(minPriceStr);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user