038d3e7d7bc7
fix: Require protected release signing injection (PB-26-025, v2.14.90)
- time
- 2026-09-02 11:14 UTC · 1 month ago
- machine
- koh2
- parent
- c944bc468b35
↓ peach-android-038d3e7d.tar.gzatlas at this save
files changed4 files
.gitignore+3 −0VERSION+1 −1app/build.gradle+31 −11scripts/kepr-build-apk.sh+15 −27.gitignore+3 −0
@@ -0,0 +1,3 @@1+# Local SDK paths and developer-only configuration must never enter another VCS.2+local.properties3+app/build.gradle+31 −11
@@ -50,18 +50,25 @@5050 5151 signingConfigs {5252 release {53- // Read from local.properties (koh-ignored) so the keystore and its54- // password never enter version history. Absent on a machine without55- // the key -> release build stays unsigned rather than failing.56- def props = new Properties()57- def f = rootProject.file("local.properties")58- if (f.exists()) { f.withInputStream { props.load(it) } }59- if (props.getProperty("peachStoreFile")) {60- storeFile file(props.getProperty("peachStoreFile"))61- storePassword props.getProperty("peachStorePassword")62- keyAlias props.getProperty("peachKeyAlias")63- keyPassword props.getProperty("peachKeyPassword")53+ // Release authority comes only from process-scoped protected54+ // injection. local.properties is limited to non-secret local SDK55+ // configuration and is never a signing-secret store.56+ def signingEnvironment = [57+ storeFile: System.getenv("PEACH_RELEASE_STORE_FILE"),58+ storePassword: System.getenv("PEACH_RELEASE_STORE_PASSWORD"),59+ keyAlias: System.getenv("PEACH_RELEASE_KEY_ALIAS"),60+ keyPassword: System.getenv("PEACH_RELEASE_KEY_PASSWORD"),61+ ]62+ def configuredValues = signingEnvironment.values().count { it != null && !it.isEmpty() }63+ if (configuredValues != 0 && configuredValues != signingEnvironment.size()) {64+ throw new GradleException("incomplete protected release signing environment")6465 }66+ if (configuredValues == signingEnvironment.size()) {67+ storeFile file(signingEnvironment.storeFile)68+ storePassword signingEnvironment.storePassword69+ keyAlias signingEnvironment.keyAlias70+ keyPassword signingEnvironment.keyPassword71+ }6572 // v3 is the only escape hatch from "this key is forever": it carries a6673 // rotation lineage, so a future key can be proven to descend from this6774 // one instead of forcing every tester to uninstall and lose their@@ -90,6 +97,19 @@9097 signingConfig signingConfigs.release9198 minifyEnabled false9299 proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"100+ }101+ }102+103+ // Never let a release task silently emit an unsigned artifact. Debug tasks104+ // remain usable in fresh workspaces that do not hold release authority.105+ gradle.taskGraph.whenReady { graph ->106+ if (graph.allTasks.any { it.project == project && it.name.toLowerCase().contains("release") } &&107+ !signingConfigs.release.isSigningReady()) {108+ throw new GradleException(109+ "release signing requires PEACH_RELEASE_STORE_FILE, " +110+ "PEACH_RELEASE_STORE_PASSWORD, PEACH_RELEASE_KEY_ALIAS, and " +111+ "PEACH_RELEASE_KEY_PASSWORD",112+ )93113 }94114 }95115 scripts/kepr-build-apk.sh+15 −27
@@ -1,8 +1,8 @@11 #!/bin/sh22 # Build and publish the signed release APK for Kepr Builds.33 #4-# The release keystore is read from local.properties (koh-ignored). The script5-# checks that configuration before starting Gradle, then validates the produced4+# Release authority is injected through protected process environment variables.5+# The script checks that configuration before starting Gradle, then validates the produced66 # APK's package, version, APK Signature Scheme, and pinned release certificate77 # before copying anything to the Kepr staging directory.88 set -eu@@ -22,44 +22,32 @@2222 # shellcheck source=scripts/kepr-build-helpers.sh2323 . "$ROOT_DIR/scripts/kepr-build-helpers.sh"2424 25-PROPERTIES_FILE="$ROOT_DIR/local.properties"26-if [ ! -r "$PROPERTIES_FILE" ]; then27- echo "refusing to publish: local.properties is missing or unreadable" >&228- exit 129-fi25+: "${PEACH_RELEASE_STORE_FILE:=}"26+: "${PEACH_RELEASE_STORE_PASSWORD:=}"27+: "${PEACH_RELEASE_KEY_ALIAS:=}"28+: "${PEACH_RELEASE_KEY_PASSWORD:=}"3029 31-property() {32- # Values are inspected only for presence/path checks; never print this file33- # because it contains the release passwords.34- sed -n "s/^$1=//p" "$PROPERTIES_FILE" | sed -n '1p'35-}36-37-STORE_FILE_PROPERTY=$(property peachStoreFile)38-STORE_PASSWORD=$(property peachStorePassword)39-KEY_ALIAS=$(property peachKeyAlias)40-KEY_PASSWORD=$(property peachKeyPassword)41-42-if [ -z "$STORE_FILE_PROPERTY" ] || [ -z "$STORE_PASSWORD" ] ||43- [ -z "$KEY_ALIAS" ] || [ -z "$KEY_PASSWORD" ]; then44- echo "refusing to publish: incomplete release signing configuration" >&230+if [ -z "$PEACH_RELEASE_STORE_FILE" ] || [ -z "$PEACH_RELEASE_STORE_PASSWORD" ] ||31+ [ -z "$PEACH_RELEASE_KEY_ALIAS" ] || [ -z "$PEACH_RELEASE_KEY_PASSWORD" ]; then32+ echo "refusing to publish: protected release signing environment is incomplete" >&24533 exit 14634 fi4735 48-case "$STORE_FILE_PROPERTY" in49- /*) STORE_FILE="$STORE_FILE_PROPERTY" ;;50- *) STORE_FILE="$ROOT_DIR/$STORE_FILE_PROPERTY" ;;36+case "$PEACH_RELEASE_STORE_FILE" in37+ /*) STORE_FILE="$PEACH_RELEASE_STORE_FILE" ;;38+ *) STORE_FILE="$ROOT_DIR/$PEACH_RELEASE_STORE_FILE" ;;5139 esac5240 if [ ! -f "$STORE_FILE" ] || [ ! -r "$STORE_FILE" ]; then5341 echo "refusing to publish: configured release keystore is missing or unreadable" >&25442 exit 15543 fi5644 57-# Resolve the SDK from the environment first, then the same local.properties58-# Gradle uses. Pick the newest *versioned* build-tools directory containing both45+# Resolve the SDK from the environment first, then the non-secret sdk.dir in46+# local.properties. Pick the newest *versioned* build-tools directory containing both5947 # tools; build-tools/latest is intentionally never trusted or consulted.6048 SDK_ROOT="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"6149 if [ -z "$SDK_ROOT" ]; then62- SDK_ROOT=$(property sdk.dir)50+ SDK_ROOT=$(sed -n 's/^sdk\.dir=//p' "$ROOT_DIR/local.properties" 2>/dev/null | sed -n '1p')6351 fi6452 if [ -z "$SDK_ROOT" ] || [ ! -d "$SDK_ROOT/build-tools" ]; then6553 echo "refusing to publish: Android SDK build-tools directory not found" >&2