Refactor the XML diff in reproducible script#14879
Open
BarbossHack wants to merge 1 commit into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contributor checklist
Fixes #1234syntaxDescription
Context
Currently, apkdiff compares XML (and AXML) files by parsing them with Androguard and recursively comparing each element and its children, reporting differences in attributes and child counts. It then attempts to whitelist some elements that are added by the Play Store.
Problems
❌ Complex diff
Comparing additions and modifications in an XML tree and presenting them in a human-readable way is not straightforward and is error-prone.
Currently, if we remove the whitelisted elements, these are the reported differences for AndroidManifest.xml:
[ XmlDifference(diff_type='child_count', path='manifest/application', attribute_name=None, first_value='13', second_value='16', child_tag='meta-data'), XmlDifference(diff_type='attribute', path='manifest/application/meta-data', attribute_name='{http://schemas.android.com/apk/res/android}name', first_value='com.android.vending.splits', second_value='com.android.stamp.source', child_tag=None), XmlDifference(diff_type='attribute', path='manifest/application/meta-data', attribute_name='{http://schemas.android.com/apk/res/android}resource', first_value='@7F180009', second_value=None, child_tag=None), XmlDifference(diff_type='attribute', path='manifest/application/meta-data', attribute_name='{http://schemas.android.com/apk/res/android}value', first_value=None, second_value='https://play.google.com/store', child_tag=None), XmlDifference(diff_type='attribute', path='manifest/uses-sdk', attribute_name='{http://schemas.android.com/apk/res/android}minSdkVersion', first_value='23', second_value='32', child_tag=None) ]These differences are difficult to interpret, and some of them are misleading. For example
com.android.vending.splitsand the new value ascom.android.stamp.source. In reality,com.android.vending.splitswas not modified;com.android.stamp.sourcewas added to the XML tree.Additionally, the current whitelisting approach is too broad and ends up hiding these inconsistencies.
❌ Maintainability and reviewability
Following the findings in #14809 (comment) , I wanted to exclude
minSdkVersionfrom the diff script. However, implementing this change in the current code made the logic harder to review, especially for people auditing the reproducibility script.Changes introduced by this MR
Refactor the XML diff function with the following flow:
a. Parse the AXML with androguard to
AXMLPrinterobjectsb. Remove the whitelisted elements from the root tree
c. Pretty-print the XML and compare the resulting output
d. Display any differences using a Git-style diff.
Add
uses-sdk//minSdkVersionto the list of whitelisted elements (as discussed in PlayStore APKs are not reproducible (8.10.2 -> 8.14.3) #14809 (comment) )Run
uvx ruff formatResults
With my refactor, if I remove all whitelist entries, the output is much more readable:
Comparing: /tmp/tmp.cNybtHNRBK/apks/splits/base-master.apk /tmp/tmp.cNybtHNRBK/org.thoughtcrime.securesms/org.thoughtcrime.securesms.apk Unzipping... Comparing zip entry names... Comparing zip entry contents... Comparing AndroidManifest.xml... AndroidManifest.xml files differ! --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,5 +1,5 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="171302" android:versionName="8.17.4" android:compileSdkVersion="36" android:compileSdkVersionCodename="16" android:requiredSplitTypes="base__abi,base__density" android:splitTypes="" package="org.thoughtcrime.securesms" platformBuildVersionCode="36" platformBuildVersionName="16"> - <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="35"/> + <uses-sdk android:minSdkVersion="32" android:targetSdkVersion="35"/> <uses-feature android:name="android.hardware.bluetooth" android:required="false"/> <uses-feature android:name="android.hardware.camera" android:required="true"/> <uses-feature android:name="android.hardware.location" android:required="false"/> @@ -694,6 +694,9 @@ </service> <receiver android:name="com.google.android.datatransport.runtime.scheduling.jobscheduling.AlarmManagerSchedulerBroadcastReceiver" android:exported="false"/> <meta-data android:name="com.android.vending.splits.required" android:value="true"/> + <meta-data android:name="com.android.stamp.source" android:value="https://play.google.com/store"/> + <meta-data android:name="com.android.stamp.type" android:value="STAMP_TYPE_DISTRIBUTION_APK"/> <meta-data android:name="com.android.vending.splits" android:resource="@7F180009"/> + <meta-data android:name="com.android.vending.derived.apk.id" android:value="4"/> </application> </manifest> APKs differ on file AndroidManifest.xml! Files extracted to the mismatches/ directory. Comparing resources.arsc... APKs don't match!I've tested this MR by comparing the following APKs: