Build the tag's APK in CI without the upload key - #10
Merged
Conversation
The release workflow held the upload keystore in four repository secrets and signed with it. That is a lot of blast radius for what CI is actually for: a runner that is compromised, or a workflow edited in a pull request, walks away with the key that identifies this app on Play, and a key cannot be rotated after the listing exists. CI now builds only the APK, with no key.properties, so Gradle falls back to the debug signing config. The artifact is for sideloading a tagged build onto a phone; the bundle for Play is built on the release machine by tool/build_release.ps1, which is now the only place the key is used. - The signature check is inverted: the workflow requires the Android debug certificate, because a real one here would mean the key had reached a runner. - The tag is checked against `version:` in pubspec.yaml, so `v2.0.0` cannot ship anything but 2.0.0, and the build is passed that version explicitly. - The artifact is named `srbguide-<version>-debug-signed.apk`, with its SHA-256, and the draft release says what it is and is not. Every run signs with a fresh debug key, so these do not update each other in place. - `create_upload_key.ps1` no longer offers to base64 the keystore for a secret that no longer exists. The four ANDROID_* secrets should be deleted from the repository; nothing reads them any more.
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.
The release workflow kept the upload keystore in four repository secrets and
signed with it. That is a lot of blast radius for what CI is for: a compromised
runner, or a workflow edited in a pull request, walks off with the key that
identifies this app on Play — and once the listing exists, that key cannot be
rotated.
What CI does now
A
v*tag builds only the APK, with noandroid/key.properties, so Gradlefalls back to the debug signing config it already has for that case. No secrets
are read.
debug certificate: a real one on a runner would mean the key had leaked.
pubspec.yaml.v2.0.0must be2.0.0, andthe build is passed that
versionName/versionCodeexplicitly, so a tag cannever claim a version the artifact does not carry.
srbguide-<version>-debug-signed.apkwith its SHA-256, andthe draft release states plainly what it is: sideload and QA only. Every run
generates its own debug key, so two CI builds do not update each other in
place — uninstall first.
analyze, the unit tests,validate_guide.dartand thetargetSdk 36checkon the merged manifest all stay.
The Play bundle is built on the release machine with
tool/build_release.ps1,which is now the only place the upload key is used, and which still verifies
the signature scheme, the certificate digest and
targetSdkbefore writing todist/.create_upload_key.ps1no longer offers to base64 the keystore for a secretthat no longer exists.
After merging
Delete
ANDROID_KEYSTORE_BASE64,ANDROID_KEYSTORE_PASSWORD,ANDROID_KEY_ALIASandANDROID_KEY_PASSWORDfromSettings → Secrets and variables → Actions. Nothing reads them, and a secretthat exists is a secret that can leak.
Verification
Ran the workflow's build path locally with
android/key.propertiesmovedaside, which is exactly the state a runner is in:
So the build succeeds without a keystore, the inverted certificate check
passes, and the manifest the
targetSdkstep reads is still produced by anAPK-only build. The workflow YAML parses.