/** * Provides classes and predicates for working with Android manifest files. */ import XML /** * An Android manifest file, named `AndroidManifest.xml`. */ class AndroidManifestXmlFile extends XmlFile { AndroidManifestXmlFile() { this.getBaseName() = "AndroidManifest.xml" and count(XmlElement e | e = this.getAChild()) = 1 and this.getAChild().getName() = "manifest" } /** * Gets the top-level `` element in this Android manifest file. */ AndroidManifestXmlElement getManifestElement() { result = this.getAChild() } /** * Holds if this Android manifest file is located in a build directory. */ predicate isInBuildDirectory() { this.getFile().getRelativePath().matches("%build%") } } /** * A `` element in an Android manifest file. */ class AndroidManifestXmlElement extends XmlElement { AndroidManifestXmlElement() { this.getParent() instanceof AndroidManifestXmlFile and this.getName() = "manifest" } /** * Gets the `` child element of this `` element. */ AndroidApplicationXmlElement getApplicationElement() { result = this.getAChild() } /** * Gets the value of the `package` attribute of this `` element. */ string getPackageAttributeValue() { result = this.getAttributeValue("package") } } /** * An `` element in an Android manifest file. */ class AndroidApplicationXmlElement extends XmlElement { AndroidApplicationXmlElement() { this.getParent() instanceof AndroidManifestXmlElement and this.getName() = "application" } /** * Gets a component child element of this `` element. */ AndroidComponentXmlElement getAComponentElement() { result = this.getAChild() } /** * Holds if this application element has the attribute `android:debuggable` set to `true`. */ predicate isDebuggable() { exists(AndroidXmlAttribute attr | this.getAnAttribute() = attr and attr.getName() = "debuggable" and attr.getValue() = "true" ) } /** * Holds if this application element has explicitly set a value for its `android:permission` attribute. */ predicate requiresPermissions() { this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull() } } /** * An `` element in an Android manifest file. */ class AndroidActivityXmlElement extends AndroidComponentXmlElement { AndroidActivityXmlElement() { this.getName() = "activity" } } /** * A `` element in an Android manifest file. */ class AndroidServiceXmlElement extends AndroidComponentXmlElement { AndroidServiceXmlElement() { this.getName() = "service" } } /** * A `` element in an Android manifest file. */ class AndroidReceiverXmlElement extends AndroidComponentXmlElement { AndroidReceiverXmlElement() { this.getName() = "receiver" } } /** * An XML attribute with the `android:` prefix. */ class AndroidXmlAttribute extends XmlAttribute { AndroidXmlAttribute() { this.getNamespace().getPrefix() = "android" } } /** * A `` element in an Android manifest file. */ class AndroidProviderXmlElement extends AndroidComponentXmlElement { AndroidProviderXmlElement() { this.getName() = "provider" } /** * Holds if this provider element has explicitly set a value for either its * `android:permission` attribute or its `android:readPermission` and `android:writePermission` * attributes. */ override predicate requiresPermissions() { this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull() or this.getAnAttribute().(AndroidPermissionXmlAttribute).isWrite() and this.getAnAttribute().(AndroidPermissionXmlAttribute).isRead() } /** * Holds if this provider element has the attribute `android:grantUriPermissions` set to `true`. */ predicate grantsUriPermissions() { exists(AndroidXmlAttribute attr | this.getAnAttribute() = attr and attr.getName() = "grantUriPermissions" and attr.getValue() = "true" ) } } /** * The attribute `android:perrmission`, `android:readPermission`, or `android:writePermission`. */ class AndroidPermissionXmlAttribute extends XmlAttribute { AndroidPermissionXmlAttribute() { this.getNamespace().getPrefix() = "android" and this.getName() = ["permission", "readPermission", "writePermission"] } /** Holds if this is an `android:permission` attribute. */ predicate isFull() { this.getName() = "permission" } /** Holds if this is an `android:readPermission` attribute. */ predicate isRead() { this.getName() = "readPermission" } /** Holds if this is an `android:writePermission` attribute. */ predicate isWrite() { this.getName() = "writePermission" } } /** * The ` element of a `` in an Android manifest file. */ class AndroidPathPermissionXmlElement extends XmlElement { AndroidPathPermissionXmlElement() { this.getParent() instanceof AndroidProviderXmlElement and this.hasName("path-permission") } } /** * An Android component element in an Android manifest file. */ class AndroidComponentXmlElement extends XmlElement { AndroidComponentXmlElement() { this.getParent() instanceof AndroidApplicationXmlElement and this.getName().regexpMatch("(activity|service|receiver|provider)") } /** * Gets an `` child element of this component element. */ AndroidIntentFilterXmlElement getAnIntentFilterElement() { result = this.getAChild() } /** * Holds if this component element has an `` child element. */ predicate hasAnIntentFilterElement() { exists(this.getAnIntentFilterElement()) } /** * Gets the value of the `android:name` attribute of this component element. */ string getComponentName() { exists(XmlAttribute attr | attr = this.getAnAttribute() and attr.getNamespace().getPrefix() = "android" and attr.getName() = "name" | result = attr.getValue() ) } /** * Gets the resolved value of the `android:name` attribute of this component element. */ string getResolvedComponentName() { if this.getComponentName().matches(".%") then result = this.getParent() .(XmlElement) .getParent() .(AndroidManifestXmlElement) .getPackageAttributeValue() + this.getComponentName() else result = this.getComponentName() } /** * Gets the value of the `android:exported` attribute of this component element. */ string getExportedAttributeValue() { exists(XmlAttribute attr | attr = this.getAnAttribute() and attr.getNamespace().getPrefix() = "android" and attr.getName() = "exported" | result = attr.getValue() ) } /** * Holds if the `android:exported` attribute of this component element is `true`. */ predicate isExported() { this.getExportedAttributeValue() = "true" } /** * 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() { exists(this.getExportedAttributeValue()) } /** * Holds if this component element has explicitly set a value for its `android:permission` attribute. */ predicate requiresPermissions() { this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull() } } /** * An `` element in an Android manifest file. */ class AndroidIntentFilterXmlElement extends XmlElement { AndroidIntentFilterXmlElement() { this.getFile() instanceof AndroidManifestXmlFile and this.getName() = "intent-filter" } /** * Gets an `` child element of this `` element. */ AndroidActionXmlElement getAnActionElement() { result = this.getAChild() } /** * Gets a `` child element of this `` element. */ AndroidCategoryXmlElement getACategoryElement() { result = this.getAChild() } } /** * An `` element in an Android manifest file. */ class AndroidActionXmlElement extends XmlElement { AndroidActionXmlElement() { this.getFile() instanceof AndroidManifestXmlFile and this.getName() = "action" } /** * Gets the name of this action. */ string getActionName() { exists(XmlAttribute attr | attr = this.getAnAttribute() and attr.getNamespace().getPrefix() = "android" and attr.getName() = "name" | result = attr.getValue() ) } } /** * 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() ) } }