Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 113 additions & 19 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -300,56 +300,150 @@ jobs:
- name: Install cross (for Android targets, if not cached)
run: command -v cross || cargo install cross --git https://github.com/cross-rs/cross --locked

- name: Build Kotlin bindings (host + Android ABIs)
- name: Build Kotlin native libraries (host + Android ABIs)
run: ./scripts/build-kotlin.sh

- name: Setup Android SDK
uses: android-actions/setup-android@v4
with:
packages: tools platform-tools platforms;android-35 build-tools;35.0.0

# iOS targets are disabled on Linux (kotlin.native.ignoreDisabledTargets);
# this runs commonTest on the host JVM against the host libidkit_kmp.
- name: Run tests
working-directory: kotlin/
run: gradle bindings:test
run: ./gradlew :idkit:testReleaseUnitTest

- name: Build Kotlin sample app
working-directory: kotlin/Examples/IDKitSampleApp
run: ./gradlew :app:assembleDebug

- name: Validate Kotlin Maven publication
- name: Build KMP sample app (Android)
working-directory: kotlin/Examples/IDKitKmpSampleApp
run: ./gradlew :androidApp:assembleDebug

# Validates the Android slice of the KMP publication. The iOS variants and
# complete root metadata can only be produced on macOS; the publish workflow
# runs there and the build fails remote publishing from non-Mac hosts.
- name: Validate Kotlin Maven publication (Android slice)
run: |
set -euo pipefail

./kotlin/Examples/IDKitSampleApp/gradlew -p kotlin :bindings:publishToMavenLocal
./kotlin/Examples/IDKitSampleApp/gradlew -p kotlin \
./kotlin/gradlew -p kotlin :idkit:publishToMavenLocal
./kotlin/gradlew -p kotlin \
-Pidkit.publish.mavenCentral=true \
:bindings:publishToMavenCentral --dry-run
:idkit:publishToMavenCentral --dry-run

VERSION="$(grep '^version=' kotlin/gradle.properties | cut -d= -f2- | tr -d '[:space:]')"
ARTIFACT_DIR="$HOME/.m2/repository/com/worldcoin/idkit/$VERSION"
ARTIFACT_BASE="$ARTIFACT_DIR/idkit-$VERSION"
REPO="$HOME/.m2/repository/com/worldcoin"
ROOT_BASE="$REPO/idkit/$VERSION/idkit-$VERSION"
ANDROID_BASE="$REPO/idkit-android/$VERSION/idkit-android-$VERSION"

for artifact in \
"$ARTIFACT_BASE.aar" \
"$ARTIFACT_BASE.pom" \
"$ARTIFACT_BASE.module" \
"$ARTIFACT_BASE-sources.jar" \
"$ARTIFACT_BASE-javadoc.jar"; do
"$ROOT_BASE.pom" \
"$ROOT_BASE.module" \
"$ANDROID_BASE.aar" \
"$ANDROID_BASE.pom" \
"$ANDROID_BASE.module" \
"$ANDROID_BASE-sources.jar"; do
if [ ! -s "$artifact" ]; then
echo "::error::Missing Maven publication artifact: $artifact"
exit 1
fi
done

grep -q '<groupId>com.worldcoin</groupId>' "$ARTIFACT_BASE.pom"
grep -q '<artifactId>idkit</artifactId>' "$ARTIFACT_BASE.pom"
grep -q '<packaging>aar</packaging>' "$ARTIFACT_BASE.pom"
grep -q '<groupId>com.worldcoin</groupId>' "$ROOT_BASE.pom"
grep -q '<artifactId>idkit</artifactId>' "$ROOT_BASE.pom"
grep -q '<packaging>aar</packaging>' "$ANDROID_BASE.pom"

AAR_CONTENTS="$(mktemp)"
jar tf "$ARTIFACT_BASE.aar" > "$AAR_CONTENTS"
jar tf "$ANDROID_BASE.aar" > "$AAR_CONTENTS"
for abi in arm64-v8a armeabi-v7a x86 x86_64; do
if ! grep -q "^jni/$abi/libidkit.so$" "$AAR_CONTENTS"; then
echo "::error::Missing native library in AAR: jni/$abi/libidkit.so"
if ! grep -q "^jni/$abi/libidkit_kmp.so$" "$AAR_CONTENTS"; then
echo "::error::Missing native library in AAR: jni/$abi/libidkit_kmp.so"
exit 1
fi
done

kotlin-ios:
name: Kotlin SDK - iOS targets (macOS)
runs-on: macos-latest
needs: rust-core
permissions:
contents: read
steps:
- uses: actions/checkout@v4

