From 3e09d86a4fecc9846f9d7fce1ad4e5bcf3277a30 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 1 Aug 2022 23:39:55 -0400 Subject: [PATCH 01/26] adding starter files --- .../ImplicitlyExportedAndroidComponent.qhelp | 38 +++++++++++++++++++ .../ImplicitlyExportedAndroidComponent.ql | 22 +++++++++++ .../2022-08-DD-android-implicit-export.md | 4 ++ ...citlyExportedAndroidComponentTest.expected | 0 .../ImplicitlyExportedAndroidComponentTest.ql | 23 +++++++++++ .../query-tests/security/CWE-926/Test.java | 3 ++ .../test/query-tests/security/CWE-926/options | 1 + 7 files changed, 91 insertions(+) create mode 100644 java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp create mode 100644 java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql create mode 100644 java/ql/src/change-notes/2022-08-DD-android-implicit-export.md create mode 100644 java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.expected create mode 100644 java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql create mode 100644 java/ql/test/query-tests/security/CWE-926/Test.java create mode 100644 java/ql/test/query-tests/security/CWE-926/options diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp new file mode 100644 index 00000000000..237c7dd0070 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp @@ -0,0 +1,38 @@ + + + + +

TODO: Replace the following +When a debugger is enabled it could allow for entry points in the application or reveal sensitive information.

+ +
+ + +

TODO: Replace the following +In Android applications either set the android:debuggable attribute to false +or do not include it in the manifest. The default value when not included is false.

+ +
+ + +

TODO: Replace the following +In the example below, the android:debuggable attribute is set to true.

+ + + +

The corrected version sets the android:debuggable attribute to false.

