diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 17e60a7436..c79836dc8a 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -12,13 +12,23 @@ dependencies { testImplementation(libs.junit) testImplementation(gradleTestKit()) - // 루트 build.gradle.kts 의 bouncycastle 하한은 별도 빌드인 여기까지 미치지 않는다 — 같은 근거(#921). + // 루트 build.gradle.kts 의 보안 하한은 별도 빌드인 여기까지 미치지 않는다 — 같은 근거(#921·#981· + // #982·#985). AGP 9.2.1 이 이 클래스패스에도 같은 취약 버전을 끌어온다(netty 는 여기 없다). constraints { listOf("bcprov-jdk18on", "bcpkix-jdk18on", "bcutil-jdk18on").forEach { artifact -> implementation("org.bouncycastle:$artifact:${libs.versions.bouncycastle.get()}") { because("GHSA-574f-3g2m-x479 등 1.84 미만 취약 — #921") } } + implementation("org.apache.commons:commons-lang3:${libs.versions.commonsLang3.get()}") { + because("GHSA-j288-q9x7-2f5v — 3.18.0 미만 취약 — #981") + } + implementation("org.bitbucket.b_c:jose4j:${libs.versions.jose4j.get()}") { + because("GHSA-3677-xxcr-wjqv — 0.9.6 미만 취약 — #982") + } + implementation("org.jdom:jdom2:${libs.versions.jdom2.get()}") { + because("GHSA-2363-cqg2-863c — 2.0.6.1 미만 취약 — #985") + } } } diff --git a/build.gradle.kts b/build.gradle.kts index 6cd6dd616e..bb76ed224c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,10 +1,10 @@ import kotlinx.kover.gradle.plugin.dsl.KoverProjectExtension import org.gradle.kotlin.dsl.configure -// AGP 9.2.1 이 buildscript classpath 로 끌어오는 bouncycastle 1.79 는 GHSA-574f-3g2m-x479(critical) -// 영향권이다(#921). 루트 classpath 는 plugins 블록 처리 시점에 리졸브가 끝나 아래 본문 훅으로는 늦고, -// buildscript 블록에서는 버전 카탈로그 accessor 를 쓸 수 없다 — libs.versions.toml 의 bouncycastle -// 과 같은 값을 리터럴로 유지할 것. +// AGP 9.2.1·Firebase App Distribution 5.3.0 이 buildscript classpath 로 끌어오는 전이 의존성 중 +// 보안 권고 영향권인 것들의 하한(#921·#975~#985). 루트 classpath 는 plugins 블록 처리 시점에 +// 리졸브가 끝나 아래 본문 훅으로는 늦고, buildscript 블록에서는 버전 카탈로그 accessor 를 쓸 수 없다 +// — libs.versions.toml 의 같은 이름 버전과 값을 맞춰 유지할 것. buildscript { dependencies { constraints { @@ -13,7 +13,22 @@ buildscript { because("GHSA-574f-3g2m-x479 등 1.84 미만 취약 — #921") } } + add("classpath", "org.apache.commons:commons-lang3:3.18.0") { + because("GHSA-j288-q9x7-2f5v — 3.18.0 미만 취약 — #981") + } + add("classpath", "org.bitbucket.b_c:jose4j:0.9.6") { + because("GHSA-3677-xxcr-wjqv — 0.9.6 미만 취약 — #982") + } + add("classpath", "org.jdom:jdom2:2.0.6.1") { + because("GHSA-2363-cqg2-863c — 2.0.6.1 미만 취약 — #985") + } } + // netty 는 grpc-netty 가 끌어오는 모듈이 10개가 넘고 서로 같은 버전이어야 해 BOM 으로 정렬한다 + // (GHSA-558v-64gr-wgg4 등 33건, #975~#980). 이 BOM 을 아래 본문의 allprojects 하한에 함께 걸면 + // AGP 의 android-reverse-meta-data usage 가 platform 변형을 못 찾아 리졸브가 깨진다. netty 는 + // 루트 buildscript classpath 에만 있고(전 프로젝트 실측 53건 전부 여기) 산출물·테스트 클래스패스에는 + // 없으므로 여기서만 건다. + add("classpath", platform("io.netty:netty-bom:4.1.137.Final")) } } @@ -32,19 +47,49 @@ plugins { alias(libs.plugins.firebase.crashlytics) apply false } -// 빌드·테스트 클래스패스의 bouncycastle 하한(#921). Robolectric 4.15.1(bcprov 1.80)·AGP 9.2.1(1.79) -// 전이 의존성이 GHSA-574f-3g2m-x479(critical) 영향권인데 상류 최신판도 1.84 미만이라 constraint 로 -// 올린다. androidLintTool 처럼 AGP 가 뒤늦게 만드는 configuration 까지 잡도록 configureEach 로 걸고, -// require 시맨틱이라 상류가 1.84 이상을 선언하게 되면 그쪽이 이긴다. APK 산출물에는 bouncycastle 이 -// 없다(releaseRuntimeClasspath 0건 실측). build-logic 은 별도 빌드라 build-logic/build.gradle.kts 가 -// 같은 하한을 선언한다. -val bouncycastleArtifacts = listOf("bcprov-jdk18on", "bcpkix-jdk18on", "bcutil-jdk18on") -val bouncycastleFloor = libs.versions.bouncycastle.get() +// 빌드·테스트 클래스패스의 보안 하한(#921·#975~#985). 상류가 취약 버전을 물고 있고 상류 최신판도 +// 아직 패치 버전 미만이라 constraint 로 올린다 — Robolectric 4.15.1(bcprov 1.80)·AGP 9.2.1(bcprov +// 1.79·commons-lang3 3.16.0·jose4j 0.9.5·jdom2 2.0.6)·Firebase App Distribution 5.3.0(netty +// 4.1.110.Final). androidLintTool 처럼 AGP 가 뒤늦게 만드는 configuration 까지 잡도록 configureEach +// 로 걸고, require 시맨틱이라 상류가 하한 이상을 선언하게 되면 그쪽이 이긴다. 여기 목록은 전부 빌드 +// 도구 경유라 APK 산출물에는 없다(releaseRuntimeClasspath 0건 실측). build-logic 은 별도 빌드라 +// build-logic/build.gradle.kts 가 같은 하한을 선언한다. +data class SecurityFloor( + val module: String, + val version: String, + val because: String, +) + +val securityFloors = + listOf("bcprov-jdk18on", "bcpkix-jdk18on", "bcutil-jdk18on").map { artifact -> + SecurityFloor( + module = "org.bouncycastle:$artifact", + version = libs.versions.bouncycastle.get(), + because = "GHSA-574f-3g2m-x479 등 1.84 미만 취약 — #921", + ) + } + + listOf( + SecurityFloor( + module = "org.apache.commons:commons-lang3", + version = libs.versions.commonsLang3.get(), + because = "GHSA-j288-q9x7-2f5v — 3.18.0 미만 취약 — #981", + ), + SecurityFloor( + module = "org.bitbucket.b_c:jose4j", + version = libs.versions.jose4j.get(), + because = "GHSA-3677-xxcr-wjqv — 0.9.6 미만 취약 — #982", + ), + SecurityFloor( + module = "org.jdom:jdom2", + version = libs.versions.jdom2.get(), + because = "GHSA-2363-cqg2-863c — 2.0.6.1 미만 취약 — #985", + ), + ) -fun Project.bouncycastleConstraintSet() = - bouncycastleArtifacts.map { artifact -> - dependencies.constraints.create("org.bouncycastle:$artifact:$bouncycastleFloor") { - because("GHSA-574f-3g2m-x479 등 1.84 미만 취약 — #921") +fun Project.securityFloorConstraintSet() = + securityFloors.map { floor -> + dependencies.constraints.create("${floor.module}:${floor.version}") { + because(floor.because) } } @@ -52,10 +97,10 @@ allprojects { // Gradle 9 는 declarable 이 아닌 configuration(compileClasspath 등)에 constraint 선언을 금지한다. // 전용 declarable configuration 에 담아 모든 resolvable configuration 이 extend 하게 한다. val floor = - configurations.create("bouncycastleConstraints") { + configurations.create("securityFloors") { isCanBeResolved = false isCanBeConsumed = false - bouncycastleConstraintSet().forEach { dependencyConstraints.add(it) } + securityFloorConstraintSet().forEach { dependencyConstraints.add(it) } } configurations.configureEach { if (name != floor.name && isCanBeResolved) extendsFrom(floor) @@ -65,7 +110,7 @@ allprojects { // 직접 단다. 루트 자신의 classpath 는 최상단 buildscript 블록이 담당한다(여기 시점엔 리졸브 완료라 불가). subprojects { buildscript.configurations.named("classpath") { - bouncycastleConstraintSet().forEach { dependencyConstraints.add(it) } + securityFloorConstraintSet().forEach { dependencyConstraints.add(it) } } } diff --git a/feature/mindrecord/presentation/build.gradle.kts b/feature/mindrecord/presentation/build.gradle.kts index 508ed9756c..9d593b9c83 100644 --- a/feature/mindrecord/presentation/build.gradle.kts +++ b/feature/mindrecord/presentation/build.gradle.kts @@ -19,7 +19,12 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.model) implementation(libs.coil.compose) - implementation(libs.compose.rich.editor) + // 쓰는 건 BasicRichTextEditor(foundation 계열)뿐인데, richeditor 가 딸려 보내는 Compose + // Multiplatform material3 1.11.0-alpha07 이 androidx material3 를 BOM 판 1.4.0 대신 + // 1.5.0-alpha17(alpha)로 끌어올린다(#973). 쓰지 않는 material3 갈래를 끊어 BOM 을 정본으로 되돌린다. + implementation(libs.compose.rich.editor) { + exclude(group = "org.jetbrains.compose.material3", module = "material3") + } testImplementation(libs.coroutines.test) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bfa04e7552..ed7c3cb3f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -48,6 +48,10 @@ kover = "0.9.9" # 빌드·테스트 클래스패스 전용 보안 하한 — GHSA-574f-3g2m-x479(critical) 등 3건이 1.84 에서 패치(#921). # 루트 build.gradle.kts 의 buildscript 블록은 카탈로그 accessor 를 못 써 같은 값을 리터럴로 둔다. bouncycastle = "1.84" +netty = "4.1.137.Final" +commonsLang3 = "3.18.0" +jose4j = "0.9.6" +jdom2 = "2.0.6.1" [libraries] androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometric" } diff --git a/konsist/build.gradle.kts b/konsist/build.gradle.kts index 9db9f77153..933a1467bd 100644 --- a/konsist/build.gradle.kts +++ b/konsist/build.gradle.kts @@ -14,4 +14,13 @@ kotlin { dependencies { testImplementation(libs.konsist) testImplementation(libs.junit) + + // konsist 0.17.3 이 kotlin-compiler-embeddable 2.0.21 경유로 coroutines 1.9.0 을 끌어와, 카탈로그 + // 선언(1.11.0)과 어긋난 채 해석된다(#974). 이 모듈은 아키텍처 테스트 전용이라 산출물에 영향이 없지만 + // 감사가 보는 testRuntimeClasspath 를 카탈로그 하나의 의도로 맞춘다. + constraints { + testImplementation(libs.coroutines.core) { + because("카탈로그 선언 버전으로 정렬 — konsist 전이 1.9.0 과 불일치(#974)") + } + } }