From 8f58fcf872b923a440d07386ee4ae366f1cb1ac7 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Fri, 3 Jan 2020 18:24:46 +0100 Subject: [PATCH 1/2] module workflow --- build.gradle | 5 +- src/main/kotlin/StringCare.kt | 261 +++++++----------- src/main/kotlin/components/AParser.kt | 11 +- src/main/kotlin/components/Execution.kt | 1 + src/main/kotlin/components/Extensions.kt | 105 +++++-- src/main/kotlin/components/Fingerprint.kt | 49 ++-- src/main/kotlin/components/Stark.kt | 5 +- src/main/kotlin/components/Tasks.kt | 2 +- src/main/kotlin/components/Vars.kt | 7 +- src/main/kotlin/components/XParser.kt | 20 +- .../kotlin/components/jni/libsignKey.dylib | Bin 178940 -> 33780 bytes src/main/kotlin/task/SCPreview.kt | 84 ++++-- src/main/kotlin/task/SCTestObfuscation.kt | 78 ++++-- src/test/kotlin/AssetsTest.kt | 17 +- src/test/kotlin/SCTest.kt | 30 +- 15 files changed, 363 insertions(+), 312 deletions(-) diff --git a/build.gradle b/build.gradle index 4618b50..d815e73 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group 'com.stringcare' -version '3.6.3' +version '3.6.4' def siteUrl = 'https://github.com/StringCare/KotlinGradlePlugin' def gitUrl = 'https://github.com/StringCare/KotlinGradlePlugin.git' @@ -14,11 +14,14 @@ def gitUrl = 'https://github.com/StringCare/KotlinGradlePlugin.git' sourceCompatibility = 1.8 repositories { + google() + jcenter() mavenCentral() } dependencies { implementation gradleApi() + implementation group: 'com.android.tools.build', name: 'gradle', version: '3.5.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation group: 'com.google.guava', name: 'guava', version: '28.0-jre' testCompile group: 'junit', name: 'junit', version: '4.12' diff --git a/src/main/kotlin/StringCare.kt b/src/main/kotlin/StringCare.kt index 1e2e950..dae47df 100644 --- a/src/main/kotlin/StringCare.kt +++ b/src/main/kotlin/StringCare.kt @@ -1,8 +1,7 @@ + import components.* -import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.internal.plugins.DslObject open class StringCare : Plugin { @@ -23,16 +22,11 @@ open class StringCare : Plugin { } @JvmStatic - internal val moduleMap: MutableMap = mutableMapOf() - - @JvmStatic - internal val variantMap: MutableMap = mutableMapOf() + internal var configuration: Configuration = defaultConfig() @JvmStatic - internal var mainModule: String = defaultMainModule + internal var variantMap = mutableMapOf() - @JvmStatic - internal var debug: Boolean = defaultDebug } private lateinit var project: Project @@ -45,181 +39,115 @@ open class StringCare : Plugin { absoluteProjectPath = project.absolutePath() this.project.afterEvaluate { - extension.modules.forEach { module -> - moduleMap[module.name] = Configuration(module.name).apply { - debug = extension.debug - } - if (module.srcFolders.isNotEmpty()) { - moduleMap[module.name]!!.srcFolders.addAll(module.srcFolders) - } - if (module.stringFiles.isNotEmpty()) { - moduleMap[module.name]!!.stringFiles.addAll(module.stringFiles) - } - if (module.assetsFiles.isNotEmpty()) { - moduleMap[module.name]!!.assetsFiles.addAll(module.assetsFiles) - } - - if (moduleMap[module.name]!!.srcFolders.isEmpty()) { - moduleMap[module.name]!!.srcFolders.addAll(defaultConfig().srcFolders) - } - if (moduleMap[module.name]!!.stringFiles.isEmpty()) { - moduleMap[module.name]!!.stringFiles.addAll(defaultConfig().stringFiles) - } + this.project.applicationVariants()?.forEach { variant -> + variantMap[variant.name] = variant.applicationId } - extension.variants.forEach { variant -> - variantMap[variant.name] = VariantApplicationId(variant.name).apply { - applicationId = variant.applicationId - mockedFingerprint = variant.mockedFingerprint - skip = variant.skip - } + configuration = this.project.config(extension) + + if (configuration.debug) { + PrintUtils.print("PATH", absoluteProjectPath) } - this.project.registerTask() + this.project.registerTask(configuration) } - this.project.gradle.addBuildListener(ExecutionListener( - debug = extension.debug, - dataFound = { _, _ -> - // nothing to do here - }, - mergeResourcesStart = { module, variant -> - fingerPrint(variantMap, module, variant, extension.debug) { key -> - when { - moduleMap.containsKey(module) -> { - val variantOrFlavor = extension.variants.find { - variant.toLowerCase().contains(it.name.toLowerCase()) - } - if (variantOrFlavor != null && variantOrFlavor.skip) { - PrintUtils.print(module, "Skipping $variant") - return@fingerPrint - } + this.project.gradle.addBuildListener( + ExecutionListener( + debug = configuration.debug, + dataFound = { _, _ -> + // nothing to do here + }, + mergeResourcesStart = { module, variant -> + configuration.name = module + if (variantMap.containsKey(variant)) { + configuration.applicationId = variantMap[variant] ?: "" + } + PrintUtils.print("", "ApplicationId: ${configuration.applicationId}", tab = true) + fingerPrint(module, variant, configuration) { key -> + if (configuration.skip) { + PrintUtils.print(module, "Skipping $variant") + return@fingerPrint + } - if ("none" == key || key.trim().isEmpty()) { - PrintUtils.print("No SHA1 key found for :$module:$variant") - return@fingerPrint - } + if ("none" == key || key.trim().isEmpty()) { + PrintUtils.print("No SHA1 key found for :$module:$variant") + return@fingerPrint + } - PrintUtils.print(module, "$variant:$key") - PrintUtils.print(module, backupStringRes) - moduleMap[module]?.let { configuration -> - backupResourceFiles(absoluteProjectPath, configuration) - } + PrintUtils.print(module, "$variant:$key") + PrintUtils.print(module, backupStringRes) + backupResourceFiles(absoluteProjectPath, configuration) - moduleMap[module]?.let { configuration -> - val files = locateResourceFiles(absoluteProjectPath, configuration) - files.forEach { file -> - modifyXML( - file.file, extension.main_module, key, extension.debug, - variantOrFlavor?.applicationId ?: "" - ) - } - } - PrintUtils.print(module, obfuscateStringRes) - } - else -> { - val defaultConfiguration = defaultConfig().apply { - name = module - } - if ("none" == key || key.trim().isEmpty()) { - PrintUtils.print("No SHA1 key found for :$module:$variant") - return@fingerPrint - } - PrintUtils.print(module, "$variant:$key") - PrintUtils.print(module, backupStringRes) - backupResourceFiles(absoluteProjectPath, defaultConfiguration) - PrintUtils.print(module, obfuscateStringRes) - val files = locateResourceFiles(absoluteProjectPath, defaultConfiguration) - files.forEach { file -> - modifyXML(file.file, extension.main_module, key, extension.debug) - } + + val files = locateResourceFiles(absoluteProjectPath, configuration) + files.forEach { file -> + modifyXML(file.file, key, configuration) } } - } + PrintUtils.print(module, obfuscateStringRes) + }, + mergeResourcesFinish = { module, _ -> + if (configuration.skip) { + return@ExecutionListener + } + PrintUtils.print(module, restoreStringRes) + restoreResourceFiles(absoluteProjectPath, module) + }, + mergeAssetsStart = { module, variant -> + configuration.name = module + if (variantMap.containsKey(variant)) { + configuration.applicationId = variantMap[variant] ?: "" + } + PrintUtils.print("", "ApplicationId: ${configuration.applicationId}", tab = true) + fingerPrint(module, variant, configuration) { key -> + if (configuration.skip) { + PrintUtils.print(module, "Skipping $variant") + return@fingerPrint + } - }, - mergeResourcesFinish = { module, variant -> - PrintUtils.print(module, restoreStringRes) - val variantOrFlavor = extension.variants.find { - variant.toLowerCase().contains(it.name.toLowerCase()) - } - if (variantOrFlavor != null && variantOrFlavor.skip) { - return@ExecutionListener - } - restoreResourceFiles(absoluteProjectPath, module) - }, - mergeAssetsStart = { module, variant -> - fingerPrint(variantMap, module, variant, extension.debug) { key -> - when { - moduleMap.containsKey(module) -> { - val variantOrFlavor = extension.variants.find { - variant.toLowerCase().contains(it.name.toLowerCase()) - } - if (variantOrFlavor != null && variantOrFlavor.skip) { - PrintUtils.print(module, "Skipping $variant") - return@fingerPrint - } + if ("none" == key || key.trim().isEmpty()) { + PrintUtils.print("No SHA1 key found for :$module:$variant") + return@fingerPrint + } - if ("none" == key || key.trim().isEmpty()) { - PrintUtils.print("No SHA1 key found for :$module:$variant") - return@fingerPrint - } + PrintUtils.print(module, "$variant:$key") + PrintUtils.print(module, backupAssets) + backupAssetsFiles(absoluteProjectPath, configuration) - PrintUtils.print(module, "$variant:$key") - PrintUtils.print(module, backupAssets) - moduleMap[module]?.let { configuration -> - backupAssetsFiles(absoluteProjectPath, configuration) - } - moduleMap[module]?.let { configuration -> - val files = locateAssetsFiles(absoluteProjectPath, configuration) - files.forEach { file -> - if (extension.debug) { - PrintUtils.print(null, file.file.getContent()) - } - obfuscateFile( - extension.main_module, - key, - file.file, - variantOrFlavor?.applicationId ?: "" - ) - if (extension.debug) { - PrintUtils.print(null, file.file.getContent()) - } - } + val files = locateAssetsFiles(absoluteProjectPath, configuration) + files.forEach { file -> + if (configuration.debug) { + PrintUtils.print(null, file.file.getContent()) + } + obfuscateFile( + key, + file.file, + configuration.applicationId + ) + if (configuration.debug) { + PrintUtils.print(null, file.file.getContent()) } - PrintUtils.print(module, obfuscateAssets) } + PrintUtils.print(module, obfuscateAssets) + } + }, + mergeAssetsFinish = { module, _ -> + if (configuration.skip) { + return@ExecutionListener } + PrintUtils.print(module, restoreAssets) + restoreAssetsFiles(absoluteProjectPath, module) } - }, - mergeAssetsFinish = { module, variant -> - PrintUtils.print(module, restoreAssets) - val variantOrFlavor = extension.variants.find { - variant.toLowerCase().contains(it.name.toLowerCase()) - } - if (variantOrFlavor != null && variantOrFlavor.skip) { - return@ExecutionListener - } - restoreAssetsFiles(absoluteProjectPath, module) - } - - )) + )) } open class Extension { - var debug: Boolean = false - var main_module: String = "app" - var modules: NamedDomainObjectContainer - @Suppress("UNCHECKED_CAST") - get() = DslObject(this).extensions.getByName("modules") as NamedDomainObjectContainer - internal set(value) { - DslObject(this).extensions.add("modules", value) - } - var variants: NamedDomainObjectContainer - @Suppress("UNCHECKED_CAST") - get() = DslObject(this).extensions.getByName("variants") as NamedDomainObjectContainer - internal set(value) { - DslObject(this).extensions.add("variants", value) - } + var assetsFiles = mutableListOf() + var stringFiles = mutableListOf() + var srcFolders = mutableListOf() + var debug = false + var skip = false + var mockedFingerprint = "" } open class Configuration(var name: String) { @@ -227,12 +155,9 @@ open class StringCare : Plugin { var stringFiles = mutableListOf() var srcFolders = mutableListOf() var debug = false - } - - open class VariantApplicationId(var name: String) { + var skip = false var applicationId = "" var mockedFingerprint = "" - var skip = false } } diff --git a/src/main/kotlin/components/AParser.kt b/src/main/kotlin/components/AParser.kt index ba6fc2d..40ea09d 100644 --- a/src/main/kotlin/components/AParser.kt +++ b/src/main/kotlin/components/AParser.kt @@ -1,8 +1,8 @@ package components -import StringCare.* +import StringCare +import StringCare.Configuration import models.AssetsFile -import models.ResourceFile import java.io.File fun locateAssetsFiles(projectPath: String, configuration: Configuration): List { @@ -36,9 +36,8 @@ fun restoreAssetsFiles(projectPath: String, module: String): List { return resourceFiles } -fun obfuscateFile(mainModule: String, key: String, file: File, mockId: String = "") { +fun obfuscateFile(key: String, file: File, mockId: String) { val obfuscation = Stark.obfuscate( - mainModule, key, file.readBytes(), mockId @@ -46,6 +45,6 @@ fun obfuscateFile(mainModule: String, key: String, file: File, mockId: String = file.writeBytes(obfuscation) } -fun revealFile(mainModule: String, key: String, file: File, mockId: String = "") { - file.writeBytes(Stark.reveal(mainModule, key, file.readBytes(), mockId)) +fun revealFile(key: String, file: File, mockId: String = "") { + file.writeBytes(Stark.reveal(key, file.readBytes(), mockId)) } diff --git a/src/main/kotlin/components/Execution.kt b/src/main/kotlin/components/Execution.kt index df4c544..adc53f7 100644 --- a/src/main/kotlin/components/Execution.kt +++ b/src/main/kotlin/components/Execution.kt @@ -1,5 +1,6 @@ package components +import StringCare import com.google.common.io.Files import models.ExecutionResult import java.io.IOException diff --git a/src/main/kotlin/components/Extensions.kt b/src/main/kotlin/components/Extensions.kt index 835c870..2fabb4d 100644 --- a/src/main/kotlin/components/Extensions.kt +++ b/src/main/kotlin/components/Extensions.kt @@ -1,10 +1,16 @@ package components import StringCare -import StringCare.* +import StringCare.Configuration +import StringCare.Extension +import com.android.build.gradle.AppExtension +import com.android.build.gradle.AppPlugin +import com.android.build.gradle.api.ApplicationVariant +import com.google.gson.Gson import groovy.json.StringEscapeUtils import models.AssetsFile import models.ResourceFile +import org.gradle.api.DomainObjectSet import org.gradle.api.Project import org.gradle.api.Task import org.slf4j.Logger @@ -245,40 +251,81 @@ fun File.assetsFile(configuration: Configuration): AssetsFile? { return if (valid) AssetsFile(validFile!!, sourceFolder, configuration.name) else null } -fun Project.absolutePath(): String = this.file(wrapperWindows).absolutePath.replace( - wrapperWindows, - emptyChar -) +fun Project.absolutePath(): String { + val fPath = this.file("build.gradle").absolutePath.replace( + "build.gradle", + emptyChar + ) + val p = fPath.split(File.separator) + return fPath.replace(p[p.size - 2] + File.separator, "") +} + +fun Project.module(): String { + val fPath = this.file("build.gradle").absolutePath.replace( + "build.gradle", + emptyChar + ) + val p = fPath.split(File.separator) + return p[p.size - 2] +} fun Project.createExtension(): Extension { - val extension = this.extensions.create(extensionName, Extension::class.java) - extension.modules = this.container(Configuration::class.java) - extension.variants = this.container(VariantApplicationId::class.java) - - StringCare.mainModule = extension.main_module - StringCare.debug = extension.debug - return extension -} - -fun Project.registerTask() { - this.tasks.addRule("Pattern: $gradleTaskNameObfuscate") { taskName -> - if (taskName.startsWith(gradleTaskNameObfuscate)) { - println("taskname $taskName") - task(taskName) { - it.doLast { - val variant = taskName.replace(gradleTaskNameObfuscate, "").uncapitalize() - println("variant $variant") - val task = this.tasks.getByName(gradleTaskNameObfuscate) as SCTestObfuscation - task.variant = variant - task.module = StringCare.mainModule - task.debug = StringCare.debug - task.greet() - } - } + return extensions.create(extensionName, Extension::class.java) +} + +fun Project.applicationVariants(): DomainObjectSet? { + if (this.plugins.hasPlugin(AppPlugin::class.java)) { + val extension = this.extensions.getByType(AppExtension::class.java) + return extension.applicationVariants + } + return null +} + +fun Project.config(extension: Extension): Configuration { + val mod = this.module() + return Configuration(mod).apply { + debug = extension.debug + skip = extension.skip + if (extension.srcFolders.isNotEmpty()) { + srcFolders.addAll(extension.srcFolders) + } + if (extension.stringFiles.isNotEmpty()) { + stringFiles.addAll(extension.stringFiles) + } + if (extension.assetsFiles.isNotEmpty()) { + assetsFiles.addAll(extension.assetsFiles) + } + if (extension.mockedFingerprint.isNotEmpty()) { + mockedFingerprint = extension.mockedFingerprint } } +} + +fun Project.registerTask(configuration: Configuration) { + val gson = Gson() + this.tasks.register(gradleTaskNameDoctor, SCPreview::class.java) this.tasks.register(gradleTaskNameObfuscate, SCTestObfuscation::class.java) + + val previewTask = this.tasks.getByPath(gradleTaskNameDoctor) as SCPreview + previewTask.module = configuration.name + previewTask.applicationId = configuration.applicationId + previewTask.srcFolders = gson.toJson(configuration.srcFolders) + previewTask.stringFiles = gson.toJson(configuration.stringFiles) + previewTask.assetsFiles = gson.toJson(configuration.assetsFiles) + previewTask.mockedFingerprint = configuration.mockedFingerprint + previewTask.debug = configuration.debug + previewTask.skip = configuration.skip + + val obfuscateTask = this.tasks.getByPath(gradleTaskNameObfuscate) as SCTestObfuscation + obfuscateTask.module = configuration.name + obfuscateTask.applicationId = configuration.applicationId + obfuscateTask.srcFolders = gson.toJson(configuration.srcFolders) + obfuscateTask.stringFiles = gson.toJson(configuration.stringFiles) + obfuscateTask.assetsFiles = gson.toJson(configuration.assetsFiles) + obfuscateTask.mockedFingerprint = configuration.mockedFingerprint + obfuscateTask.debug = configuration.debug + obfuscateTask.skip = configuration.skip } fun Process.outputString(): String { diff --git a/src/main/kotlin/components/Fingerprint.kt b/src/main/kotlin/components/Fingerprint.kt index a01018e..c8a2780 100644 --- a/src/main/kotlin/components/Fingerprint.kt +++ b/src/main/kotlin/components/Fingerprint.kt @@ -10,18 +10,18 @@ private class Fingerprint { private var moduleLocated = false } - fun extract(module: String, variant: String, debug: Boolean, trace: String): String { + fun extract(module: String, variant: String, configuration: StringCare.Configuration, trace: String): String { val lines = trace.split("\n") lines.forEach { line -> when { - line.toLowerCase().contains("downloading") -> if (debug) { - PrintUtils.print(module, line, debug) + line.toLowerCase().contains("downloading") -> if (configuration.debug) { + PrintUtils.print(module, line, configuration.debug) } - line.toLowerCase().contains("unzipping") -> if (debug) { - PrintUtils.print(module, line, debug) + line.toLowerCase().contains("unzipping") -> if (configuration.debug) { + PrintUtils.print(module, line, configuration.debug) } - line.toLowerCase().contains("permissions") -> if (debug) { - PrintUtils.print(module, line, debug) + line.toLowerCase().contains("permissions") -> if (configuration.debug) { + PrintUtils.print(module, line, configuration.debug) } line.toLowerCase().contains("config:") && moduleLocated && variantLocated -> { val k = line.split(": ")[1].trim() @@ -29,10 +29,10 @@ private class Fingerprint { if (!valid) { key = k PrintUtils.print(module, "\uD83E\uDD2F no config defined for variant $variant", true) - if (debug) { + if (configuration.debug) { until = key } - } else if (debug) { + } else if (configuration.debug) { PrintUtils.print(module, "Module: $module", true) PrintUtils.print(module, "Variant: $variant", true) } @@ -40,8 +40,8 @@ private class Fingerprint { } line.toLowerCase().contains("sha1") && moduleLocated && variantLocated -> { key = line.split(" ")[1] - if (debug) { - PrintUtils.print(module, line, debug) + if (configuration.debug) { + PrintUtils.print(module, line, configuration.debug) } } line.toLowerCase().contains("error") -> { @@ -49,12 +49,12 @@ private class Fingerprint { } line.toLowerCase().contains("valid until") && moduleLocated && variantLocated -> { until = line.split(": ")[1] - if (debug) { - PrintUtils.print(module, line, debug) + if (configuration.debug) { + PrintUtils.print(module, line, configuration.debug) } } - line.toLowerCase().contains("store") && moduleLocated && variantLocated -> if (debug) { - PrintUtils.print(module, line, debug) + line.toLowerCase().contains("store") && moduleLocated && variantLocated -> if (configuration.debug) { + PrintUtils.print(module, line, configuration.debug) } line.toLowerCase().contains("variant") && moduleLocated -> { val locV = line.split(" ")[1] @@ -64,7 +64,7 @@ private class Fingerprint { } line.toLowerCase().contains(":$module") -> moduleLocated = true } - if (key != null && (!debug || debug && until != null)) { + if (key != null && (!configuration.debug || configuration.debug && until != null)) { return key!! } } @@ -76,26 +76,23 @@ private class Fingerprint { * Gets the signing report trace and extracts the fingerprint */ fun fingerPrint( - variantMap: MutableMap, module: String, variant: String, - debug: Boolean, + configuration: StringCare.Configuration, keyFound: (key: String) -> Unit ) { - if (variantMap.containsKey(variant)) { - if (variantMap[variant]!!.mockedFingerprint.isNotEmpty()) { - keyFound(variantMap[variant]!!.mockedFingerprint) - return - } + if (configuration.mockedFingerprint.isNotEmpty()) { + keyFound(configuration.mockedFingerprint) + return } signingReportTask().runCommand { _, report -> - keyFound(report.extractFingerprint(module, variant, debug)) + keyFound(report.extractFingerprint(module, variant, configuration)) } } /** * Returns the SHA1 fingerprint for the given trace */ -fun String.extractFingerprint(module: String = "app", variant: String = "debug", debug: Boolean = false): String { - return Fingerprint().extract(module, variant, debug, this) +fun String.extractFingerprint(module: String = "app", variant: String = "debug", configuration: StringCare.Configuration): String { + return Fingerprint().extract(module, variant, configuration, this) } \ No newline at end of file diff --git a/src/main/kotlin/components/Stark.kt b/src/main/kotlin/components/Stark.kt index 53b624c..fbda41c 100644 --- a/src/main/kotlin/components/Stark.kt +++ b/src/main/kotlin/components/Stark.kt @@ -1,5 +1,6 @@ package components +import StringCare import java.io.File import java.io.FileOutputStream import java.util.zip.ZipFile @@ -66,10 +67,10 @@ open class Stark { } @JvmStatic - external fun obfuscate(mainModule: String, key: String, value: ByteArray, mockId: String): ByteArray + external fun obfuscate(key: String, value: ByteArray, mockId: String): ByteArray @JvmStatic - external fun reveal(mainModule: String, key: String, value: ByteArray, mockId: String): ByteArray + external fun reveal(key: String, value: ByteArray, mockId: String): ByteArray } } \ No newline at end of file diff --git a/src/main/kotlin/components/Tasks.kt b/src/main/kotlin/components/Tasks.kt index b4cf1ab..0aad30e 100644 --- a/src/main/kotlin/components/Tasks.kt +++ b/src/main/kotlin/components/Tasks.kt @@ -49,7 +49,7 @@ internal fun basicGradleTask(directory: String): String { internal fun obfuscationTestGradleTask(directory: String): String { return """ cd $directory && - ${gradleWrapper()} ${gradleTaskNameObfuscate}Debug + ${gradleWrapper()} $gradleTaskNameObfuscate """.trimIndent() } diff --git a/src/main/kotlin/components/Vars.kt b/src/main/kotlin/components/Vars.kt index 3ae57ae..b6dce04 100644 --- a/src/main/kotlin/components/Vars.kt +++ b/src/main/kotlin/components/Vars.kt @@ -1,13 +1,8 @@ package components -import java.io.File - -internal const val version = "3.6.3" +internal const val version = "3.6.4" internal const val testProjectName = "KotlinSample" -internal const val defaultDebug = false internal const val defaultMainModule = "app" -internal const val defaultVariant = "debug" -internal val mainModuleTest = "$testProjectName${File.separator}$defaultMainModule" internal const val gradleTaskNameDoctor = "stringcarePreview" internal const val gradleTaskNameObfuscate = "stringcareTestObfuscate" internal const val extensionName = "stringcare" diff --git a/src/main/kotlin/components/XParser.kt b/src/main/kotlin/components/XParser.kt index b928896..8d4d6ac 100644 --- a/src/main/kotlin/components/XParser.kt +++ b/src/main/kotlin/components/XParser.kt @@ -1,6 +1,7 @@ package components -import StringCare.* +import StringCare +import StringCare.Configuration import models.ResourceFile import models.SAttribute import models.StringEntity @@ -80,9 +81,9 @@ fun parseXML(file: File): List { return entities } -fun modifyXML(file: File, mainModule: String, key: String, debug: Boolean, mockId: String = "") { +fun modifyXML(file: File, key: String, configuration: Configuration) { val stringEntities = parseXML(file) - if (debug) { + if (configuration.debug) { PrintUtils.print(null, file.getContent(), true) } @@ -94,20 +95,19 @@ fun modifyXML(file: File, mainModule: String, key: String, debug: Boolean, mockI it.tag == "string" && it.index == i } entity?.let { - node.textContent = obfuscateStringEntity(mainModule, key, it, mockId).value + node.textContent = obfuscateStringEntity(key, it, configuration.applicationId).value } } file.updateXML(doc) file.removeAttributes() - if (debug) { + if (configuration.debug) { PrintUtils.print(null, file.getContent(), true) } } -fun obfuscateStringEntity(mainModule: String, key: String, entity: StringEntity, mockId: String = ""): StringEntity { - val obfuscation = Stark.obfuscate( - mainModule, key, when (entity.androidTreatment) { +fun obfuscateStringEntity(key: String, entity: StringEntity, mockId: String): StringEntity { + val obfuscation = Stark.obfuscate(key, when (entity.androidTreatment) { true -> entity.value.androidTreatment() false -> entity.value.unescape() }.toByteArray(), @@ -116,9 +116,9 @@ fun obfuscateStringEntity(mainModule: String, key: String, entity: StringEntity, return StringEntity(entity.name, entity.attributes, obfuscation, entity.tag, entity.index, entity.androidTreatment) } -fun revealStringEntity(mainModule: String, key: String, entity: StringEntity, mockId: String = ""): StringEntity { +fun revealStringEntity(key: String, entity: StringEntity, mockId: String): StringEntity { val arr: ByteArray = entity.value.split(", ").map { it.toInt().toByte() }.toByteArray() - val original = String(Stark.reveal(mainModule, key, arr, mockId)) + val original = String(Stark.reveal(key, arr, mockId)) return StringEntity(entity.name, entity.attributes, original, entity.tag, entity.index, entity.androidTreatment) } diff --git a/src/main/kotlin/components/jni/libsignKey.dylib b/src/main/kotlin/components/jni/libsignKey.dylib index 04fbde87c1d8f231067c15d4dbcd5fa6c20cc49c..dfe39e59a1ba6c440b22fbeafa233c21331dc543 100755 GIT binary patch literal 33780 zcmeHQ3wT?_l^*$#7-*1rl}9Ntk3do=NRG*)bg4sp;kvOC5yzC4O|NBHi5>h9vK$gv zQw8JJ7gep7HZ2Q%q%ACI7pS*ws0&SSLN+#R7Yvl*(!#2|cD%SnKp>{=;{DIeT8Ni{ zWo7br=>@XrL4`rED}~Kik)oI8kLVeV!RAmf47^Nww1-+NpI*vS#X{zw_pWk(Kd{Z*6W*E z8;qvbb!|Y(q?c6miWPqZAB;cJliLNIie+n-8Ov9#tdZJc=|{2RBm*YBqd?6Nieap8 zv#AN--1JTXu6RI?>RS(u=UfXwOnh+!Nyk=DuF@7?8L-8d)n~(K_8aY+Tk6}IjZh>! zDZO#43DC3i1sh{$S!IX~qro5Xr!zA78!wk-$}AYcc6#ZWGDZE=5e%cMa^(uGqSCTe zpTaCxv4Zz6lrYI%4YA0WgZF#{lCM3#y=i^xis0r88#XsL)uRl3i9+;@fgbT#gt!iI zBjQFdI$XopHV7|t6=NkZWF!U6qH7rILi&>pjP+xnjzJn##DgM6;ll{mAsmk|*|Q|T z*s>gjA3>hrJ@Vs=%N|dXc6zI33ujV7j?9UFMJ3KgZZ6#0wNma+-N&Brqd^gG-=RH($}a#(cUtzE7KAy4g9T$LIWM9=xI1F`%1| z>!#Mv9{>?O);YkRr$iTbnS0FI@vhEs=KivuvH~6BqWnJi2luXGJ>HqnV;w{MyJs^t zN*tgRggX-aAz+ANQ|sf6D2{34T>}%QHa>dq#KeU8BpMtstA9-uxK7p2M0N1ydI!PX zeUu``FF=_d)B5$Uy}q@N0DXkt2e0ZrX5EGExwlZ8%srQ>{uQHRNr5<`~oWnO+p^; zKpq$=HhMB%PRSihm?t1x)T8MJvKp|-Y74~bKBhO9eE5MgfS#`WEl)A>NxeYuY(5F+;O$Y zfbMnskm8Hqo0u5XUZ(nkT7n`y)-7>Bo<@ELX_cuB3t&V7(6`QEi~;1I$Jlnr3NsKJ zokOK4;a=pT&HxGK)*`5I0@=JLzKWWB8BN|Ql08MS+QbC(JrS#YnQ$xIyI6&5PX)SK zVGdNp3kE98eTb6CG;3dub-WBZ+xd4&)aVgau(6#d1Yh0dpf7EL(1JkI1Dy#`O^ZSr zOGM`f`C1!+C=dJ(WCY98V-mqNXufMgdE%W>ekReNF5NF%@K6iyp$9@Iwwcx68oh}r z4Iv1)+xZv_V3d}_dVFUiH5u>V{7K409x```%$OJ){>)!eJPTxgKba3Y#_y&?OpKGh zWiO5E+DB5&D$+b^VP7w>JGb#XBw(s`{Uh5<@*7VVgkenk_!meI;UABeR{}KAT2sG3kQk?S3vD4_6t`$CHi$CM<3aRk$JLmmn~BM;$7U zvLZr(?97fHITJuVw{C*2@LE*RyaQU#ZcvP(N{=2BCAzu0qGzbW?2ZuH<&rMhDA66^ z3n(K1M^{azD)M3i)!f79N#r3QBGkfkJNx+W^HaS5mD-_Sz-X}{0jg9}i|}Jm0ecST z{k9xPrlXJ76;O{s^bIOX`ONKR^<5(SIm)I5Jan!X%1lBRGs|{fgCh4a>G`B2tuN&N zF4bcXkIRbW389FX1>n*(J~50zFQLJ_1?8#vgY;MGn^_BN*m0~HbdJ+ww-t$T6Pk$O zB6ms(`~(mXK`NLWOh{cD7NnuaS@TzozGZ9(RBjn?FW8xc8*ne!kr2^+!y>wSKt$i} z6VcXfB8vBls5^?N%8YLkQFjQDZpIr$w6#t|JFXVdP9GxoE#yMn^F=lHf^Kp>?)i#V zoLrrIzG`4A`EvJs)#|s&>AUAER_+c7Nb%Pabw!*~ii@1q zlct~Mht_q%zWc=h9in`T(XO;_)fc{8_Jbypto7-Zv2xO;w10Vu5>3nTZh>d+Ob}P* zApgdGtW9?70r)YQw{+0`#VTP@veSBeCwY=o%^f46CYHuqhp9^oEFP*z)8adY&C3o! z7(;|I%RCOyNYPsZ7&&(IUt(rQ}k8Od5Mk9 z6N$oSfCbqW2^sDn>GOIlAk*xlrpSF2^pF+<`@Uh-yNOqL0>otj$Do1eN8oYe-B#YI!HLh8B@K%1KdZv%hr->B_z&>?NjI z$g~uT!mh}S_=dMrHqzOLDem^Fcy$!!BuK)orfu*So02G z<##@1$VpwvQ%>+^cO&r?s~*zL7r`58EUkaK_PF(~7tvjeO}vDdT16BV?A6T#reDfI z!PSTcyVu}NiURs%(J_$$-*ZXzkh|+`Du{_zVsXMWA9L^eP;qPrrNL{T87F(^zkxj5 zyRb)u!>S!psE_NeH+A=q_eij7x66Fge9ZeO_$0HVJ^T(~e+S-K)-$loy=$lo^N?<`PBR|Cdz2O+8F@4~{0yPs5bjAxjPA>c z)yK!N${78j=yYe2&S$=a3^c%qG_pw;ltz|Lkr<>Ml*AZ#pTfA340#8Uk?2yvFw-=m zKrYS$=~6`{UPr{tUy(#XT1-kU)N-tFuh4X)u>4&*!H4j|Z^*{NVQZ3TjPmk58vd4`PqcQ_Hc!GGUfTy^lR! z26WitOCn0IXmjjOFq_gxRpCpptmV?eF;Oku=l=Tc=pS&xvfG-{%jJAdwkZwSxmwP( zJ{S81n(Q&r*Csgj>1axvV8C)a6a2q&?jMA|pf)&tKtuDwMWpGc%Eh{ZX4XM|rXWol zfI+UMYNh*b-_I0#-w_@LEj&mrn-An%{;U3WjiuVl$q(cKLD=b+SpG|TEn*O-cNYl` zJ?Iyf0gLrms7N=jFVbV>Ab&+MvXfFiVgY5NC*-YB8_w|~`CLLYO)WX7jUmOlI>++e zT{yghl;I(($1u+g77SBSOdF%QOB)+~mfXE)#G?1U{R1_7`yTX9^ht~*ou=T^flsuG z^S*nCJ_Z+jjqp1JfFoz@(#$1#>?U-26FPkp+T4Uri$!08|G=tznq&?KC+@i^`Ad0{ z&Y@j?kHk&#OR>yK9UNb2<=!q9U&J5O`CrmNg6|@Lj__y%VmMI7;^q^`^FE5T6^Z|U z?0|<3pygPkwuI_DUh-odFw3lFg>#-_dhhQw0Q&X?iBCvJd@b}YuFRWQ|>{IMS}9Zlj}<^|D5fw z(&K$-_;P%yk5Zp#o5UZ4;xaKmN#hYcu(bMVtVlEMEnQ#AK%7^}K8YjW^n8-pp1|jA zbXFx*B&WTo@#WPL4?1k=XS%KlrL!U8>tQ$3{afaLF;@$wr}}^^Pt1o{=h|^ko*DGMAAdJRgQEA z%Vh67UNtsd%XXZPFtPhO};JXco03<1y%rRJ%#BZtq<0CDSM|r@%Dr2V|x5@*|$ZH z^gCC%nu`x&{~j@43w|JPA>XX;`=pQ7Dr1B{f@2}{$z!bphWYm(AIo3K{$tPtE%*nH zowDWoY#zw6H)@~8luQ}1T>ov`FUC6|h8Uj|xLk(P=dpeKY)KF8e^7st;{AenC&c?j@qSsnUlnia%f!6@7Vk0f{*!no z#rsY1-Y?#CzA-Ux9Pg_5N8d%XtY%`~QLv!iM(W(?t5_&CE`k+~{+?1zlzNI%H&bee zQvXP)U6jJSjvOox)K%7Hd3%QmvF) zMX3g)a6mtYUB%c{b9!AY5CTbXJ>fd~vkUHUv724h3O<{?0(!5wj`~v`d&@QFuX*fS zd0%n?*q(pFEBWl9{2E!WnDv5kZ{CG3>;)z_P{^&Bd)_DI)S(x=YjhXN3ovv zT!{Ox*gcmcW@xk-ar7+ELEJYF^$`!_;%Pr(=pgQnp2|X!6ITMT#|3`CSSXLVqF4%S zD`YMY7P-T-S$-edj~>HZp<}_*vCP%yX08D&?aNPKE*~^=;8fZIE5Vufj09#RFe8B(3Cu`fMglVun32GY1ZE^KBY_zS%t&BH0{=%P zP=vDzdOXE4j($YOA^J>+htBBe=~Hl$zIoy4Rrq}hKdH(?3V)amSMc;Lkntx}I~6Kk zsp6|uT(9D075mPW^+GD%sPgIDg&sOz5wXgb5Pr$4;9FIEx{A+Lu}8%g{!Bb0ff)(R zNMJ?+GZL7Qz>EZDBrqd^841itU`7Hn68Qfhf#c3%b*CYxOrbMK&87J@{K*y^N}?T>R;km0IgY!|S~w7^x0M8rvEwD>UC?V?&_X-`?(9 z5vW;cuq(r8)q!5RGI%{*?pPKM`!{P%EJv}t>48fg8mMFR@R^=&W>ovTdLS-3!`Uv2(7f<-`*5}MbKYktXyNP@fd5ohNi7vQM1&j zS!A%NQr{y${W3#xE{rc)WVb!#tir;QYvtc|TpeuhXbHml!i~aaS1Rc^yKsn|Uu58K zMK(pqzFLT6XnIKsnwzG9rau%4wl<{LjJl>yb7`ofz0s(LbJXBlSh!~zXfF4X#$1d6 zW#QgwpjYA5pw=wB-9c+3OE(C1Y6pJaIY+!BoNjDYX`_& z1}kKF#^=~MpT;#kBhc0oYHKAAWzD0%4Y-k zjbolPW%hiY#Rq#lpXE)fFJnK=_p+YZEDuiY;e7a>ky-f#xKx|Z`aXXC!SFj~^x@gIX?trCIH&}%_n)9+MHw?NRjmR?NqY6sU1fR(j!U>aCBrUHd2fNo z=PB`&^yZh>CGyM5eV)2gO!){aGmsTlwSiyHFcmRJomY)Aq za8kkN;a)J+FP|gJLkbQl_~!~9Q1HtNPAa$%?NI$<+~=idrGm>9+-kw<``QB*KJFXS z^PYk|>buxy!Iz*%zcIjbn}U4`en7z?1;3}@J_R@9UNhAnR`7EQE-n$|*b4j}SoBxH zaRt{Y_=gG(DfkTq4=DJH=(nJ+;9&)qoXNy<3HmMQE4WX=J_VnR?~qhJpx_%6>{EA} z=?|b#c~rq?;Cm&(C1*?e%?hqlaG!#E75o>s^EFh1FFAG!FMW{ z{182lC>WkYJfjLOQQucD!k@XJ`n?LiQ^9=-{)2*t6?`%7m=iubPtvE6fFLrGDu#n z;Nk_6{(TDeC^#Q?<*9z1f>$bdSi##AT=Fql|1AZVEBKT64o>w$3hq&GuY%uJaG!;b z$&>Ku{y#n43NATc!n1K#pUQm-HWj>0!OtqVPr+y7FU1jlLcyAXi!YG$)+^Yf;O7V~ zkiTUhj4=h%ZyM-%Pr>xt26~F&2MC{j<3P{Z3Z~yW(6dOv^qU8IDith#`vCaw6->W@ zpr=j2^jipeZdNe;CW4-=3Z~yi(6gQ3qnP+R=mg_u5sZGGNWZV3hs=^-`ke(m%Mb{r z-(}F#h(Iv?UW1+=BM?l#=b-0Z1cK@J8}zJ2+U<&_?nQh_!PfnVyA*8QlXyhI)_sZB z6l~p_I35fWJ?s9&ClqYmqX;Y5x=(SBg|F^a{7%8v{fZOwCB0$gKR&5o>%K+3f_=(f zzo+1kf`6r8>psP~7+c~mb*}<2x`6-X(<@rc8Sr%wgCC(DA%M_;5JXsqupWW* ztqEZR!nFv^2w{YFga|?h!bXJa5n2#h5jG*TA%qY%Biw*+Bf>u*+=LKC*n-fB@CAgM z5&o8ZlrSUr9xlBKodiKGO>xL~DBCKaO~)P%WTt6f2}(zNqlY#Q@x0W5nR!&2Rahg= zt*qUPqxc}lq)xm^4@tAoCfRK{@IJt<&{6MDcZT*#hk7{bsPjQ~XH-$0vSn_t9NVaa z;Mw+9HmvNu+57Lg4qDy09ZRE5>k$G@4vr9I97h<2^PDr?QpO?e;hbEzsS#um`PL){d9CNCK!~tfSh~wlE+0G+NWQa3xd~nJ*v!slS!^+%WrI4F(cIn8G zU%;$$OzQwM>tk4&;lmr)<=)DQR!w}gB2p`+bV9E@$HpnEThk_O>N@MM(N@s`_f$o6 zu{e0f>Go6!Eu`-Pb~3HON*})K)URG4zW*4;^SiVZ;r zwZl_@P?h=)VC!9GL!r<#Gz3ExpKfgJ9M*Rr(8(CqX__%s;e1;T|N{vcM@C{Y<_^hKjNB>B*R+_7|($cKH8Te4#GUX0dHF>nEdXJ_-o_3o&ScA^bu~ki8 zQ1E6^aA0LnKRg=+kqniuDr;%m7_3}P!+a#hV-d7j)9{bz8Qw+VAhiXpSQiWjTLVGz zO=XeIp&*!AK6Uqs@47bx!)=a1I3j(5Mqj+HDcl}$G?$6ow44EtP3=Z=TkCpfj~AsbWmvaPFbEE#*EFvu zeOCniDm8q#v@q3~4~8xUW%pNyIRF?}D5dC~5o#j^3Qvd3mIVAEe*pKcoFY4{iQZd= z6|%)6=uX3u&3o*cg@W+5~ za{(o(YY$V_1~2?dN<7ol6)$w2)I!R~sTazPl&M@x_puK3dQy39xOZjiS=Ca1Ljx^K z+SbtpCf^!kH5OBjWzvzDy)r$$asz)8SMP8P4!7WR2~{L}{G*G&K7d zW+7&UN$;jX8|gA!HkBi?U|iH5#0=!z;~Y%r4%uj_^^{}6oRSMC7m{*w6kol$PR~WAEGL9*tkL#_Fr$! zQk5&DBcYqRQ)Zo3RHPqHPd7D{J3=$-;kagq%ITXe=PP}=-8(C((o$s_#40UVt}bkY zIyJ#U71DE)jp|g^x64Z%$6r$+btH6YA$2&l+;o`6;jR1RX=&LsjB&Q7L!x&%~G=x=Z;Ls{VHkpT-Fa@VO#19l~2e4Sz zVvL;_wu$@j%plvyn>6vam|CE0!+K e!djTkLRmJ}qHOX@7h*ugXNyp{X(QbXW&aCA+00V_ literal 178940 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 j;tp=boPrFojSbAX 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 - val files = locateResourceFiles(absoluteProjectPath, entry.value) - println("Located files(${files.size}) for obfuscating") + val task = this; + project.applicationVariants()?.forEach { variant -> + println("\t== ${variant.name} ======================================") + val configuration = StringCare.Configuration(module).apply { + val lSrcFolders = gson.fromJson(task.srcFolders, MutableList::class.java) + if (task.srcFolders.isNotEmpty()) { + srcFolders.addAll(lSrcFolders as MutableList) + } + val lStringFiles = gson.fromJson(task.stringFiles, MutableList::class.java) + if (task.stringFiles.isNotEmpty()) { + stringFiles.addAll(lStringFiles as MutableList) + } + val lAssetsFiles = gson.fromJson(task.assetsFiles, MutableList::class.java) + if (task.assetsFiles.isNotEmpty()) { + assetsFiles.addAll(lAssetsFiles as MutableList) + } + if (task.mockedFingerprint.isNotEmpty()) { + mockedFingerprint = task.mockedFingerprint + } + applicationId = variant.applicationId + skip = task.skip + debug = task.debug + } + val files = locateResourceFiles(absoluteProjectPath, configuration) + println("\tLocated files(${files.size}) for obfuscating") + println("\tConfig (${gson.toJson(configuration)})") files.forEach { file -> - println("- ${file.file.name}") - println("\t${file.module}${File.separator}${file.sourceFolder}${File.separator}") + println("\t- ${file.file.name}") + println("\t\t${file.module}${File.separator}${file.sourceFolder}${File.separator}") val entities = parseXML(file.file) - println("path: ${file.file.absolutePath}") + println("\tpath: ${file.file.absolutePath}") println("") - println("============================") + println("\t============================") entities.forEach { entity -> entity.attributes.forEach { attribute -> - println("\"${attribute.name}\": \"${attribute.value}\"") + println("\t\"${attribute.name}\": \"${attribute.value}\"") } - println("\"value\": \"${entity.value}\"") - println("============================") + println("\t\"value\": \"${entity.value}\"") + println("\t============================") } println("") - println("=== content ================") - println(file.file.getContent()) - println("============================") + println("\t=== content ================") + println("" + file.file.getContent()) + println("\t============================") } } println("== END REPORT ==================================") - - } } diff --git a/src/main/kotlin/task/SCTestObfuscation.kt b/src/main/kotlin/task/SCTestObfuscation.kt index 88c20d7..4658b94 100644 --- a/src/main/kotlin/task/SCTestObfuscation.kt +++ b/src/main/kotlin/task/SCTestObfuscation.kt @@ -1,7 +1,8 @@ package task +import StringCare import StringCare.Companion.absoluteProjectPath -import StringCare.Companion.moduleMap +import com.google.gson.Gson import components.* import org.gradle.api.DefaultTask import org.gradle.api.tasks.Input @@ -10,48 +11,83 @@ import org.gradle.api.tasks.TaskAction open class SCTestObfuscation : DefaultTask() { @Input - var variant: String? = null + var module = String() @Input - var module: String? = null + var assetsFiles = String() @Input - var debug: Boolean? = null + var stringFiles = String() + + @Input + var srcFolders = String() + + @Input + var debug = false + + @Input + var skip = false + + @Input + var applicationId = "" + + @Input + var mockedFingerprint = "" @TaskAction fun greet() { println("== TEST OBFUSCATION ======================================") - println("Modules (${moduleMap.size})") + var key = "" + val gson = Gson() - moduleMap.forEach { entry -> - var key = "" + val task = this + project.applicationVariants()?.forEach { variant -> + println("\t== ${variant.name} ======================================") + val configuration = StringCare.Configuration(module).apply { + val lSrcFolders = gson.fromJson(task.srcFolders, MutableList::class.java) + if (task.srcFolders.isNotEmpty()) { + srcFolders.addAll(lSrcFolders as MutableList) + } + val lStringFiles = gson.fromJson(task.stringFiles, MutableList::class.java) + if (task.stringFiles.isNotEmpty()) { + stringFiles.addAll(lStringFiles as MutableList) + } + val lAssetsFiles = gson.fromJson(task.assetsFiles, MutableList::class.java) + if (task.assetsFiles.isNotEmpty()) { + assetsFiles.addAll(lAssetsFiles as MutableList) + } + if (task.mockedFingerprint.isNotEmpty()) { + mockedFingerprint = task.mockedFingerprint + } + applicationId = variant.applicationId + skip = task.skip + debug = task.debug + } signingReportTask().runCommand { _, result -> - key = result.extractFingerprint(entry.value.name, variant ?: defaultVariant, debug ?: defaultDebug) + key = result.extractFingerprint(module, variant.name, configuration) } - println("fingerprint: $key") - println("variant: ${variant ?: "debug"}") - val filesToObfuscate = backupResourceFiles(absoluteProjectPath, entry.value) + val filesToObfuscate = backupResourceFiles(absoluteProjectPath, configuration) filesToObfuscate.forEach { file -> val originalEntities = parseXML(file.file) - println("============================") - println("path: ${file.file.absolutePath}") + println("\t============================") + println("\tpath: ${file.file.absolutePath}") originalEntities.forEach { entity -> entity.attributes.forEach { attribute -> println("\"${attribute.name}\": \"${attribute.value}\"") } - println("\"value\": \"${entity.value}\"") - println("============================") + println("\"\tvalue\": \"${entity.value}\"") + println("\t============================") } println("") - println("=== content ================") + println("\t=== content ================") println(file.file.getContent()) - println("============================") - modifyXML(file.file, module!!, key, true) - println("=== content obfuscated ================") + println("\t============================") + modifyXML(file.file, key, configuration) + println("\t=== content obfuscated ================") println(file.file.getContent()) - println("============================") + println("\t============================") } - restoreResourceFiles(absoluteProjectPath, entry.value.name) + restoreResourceFiles(absoluteProjectPath, module) } println("== END OBFUSCATION ==================================") diff --git a/src/test/kotlin/AssetsTest.kt b/src/test/kotlin/AssetsTest.kt index 00bd3e4..2fdcc6b 100644 --- a/src/test/kotlin/AssetsTest.kt +++ b/src/test/kotlin/AssetsTest.kt @@ -9,6 +9,7 @@ class AssetsTest { private val configuration = defaultConfig().apply { debug = true + applicationId = "com.stringcare.sample" stringFiles.add("strings_extra.xml") srcFolders.add("src/other_source") } @@ -64,7 +65,7 @@ class AssetsTest { val temp = tempPath() signingReportTask(temp).runCommand { _, report -> println(report) - val key = report.extractFingerprint(variant = "prodDebug") + val key = report.extractFingerprint(variant = "prodDebug", configuration = configuration) println(key) assert(key.isNotEmpty()) val files = locateAssetsFiles( @@ -78,9 +79,9 @@ class AssetsTest { val original = it.file.getContent() println("original: \n $original") obfuscateFile( - "$temp${File.separator}$mainModuleTest", key, - it.file + it.file, + configuration.applicationId ) val obfuscated = it.file.getContent() println("obfuscated: \n $obfuscated") @@ -94,7 +95,7 @@ class AssetsTest { val temp = tempPath() signingReportTask(temp).runCommand { _, report -> println(report) - val key = report.extractFingerprint(variant = "prodDebug") + val key = report.extractFingerprint(variant = "prodDebug", configuration = configuration) println(key) assert(key.isNotEmpty()) val files = locateAssetsFiles( @@ -108,17 +109,17 @@ class AssetsTest { val original = it.file.getContent() println("original: \n $original") obfuscateFile( - "$temp${File.separator}$mainModuleTest", key, - it.file + it.file, + configuration.applicationId ) val obfuscated = it.file.getContent() println("obfuscated: \n $obfuscated") assert(original != obfuscated) revealFile( - "$temp${File.separator}$mainModuleTest", key, - it.file + it.file, + configuration.applicationId ) val reveal = it.file.getContent() println("reveal: \n $reveal") diff --git a/src/test/kotlin/SCTest.kt b/src/test/kotlin/SCTest.kt index 1223517..91c1f0e 100644 --- a/src/test/kotlin/SCTest.kt +++ b/src/test/kotlin/SCTest.kt @@ -7,9 +7,9 @@ import java.io.File class SCTest { - // private val logger by logger() - private val configuration = defaultConfig().apply { + debug = true + applicationId = "com.stringcare.sample" stringFiles.add("strings_extra.xml") srcFolders.add("src/other_source") } @@ -41,7 +41,7 @@ class SCTest { val temp = tempPath() signingReportTask(temp).runCommand { _, report -> println(report) - assert(report.extractFingerprint(variant = "prodDebug").split(":").size == 20) + assert(report.extractFingerprint(variant = "prodDebug", configuration = configuration).split(":").size == 20) } } @@ -97,7 +97,7 @@ class SCTest { val temp = tempPath() signingReportTask(temp).runCommand { _, report -> println(report) - val key = report.extractFingerprint(variant = "prodDebug") + val key = report.extractFingerprint(variant = "prodDebug", configuration = configuration) println(key) assert(key.isNotEmpty()) val files = locateResourceFiles("$temp${File.separator}$testProjectName", configuration) @@ -105,10 +105,9 @@ class SCTest { val entities = parseXML(file.file) entities.forEach { entity -> val obfuscated = obfuscateStringEntity( - "$temp${File.separator}$mainModuleTest", key, entity, - "" + configuration.applicationId ) assert(obfuscated.value != entity.value) } @@ -121,7 +120,7 @@ class SCTest { val temp = tempPath() signingReportTask(temp).runCommand { _, report -> println(report) - val key = report.extractFingerprint(variant = "prodDebug") + val key = report.extractFingerprint(variant = "prodDebug", configuration = configuration) println(key) assert(key.isNotEmpty()) val files = locateResourceFiles("$temp${File.separator}$testProjectName", configuration) @@ -129,16 +128,16 @@ class SCTest { val entities = parseXML(file.file) entities.forEach { entity -> val obfuscated = obfuscateStringEntity( - "$temp${File.separator}$mainModuleTest", key, - entity + entity, + configuration.applicationId ) assert(obfuscated.value != entity.value) val original = revealStringEntity( - "$temp${File.separator}$mainModuleTest", key, - obfuscated + obfuscated, + configuration.applicationId ) assert( @@ -162,7 +161,7 @@ class SCTest { files.forEach { file -> val entities = parseXML(file.file) assert(entities.isNotEmpty()) - modifyXML(file.file, "$temp${File.separator}$mainModuleTest", report.extractFingerprint(), true) + modifyXML(file.file, report.extractFingerprint(configuration = configuration), configuration) } val filesObfuscated = locateResourceFiles("$temp${File.separator}$testProjectName", configuration) filesObfuscated.forEach { file -> @@ -182,7 +181,7 @@ class SCTest { files.forEach { file -> val entities = parseXML(file.file) assert(entities.isNotEmpty()) - modifyXML(file.file, "$temp${File.separator}$mainModuleTest", report.extractFingerprint(), true) + modifyXML(file.file, report.extractFingerprint(configuration = configuration), configuration) } val filesObfuscated = locateResourceFiles("$temp${File.separator}$testProjectName", configuration) filesObfuscated.forEach { file -> @@ -228,9 +227,8 @@ class SCTest { assert(entities.isNotEmpty()) modifyXML( file.file, - "$temp${File.separator}$mainModuleTest", - report.extractFingerprint(), - true + report.extractFingerprint(configuration = configuration), + configuration ) } val filesObfuscated = locateResourceFiles( From 07b38cdc816b0e8dbb5a7a64f8a0aa22e897e157 Mon Sep 17 00:00:00 2001 From: Efra Espada Date: Fri, 3 Jan 2020 18:45:22 +0100 Subject: [PATCH 2/2] v4.0.0 --- build.gradle | 2 +- src/main/kotlin/components/Vars.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index d815e73..1b649c0 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group 'com.stringcare' -version '3.6.4' +version '4.0.0' def siteUrl = 'https://github.com/StringCare/KotlinGradlePlugin' def gitUrl = 'https://github.com/StringCare/KotlinGradlePlugin.git' diff --git a/src/main/kotlin/components/Vars.kt b/src/main/kotlin/components/Vars.kt index b6dce04..a3b8744 100644 --- a/src/main/kotlin/components/Vars.kt +++ b/src/main/kotlin/components/Vars.kt @@ -1,6 +1,6 @@ package components -internal const val version = "3.6.4" +internal const val version = "4.0.0" internal const val testProjectName = "KotlinSample" internal const val defaultMainModule = "app" internal const val gradleTaskNameDoctor = "stringcarePreview"