+ + + +
+ + +
  • + TODO: REPLACE LINKS. Android Developers: + The android:debuggable attribute. +
  • + +
    +
    diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql new file mode 100644 index 00000000000..be5c1742500 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -0,0 +1,22 @@ +/** + * @name Implicitly imported Android component + * @description TODO after more background reading + * @kind problem (TODO: confirm after more background reading) + * @problem.severity warning (TODO: confirm after more background reading) + * @security-severity 0.1 (TODO: run script) + * @id java/android/implicitly-imported-component + * @tags security + * external/cwe/cwe-926 + * @precision TODO after MRVA + */ + +import java +import semmle.code.xml.AndroidManifest + +// TODO: change query +from AndroidXmlAttribute androidXmlAttr +where + androidXmlAttr.getName() = "debuggable" and + androidXmlAttr.getValue() = "true" and + not androidXmlAttr.getLocation().getFile().getRelativePath().matches("%build%") +select androidXmlAttr, "The 'android:debuggable' attribute is enabled." diff --git a/java/ql/src/change-notes/2022-08-DD-android-implicit-export.md b/java/ql/src/change-notes/2022-08-DD-android-implicit-export.md new file mode 100644 index 00000000000..fa9252b8cfc --- /dev/null +++ b/java/ql/src/change-notes/2022-08-DD-android-implicit-export.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, `java/android/implicitly-imported-component`, to detect if an Android component can become implicitly exported. diff --git a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.expected b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql new file mode 100644 index 00000000000..fd723ac9548 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql @@ -0,0 +1,23 @@ +import java +import semmle.code.xml.AndroidManifest +import TestUtilities.InlineExpectationsTest + +// TODO: update for implicit export query +class DebuggableAttributeTrueTest extends InlineExpectationsTest { + DebuggableAttributeTrueTest() { this = "DebuggableAttributeEnabledTest" } + + override string getARelevantTag() { result = "hasDebuggableAttributeEnabled" } + + override predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "hasDebuggableAttributeEnabled" and + exists(AndroidXmlAttribute androidXmlAttr | + androidXmlAttr.getName() = "debuggable" and + androidXmlAttr.getValue() = "true" and + not androidXmlAttr.getLocation().getFile().getRelativePath().matches("%build%") + | + androidXmlAttr.getLocation() = location and + element = androidXmlAttr.toString() and + value = "" + ) + } +} diff --git a/java/ql/test/query-tests/security/CWE-926/Test.java b/java/ql/test/query-tests/security/CWE-926/Test.java new file mode 100644 index 00000000000..4566fbca2ad --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-926/Test.java @@ -0,0 +1,3 @@ +public class Test { + +} diff --git a/java/ql/test/query-tests/security/CWE-926/options b/java/ql/test/query-tests/security/CWE-926/options new file mode 100644 index 00000000000..dacd3cb21df --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-926/options @@ -0,0 +1 @@ +//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0 From 8d5bbc458f137fc819416ae1625eb97bc9c81707 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Thu, 4 Aug 2022 09:58:10 -0400 Subject: [PATCH 02/26] first draft of query and tests --- .../ImplicitlyExportedAndroidComponent.ql | 24 ++++++++++------- .../security/CWE-926/AndroidManifest.xml | 26 +++++++++++++++++++ .../ImplicitlyExportedAndroidComponentTest.ql | 21 +++++++-------- 3 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index be5c1742500..c1915d670e3 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -1,10 +1,10 @@ /** - * @name Implicitly imported Android component + * @name Implicitly exported Android component * @description TODO after more background reading - * @kind problem (TODO: confirm after more background reading) + * @kind problem * @problem.severity warning (TODO: confirm after more background reading) * @security-severity 0.1 (TODO: run script) - * @id java/android/implicitly-imported-component + * @id java/android/implicitly-exported-component * @tags security * external/cwe/cwe-926 * @precision TODO after MRVA @@ -13,10 +13,16 @@ import java import semmle.code.xml.AndroidManifest -// TODO: change query -from AndroidXmlAttribute androidXmlAttr +from AndroidComponentXmlElement compElem where - androidXmlAttr.getName() = "debuggable" and - androidXmlAttr.getValue() = "true" and - not androidXmlAttr.getLocation().getFile().getRelativePath().matches("%build%") -select androidXmlAttr, "The 'android:debuggable' attribute is enabled." + not compElem.hasAttribute("exported") and + compElem.getAChild().hasName("intent-filter") and + not compElem.hasAttribute("permission") and + not compElem + .getAnIntentFilterElement() + .getAnActionElement() + .getActionName() + .matches("android.intent.action.%") and // filter out anything that is android intent (e.g. don't just filter out MAIN) because I think those are fine (but need to look at docs to confirm) + //not compElem.getAnIntentFilterElement().getAnActionElement().getActionName() = "android.intent.category.LAUNCHER" and // I should add this as well, but above will techincally filter out since they always seem to occur together + not compElem.getFile().getRelativePath().matches("%build%") // switch to not isInBuildDirectory() once new predicate is merged into main +select compElem, "This component is implicitly exported." diff --git a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml new file mode 100644 index 00000000000..7384880eec6 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql index fd723ac9548..1f63a048199 100644 --- a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql +++ b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql @@ -2,21 +2,20 @@ import java import semmle.code.xml.AndroidManifest import TestUtilities.InlineExpectationsTest -// TODO: update for implicit export query -class DebuggableAttributeTrueTest extends InlineExpectationsTest { - DebuggableAttributeTrueTest() { this = "DebuggableAttributeEnabledTest" } +class ImplicitlyExportedAndroidComponentTest extends InlineExpectationsTest { + ImplicitlyExportedAndroidComponentTest() { this = "ImplicitlyExportedAndroidComponentTest" } - override string getARelevantTag() { result = "hasDebuggableAttributeEnabled" } + override string getARelevantTag() { result = "hasImplicitExport" } override predicate hasActualResult(Location location, string element, string tag, string value) { - tag = "hasDebuggableAttributeEnabled" and - exists(AndroidXmlAttribute androidXmlAttr | - androidXmlAttr.getName() = "debuggable" and - androidXmlAttr.getValue() = "true" and - not androidXmlAttr.getLocation().getFile().getRelativePath().matches("%build%") + tag = "hasImplicitExport" and + exists(AndroidComponentXmlElement compElem, AndroidIntentFilterXmlElement intFiltElem | + not compElem.hasAttribute("exported") and + //compElem.getAnIntentFilterElement() instanceof AndroidIntentFilterXmlElement + not intFiltElem.getParent() = compElem | - androidXmlAttr.getLocation() = location and - element = androidXmlAttr.toString() and + compElem.getLocation() = location and + element = compElem.toString() and value = "" ) } From a6ecac6e0024ceaac78e41ba85f1b7f3e2d1708f Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Fri, 5 Aug 2022 01:16:59 -0400 Subject: [PATCH 03/26] third draft with category launcher and permission element excluded --- .../ImplicitlyExportedAndroidComponent.ql | 58 ++++++++++++++++--- .../security/CWE-926/AndroidManifest.xml | 11 +++- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index c1915d670e3..61882bdef14 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -13,16 +13,56 @@ import java import semmle.code.xml.AndroidManifest -from AndroidComponentXmlElement compElem +// FIRST DRAFT +// from AndroidComponentXmlElement compElem +// where +// not compElem.hasAttribute("exported") and +// compElem.getAChild().hasName("intent-filter") and +// not compElem.hasAttribute("permission") and +// not compElem +// .getAnIntentFilterElement() +// .getAnActionElement() +// .getActionName() +// .matches("android.intent.action.MAIN") and // filter out anything that is android intent (e.g. don't just filter out MAIN) because I think those are fine (but need to look at docs to confirm) +// //not compElem.getAnIntentFilterElement().getAnActionElement().getActionName() = "android.intent.category.LAUNCHER" and // I should add this as well, but above will techincally filter out since they always seem to occur together +// not compElem.getFile().getRelativePath().matches("%build%") // switch to not isInBuildDirectory() once new predicate is merged into main +// select compElem, "This component is implicitly exported." +// SECOND DRAFT +// from AndroidComponentXmlElement compElem +// where +// // Does NOT have `exported` attribute +// not compElem.hasAttribute("exported") and +// // and DOES have an intent-filter (DOUBLE-CHECK THIS CODE AND CHECK AGAINST OTHER VERSIONS THAT SEEMED TO WORK THE SAME) +// compElem.getAChild().hasName("intent-filter") and // compElem.getAChild("intent-filter"); need hasComponent with exists(...) here? +// // and does NOT have `permission` attribute +// not compElem.hasAttribute("permission") and +// // and is NOT in build directory (NOTE: switch to not isInBuildDirectory() once new predicate is merged into main) +// not compElem.getFile().getRelativePath().matches("%build%") and +// // and does NOT have a LAUNCHER category, see docs: https://developer.android.com/about/versions/12/behavior-changes-12#exported +// // Constant Value: "android.intent.category.LAUNCHER" from https://developer.android.com/reference/android/content/Intent#CATEGORY_LAUNCHER +// // I think beloew is actually too coarse because there can be multiple intent-filters in one component, so 2nd intent-filter without the launcher +// // could maybe be an issue, e.g. https://github.com/microsoft/DynamicsWOM/blob/62c2dad4cbbd4496a55aa3f644336044105bb1c1/app/src/main/AndroidManifest.xml#L56-L66 +// not compElem.getAnIntentFilterElement().getAChild("category").getAttributeValue("name") = +// "android.intent.category.LAUNCHER" // double-check this code (especially use of getAChild and pattern match with LAUNCHER (e.g. should I do .%LAUNCHER instead?--No, constant value per docs), etc.), and definitely need to add stuff to library for this; should use exists(...) here? +// select compElem, "This component is implicitly exported." +// THIRD DRAFT +from AndroidComponentXmlElement compElem, AndroidManifestXmlElement manifestElem where + // Does NOT have `exported` attribute not compElem.hasAttribute("exported") and - compElem.getAChild().hasName("intent-filter") and + // and DOES have an intent-filter (DOUBLE-CHECK THIS CODE AND CHECK AGAINST OTHER VERSIONS THAT SEEMED TO WORK THE SAME) + compElem.getAChild().hasName("intent-filter") and // compElem.getAChild("intent-filter"); need hasComponent with exists(...) here? + // and does NOT have `permission` attribute not compElem.hasAttribute("permission") and - not compElem - .getAnIntentFilterElement() - .getAnActionElement() - .getActionName() - .matches("android.intent.action.%") and // filter out anything that is android intent (e.g. don't just filter out MAIN) because I think those are fine (but need to look at docs to confirm) - //not compElem.getAnIntentFilterElement().getAnActionElement().getActionName() = "android.intent.category.LAUNCHER" and // I should add this as well, but above will techincally filter out since they always seem to occur together - not compElem.getFile().getRelativePath().matches("%build%") // switch to not isInBuildDirectory() once new predicate is merged into main + // and is NOT in build directory (NOTE: switch to not isInBuildDirectory() once new predicate is merged into main) + not compElem.getFile().getRelativePath().matches("%build%") and + // and does NOT have a LAUNCHER category, see docs: https://developer.android.com/about/versions/12/behavior-changes-12#exported + // Constant Value: "android.intent.category.LAUNCHER" from https://developer.android.com/reference/android/content/Intent#CATEGORY_LAUNCHER + // I think beloew is actually filtering out too much because there can be multiple intent-filters in one component, so 2nd intent-filter without the launcher + // could maybe be an issue, e.g. https://github.com/microsoft/DynamicsWOM/blob/62c2dad4cbbd4496a55aa3f644336044105bb1c1/app/src/main/AndroidManifest.xml#L56-L66 + not compElem.getAnIntentFilterElement().getAChild("category").getAttributeValue("name") = + "android.intent.category.LAUNCHER" and // double-check this code (especially use of getAChild and pattern match with LAUNCHER (e.g. should I do .%LAUNCHER instead?--No, constant value per docs), etc.), and definitely need to add stuff to library for this; should use exists(...) here? + // and NO element in manifest file since that will be applied to the component even if no `permission` attribute directly + // set on component per the docs: + not manifestElem.getAChild().hasName("permission") select compElem, "This component is implicitly exported." diff --git a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml index 7384880eec6..f1b4cea98b9 100644 --- a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml @@ -13,7 +13,7 @@ android:supportsRtl="true" android:theme="@style/Theme.HappyBirthday" tools:targetApi="31"> - @@ -21,6 +21,13 @@ - + + + + + + + From 60921a03559aeed29ce2a2c14bf0d1bdb999147d Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Fri, 5 Aug 2022 08:17:20 -0400 Subject: [PATCH 04/26] switched to checking for permission attr in application elem instead of in manifest elem --- .../ImplicitlyExportedAndroidComponent.ql | 16 ++++++++-------- .../security/CWE-926/AndroidManifest.xml | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index 61882bdef14..fef760e7328 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -46,23 +46,23 @@ import semmle.code.xml.AndroidManifest // "android.intent.category.LAUNCHER" // double-check this code (especially use of getAChild and pattern match with LAUNCHER (e.g. should I do .%LAUNCHER instead?--No, constant value per docs), etc.), and definitely need to add stuff to library for this; should use exists(...) here? // select compElem, "This component is implicitly exported." // THIRD DRAFT -from AndroidComponentXmlElement compElem, AndroidManifestXmlElement manifestElem +from AndroidComponentXmlElement compElem, AndroidApplicationXmlElement appElem where // Does NOT have `exported` attribute not compElem.hasAttribute("exported") and - // and DOES have an intent-filter (DOUBLE-CHECK THIS CODE AND CHECK AGAINST OTHER VERSIONS THAT SEEMED TO WORK THE SAME) - compElem.getAChild().hasName("intent-filter") and // compElem.getAChild("intent-filter"); need hasComponent with exists(...) here? + // and DOES have an intent-filter + compElem.getAChild().hasName("intent-filter") and // compElem.getAChild("intent-filter") // and does NOT have `permission` attribute not compElem.hasAttribute("permission") and // and is NOT in build directory (NOTE: switch to not isInBuildDirectory() once new predicate is merged into main) not compElem.getFile().getRelativePath().matches("%build%") and // and does NOT have a LAUNCHER category, see docs: https://developer.android.com/about/versions/12/behavior-changes-12#exported // Constant Value: "android.intent.category.LAUNCHER" from https://developer.android.com/reference/android/content/Intent#CATEGORY_LAUNCHER - // I think beloew is actually filtering out too much because there can be multiple intent-filters in one component, so 2nd intent-filter without the launcher + // I think below is actually filtering out too much because there can be multiple intent-filters in one component, so 2nd intent-filter without the launcher // could maybe be an issue, e.g. https://github.com/microsoft/DynamicsWOM/blob/62c2dad4cbbd4496a55aa3f644336044105bb1c1/app/src/main/AndroidManifest.xml#L56-L66 not compElem.getAnIntentFilterElement().getAChild("category").getAttributeValue("name") = - "android.intent.category.LAUNCHER" and // double-check this code (especially use of getAChild and pattern match with LAUNCHER (e.g. should I do .%LAUNCHER instead?--No, constant value per docs), etc.), and definitely need to add stuff to library for this; should use exists(...) here? - // and NO element in manifest file since that will be applied to the component even if no `permission` attribute directly - // set on component per the docs: - not manifestElem.getAChild().hasName("permission") + "android.intent.category.LAUNCHER" and + // and NO android:permission attribute in element since that will be applied to the component even + // if no `permission` attribute directly set on component per the docs: + not appElem.hasAttribute("permission") select compElem, "This component is implicitly exported." diff --git a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml index f1b4cea98b9..02b45fa4b61 100644 --- a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml @@ -12,6 +12,7 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HappyBirthday" + android:permission="android.permission.SEND_SMS" tools:targetApi="31"> From eea1089ee0c5645aec5bdec9678aee2e8666a7ae Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Wed, 17 Aug 2022 14:19:50 -0400 Subject: [PATCH 05/26] resolved merge conflict in AndroidManifest --- .../lib/semmle/code/xml/AndroidManifest.qll | 60 +++++++++++++++++ .../ImplicitlyExportedAndroidComponent.qhelp | 26 ++++++-- .../ImplicitlyExportedAndroidComponent.ql | 64 ++----------------- 3 files changed, 86 insertions(+), 64 deletions(-) diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index 7d414cabe63..db361278775 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -67,6 +67,9 @@ class AndroidApplicationXmlElement extends XmlElement { attr.getValue() = "true" ) } + + /** Holds if this component element has an attribute with the name `permission`. */ + predicate hasPermissionAttribute() { exists(this.getAttribute("permission")) } } /** @@ -170,6 +173,11 @@ class AndroidComponentXmlElement extends XmlElement { */ AndroidIntentFilterXmlElement getAnIntentFilterElement() { result = this.getAChild() } + /** + * Holds if this component element has an `` child element. + */ + predicate hasAnIntentFilterElement() { this.getAChild().hasName("intent-filter") } + /** * Gets the value of the `android:name` attribute of this component element. */ @@ -220,6 +228,23 @@ class AndroidComponentXmlElement extends XmlElement { * Holds if the `android:exported` attribute of this component element is explicitly set to `false`. */ predicate isNotExported() { this.getExportedAttributeValue() = "false" } + + /** + * Holds if this component element has an `android:exported` attribute. + */ + predicate hasExportedAttribute() { this.hasAttribute("exported") } + + /** Holds if this component element has an attribute with the name `permission`. */ + predicate hasPermissionAttribute() { exists(this.getAttribute("permission")) } + + predicate isImplicitlyExported() { + not this.hasExportedAttribute() and + this.hasAnIntentFilterElement() and // Note: did not use getAnIntentFilterElement since don't need a return value + not this.hasPermissionAttribute() and + not this.getParent().(AndroidApplicationXmlElement).hasPermissionAttribute() and + not this.getAnIntentFilterElement().hasLauncherCategoryElement() and + not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() + } } /** @@ -234,6 +259,19 @@ class AndroidIntentFilterXmlElement extends XmlElement { * Gets an `` child element of this `` element. */ AndroidActionXmlElement getAnActionElement() { result = this.getAChild() } + + /** + * Gets a `` child element of this `` element. + */ + AndroidCategoryXmlElement getACategoryElement() { result = this.getAChild("category") } + + /** + * Holds if this `` element has a `` child element + * named "android.intent.category.LAUNCHER". + */ + predicate hasLauncherCategoryElement() { + this.getACategoryElement().getAttributeValue("name") = "android.intent.category.LAUNCHER" + } } /** @@ -257,3 +295,25 @@ class AndroidActionXmlElement extends XmlElement { ) } } + +/** + * A `` element in an Android manifest file. + */ +class AndroidCategoryXmlElement extends XMLElement { + AndroidCategoryXmlElement() { + this.getFile() instanceof AndroidManifestXmlFile and this.getName() = "category" + } + + /** + * Gets the name of this category. + */ + string getCategoryName() { + exists(XMLAttribute attr | + attr = this.getAnAttribute() and + attr.getNamespace().getPrefix() = "android" and + attr.getName() = "name" + | + result = attr.getValue() + ) + } +} diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp index 237c7dd0070..8cac72b1267 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp @@ -4,21 +4,25 @@ -

    TODO: Replace the following -When a debugger is enabled it could allow for entry points in the application or reveal sensitive information.

    +

    The Android manifest file defines configuration settings for Android applications. +In this file, the android:debuggable attribute of the application element can be used to +define whether or not the application can be debugged. When set to true, this attribute will allow the +application to be debugged even when running on a device in user mode.

    + +

    When a debugger is enabled it could allow for entry points in the application or reveal sensitive information. +As a result, android:debuggable should only be enabled during development and should be disabled in +production builds.

    -

    TODO: Replace the following -In Android applications either set the android:debuggable attribute to false +

    In Android applications either set the android:debuggable attribute to false or do not include it in the manifest. The default value when not included is false.

    -

    TODO: Replace the following -In the example below, the android:debuggable attribute is set to true.

    +

    In the example below, the android:debuggable attribute is set to true.

    @@ -30,9 +34,17 @@ In the example below, the android:debuggable attribute is set to
  • - TODO: REPLACE LINKS. Android Developers: + Android Developers: + App Manifest Overview. +
  • +
  • + Android Developers: The android:debuggable attribute.
  • +
  • + Android Developers: + Enable debugging. +
  • diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index fef760e7328..af64b2b6461 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -1,68 +1,18 @@ /** * @name Implicitly exported Android component - * @description TODO after more background reading + * @description An Android component with an '' and no 'android:exported' attribute is implicitly exported. This can allow for improper access to the component and its data. * @kind problem - * @problem.severity warning (TODO: confirm after more background reading) - * @security-severity 0.1 (TODO: run script) + * @problem.severity warning + * @security-severity 8.2 * @id java/android/implicitly-exported-component * @tags security * external/cwe/cwe-926 - * @precision TODO after MRVA + * @precision high */ import java import semmle.code.xml.AndroidManifest -// FIRST DRAFT -// from AndroidComponentXmlElement compElem -// where -// not compElem.hasAttribute("exported") and -// compElem.getAChild().hasName("intent-filter") and -// not compElem.hasAttribute("permission") and -// not compElem -// .getAnIntentFilterElement() -// .getAnActionElement() -// .getActionName() -// .matches("android.intent.action.MAIN") and // filter out anything that is android intent (e.g. don't just filter out MAIN) because I think those are fine (but need to look at docs to confirm) -// //not compElem.getAnIntentFilterElement().getAnActionElement().getActionName() = "android.intent.category.LAUNCHER" and // I should add this as well, but above will techincally filter out since they always seem to occur together -// not compElem.getFile().getRelativePath().matches("%build%") // switch to not isInBuildDirectory() once new predicate is merged into main -// select compElem, "This component is implicitly exported." -// SECOND DRAFT -// from AndroidComponentXmlElement compElem -// where -// // Does NOT have `exported` attribute -// not compElem.hasAttribute("exported") and -// // and DOES have an intent-filter (DOUBLE-CHECK THIS CODE AND CHECK AGAINST OTHER VERSIONS THAT SEEMED TO WORK THE SAME) -// compElem.getAChild().hasName("intent-filter") and // compElem.getAChild("intent-filter"); need hasComponent with exists(...) here? -// // and does NOT have `permission` attribute -// not compElem.hasAttribute("permission") and -// // and is NOT in build directory (NOTE: switch to not isInBuildDirectory() once new predicate is merged into main) -// not compElem.getFile().getRelativePath().matches("%build%") and -// // and does NOT have a LAUNCHER category, see docs: https://developer.android.com/about/versions/12/behavior-changes-12#exported -// // Constant Value: "android.intent.category.LAUNCHER" from https://developer.android.com/reference/android/content/Intent#CATEGORY_LAUNCHER -// // I think beloew is actually too coarse because there can be multiple intent-filters in one component, so 2nd intent-filter without the launcher -// // could maybe be an issue, e.g. https://github.com/microsoft/DynamicsWOM/blob/62c2dad4cbbd4496a55aa3f644336044105bb1c1/app/src/main/AndroidManifest.xml#L56-L66 -// not compElem.getAnIntentFilterElement().getAChild("category").getAttributeValue("name") = -// "android.intent.category.LAUNCHER" // double-check this code (especially use of getAChild and pattern match with LAUNCHER (e.g. should I do .%LAUNCHER instead?--No, constant value per docs), etc.), and definitely need to add stuff to library for this; should use exists(...) here? -// select compElem, "This component is implicitly exported." -// THIRD DRAFT -from AndroidComponentXmlElement compElem, AndroidApplicationXmlElement appElem -where - // Does NOT have `exported` attribute - not compElem.hasAttribute("exported") and - // and DOES have an intent-filter - compElem.getAChild().hasName("intent-filter") and // compElem.getAChild("intent-filter") - // and does NOT have `permission` attribute - not compElem.hasAttribute("permission") and - // and is NOT in build directory (NOTE: switch to not isInBuildDirectory() once new predicate is merged into main) - not compElem.getFile().getRelativePath().matches("%build%") and - // and does NOT have a LAUNCHER category, see docs: https://developer.android.com/about/versions/12/behavior-changes-12#exported - // Constant Value: "android.intent.category.LAUNCHER" from https://developer.android.com/reference/android/content/Intent#CATEGORY_LAUNCHER - // I think below is actually filtering out too much because there can be multiple intent-filters in one component, so 2nd intent-filter without the launcher - // could maybe be an issue, e.g. https://github.com/microsoft/DynamicsWOM/blob/62c2dad4cbbd4496a55aa3f644336044105bb1c1/app/src/main/AndroidManifest.xml#L56-L66 - not compElem.getAnIntentFilterElement().getAChild("category").getAttributeValue("name") = - "android.intent.category.LAUNCHER" and - // and NO android:permission attribute in element since that will be applied to the component even - // if no `permission` attribute directly set on component per the docs: - not appElem.hasAttribute("permission") -select compElem, "This component is implicitly exported." +from AndroidComponentXmlElement compElement +where compElement.isImplicitlyExported() +select compElement, "This component is implicitly exported." From 10fa687e26008c5d4c39571a9afc92fe88becd12 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Fri, 5 Aug 2022 16:35:28 -0400 Subject: [PATCH 06/26] updated help file and unit tests --- .../src/Security/CWE/CWE-926/ExampleBad.xml | 11 ++++ .../src/Security/CWE/CWE-926/ExampleGood.xml | 12 +++++ .../ImplicitlyExportedAndroidComponent.qhelp | 33 +++++++----- .../security/CWE-926/AndroidManifest.xml | 21 +++++++- .../ImplicitlyExportedAndroidComponentTest.ql | 10 ++-- .../CWE-926/Testbuild/AndroidManifest.xml | 53 +++++++++++++++++++ 6 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 java/ql/src/Security/CWE/CWE-926/ExampleBad.xml create mode 100644 java/ql/src/Security/CWE/CWE-926/ExampleGood.xml create mode 100644 java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml diff --git a/java/ql/src/Security/CWE/CWE-926/ExampleBad.xml b/java/ql/src/Security/CWE/CWE-926/ExampleBad.xml new file mode 100644 index 00000000000..ec60406c460 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-926/ExampleBad.xml @@ -0,0 +1,11 @@ + + + + android:name=".Activity"> + + + + + + diff --git a/java/ql/src/Security/CWE/CWE-926/ExampleGood.xml b/java/ql/src/Security/CWE/CWE-926/ExampleGood.xml new file mode 100644 index 00000000000..f19184b8c13 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-926/ExampleGood.xml @@ -0,0 +1,12 @@ + + + + android:name=".Activity"> + android:exported="false" + + + + + + diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp index 8cac72b1267..8aa88304c09 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp @@ -5,30 +5,27 @@

    The Android manifest file defines configuration settings for Android applications. -In this file, the android:debuggable attribute of the application element can be used to -define whether or not the application can be debugged. When set to true, this attribute will allow the -application to be debugged even when running on a device in user mode.

    +In this file, components can be declared with intent filters which specify the types of intents the component can respond to. +If the android:exported attribute is omitted from the component when an intent filter is included, +then the component will be implicitly exported.

    -

    When a debugger is enabled it could allow for entry points in the application or reveal sensitive information. -As a result, android:debuggable should only be enabled during development and should be disabled in -production builds.

    +

    An implicitly exported component could allow for improper access to the component and its data.

    -

    In Android applications either set the android:debuggable attribute to false -or do not include it in the manifest. The default value when not included is false.

    +

    Explicitly set the android:exported attribute for every component or use permissions to limit access to the component.

    -

    In the example below, the android:debuggable attribute is set to true.

    +

    In the example below, the component android:exported attribute is omitted when an intent filter is used.

    - + -

    The corrected version sets the android:debuggable attribute to false.

    +

    A corrected version sets the android:exported attribute to false.

    - +
    @@ -39,11 +36,19 @@ or do not include it in the manifest. The default value when not included is
  • Android Developers: - The android:debuggable attribute. + intent-filter-element.
  • Android Developers: - Enable debugging. + The android:exported attribute. +
  • +
  • + Android Developers: + The android:permission attribute. +
  • +
  • + Android Developers: + Safer component exporting.
  • diff --git a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml index 02b45fa4b61..d1ecefda4a1 100644 --- a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml @@ -12,7 +12,6 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HappyBirthday" - android:permission="android.permission.SEND_SMS" tools:targetApi="31"> @@ -29,6 +28,26 @@
    + + + + + + + + + + + + + + +
    diff --git a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql index 1f63a048199..a8c9587ea49 100644 --- a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql +++ b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql @@ -9,13 +9,9 @@ class ImplicitlyExportedAndroidComponentTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasImplicitExport" and - exists(AndroidComponentXmlElement compElem, AndroidIntentFilterXmlElement intFiltElem | - not compElem.hasAttribute("exported") and - //compElem.getAnIntentFilterElement() instanceof AndroidIntentFilterXmlElement - not intFiltElem.getParent() = compElem - | - compElem.getLocation() = location and - element = compElem.toString() and + exists(AndroidComponentXmlElement compElement | compElement.isImplicitlyExported() | + compElement.getLocation() = location and + element = compElement.toString() and value = "" ) } diff --git a/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml new file mode 100644 index 00000000000..70896921295 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 33c48ec685f1bfb161ae9bbdf58bd166865d53df Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 8 Aug 2022 13:31:59 -0400 Subject: [PATCH 07/26] updated change note --- .../ql/src/change-notes/2022-08-09-android-implicit-export.md | 4 ++++ .../ql/src/change-notes/2022-08-DD-android-implicit-export.md | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 java/ql/src/change-notes/2022-08-09-android-implicit-export.md delete mode 100644 java/ql/src/change-notes/2022-08-DD-android-implicit-export.md diff --git a/java/ql/src/change-notes/2022-08-09-android-implicit-export.md b/java/ql/src/change-notes/2022-08-09-android-implicit-export.md new file mode 100644 index 00000000000..31caced22f3 --- /dev/null +++ b/java/ql/src/change-notes/2022-08-09-android-implicit-export.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, `java/android/implicitly-exported-component`, to detect if Android components are implicitly exported in the Android manifest. diff --git a/java/ql/src/change-notes/2022-08-DD-android-implicit-export.md b/java/ql/src/change-notes/2022-08-DD-android-implicit-export.md deleted file mode 100644 index fa9252b8cfc..00000000000 --- a/java/ql/src/change-notes/2022-08-DD-android-implicit-export.md +++ /dev/null @@ -1,4 +0,0 @@ ---- -category: newQuery ---- -* Added a new query, `java/android/implicitly-imported-component`, to detect if an Android component can become implicitly exported. From a99d7ffaafb87e01f1a1f3d1b0c60b9895110d4c Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 8 Aug 2022 14:02:53 -0400 Subject: [PATCH 08/26] minor wording update in change note --- java/ql/src/change-notes/2022-08-09-android-implicit-export.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/change-notes/2022-08-09-android-implicit-export.md b/java/ql/src/change-notes/2022-08-09-android-implicit-export.md index 31caced22f3..beea9a8d3bf 100644 --- a/java/ql/src/change-notes/2022-08-09-android-implicit-export.md +++ b/java/ql/src/change-notes/2022-08-09-android-implicit-export.md @@ -1,4 +1,4 @@ --- category: newQuery --- -* Added a new query, `java/android/implicitly-exported-component`, to detect if Android components are implicitly exported in the Android manifest. +* Added a new query, `java/android/implicitly-exported-component`, to detect if components are implicitly exported in the Android manifest. From 55bd9f943fc9c49c035c99440daaa42e762663da Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 8 Aug 2022 14:53:54 -0400 Subject: [PATCH 09/26] minor wording updates in help file --- .../CWE-926/ImplicitlyExportedAndroidComponent.qhelp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp index 8aa88304c09..f9efb0d631b 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.qhelp @@ -5,9 +5,9 @@

    The Android manifest file defines configuration settings for Android applications. -In this file, components can be declared with intent filters which specify the types of intents the component can respond to. -If the android:exported attribute is omitted from the component when an intent filter is included, -then the component will be implicitly exported.

    +In this file, components can be declared with intent filters which specify what the components can do and what types +of intents the components can respond to. If the android:exported attribute is omitted from the component +when an intent filter is included, then the component will be implicitly exported.

    An implicitly exported component could allow for improper access to the component and its data.

    @@ -19,7 +19,7 @@ then the component will be implicitly exported.

    -

    In the example below, the component android:exported attribute is omitted when an intent filter is used.

    +

    In the example below, the android:exported attribute is omitted when an intent filter is used.

    @@ -36,7 +36,7 @@ then the component will be implicitly exported.

  • Android Developers: - intent-filter-element. + The <intent-filter> element.
  • Android Developers: From 084b9830bccfdba6e14bd397e6f23853cb5cb223 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Wed, 17 Aug 2022 14:27:01 -0400 Subject: [PATCH 10/26] resolved merge conflict in AndroidManifest --- java/ql/lib/semmle/code/xml/AndroidManifest.qll | 15 ++++++++------- .../CWE-926/ImplicitlyExportedAndroidComponent.ql | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index db361278775..1203698441d 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -68,8 +68,10 @@ class AndroidApplicationXmlElement extends XmlElement { ) } - /** Holds if this component element has an attribute with the name `permission`. */ - predicate hasPermissionAttribute() { exists(this.getAttribute("permission")) } + /** + * Holds if this application element has explicitly set a value for its `android:permission` attribute. + */ + predicate requiresPermissions() { this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull() } } /** @@ -234,14 +236,13 @@ class AndroidComponentXmlElement extends XmlElement { */ predicate hasExportedAttribute() { this.hasAttribute("exported") } - /** Holds if this component element has an attribute with the name `permission`. */ - predicate hasPermissionAttribute() { exists(this.getAttribute("permission")) } - + // /** Holds if this component element has an attribute with the name `permission`. */ + // predicate hasPermissionAttribute() { exists(this.getAttribute("permission")) } predicate isImplicitlyExported() { not this.hasExportedAttribute() and this.hasAnIntentFilterElement() and // Note: did not use getAnIntentFilterElement since don't need a return value - not this.hasPermissionAttribute() and - not this.getParent().(AndroidApplicationXmlElement).hasPermissionAttribute() and + not this.hasAttribute("permission") and // not seeing how isFull() is any better than this..., this seems to more directly check what I want... + not this.getParent().(AndroidApplicationXmlElement).hasAttribute("permission") and not this.getAnIntentFilterElement().hasLauncherCategoryElement() and not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() } diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index af64b2b6461..f68bc9a2556 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -7,7 +7,7 @@ * @id java/android/implicitly-exported-component * @tags security * external/cwe/cwe-926 - * @precision high + * @precision medium */ import java From 825df218a333caf30db3d8cd0e48cfe27abd6312 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Tue, 9 Aug 2022 14:57:13 -0400 Subject: [PATCH 11/26] adding library change note --- .../change-notes/2022-08-09-android-implicit-export.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 java/ql/lib/change-notes/2022-08-09-android-implicit-export.md diff --git a/java/ql/lib/change-notes/2022-08-09-android-implicit-export.md b/java/ql/lib/change-notes/2022-08-09-android-implicit-export.md new file mode 100644 index 00000000000..8e8684376d2 --- /dev/null +++ b/java/ql/lib/change-notes/2022-08-09-android-implicit-export.md @@ -0,0 +1,10 @@ +--- +category: feature +--- +* Added a new predicate, `hasAnIntentFilterElement`, in the `AndroidComponentXmlElement` class to detect if a component contains an intent filter element. +* Added a new predicate, `hasExportedAttribute`, in the `AndroidComponentXmlElement` class to detect if a component has an `android:exported` attribute. +* Added a new predicate, `isImplicitlyExported`, in the `AndroidComponentXmlElement` class to detect if a component is implicitly exported. +* Added a new predicate, `getACategoryElement`, in the `AndroidIntentFilterXmlElement` class to detect if an intent filter contains a category element. +* Added a new predicate, `hasLauncherCategoryElement`, in the `AndroidIntentFilterXmlElement` class to detect if an intent filter contains a launcher category element. +* Added a new class, `AndroidCategoryXmlElement`, to represent a category element in an Android manifest file. +* Added a new predicate, `getCategoryName`, in the `AndroidCategoryXmlElement` class to get the name of the category element. From 58d3d89b2ef480069bae5f992ac409a5f1220bc6 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Wed, 17 Aug 2022 14:29:15 -0400 Subject: [PATCH 12/26] resolved merge conflict in AndroidManifest --- .../ImplicitlyExportedAndroidComponent.qll | 15 ++++++++++++++ .../lib/semmle/code/xml/AndroidManifest.qll | 20 +++++++------------ .../ImplicitlyExportedAndroidComponent.ql | 8 ++++---- 3 files changed, 26 insertions(+), 17 deletions(-) create mode 100644 java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll new file mode 100644 index 00000000000..171efdb2825 --- /dev/null +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -0,0 +1,15 @@ +/** Provides a class to reason about Android implicitly exported components. */ + +private import semmle.code.xml.AndroidManifest + +class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { + //ImplicitlyExportedAndroidComponent() { } + predicate isImplicitlyExported() { + not this.hasExportedAttribute() and + this.hasAnIntentFilterElement() and + not this.requiresPermissions() and + not this.getParent().(AndroidApplicationXmlElement).hasAttribute("permission") and + not this.getAnIntentFilterElement().hasLauncherCategoryElement() and + not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() + } +} diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index 1203698441d..08042eedb1d 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -113,7 +113,7 @@ class AndroidProviderXmlElement extends AndroidComponentXmlElement { * `android:permission` attribute or its `android:readPermission` and `android:writePermission` * attributes. */ - predicate requiresPermissions() { + override predicate requiresPermissions() { this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull() or this.getAnAttribute().(AndroidPermissionXmlAttribute).isWrite() and @@ -236,16 +236,10 @@ class AndroidComponentXmlElement extends XmlElement { */ predicate hasExportedAttribute() { this.hasAttribute("exported") } - // /** Holds if this component element has an attribute with the name `permission`. */ - // predicate hasPermissionAttribute() { exists(this.getAttribute("permission")) } - predicate isImplicitlyExported() { - not this.hasExportedAttribute() and - this.hasAnIntentFilterElement() and // Note: did not use getAnIntentFilterElement since don't need a return value - not this.hasAttribute("permission") and // not seeing how isFull() is any better than this..., this seems to more directly check what I want... - not this.getParent().(AndroidApplicationXmlElement).hasAttribute("permission") and - not this.getAnIntentFilterElement().hasLauncherCategoryElement() and - not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() - } + /** + * Holds if this component element has explicitly set a value for its `android:permission` attribute. + */ + predicate requiresPermissions() { this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull() } } /** @@ -268,10 +262,10 @@ class AndroidIntentFilterXmlElement extends XmlElement { /** * Holds if this `` element has a `` child element - * named "android.intent.category.LAUNCHER". + * named `android.intent.category.LAUNCHER`. */ predicate hasLauncherCategoryElement() { - this.getACategoryElement().getAttributeValue("name") = "android.intent.category.LAUNCHER" + this.getACategoryElement().getCategoryName() = "android.intent.category.LAUNCHER" } } diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index f68bc9a2556..eea7c3f5806 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -11,8 +11,8 @@ */ import java -import semmle.code.xml.AndroidManifest +import semmle.code.java.security.ImplicitlyExportedAndroidComponent -from AndroidComponentXmlElement compElement -where compElement.isImplicitlyExported() -select compElement, "This component is implicitly exported." +from ImplicitlyExportedAndroidComponent impExpAndroidComp +where impExpAndroidComp.isImplicitlyExported() +select impExpAndroidComp, "This component is implicitly exported." From b88d545c4948d0bc8c1664bd31bb05452738a9f1 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Wed, 10 Aug 2022 17:07:58 -0400 Subject: [PATCH 13/26] added unit tests --- .../security/CWE-926/AndroidManifest.xml | 94 +++++++++++++------ .../ImplicitlyExportedAndroidComponentTest.ql | 10 +- .../AndroidManifest.xml | 90 ++++++++++++++++++ .../CWE-926/Testbuild/AndroidManifest.xml | 94 +++++++++++++------ 4 files changed, 226 insertions(+), 62 deletions(-) create mode 100644 java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml diff --git a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml index d1ecefda4a1..9b4258baaf3 100644 --- a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml @@ -12,9 +12,71 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HappyBirthday" - tools:targetApi="31"> - + tools:targetApi="31"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -22,32 +84,6 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql index a8c9587ea49..155628108cc 100644 --- a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql +++ b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql @@ -1,5 +1,5 @@ import java -import semmle.code.xml.AndroidManifest +import semmle.code.java.security.ImplicitlyExportedAndroidComponent import TestUtilities.InlineExpectationsTest class ImplicitlyExportedAndroidComponentTest extends InlineExpectationsTest { @@ -9,9 +9,11 @@ class ImplicitlyExportedAndroidComponentTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasImplicitExport" and - exists(AndroidComponentXmlElement compElement | compElement.isImplicitlyExported() | - compElement.getLocation() = location and - element = compElement.toString() and + exists(ImplicitlyExportedAndroidComponent impExpAndroidComp | + impExpAndroidComp.isImplicitlyExported() + | + impExpAndroidComp.getLocation() = location and + element = impExpAndroidComp.toString() and value = "" ) } diff --git a/java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml new file mode 100644 index 00000000000..ba7d970d3c0 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml index 70896921295..2777e704cfc 100644 --- a/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml @@ -12,9 +12,71 @@ android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.HappyBirthday" - tools:targetApi="31"> - + tools:targetApi="31"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -22,32 +84,6 @@ - - - - - - - - - - - - - - - - - - - - From 115f76ac5a196b1b3feaa6ffa1fe7a260a92ff29 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Wed, 10 Aug 2022 20:57:50 -0400 Subject: [PATCH 14/26] fixed typo in unit tests; added a couple more tests --- .../security/CWE-926/AndroidManifest.xml | 22 +++++++++++++++++-- .../AndroidManifest.xml | 22 +++++++++++++++++-- .../CWE-926/Testbuild/AndroidManifest.xml | 22 +++++++++++++++++-- 3 files changed, 60 insertions(+), 6 deletions(-) diff --git a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml index 9b4258baaf3..7a5b89ce48c 100644 --- a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml @@ -43,7 +43,7 @@ - + - + + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml index ba7d970d3c0..de229fb9be9 100644 --- a/java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/TestApplicationPermission/AndroidManifest.xml @@ -44,7 +44,7 @@ - + - + + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml index 2777e704cfc..336734a565f 100644 --- a/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/Testbuild/AndroidManifest.xml @@ -43,7 +43,7 @@ - + - + + + + + + + + + + + + + + + From 9968d5d816bb2bed8dc2d0dfab129c07a8fe04d6 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Wed, 10 Aug 2022 23:06:13 -0400 Subject: [PATCH 15/26] updated predicates --- .../ImplicitlyExportedAndroidComponent.qll | 15 +++++++++++++-- java/ql/lib/semmle/code/xml/AndroidManifest.qll | 2 +- .../CWE-926/ImplicitlyExportedAndroidComponent.ql | 3 +++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll index 171efdb2825..9445457f19f 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -1,9 +1,20 @@ -/** Provides a class to reason about Android implicitly exported components. */ +/** Provides a class to identify implicitly exported Android components. */ private import semmle.code.xml.AndroidManifest +/** Represents an implicitly exported Android component */ class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { - //ImplicitlyExportedAndroidComponent() { } + // ImplicitlyExportedAndroidComponent() { + // not this.hasExportedAttribute() and + // this.hasAnIntentFilterElement() and + // not this.requiresPermissions() and + // not this.getParent().(AndroidApplicationXmlElement).hasAttribute("permission") and + // not this.getAnIntentFilterElement().hasLauncherCategoryElement() and + // not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() + // } + /** + * Holds if this Android component is implicitly exported. + */ predicate isImplicitlyExported() { not this.hasExportedAttribute() and this.hasAnIntentFilterElement() and diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index 08042eedb1d..1af5eee722d 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -178,7 +178,7 @@ class AndroidComponentXmlElement extends XmlElement { /** * Holds if this component element has an `` child element. */ - predicate hasAnIntentFilterElement() { this.getAChild().hasName("intent-filter") } + predicate hasAnIntentFilterElement() { exists(this.getAnIntentFilterElement()) } /** * Gets the value of the `android:name` attribute of this component element. diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index eea7c3f5806..09d8393f490 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -16,3 +16,6 @@ import semmle.code.java.security.ImplicitlyExportedAndroidComponent from ImplicitlyExportedAndroidComponent impExpAndroidComp where impExpAndroidComp.isImplicitlyExported() select impExpAndroidComp, "This component is implicitly exported." +// from ImplicitlyExportedAndroidComponent impExpAndroidComp +// where exists(impExpAndroidComp) +// select impExpAndroidComp, "This component is implicitly exported." From 0934c1d1840766b6992637ad233dbaff63dd9b11 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 22 Aug 2022 11:14:57 -0400 Subject: [PATCH 16/26] resolved merge conflict in AndroidManifest lib --- .../ImplicitlyExportedAndroidComponent.qll | 29 ++++++++++--------- .../lib/semmle/code/xml/AndroidManifest.qll | 24 ++++++++------- .../ImplicitlyExportedAndroidComponent.ql | 8 ++--- .../ImplicitlyExportedAndroidComponentTest.ql | 2 -- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll index 9445457f19f..2b122619a3b 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -4,23 +4,26 @@ private import semmle.code.xml.AndroidManifest /** Represents an implicitly exported Android component */ class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { - // ImplicitlyExportedAndroidComponent() { + ImplicitlyExportedAndroidComponent() { + not this.hasExportedAttribute() and + this.hasAnIntentFilterElement() and + not this.getAnIntentFilterElement().getACategoryElement().getCategoryName() = + "android.intent.category.LAUNCHER" and + not this.requiresPermissions() and + not this.getParent().(AndroidApplicationXmlElement).requiresPermissions() and + //not this.getAnIntentFilterElement().hasLauncherCategoryElement() and + not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() + //this.getFile() instanceof SourceAndroidManifestXmlFile + } + // predicate isImplicitlyExported() { // not this.hasExportedAttribute() and // this.hasAnIntentFilterElement() and // not this.requiresPermissions() and // not this.getParent().(AndroidApplicationXmlElement).hasAttribute("permission") and // not this.getAnIntentFilterElement().hasLauncherCategoryElement() and - // not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() + // not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() //and + // not this.getAnIntentFilterElement().getAnActionElement().getActionName().matches("%MEDIA%") and // try MEDIA exclusion -- MRVA returns 251 results, so only removed 13 + // not this.getAnIntentFilterElement().getAnActionElement().getActionName() = + // "android.intent.action.MAIN" // try MAIN exclusion -- MRVA returns 193 results, so removed 251-193 = 58 results // } - /** - * Holds if this Android component is implicitly exported. - */ - predicate isImplicitlyExported() { - not this.hasExportedAttribute() and - this.hasAnIntentFilterElement() and - not this.requiresPermissions() and - not this.getParent().(AndroidApplicationXmlElement).hasAttribute("permission") and - not this.getAnIntentFilterElement().hasLauncherCategoryElement() and - not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() - } } diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index 1af5eee722d..fd2339d50da 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -25,6 +25,9 @@ class AndroidManifestXmlFile extends XmlFile { predicate isInBuildDirectory() { this.getFile().getRelativePath().matches("%build%") } } +// class SourceAndroidManifestXmlFile extends AndroidManifestXmlFile { +// SourceAndroidManifestXmlFile() { not this.getFile().getRelativePath().matches("%build%") } +// } /** * A `` element in an Android manifest file. */ @@ -139,6 +142,7 @@ class AndroidPermissionXmlAttribute extends XmlAttribute { AndroidPermissionXmlAttribute() { this.getNamespace().getPrefix() = "android" and this.getName() = ["permission", "readPermission", "writePermission"] + //this.getName() = ["permission"] } /** Holds if this is an `android:permission` attribute. */ @@ -234,7 +238,8 @@ class AndroidComponentXmlElement extends XmlElement { /** * Holds if this component element has an `android:exported` attribute. */ - predicate hasExportedAttribute() { this.hasAttribute("exported") } + //predicate hasExportedAttribute() { this.hasAttribute("exported") } + predicate hasExportedAttribute() { exists(this.getExportedAttributeValue()) } /** * Holds if this component element has explicitly set a value for its `android:permission` attribute. @@ -258,15 +263,14 @@ class AndroidIntentFilterXmlElement extends XmlElement { /** * Gets a `` child element of this `` element. */ - AndroidCategoryXmlElement getACategoryElement() { result = this.getAChild("category") } - - /** - * Holds if this `` element has a `` child element - * named `android.intent.category.LAUNCHER`. - */ - predicate hasLauncherCategoryElement() { - this.getACategoryElement().getCategoryName() = "android.intent.category.LAUNCHER" - } + AndroidCategoryXmlElement getACategoryElement() { result = this.getAChild() } + // /** + // * Holds if this `` element has a `` child element + // * named `android.intent.category.LAUNCHER`. + // */ + // predicate hasLauncherCategoryElement() { + // this.getACategoryElement().getCategoryName() = "android.intent.category.LAUNCHER" + // } } /** diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index 09d8393f490..ad08139241d 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -13,9 +13,9 @@ import java import semmle.code.java.security.ImplicitlyExportedAndroidComponent -from ImplicitlyExportedAndroidComponent impExpAndroidComp -where impExpAndroidComp.isImplicitlyExported() -select impExpAndroidComp, "This component is implicitly exported." // from ImplicitlyExportedAndroidComponent impExpAndroidComp -// where exists(impExpAndroidComp) +// where impExpAndroidComp.isImplicitlyExported() // select impExpAndroidComp, "This component is implicitly exported." +from ImplicitlyExportedAndroidComponent impExpAndroidComp +//where exists(impExpAndroidComp) +select impExpAndroidComp, "This component is implicitly exported." diff --git a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql index 155628108cc..fbda52d36ab 100644 --- a/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql +++ b/java/ql/test/query-tests/security/CWE-926/ImplicitlyExportedAndroidComponentTest.ql @@ -10,8 +10,6 @@ class ImplicitlyExportedAndroidComponentTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "hasImplicitExport" and exists(ImplicitlyExportedAndroidComponent impExpAndroidComp | - impExpAndroidComp.isImplicitlyExported() - | impExpAndroidComp.getLocation() = location and element = impExpAndroidComp.toString() and value = "" From eacce0307350aa67296bba1b71b8d99ffa49501b Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 22 Aug 2022 11:20:22 -0400 Subject: [PATCH 17/26] resolved merge conflict in AndroidManifest lib --- .../ImplicitlyExportedAndroidComponent.qll | 17 +++-------------- java/ql/lib/semmle/code/xml/AndroidManifest.qll | 11 ----------- .../ImplicitlyExportedAndroidComponent.ql | 4 ---- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll index 2b122619a3b..8de2efb2be1 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -11,19 +11,8 @@ class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { "android.intent.category.LAUNCHER" and not this.requiresPermissions() and not this.getParent().(AndroidApplicationXmlElement).requiresPermissions() and - //not this.getAnIntentFilterElement().hasLauncherCategoryElement() and - not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() - //this.getFile() instanceof SourceAndroidManifestXmlFile + not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() //and + //not this.getAnIntentFilterElement().getAnActionElement().getActionName() = + // "android.intent.action.MAIN" } - // predicate isImplicitlyExported() { - // not this.hasExportedAttribute() and - // this.hasAnIntentFilterElement() and - // not this.requiresPermissions() and - // not this.getParent().(AndroidApplicationXmlElement).hasAttribute("permission") and - // not this.getAnIntentFilterElement().hasLauncherCategoryElement() and - // not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() //and - // not this.getAnIntentFilterElement().getAnActionElement().getActionName().matches("%MEDIA%") and // try MEDIA exclusion -- MRVA returns 251 results, so only removed 13 - // not this.getAnIntentFilterElement().getAnActionElement().getActionName() = - // "android.intent.action.MAIN" // try MAIN exclusion -- MRVA returns 193 results, so removed 251-193 = 58 results - // } } diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index fd2339d50da..0e1977d5f17 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -25,9 +25,6 @@ class AndroidManifestXmlFile extends XmlFile { predicate isInBuildDirectory() { this.getFile().getRelativePath().matches("%build%") } } -// class SourceAndroidManifestXmlFile extends AndroidManifestXmlFile { -// SourceAndroidManifestXmlFile() { not this.getFile().getRelativePath().matches("%build%") } -// } /** * A `` element in an Android manifest file. */ @@ -238,7 +235,6 @@ class AndroidComponentXmlElement extends XmlElement { /** * Holds if this component element has an `android:exported` attribute. */ - //predicate hasExportedAttribute() { this.hasAttribute("exported") } predicate hasExportedAttribute() { exists(this.getExportedAttributeValue()) } /** @@ -264,13 +260,6 @@ class AndroidIntentFilterXmlElement extends XmlElement { * Gets a `` child element of this `` element. */ AndroidCategoryXmlElement getACategoryElement() { result = this.getAChild() } - // /** - // * Holds if this `` element has a `` child element - // * named `android.intent.category.LAUNCHER`. - // */ - // predicate hasLauncherCategoryElement() { - // this.getACategoryElement().getCategoryName() = "android.intent.category.LAUNCHER" - // } } /** diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index ad08139241d..00d5833b60a 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -13,9 +13,5 @@ import java import semmle.code.java.security.ImplicitlyExportedAndroidComponent -// from ImplicitlyExportedAndroidComponent impExpAndroidComp -// where impExpAndroidComp.isImplicitlyExported() -// select impExpAndroidComp, "This component is implicitly exported." from ImplicitlyExportedAndroidComponent impExpAndroidComp -//where exists(impExpAndroidComp) select impExpAndroidComp, "This component is implicitly exported." From fba9ffd49a7a45842dbc3322d923470605cb0bca Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Thu, 11 Aug 2022 21:43:26 -0400 Subject: [PATCH 18/26] update lib change note --- .../change-notes/2022-08-09-android-implicit-export.md | 10 ---------- ...-08-09-android-manifest-new-class-and-predicates.md | 8 ++++++++ 2 files changed, 8 insertions(+), 10 deletions(-) delete mode 100644 java/ql/lib/change-notes/2022-08-09-android-implicit-export.md create mode 100644 java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md diff --git a/java/ql/lib/change-notes/2022-08-09-android-implicit-export.md b/java/ql/lib/change-notes/2022-08-09-android-implicit-export.md deleted file mode 100644 index 8e8684376d2..00000000000 --- a/java/ql/lib/change-notes/2022-08-09-android-implicit-export.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -category: feature ---- -* Added a new predicate, `hasAnIntentFilterElement`, in the `AndroidComponentXmlElement` class to detect if a component contains an intent filter element. -* Added a new predicate, `hasExportedAttribute`, in the `AndroidComponentXmlElement` class to detect if a component has an `android:exported` attribute. -* Added a new predicate, `isImplicitlyExported`, in the `AndroidComponentXmlElement` class to detect if a component is implicitly exported. -* Added a new predicate, `getACategoryElement`, in the `AndroidIntentFilterXmlElement` class to detect if an intent filter contains a category element. -* Added a new predicate, `hasLauncherCategoryElement`, in the `AndroidIntentFilterXmlElement` class to detect if an intent filter contains a launcher category element. -* Added a new class, `AndroidCategoryXmlElement`, to represent a category element in an Android manifest file. -* Added a new predicate, `getCategoryName`, in the `AndroidCategoryXmlElement` class to get the name of the category element. diff --git a/java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md b/java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md new file mode 100644 index 00000000000..27740fca13e --- /dev/null +++ b/java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md @@ -0,0 +1,8 @@ +--- +category: feature +--- +* Added a new predicate, `requiresPermissions`, in the `AndroidComponentXmlElement` and `AndroidApplicationXmlElement` classes to detect if the element has explicitly set a value for its `android:permission` attribute. Also added `override` annotation to the pre-existing `requiresPermissions` predicate in the `AndroidProviderXmlElement` class. +* Added a new predicate, `hasAnIntentFilterElement`, in the `AndroidComponentXmlElement` class to detect if a component contains an intent filter element. +* Added a new predicate, `hasExportedAttribute`, in the `AndroidComponentXmlElement` class to detect if a component has an `android:exported` attribute. +* Added a new class, `AndroidCategoryXmlElement`, to represent a category element in an Android manifest file. Also added a new predicate, `getCategoryName`, in this class to get the name of the category element. +* Added a new predicate, `getACategoryElement`, in the `AndroidIntentFilterXmlElement` class to get a category element of an intent filter. From c5526ffef8feaf82d8a6ae0d305af94e8c57a677 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Thu, 11 Aug 2022 21:48:48 -0400 Subject: [PATCH 19/26] update class QLDoc to start with 'An' --- .../code/java/security/ImplicitlyExportedAndroidComponent.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll index 8de2efb2be1..9d2d278c9be 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -2,7 +2,7 @@ private import semmle.code.xml.AndroidManifest -/** Represents an implicitly exported Android component */ +/** An implicitly exported Android component */ class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { ImplicitlyExportedAndroidComponent() { not this.hasExportedAttribute() and From eee12264c3e664b37104622f51ada4c3eacdd4ce Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Fri, 12 Aug 2022 09:22:39 -0400 Subject: [PATCH 20/26] excluded action main from query results, added unit test --- .../java/security/ImplicitlyExportedAndroidComponent.qll | 6 +++--- .../test/query-tests/security/CWE-926/AndroidManifest.xml | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll index 9d2d278c9be..9b9f484678e 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -9,10 +9,10 @@ class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { this.hasAnIntentFilterElement() and not this.getAnIntentFilterElement().getACategoryElement().getCategoryName() = "android.intent.category.LAUNCHER" and + not this.getAnIntentFilterElement().getAnActionElement().getActionName() = + "android.intent.action.MAIN" and not this.requiresPermissions() and not this.getParent().(AndroidApplicationXmlElement).requiresPermissions() and - not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() //and - //not this.getAnIntentFilterElement().getAnActionElement().getActionName() = - // "android.intent.action.MAIN" + not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() } } diff --git a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml index 7a5b89ce48c..210c97b26a2 100644 --- a/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml +++ b/java/ql/test/query-tests/security/CWE-926/AndroidManifest.xml @@ -102,6 +102,13 @@ + + + + + + From e003e2c809457d8c3f361128b31fa70240cbd6e7 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Fri, 12 Aug 2022 10:04:53 -0400 Subject: [PATCH 21/26] lib change note updates --- .../2022-08-09-android-manifest-new-class-and-predicates.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md b/java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md index 27740fca13e..95a1d8997cb 100644 --- a/java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md +++ b/java/ql/lib/change-notes/2022-08-09-android-manifest-new-class-and-predicates.md @@ -1,8 +1,8 @@ --- category: feature --- -* Added a new predicate, `requiresPermissions`, in the `AndroidComponentXmlElement` and `AndroidApplicationXmlElement` classes to detect if the element has explicitly set a value for its `android:permission` attribute. Also added `override` annotation to the pre-existing `requiresPermissions` predicate in the `AndroidProviderXmlElement` class. +* Added a new predicate, `requiresPermissions`, in the `AndroidComponentXmlElement` and `AndroidApplicationXmlElement` classes to detect if the element has explicitly set a value for its `android:permission` attribute. * Added a new predicate, `hasAnIntentFilterElement`, in the `AndroidComponentXmlElement` class to detect if a component contains an intent filter element. * Added a new predicate, `hasExportedAttribute`, in the `AndroidComponentXmlElement` class to detect if a component has an `android:exported` attribute. -* Added a new class, `AndroidCategoryXmlElement`, to represent a category element in an Android manifest file. Also added a new predicate, `getCategoryName`, in this class to get the name of the category element. +* Added a new class, `AndroidCategoryXmlElement`, to represent a category element in an Android manifest file. * Added a new predicate, `getACategoryElement`, in the `AndroidIntentFilterXmlElement` class to get a category element of an intent filter. From efac4b197df395eb2b91820e33a8018d57ed4eb5 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Fri, 12 Aug 2022 12:16:49 -0400 Subject: [PATCH 22/26] removed another comment --- java/ql/lib/semmle/code/xml/AndroidManifest.qll | 1 - 1 file changed, 1 deletion(-) diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index 0e1977d5f17..1560e598b90 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -139,7 +139,6 @@ class AndroidPermissionXmlAttribute extends XmlAttribute { AndroidPermissionXmlAttribute() { this.getNamespace().getPrefix() = "android" and this.getName() = ["permission", "readPermission", "writePermission"] - //this.getName() = ["permission"] } /** Holds if this is an `android:permission` attribute. */ From ac07544d70f63170c958683c9a670aa9f89bc3c5 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Fri, 12 Aug 2022 12:25:43 -0400 Subject: [PATCH 23/26] group negated expressions together --- .../code/java/security/ImplicitlyExportedAndroidComponent.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll index 9b9f484678e..c38181d1a49 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -5,8 +5,8 @@ private import semmle.code.xml.AndroidManifest /** An implicitly exported Android component */ class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { ImplicitlyExportedAndroidComponent() { - not this.hasExportedAttribute() and this.hasAnIntentFilterElement() and + not this.hasExportedAttribute() and not this.getAnIntentFilterElement().getACategoryElement().getCategoryName() = "android.intent.category.LAUNCHER" and not this.getAnIntentFilterElement().getAnActionElement().getActionName() = From f34e23bdbacf90abdd8236ba6482f2be2d9c0367 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Wed, 17 Aug 2022 14:07:10 -0400 Subject: [PATCH 24/26] adjusted comments and precision level --- .../security/ImplicitlyExportedAndroidComponent.qll | 11 ++++++++++- .../CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll index c38181d1a49..4aa21c4a260 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitlyExportedAndroidComponent.qll @@ -2,15 +2,24 @@ private import semmle.code.xml.AndroidManifest -/** An implicitly exported Android component */ +/** + * An Android component without an `exported` attribute explicitly set + * that also has an `intent-filter` element. + */ class ImplicitlyExportedAndroidComponent extends AndroidComponentXmlElement { ImplicitlyExportedAndroidComponent() { this.hasAnIntentFilterElement() and not this.hasExportedAttribute() and + // Components with category LAUNCHER or with action MAIN need to be exported since + // they are entry-points to the application. As a result, we do not consider these + // components to be implicitly exported since the developer intends them to be exported anyways. not this.getAnIntentFilterElement().getACategoryElement().getCategoryName() = "android.intent.category.LAUNCHER" and not this.getAnIntentFilterElement().getAnActionElement().getActionName() = "android.intent.action.MAIN" and + // The `permission` attribute can be used to limit components' exposure to other applications. + // As a result, we do not consider components with an explicitly set `permission` attribute to be + // implicitly exported since the developer has already limited access to such components. not this.requiresPermissions() and not this.getParent().(AndroidApplicationXmlElement).requiresPermissions() and not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index 00d5833b60a..eb12d13b060 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -7,7 +7,7 @@ * @id java/android/implicitly-exported-component * @tags security * external/cwe/cwe-926 - * @precision medium + * @precision high */ import java From 733078183ed3695edb5ea5060b49cbe25fe7626d Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 22 Aug 2022 11:08:42 -0400 Subject: [PATCH 25/26] update query description --- .../Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql index eb12d13b060..9796a3f59e2 100644 --- a/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql +++ b/java/ql/src/Security/CWE/CWE-926/ImplicitlyExportedAndroidComponent.ql @@ -1,6 +1,6 @@ /** * @name Implicitly exported Android component - * @description An Android component with an '' and no 'android:exported' attribute is implicitly exported. This can allow for improper access to the component and its data. + * @description Android components with an '' and no 'android:exported' attribute are implicitly exported, which can allow for improper access to the components themselves and to their data. * @kind problem * @problem.severity warning * @security-severity 8.2 From 0136c7542b5521081c492cda9445d6d7d0de1281 Mon Sep 17 00:00:00 2001 From: Jami Cogswell Date: Mon, 22 Aug 2022 12:39:43 -0400 Subject: [PATCH 26/26] update XML to Xml due to recent deprecation --- java/ql/lib/semmle/code/xml/AndroidManifest.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index 1560e598b90..b5c79efa853 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -286,7 +286,7 @@ class AndroidActionXmlElement extends XmlElement { /** * A `` element in an Android manifest file. */ -class AndroidCategoryXmlElement extends XMLElement { +class AndroidCategoryXmlElement extends XmlElement { AndroidCategoryXmlElement() { this.getFile() instanceof AndroidManifestXmlFile and this.getName() = "category" } @@ -295,7 +295,7 @@ class AndroidCategoryXmlElement extends XMLElement { * Gets the name of this category. */ string getCategoryName() { - exists(XMLAttribute attr | + exists(XmlAttribute attr | attr = this.getAnAttribute() and attr.getNamespace().getPrefix() = "android" and attr.getName() = "name"