diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b69872f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,24 @@ +version: 2 +updates: + # Gradle dependencies, including the gradle/libs.versions.toml version catalog. + # Note: Dependabot *alerts* for Gradle need the dependency graph, which this repo + # submits from .github/workflows/dependency-submission.yml. + - package-ecosystem: gradle + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 5 + groups: + androidx: + patterns: + - "androidx.*" + kotlin: + patterns: + - "org.jetbrains.kotlin*" + - "org.jetbrains.kotlinx*" + + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 5 diff --git a/.github/workflows/dependency-submission.yml b/.github/workflows/dependency-submission.yml new file mode 100644 index 0000000..f998ee7 --- /dev/null +++ b/.github/workflows/dependency-submission.yml @@ -0,0 +1,36 @@ +# Submits the resolved Gradle dependency graph to GitHub so that Dependabot can +# raise alerts for transitive dependencies. Without this, GitHub only sees the +# GitHub Actions used by this repository and none of the Gradle dependencies. +name: Dependency Submission + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: write + +jobs: + dependency-submission: + name: Submit Gradle dependency graph + runs-on: ubuntu-latest + + steps: + - name: Harden Runner + uses: step-security/harden-runner@0634a2670c59f64b4a01f0f96f84700a4088b9f0 # v2.12.0 + with: + egress-policy: audit + + - name: Check out code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up JDK 17 + uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1 + with: + java-version: "17" + distribution: "temurin" + + - name: Generate and submit dependency graph + uses: gradle/actions/dependency-submission@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0 diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index c9d0b3f..f259b2a 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -35,7 +35,12 @@ jobs: cache: gradle - name: Setup Android SDK - uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3.2.2 + uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1 + with: + # The action's default is "tools platform-tools". `tools` is obsolete and Google + # has delisted it, so sdkmanager exits 1 and fails the step. platform-tools is + # installed again by "Install SDK components" below, so this loses nothing. + packages: platform-tools # Setup KVM for hardware acceleration - name: Setup KVM diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 9622652..8d3bf67 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -5,7 +5,6 @@ plugins { kotlin("android") kotlin("kapt") kotlin("plugin.serialization") version "2.1.0" - id("org.jetbrains.dokka") version "1.9.20" id("jacoco") id("com.vanniktech.maven.publish") version "0.31.0" id("org.sonarqube") version "4.4.1.3373" @@ -20,6 +19,44 @@ jacoco { toolVersion = "0.8.11" } +// Raise known-vulnerable transitive dependencies of the build toolchain to patched +// versions. Neither is a dependency of the SDK itself - both are pulled in by the Android +// Gradle Plugin's Unified Test Platform, so the published AAR and its POM are unaffected. +// +// These are floors, not overrides: `useVersion` on its own would also drag a *newer* +// version back down, so anything at or above the floor is left alone and only older +// versions are raised. That matters now that Dependabot bumps `agp` weekly and each bump +// can ship newer transitives of its own. +run { + val securityFloors = mapOf( + "io.netty" to libs.versions.netty.get(), + "com.google.protobuf" to libs.versions.protobuf.get(), + ) + + fun isBelowFloor(current: String?, floor: String): Boolean { + if (current.isNullOrBlank()) return true + fun numericParts(v: String) = v.split('.', '-', '_').mapNotNull(String::toIntOrNull) + val actual = numericParts(current) + val wanted = numericParts(floor) + for (i in 0 until maxOf(actual.size, wanted.size)) { + val a = actual.getOrElse(i) { 0 } + val b = wanted.getOrElse(i) { 0 } + if (a != b) return a < b + } + return false + } + + configurations.configureEach { + resolutionStrategy.eachDependency { + val floor = securityFloors[requested.group] ?: return@eachDependency + if (isBelowFloor(requested.version, floor)) { + useVersion(floor) + because("security floor - see gradle/libs.versions.toml") + } + } + } +} + android { namespace = "com.formbricks.android" compileSdk = 35 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 35174a5..70940f5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,6 +22,17 @@ lifecycleViewmodelKtx = "2.9.0" fragmentKtx = "1.8.7" databindingCommon = "8.9.2" +# Security floors for vulnerable transitive dependencies of the Android Gradle Plugin's +# Unified Test Platform. These never reach the published AAR - see the resolutionStrategy +# block in android/build.gradle.kts. +# +# Dependabot does not track these: its Gradle parser only reaches [versions] through a +# `version.ref` in [libraries]/[plugins], and nothing references them. Re-check them by +# hand when `agp` is upgraded - because they are floors, one at or below what AGP already +# ships is a no-op, so a stale entry is inert rather than harmful. +netty = "4.1.138.Final" +protobuf = "3.25.9" + [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } junit = { group = "junit", name = "junit", version.ref = "junit" }