- name: Read rust-toolchain
id: rust-version
run: echo "toolchain=$(yq '.toolchain.channel' rust-toolchain.toml)" >> $GITHUB_OUTPUT

- name: Install Rust
uses: dtolnay/rust-toolchain@stable

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. An attacker compromises the dtolnay/rust-toolchain repository or performs a man-in-the-middle attack
  2. They push a malicious version of the action to the stable tag—for example, injecting code that steals environment secrets or injects a backdoor into your build artifacts
  3. The next time your workflow runs, it automatically pulls the compromised stable version since the action is not pinned to a specific commit
  4. The malicious code executes with the permissions of your GitHub Actions runner, which in this case has contents: read access, 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
  1. Replace the floating action reference dtolnay/rust-toolchain@stable with a full 40-character commit SHA for the exact action release you want to use, for example dtolnay/rust-toolchain@<full-commit-sha>.
  2. Keep the intended version visible by adding a comment after the SHA, such as uses: dtolnay/rust-toolchain@<full-commit-sha> # stable or # v1.x.y, so future updates are easier to manage.
  3. 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 the toolchain: 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.

with:
toolchain: ${{ steps.rust-version.outputs.toolchain }}

- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: "17"

- name: Setup Android SDK
uses: android-actions/setup-android@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Inject malicious code into your build artifacts (the Android SDK setup)
  2. Steal secrets from the workflow environment (like signing keys or credentials used during the build)
  3. 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
  1. Replace the tag-based action reference with a full 40-character commit SHA in the uses line for android-actions/setup-android.
  2. 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.
  3. 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.
  4. Leave the rest of the step unchanged, including the existing with: configuration for packages.

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.

with:
packages: tools platform-tools platforms;android-35 build-tools;35.0.0

- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. The attacker gains write access to the Swatinem/rust-cache repository (e.g., through a compromised account or insider threat)
  2. They create a malicious version of rust-cache that exfiltrates secrets or corrupts build artifacts
  3. They force-push this malicious code to the v2 tag, overwriting the old commit
  4. On your next workflow run, GitHub checks out the code at the v2 tag and executes the attacker's malicious version
  5. 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
  1. Replace uses: Swatinem/rust-cache@v2 with a full 40-character commit SHA for the exact action release you want to keep using, for example uses: Swatinem/rust-cache@<full-commit-sha>.
  2. 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.
  3. Get the SHA from the action's GitHub release or tag page and make sure it matches the v2 release you intend to use. This makes the workflow use an immutable action version instead of a moving tag.
  4. 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.

with:
key: kotlin-ios

- name: Build Kotlin native libraries (host + iOS)
run: SKIP_ANDROID=1 ./scripts/build-kotlin.sh

# commonTest on the iOS simulator: exercises the cinterop bridge and the
# statically linked Rust core on Kotlin/Native.
- name: Run iOS simulator tests
working-directory: kotlin/
run: ./gradlew :idkit:iosSimulatorArm64Test

# Rehearse the full multi-target publication so release-day breakage in
# variant metadata or publication wiring is caught on PRs (the release
# itself is the first time the real publish tasks otherwise run). The
# rehearsal flag skips the Android .so check — mac runners cannot
# cross-build them — and remote publishing rejects that flag, so it
# cannot leak into a release.
- name: Rehearse full KMP publication (Maven Local)
run: |
set -euo pipefail

./kotlin/gradlew -p kotlin :idkit:publishToMavenLocal \
-Pidkit.rehearsal.allowMissingAndroidNativeLibs=true

VERSION="$(grep '^version=' kotlin/gradle.properties | cut -d= -f2- | tr -d '[:space:]')"
REPO="$HOME/.m2/repository/com/worldcoin"

for module in idkit-iosarm64 idkit-iossimulatorarm64 idkit-iosx64; do
for ext in klib pom module; do
artifact="$REPO/$module/$VERSION/$module-$VERSION.$ext"
if [ ! -s "$artifact" ]; then
echo "::error::Missing Maven publication artifact: $artifact"
exit 1
fi
done
done

# The root module metadata is what consumers resolve; every target
# variant must be present in it.
ROOT_MODULE="$REPO/idkit/$VERSION/idkit-$VERSION.module"
for variant in \
releaseApiElements-published \
iosArm64ApiElements-published \
iosSimulatorArm64ApiElements-published \
iosX64ApiElements-published; do
if ! grep -q "\"$variant\"" "$ROOT_MODULE"; then
echo "::error::Root Gradle module metadata is missing variant: $variant"
exit 1
fi
done
Loading
Loading