feat: kotlin/android sdk to kmp - #299
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7a37dec. Configure here.
| ?: throw IDKitException("unexpected_response", "confirmed status is missing its result") | ||
| IDKitStatus.Confirmed( | ||
| IDKitResult.fromJson(IdKitJson.encodeToString(JsonObject.serializer(), result)), | ||
| ) |
There was a problem hiding this comment.
Confirmed rawJson not verbatim
Medium Severity
When mapping a confirmed poll status, IDKitResult is built by re-encoding the parsed JsonObject through IdKitJson instead of keeping the Rust wire JSON. That conflicts with rawJson being documented as untouched for backend verify, and IdKitJson settings can omit nulls and change serialization so POST bodies may not match what the core emitted.
Reviewed by Cursor Bugbot for commit 7a37dec. Configure here.
| pendingRequest?.close() | ||
| http.close() | ||
| scope.cancel() | ||
| } |
There was a problem hiding this comment.
Close before polling stops
Medium Severity
Lifecycle and regenerate paths call IDKitRequest.close() while a polling coroutine may still be in statusFlow or pollStatusOnce. The KMP sample’s generateRequest correctly uses cancelAndJoin before close, but dispose closes the request first and only then cancels the scope, and the Android sample cancels the job without joining before closing the prior request.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 7a37dec. Configure here.
| - name: Build Kotlin artifact | ||
| uses: gradle/gradle-build-action@v3 | ||
| - name: Setup Android SDK | ||
| uses: android-actions/setup-android@v4 |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
GitHub Action android-actions/setup-android@v4 is not pinned to a commit SHA, allowing attackers who compromise the repository to inject backdoor code into your build and published artifacts.
More details about this
The GitHub Action android-actions/setup-android is pinned to a version tag (@v4) instead of a full commit SHA. Version tags in GitHub repositories can be reassigned or deleted by the maintainer, and a malicious actor who compromises the android-actions repository could push code to the v4 tag, causing your workflow to execute their backdoor code.
Attack scenario:
- An attacker compromises the
android-actions/setup-androidrepository through credential theft or social engineering - They force-push new malicious code to the
v4tag, overwriting the legitimate version - Your workflow runs and fetches the
v4tag, which now points to the attacker's code instead of the original - The
setup-androidaction runs their payload, potentially stealing repository secrets (likeMAVEN_CENTRAL_PASSWORDandMAVEN_SIGNING_KEYvisible in your workflow), exfiltrating source code, or injecting malware into your build artifacts - Your published Kotlin artifacts get distributed to Maven Central with embedded backdoor code, affecting all downstream users
To resolve this comment:
✨ Commit fix suggestion
-
Replace the tag reference in the
usesline with the action's full 40-character commit SHA instead of@v4.
Changeuses: android-actions/setup-android@v4touses: android-actions/setup-android@<full-commit-sha> # v4. -
Keep the version as an inline comment after the SHA, for example
# v4, so it is still clear which release you intended to use. -
Choose the SHA from the exact release you want to trust on the
android-actions/setup-androidrepository's release or tag page, and copy the full commit hash for that version.
Pinning to the commit SHA makes the workflow use an immutable action revision instead of a movable tag.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.
You can view more details about this finding in the Semgrep AppSec Platform.
| packages: tools platform-tools platforms;android-35 build-tools;35.0.0 | ||
|
|
||
| - name: Cache Rust dependencies | ||
| uses: Swatinem/rust-cache@v2 |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
GitHub Action Swatinem/rust-cache@v2 is not pinned to a commit SHA, allowing an attacker with repository write access to inject malicious code that executes in your workflow.
More details about this
The GitHub Action Swatinem/rust-cache@v2 is pinned to a version tag rather than a full commit SHA. An attacker with write access to the rust-cache repository could force-push a malicious commit to the v2 tag, and your workflow would automatically pull and execute the compromised code on the next run without any warning.
Here's how an attacker could exploit this:
- The attacker gains write access to the
Swatinem/rust-cacherepository (e.g., through a compromised account or insider threat) - They create a malicious version of rust-cache that exfiltrates secrets or corrupts build artifacts
- They force-push this malicious code to the
v2tag, overwriting the old commit - On your next workflow run, GitHub checks out the code at the
v2tag and executes the attacker's malicious version - The attacker could then steal your
GITHUB_TOKEN, AWS credentials, or other secrets available in the workflow environment, or inject a backdoor into your Kotlin SDK binaries
By pinning to a full commit SHA (e.g., Swatinem/rust-cache@a1234567890abcdef1234567890abcdef12345678), you ensure that only that exact commit is ever executed, making tag tampering impossible.
To resolve this comment:
✨ Commit fix suggestion
- Replace
uses: Swatinem/rust-cache@v2with a full 40-character commit SHA for the exact action release you want to keep using, for exampleuses: Swatinem/rust-cache@<full-commit-sha>. - Add the tag or version as a comment after the SHA so the workflow stays readable, for example
uses: Swatinem/rust-cache@<full-commit-sha> # v2.x.y. - Get the SHA from the action's GitHub release or tag page and make sure it matches the
v2release you intend to use. This makes the workflow use an immutable action version instead of a moving tag. - Keep the existing
with:block unchanged unless the pinned release notes require an input change.
Alternatively, if you need to stay on the latest v2 behavior automatically, replace this third-party action with a local action such as uses: ./.github/actions/rust-cache that you control directly.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.
You can view more details about this finding in the Semgrep AppSec Platform.
| java-version: "17" | ||
|
|
||
| - name: Setup Android SDK | ||
| uses: android-actions/setup-android@v4 |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
GitHub Actions workflow uses a mutable version tag (@v4) for the android-actions/setup-android action instead of pinning to a specific commit SHA, allowing attackers to inject malicious code into CI/CD runs.
More details about this
The android-actions/setup-android action is referenced using a mutable version tag (@v4) instead of being pinned to a specific commit SHA. This means the action can change at any time without your knowledge or control.
Exploit scenario: An attacker could compromise the android-actions/setup-android repository and push malicious code to the v4 tag. The next time this workflow runs, it would automatically execute the backdoored action. With this step running in your CI/CD pipeline, the attacker could:
- Inject malicious code into your build artifacts (the Android SDK setup)
- Steal secrets from the workflow environment (like signing keys or credentials used during the build)
- Compromise your build outputs before they're published
Since this action runs early in your workflow (Setup Android SDK step) and has elevated access to your repository context, an attacker could compromise your entire Kotlin SDK build process.
To resolve this comment:
✨ Commit fix suggestion
- Replace the tag-based action reference with a full 40-character commit SHA in the
usesline forandroid-actions/setup-android. - Keep the readable version as a comment after the SHA so future updates are easier to track, for example:
uses: android-actions/setup-android@<full-commit-sha> # v4. - Verify that the pinned commit corresponds to the exact action release you want to keep using, instead of pinning a moving tag like
@v4. Pinning to a commit makes the action immutable, so the workflow cannot silently pick up changed code later. - Leave the rest of the step unchanged, including the existing
with:configuration forpackages.
Alternatively, if you need to avoid depending on a third-party action here, replace android-actions/setup-android with a trusted local action such as ./.github/actions/... or a GitHub-owned action that provides the same setup behavior.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.
You can view more details about this finding in the Semgrep AppSec Platform.
| run: echo "toolchain=$(yq '.toolchain.channel' rust-toolchain.toml)" >> $GITHUB_OUTPUT | ||
|
|
||
| - name: Install Rust | ||
| uses: dtolnay/rust-toolchain@stable |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
GitHub Action not pinned to commit SHA; @stable tag can be updated with malicious code, compromising your workflow at runtime.
More details about this
The GitHub Action dtolnay/rust-toolchain@stable is pinned to the stable tag rather than a specific commit SHA. This means the action can change at any time when the tag is updated.
Attack scenario:
- An attacker compromises the
dtolnay/rust-toolchainrepository or performs a man-in-the-middle attack - They push a malicious version of the action to the
stabletag—for example, injecting code that steals environment secrets or injects a backdoor into your build artifacts - The next time your workflow runs, it automatically pulls the compromised
stableversion since the action is not pinned to a specific commit - The malicious code executes with the permissions of your GitHub Actions runner, which in this case has
contents: readaccess, allowing it to read your repository code and potentially exfiltrate sensitive data
By pinning to a full commit SHA (e.g., dtolnay/rust-toolchain@a6e1e2f3c4b5d6e7f8a9b0c1d2e3f4a5b6c7d8e9), you ensure that only that exact version of the action runs, making it immutable and preventing this type of supply-chain attack.
To resolve this comment:
✨ Commit fix suggestion
- Replace the floating action reference
dtolnay/rust-toolchain@stablewith a full 40-character commit SHA for the exact action release you want to use, for exampledtolnay/rust-toolchain@<full-commit-sha>. - Keep the intended version visible by adding a comment after the SHA, such as
uses: dtolnay/rust-toolchain@<full-commit-sha> # stableor# v1.x.y, so future updates are easier to manage. - Leave the
with:block unchanged so the workflow still installs the toolchain from${{ steps.rust-version.outputs.toolchain }}. Pinning the action version makes the workflow immutable, while thetoolchain:input can still select the Rust version to install.
Alternatively, if you need the workflow to track a newer release of this action, update it to that release first, then pin that release's commit SHA instead of using a tag like @stable.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by third-party-action-not-pinned-to-commit-sha.
You can view more details about this finding in the Semgrep AppSec Platform.
| <plist version="1.0"> | ||
| <dict> | ||
| <key>CFBundleDevelopmentRegion</key> | ||
| <string>en</string> | ||
| <key>CFBundleDisplayName</key> | ||
| <string>IDKit KMP Sample</string> | ||
| <key>CFBundleExecutable</key> | ||
| <string>$(EXECUTABLE_NAME)</string> | ||
| <key>CFBundleIdentifier</key> | ||
| <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
| <key>CFBundleInfoDictionaryVersion</key> | ||
| <string>6.0</string> | ||
| <key>CFBundleName</key> | ||
| <string>$(PRODUCT_NAME)</string> | ||
| <key>CFBundlePackageType</key> | ||
| <string>APPL</string> | ||
| <key>CFBundleShortVersionString</key> | ||
| <string>$(MARKETING_VERSION)</string> | ||
| <key>CFBundleURLTypes</key> | ||
| <array> | ||
| <dict> | ||
| <key>CFBundleURLName</key> | ||
| <string>org.worldcoin.idkit.kmp.sample.callback</string> | ||
| <key>CFBundleURLSchemes</key> | ||
| <array> | ||
| <string>idkitkmpsample</string> | ||
| </array> | ||
| </dict> | ||
| </array> | ||
| <key>CFBundleVersion</key> | ||
| <string>$(CURRENT_PROJECT_VERSION)</string> | ||
| <key>UILaunchScreen</key> | ||
| <dict/> | ||
| </dict> | ||
| </plist> |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
The application's App Transport Security (ATS) configuration does not leverage the in-built public key pinning mechanisms. The application should consider leverage ATS public key pinning to ensure that the application only communicates to serves with an allow-listed certificate (and public key). By default the device will allow connections if the default trust store (CA store) posesses the right certificates. The number of accepted Certificate Authorities by default is hundreds. Using public key pinning vastly reduces the attack surface.
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by ATS-consider-pinning.
You can view more details about this finding in the Semgrep AppSec Platform.
|
Semgrep found 6
GitHub Actions step uses a mutable tag or branch reference. Tags and branch names can be silently repointed by the action owner, enabling supply-chain attacks — as seen in the trivy-action and kics-github-action compromises. Pin the reference to a full 40-character commit SHA instead, e.g. |


