From 714bc6ad3519fcd42c06f832423cef5d665009de Mon Sep 17 00:00:00 2001 From: Ivan Dyatlov Date: Mon, 24 Jun 2024 10:03:06 +0100 Subject: [PATCH 1/4] Use apkSigner instead of zip file contents to extract digest of apk (to support all signing schemes) --- buildSrc/src/main/kotlin/Versions.kt | 2 + vendor/vendor-android/base/build.gradle.kts | 1 + .../android/executor/ApkFileHasher.kt | 38 +++++++------------ 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 979c641d4..0b7b8a7e3 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -10,6 +10,7 @@ object Versions { val slf4jAPI = "1.0.0" val logbackClassic = "1.2.3" val axmlParser = "1.0" + val apkSig = "8.5.0" val bugsnag = "3.6.1" val androidGradleVersion = "8.0.2" @@ -66,6 +67,7 @@ object Libraries { val ktorAuth = "io.ktor:ktor-client-auth-jvm:${Versions.ktor}" val ktorApacheClient = "io.ktor:ktor-client-apache:${Versions.ktor}" val axmlParser = "com.shazam:axmlparser:${Versions.axmlParser}" + val apkSig = "com.android.tools.build:apksig:${Versions.apkSig}" val gson = "com.google.code.gson:gson:${Versions.gson}" val apacheCommonsText = "org.apache.commons:commons-text:${Versions.apacheCommonsText}" val apacheCommonsIO = "commons-io:commons-io:${Versions.apacheCommonsIO}" diff --git a/vendor/vendor-android/base/build.gradle.kts b/vendor/vendor-android/base/build.gradle.kts index 56b00fa50..cbc706d39 100644 --- a/vendor/vendor-android/base/build.gradle.kts +++ b/vendor/vendor-android/base/build.gradle.kts @@ -10,6 +10,7 @@ dependencies { implementation(Libraries.kotlinLogging) implementation(Libraries.dexTestParser) implementation(Libraries.axmlParser) + implementation(Libraries.apkSig) implementation(Libraries.jacksonAnnotations) implementation(Libraries.scalr) implementation(project(":core")) diff --git a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/ApkFileHasher.kt b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/ApkFileHasher.kt index f8f072a31..8d00d750b 100644 --- a/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/ApkFileHasher.kt +++ b/vendor/vendor-android/base/src/main/kotlin/com/malinskiy/marathon/android/executor/ApkFileHasher.kt @@ -1,40 +1,28 @@ package com.malinskiy.marathon.android +import com.android.apksig.ApkVerifier import com.malinskiy.marathon.io.FileHasher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File -import java.nio.file.FileSystems -import java.nio.file.Files -import java.nio.file.Paths +import java.security.MessageDigest +import java.util.HexFormat /** * Extracts digest of APK contents from signature file */ class ApkFileHasher : FileHasher { - override suspend fun getHash(file: File): String = withContext(Dispatchers.IO) { - val zipFile = Paths.get(file.absolutePath) - - FileSystems.newFileSystem(zipFile, null as ClassLoader?) - .use { fileSystem -> - val certFile = fileSystem.getPath(SIGNATURE_FILE_PATH) - Files.newInputStream(certFile) - .use { - it - .bufferedReader() - .lineSequence() - .firstOrNull { line -> line.contains(DIGEST_MANIFEST_PROPERTY) } - ?.substringAfter(PROPERTY_DELIMITER) - ?.trim() - ?: throw IllegalArgumentException("Manifest digest not found") - } - } - } + val sha256digest = MessageDigest.getInstance("SHA-256") - private companion object { - private const val SIGNATURE_FILE_PATH = "META-INF/CERT.SF" - private const val DIGEST_MANIFEST_PROPERTY = "-Digest-Manifest: " - private const val PROPERTY_DELIMITER = ": " + override suspend fun getHash(file: File): String = withContext(Dispatchers.IO) { + val result = ApkVerifier.Builder(file).build().verify() + if (result.signerCertificates.isNotEmpty()) { + val certificate = result.signerCertificates.first() + // https://cs.android.com/android/platform/superproject/main/+/main:tools/apksig/src/apksigner/java/com/android/apksigner/ApkSignerTool.java;l=1151;drc=d5137445c0d4067406cb3e38aade5507ff2fcd16 + HexFormat.of().formatHex(sha256digest.digest(certificate.encoded)) + } else { + throw IllegalArgumentException("Certificate not found") + } } } From e0b06a77b0b5aabf76900200e2d43b3fc3aa5ff0 Mon Sep 17 00:00:00 2001 From: Ivan Dyatlov Date: Mon, 24 Jun 2024 14:06:11 +0100 Subject: [PATCH 2/4] Downgrade to 8.4.2 --- buildSrc/src/main/kotlin/Versions.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index 0b7b8a7e3..e0083a596 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -10,7 +10,7 @@ object Versions { val slf4jAPI = "1.0.0" val logbackClassic = "1.2.3" val axmlParser = "1.0" - val apkSig = "8.5.0" + val apkSig = "8.4.2" val bugsnag = "3.6.1" val androidGradleVersion = "8.0.2" From be1e838c25d8f52268042a263b9d54186ad3dac4 Mon Sep 17 00:00:00 2001 From: Ivan Dyatlov Date: Mon, 24 Jun 2024 14:39:48 +0100 Subject: [PATCH 3/4] Use Gradle version for ApkSig, remove deprecated API usage --- buildSrc/src/main/kotlin/Versions.kt | 5 ++--- cli/build.gradle.kts | 6 +++--- .../kotlin/com/malinskiy/marathon/ConfigurationFactory.kt | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index e0083a596..f27c86471 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -10,10 +10,9 @@ object Versions { val slf4jAPI = "1.0.0" val logbackClassic = "1.2.3" val axmlParser = "1.0" - val apkSig = "8.4.2" val bugsnag = "3.6.1" - val androidGradleVersion = "8.0.2" + val androidGradleVersion = "8.4.2" val spek = "1.1.5" val junit5 = "5.6.0" @@ -67,7 +66,7 @@ object Libraries { val ktorAuth = "io.ktor:ktor-client-auth-jvm:${Versions.ktor}" val ktorApacheClient = "io.ktor:ktor-client-apache:${Versions.ktor}" val axmlParser = "com.shazam:axmlparser:${Versions.axmlParser}" - val apkSig = "com.android.tools.build:apksig:${Versions.apkSig}" + val apkSig = "com.android.tools.build:apksig:${Versions.androidGradleVersion}" val gson = "com.google.code.gson:gson:${Versions.gson}" val apacheCommonsText = "org.apache.commons:commons-text:${Versions.apacheCommonsText}" val apacheCommonsIO = "commons-io:commons-io:${Versions.apacheCommonsIO}" diff --git a/cli/build.gradle.kts b/cli/build.gradle.kts index 69ef122af..cb54f09f6 100644 --- a/cli/build.gradle.kts +++ b/cli/build.gradle.kts @@ -59,14 +59,14 @@ buildConfig { } sourceSets["main"].java { - srcDirs.add(File(buildDir, "gen")) + srcDirs.add(layout.buildDirectory.dir("gen").get().asFile) } // At the moment for non-Android projects you need to explicitly // mark the generated code for correct highlighting in IDE. idea { module { - sourceDirs = sourceDirs + file("${project.buildDir}/gen/buildconfig/src/main") - generatedSourceDirs = generatedSourceDirs + file("${project.buildDir}/gen/buildconfig/src/main") + sourceDirs = sourceDirs + project.layout.buildDirectory.dir("gen/buildconfig/src/main").get().asFile + generatedSourceDirs = generatedSourceDirs + project.layout.buildDirectory.dir("gen/buildconfig/src/main").get().asFile } } diff --git a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt index 443ef9d92..262cee6cd 100644 --- a/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt +++ b/marathon-gradle-plugin/src/main/kotlin/com/malinskiy/marathon/ConfigurationFactory.kt @@ -72,7 +72,7 @@ private fun createConfiguration( private fun getOutputDirectory(project: Project, extensionConfig: MarathonExtension): File = extensionConfig.baseOutputDir?.let { File(it) } - ?: project.buildDir.resolve("reports/marathon") + ?: project.layout.buildDirectory.dir("reports/marathon").get().asFile private fun createAndroidConfiguration( extension: MarathonExtension, From 8fad85a74ee7da5bca26b1cffe098f6737fc3cb3 Mon Sep 17 00:00:00 2001 From: Ivan Dyatlov Date: Mon, 24 Jun 2024 16:29:30 +0100 Subject: [PATCH 4/4] Add default password previously provided by axiom/rsync-server container itself --- .../kotlin/com/malinskiy/marathon/ios/DerivedDataManagerSpek.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vendor/vendor-ios/src/test/kotlin/com/malinskiy/marathon/ios/DerivedDataManagerSpek.kt b/vendor/vendor-ios/src/test/kotlin/com/malinskiy/marathon/ios/DerivedDataManagerSpek.kt index 45d6ec177..9ba52c8f2 100644 --- a/vendor/vendor-ios/src/test/kotlin/com/malinskiy/marathon/ios/DerivedDataManagerSpek.kt +++ b/vendor/vendor-ios/src/test/kotlin/com/malinskiy/marathon/ios/DerivedDataManagerSpek.kt @@ -53,6 +53,7 @@ object DerivedDataManagerSpek : Spek( "/root/.ssh/authorized_keys", BindMode.READ_WRITE ) + .withEnv("PASSWORD", "pass") .withExposedPorts(22, 873) container.start()