mirror of
https://github.com/github/codeql.git
synced 2025-12-21 11:16:30 +01:00
Add Fragment injection in PreferenceActivity query
This commit is contained in:
@@ -5,6 +5,32 @@ 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, BooleanLiteral bool |
|
||||
retStmt.getEnclosingCallable() = this and
|
||||
// Using taint tracking to handle logical expressions, like
|
||||
// fragmentName.equals("safe") || true
|
||||
TaintTracking::localExprTaint(bool, retStmt.getResult())
|
||||
|
|
||||
bool.getBooleanValue() = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sink for Fragment injection vulnerabilities,
|
||||
* that is, method calls that dynamically add Fragments to Activities.
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
class UnsafeActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
// BAD: any Fragment name can be provided.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class SafeActivity extends PreferenceActivity {
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
// Good: only trusted Fragment names are allowed.
|
||||
return SafeFragment1.class.getName().equals(fragmentName)
|
||||
|| SafeFragment2.class.getName().equals(fragmentName)
|
||||
|| SafeFragment3.class.getName().equals(fragmentName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
|
||||
<qhelp>
|
||||
<include src="FragmentInjection.inc.qhelp"></include>
|
||||
</qhelp>
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* @name Android Fragment injection in PreferenceActivity
|
||||
* @description An insecure implementation of the isValidFragment method
|
||||
* of the PreferenceActivity class may lead to Fragment injection.
|
||||
* @kind problem
|
||||
* @problem.severity error
|
||||
* @security-severity 9.8
|
||||
* @precision high
|
||||
* @id java/android/fragment-injection-preference-activity
|
||||
* @tags security
|
||||
* external/cwe/cwe-470
|
||||
*/
|
||||
|
||||
import java
|
||||
import semmle.code.java.security.FragmentInjection
|
||||
|
||||
from IsValidFragmentMethod m
|
||||
where m.isUnsafe()
|
||||
select m,
|
||||
"The 'isValidFragment' method always returns true. This makes the exported Activity $@ vulnerable to Fragment Injection.",
|
||||
m.getDeclaringType(), m.getDeclaringType().getName()
|
||||
@@ -19,5 +19,8 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".SafePreferenceActivity" android:exported="true" />
|
||||
<activity android:name=".UnsafePreferenceActivity" android:exported="true" />
|
||||
<activity android:name=".UnexportedPreferenceActivity" android:exported="false" />
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import java
|
||||
import semmle.code.java.security.FragmentInjection
|
||||
import TestUtilities.InlineExpectationsTest
|
||||
|
||||
class FragmentInjectionInPreferenceActivityTest extends InlineExpectationsTest {
|
||||
FragmentInjectionInPreferenceActivityTest() { this = "FragmentInjectionInPreferenceActivityTest" }
|
||||
|
||||
override string getARelevantTag() { result = "hasPreferenceFragmentInjection" }
|
||||
|
||||
override predicate hasActualResult(Location location, string element, string tag, string value) {
|
||||
tag = "hasPreferenceFragmentInjection" and
|
||||
exists(IsValidFragmentMethod isValidFragment | isValidFragment.isUnsafe() |
|
||||
isValidFragment.getLocation() = location and
|
||||
element = isValidFragment.toString() and
|
||||
value = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.example.myapp;
|
||||
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
public class SafePreferenceActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) { // Safe: not all paths return true
|
||||
return fragmentName.equals("MySafeFragment") || fragmentName.equals("MySafeFragment2");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.example.myapp;
|
||||
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
public class UnexportedPreferenceActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) { // Safe: not exported
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.example.myapp;
|
||||
|
||||
import android.preference.PreferenceActivity;
|
||||
|
||||
public class UnsafePreferenceActivity extends PreferenceActivity {
|
||||
|
||||
@Override
|
||||
protected boolean isValidFragment(String fragmentName) { // $ hasPreferenceFragmentInjection
|
||||
return fragmentName.equals("MySafeClass") || true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user