Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import java.util.Properties

plugins {
id("com.android.application")
id("kotlin-android")
Expand Down Expand Up @@ -30,11 +32,52 @@ android {
versionName = flutter.versionName
}

signingConfigs {
// Release signing: configured via local key.properties (NOT committed) OR
// CI-injected env vars (SOLUX_KEYSTORE_PATH / _PASSWORD / _KEY_ALIAS / _KEY_PASSWORD).
// Falls back to debug keystore only in dev builds — release builds without a
// properly-configured release keystore will fail with a clear error.
create("release") {
val keyPropsFile = rootProject.file("key.properties")
if (keyPropsFile.exists()) {
val keyProps = Properties().apply {
load(keyPropsFile.inputStream())
}
storeFile = file(keyProps.getProperty("storeFile") ?: "")
storePassword = keyProps.getProperty("storePassword") ?: ""
keyAlias = keyProps.getProperty("keyAlias") ?: ""
keyPassword = keyProps.getProperty("keyPassword") ?: ""
} else {
System.getenv("SOLUX_KEYSTORE_PATH")?.let {
storeFile = file(it)
storePassword = System.getenv("SOLUX_KEYSTORE_PASSWORD") ?: ""
keyAlias = System.getenv("SOLUX_KEY_ALIAS") ?: ""
keyPassword = System.getenv("SOLUX_KEY_PASSWORD") ?: ""
}
}
}
}

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.getByName("debug")
// Audit H2 (2026-05-07): release builds previously signed with the debug
// keystore — anyone could resign the published APK and impersonate Solux.
// Now signs with the release config (loaded from key.properties or
// CI env vars). Build fails fast if neither is provided.
signingConfig = signingConfigs.findByName("release")
?: throw GradleException("Release signing config not found. Provide " +
"key.properties OR set SOLUX_KEYSTORE_* env vars before building.")

// Audit H3 (2026-05-07): R8 minify + resource shrinking. When the crypto
// layer lands, an unminified release binary lets attackers read keystore
// logic + signing flow trivially. Enabling R8 now (pre-crypto) so the
// discipline is in place before any sensitive code lands.
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Solux Android ProGuard / R8 rules.
# Audit H3 (2026-05-07): R8 minify + resource shrinking enabled on release.
# Add specific keep rules below as new dependencies need them.

# Flutter built-in
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }

# When the crypto layer lands, add explicit -keep rules for any
# cryptographic classes that R8 might inadvertently strip.