182 行
7.3 KiB
Plaintext
182 行
7.3 KiB
Plaintext
plugins {
|
|
alias(libs.plugins.android.application) apply false
|
|
alias(libs.plugins.android.library) apply false
|
|
alias(libs.plugins.kotlin.compose) apply false
|
|
alias(libs.plugins.kotlin.serialization) apply false
|
|
}
|
|
|
|
group = "com.xuqm"
|
|
// Global fallback — individual modules override via their own SDK_<NAME>_VERSION property.
|
|
// Publish all at once: ./gradlew publish -PPUBLISH_VERSION=1.0.2
|
|
// Publish one module: ./gradlew :sdk-update:publish -PSDK_UPDATE_VERSION=2.0.0-SNAPSHOT
|
|
version = providers.gradleProperty("PUBLISH_VERSION").getOrElse("0.1.0-SNAPSHOT")
|
|
|
|
ext["nexusUrl"] = "https://nexus.xuqinmin.com/repository/android-hosted/"
|
|
ext["nexusUser"] = providers.gradleProperty("NEXUS_USER").getOrElse("")
|
|
ext["nexusPassword"] = providers.gradleProperty("NEXUS_PASSWORD").getOrElse("")
|
|
|
|
val verifyNoSensitiveLogging by tasks.registering {
|
|
group = "verification"
|
|
description = "阻止日志、标准输出和异常文案包含凭据或用户标识实际值。"
|
|
doLast {
|
|
val sensitiveIdentifiers = Regex(
|
|
"""(?i)\b(accessToken|token|userSig|pushToken|password|apiKey|signingKey|""" +
|
|
"""userId|groupId|targetId|messageId|requestId|toId|fromId|phone|email|regId)\b""",
|
|
)
|
|
val sensitiveInterpolation = Regex(
|
|
"""(?i)\$(?:\{[^}]*\b(accessToken|token|userSig|pushToken|password|apiKey|""" +
|
|
"""signingKey|userId|groupId|targetId|messageId|requestId|toId|fromId|""" +
|
|
"""phone|email|regId)\b[^}]*}|(accessToken|token|userSig|pushToken|password|""" +
|
|
"""apiKey|signingKey|userId|groupId|targetId|messageId|requestId|toId|""" +
|
|
"""fromId|phone|email|regId)\b)""",
|
|
)
|
|
val sinkStart = Regex(
|
|
"""(?:(?:android\.util\.)?Log\.[vdiew]|println|System\.out\.print(?:ln)?|""" +
|
|
"""[A-Za-z_][A-Za-z0-9_]*Exception|error)""" +
|
|
"""\s*\(""",
|
|
)
|
|
|
|
fun invocationAt(source: String, start: Int): String {
|
|
val open = source.indexOf('(', start)
|
|
if (open < 0) return source.substring(start)
|
|
var depth = 0
|
|
var quote: Char? = null
|
|
var escaped = false
|
|
for (index in open until source.length) {
|
|
val char = source[index]
|
|
if (quote != null) {
|
|
if (escaped) {
|
|
escaped = false
|
|
} else if (char == '\\') {
|
|
escaped = true
|
|
} else if (char == quote) {
|
|
quote = null
|
|
}
|
|
continue
|
|
}
|
|
if (char == '"' || char == '\'') {
|
|
quote = char
|
|
continue
|
|
}
|
|
if (char == '(') depth++
|
|
if (char == ')' && --depth == 0) return source.substring(start, index + 1)
|
|
}
|
|
return source.substring(start)
|
|
}
|
|
|
|
fun withoutStringLiterals(value: String): String {
|
|
val result = StringBuilder(value.length)
|
|
var quote: Char? = null
|
|
var escaped = false
|
|
value.forEach { char ->
|
|
if (quote != null) {
|
|
if (escaped) {
|
|
escaped = false
|
|
} else if (char == '\\') {
|
|
escaped = true
|
|
} else if (char == quote) {
|
|
quote = null
|
|
}
|
|
result.append(' ')
|
|
} else if (char == '"' || char == '\'') {
|
|
quote = char
|
|
result.append(' ')
|
|
} else {
|
|
result.append(char)
|
|
}
|
|
}
|
|
return result.toString()
|
|
}
|
|
|
|
val violations = mutableListOf<String>()
|
|
fileTree(rootDir) {
|
|
include("**/*.kt", "**/*.java", "**/*.kts", "**/*.gradle")
|
|
exclude("**/build/**", "**/.gradle/**")
|
|
}.files.sortedBy { it.path }.forEach { sourceFile ->
|
|
val source = sourceFile.readText(Charsets.UTF_8)
|
|
sinkStart.findAll(source).forEach { match ->
|
|
val invocation = invocationAt(source, match.range.first)
|
|
if (
|
|
sensitiveInterpolation.containsMatchIn(invocation) ||
|
|
sensitiveIdentifiers.containsMatchIn(withoutStringLiterals(invocation))
|
|
) {
|
|
val line = source.take(match.range.first).count { it == '\n' } + 1
|
|
violations += "${sourceFile.relativeTo(rootDir)}:$line"
|
|
}
|
|
}
|
|
}
|
|
check(violations.isEmpty()) {
|
|
"Sensitive value may reach a log, stdout, or exception message:\n" +
|
|
violations.joinToString("\n")
|
|
}
|
|
}
|
|
}
|
|
|
|
subprojects {
|
|
tasks.matching {
|
|
it.name == "testDebugUnitTest" || it.name == "lintRelease" || it.name == "check"
|
|
}.configureEach {
|
|
dependsOn(rootProject.tasks.named("verifyNoSensitiveLogging"))
|
|
}
|
|
}
|
|
|
|
val verifySdkManifestOwnership by tasks.registering {
|
|
group = "verification"
|
|
description = "验证 common 无宿主 Manifest 副作用,扩展能力各自声明所需组件。"
|
|
dependsOn(
|
|
":sdk-core:processDebugManifest",
|
|
":sdk-file:processDebugManifest",
|
|
":sdk-update:processDebugManifest",
|
|
":sdk-push:processDebugManifest",
|
|
":sdk-im:processDebugManifest",
|
|
":sdk-webview:processDebugManifest",
|
|
":sdk-bugcollect:processDebugManifest",
|
|
)
|
|
doLast {
|
|
fun mergedManifest(module: String): String =
|
|
file("$module/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml")
|
|
.readText()
|
|
|
|
val core = mergedManifest("sdk-core")
|
|
check("XuqmMergedProvider" !in core)
|
|
check("POST_NOTIFICATIONS" !in core)
|
|
check("androidx.core.content.FileProvider" !in core)
|
|
|
|
val update = mergedManifest("sdk-update")
|
|
check("XuqmMergedProvider" in update)
|
|
check("androidx.core.content.FileProvider" !in update)
|
|
|
|
val file = mergedManifest("sdk-file")
|
|
check("XuqmMergedProvider" in file)
|
|
check("androidx.core.content.FileProvider" in file)
|
|
|
|
val push = mergedManifest("sdk-push")
|
|
check("XuqmMergedProvider" in push)
|
|
check("POST_NOTIFICATIONS" in push)
|
|
|
|
listOf("sdk-im", "sdk-webview", "sdk-bugcollect").forEach { module ->
|
|
check("XuqmMergedProvider" in mergedManifest(module))
|
|
}
|
|
}
|
|
}
|
|
|
|
val verifySampleManifestMerge by tasks.registering {
|
|
group = "verification"
|
|
description = "验证多个扩展合并后只存在一个初始化 Provider。"
|
|
dependsOn(":sample-app:processDebugMainManifest")
|
|
doLast {
|
|
val manifest = file(
|
|
"sample-app/build/intermediates/merged_manifest/debug/" +
|
|
"processDebugMainManifest/AndroidManifest.xml",
|
|
).readText()
|
|
check(Regex("com\\.xuqm\\.sdk\\.internal\\.XuqmMergedProvider")
|
|
.findAll(manifest).count() == 1)
|
|
check(Regex("androidx\\.core\\.content\\.FileProvider")
|
|
.findAll(manifest).count() == 1)
|
|
check(Regex("com\\.xuqm\\.common_plugin_applied")
|
|
.findAll(manifest).count() == 1)
|
|
check("com.xuqm.bugcollect_build_enabled" in manifest)
|
|
check("com.xuqm.bugcollect_automatic_enabled" in manifest)
|
|
}
|
|
}
|