Migrate Kotlin/Android SDK to Kotlin Multiplatform (KMP)
Replaces the Android-only, UniFFI-generated Kotlin bindings w/ KMP SDK targeting Android and iOS from a single common API.
Changes
rust/kmp-fficrate exposing a stable C ABI (idkit_kmp.h) w/ handle-based session registry, JSON envelope layer for config/results, and FFI contract testscorebridge to support new FFI surfacekotlin/idkitKMP module w/ a common API with native bridges for Android (JNI) and iOS (cinterop)IDKitKmpSampleAppwith shared business logic and native UI (Compose/SwiftUI); legacy Android example udpated to the new SDKbuild-kotlin.sh/package-kotlin.sh, CI, and publishing jobs for new artifactsNote
High Risk
Large release and architecture shift: new FFI boundary, native artifacts per platform, macOS-gated publishing, and breaking Kotlin SDK API/artifact layout for all Android integrators.
Overview
Replaces the Android-only UniFFI/JNA
kotlin/bindingsmodule with a Kotlin Multiplatformkotlin/idkitSDK (Android + iOS) backed by a new hand-written C FFI instead of generated UniFFI Kotlin.The Rust workspace gains
idkit-kmp-ffi(libidkit_kmp) with JSON request/response envelopes, opaque request handles, and a dedicatedkmp-android-releaseprofile (panic = unwind) so FFI can turn panics into errors instead of aborting Android apps. The public Kotlin API stays undercom.worldcoin.idkitbut 5.0.0 breaks several behaviors (suspendpreset/constraints,IDKitRequest.close(), types moved offuniffi.*,IDKitException, constraint/preset modeling, etc.) as documented inkotlin/README.md.CI and release plumbing target the new artifacts: Linux CI builds/tests Android + validates the Android Maven slice (
libidkit_kmp.soin the AAR); a new macOSkotlin-iosjob runs simulator tests and rehearses full KMPpublishToMavenLocal.publish-kotlindrops the UniFFI host/bindgen job, cross-buildsidkit-kmp-ffifor Android ABIs, builds iOS static libs on the publish runner, and moves publish tomacos-latestwith ordered GitHub Packages uploads (platform modules first, root metadata last).Adds
IDKitKmpSampleApp(shared flow + Compose/SwiftUI) and retargetsIDKitSampleAppto:idkit. Removes the old bindings sources/tests from the tree in favor of the new module.Reviewed by Cursor Bugbot for commit 7a37dec. Bugbot is set up for automated code reviews on this repo. Configure here.