Skip to content

A11: a Web2 application connects to a wallet (DSM Connect), proven with a game on three phones #1183

A11: a Web2 application connects to a wallet (DSM Connect), proven with a game on three phones

A11: a Web2 application connects to a wallet (DSM Connect), proven with a game on three phones #1183

Workflow file for this run

# CodeQL security analysis — advanced setup, in-repo and reviewable.
#
# No untrusted GitHub event inputs are used: the only expressions are
# `matrix.language` / `matrix.build-mode`, both defined in this file.
#
# WHY THIS FILE EXISTS
#
# CodeQL *security* analysis has not run on this repository since 2026-03-26.
# A workflow named "CodeQL" has been green every day since, which is why the
# gap was invisible. The green run is a different product:
#
# Initialize CodeQL: analysis-kinds: code-quality
# disable-default-queries: true
# - uses: code-quality
#
# That is GitHub's *code quality* default setup. It runs the maintainability
# suite over javascript-typescript and python and uploads `*.quality.sarif`.
# It produces no security results, and `code-scanning/default-setup` reports
# `state: not-configured` — the security half was switched off and only the
# quality half kept running.
#
# What the last real security scan (2026-03-26) covered, and found:
#
# /language:rust 178 results -> 100 alerts
# /language:actions 4 results
# /language:javascript-typescript 4 results
# /language:python 0 results
# /language:c-cpp 0 results
#
# All 100 rust alerts were triaged by the owner on 2026-03-26 and 2026-03-29
# (50 "false positive", 50 "won't fix"). Dismissals are keyed by alert
# fingerprint and survive re-enablement, so restoring this workflow does not
# resurrect them — but it also means the current "0 open alerts" reflects that
# triage, not five months of clean scans.
#
# Everything merged since 2026-03-26 — the FORS address collision, the
# forged-signature verifier, the domain-separation cut, the Kyber binding
# enforcement, the BLE trust-root work — has never been seen by a security
# query. A settings toggle can be flipped with no diff and no notification;
# a committed workflow can only change through review. That is the fix.
name: CodeQL
on:
push:
branches: [main]
paths-ignore:
# A markdown-only change cannot alter any CI outcome, so running the
# pipeline on one buys nothing and costs ~95 minutes of runner time
# (Coverage alone is ~50). Verified before adding this, not assumed:
# - no `.md` is compiled in — zero `include_str!("*.md")` in the tree;
# - every gate that scans the tree excludes markdown explicitly
# (`--glob '!**/*.md'` in no_clock_and_no_json.sh and
# bridge_contracts_gate.sh, `--exclude='*.md'` in
# check_forbidden_symbols.sh);
# - the `main-branch-protect` ruleset requires deletion protection,
# non-fast-forward, a reviewed PR and signed commits, and NO status
# checks — so a skipped workflow cannot leave a PR unmergeable,
# which is the usual trap with `paths-ignore`.
# Only `**/*.md` is listed. A non-markdown file under `docs/` (none
# today: docs/ holds only audits and bench proofs) still runs the
# pipeline, because "it lives under docs/" is not evidence that nothing
# consumes it.
- '**/*.md'
# Run on every PR regardless of base branch (see ci.yml) so stacked PRs get
# scanned. The markdown skip below is consistent with the note that follows:
# alerts are already restricted to the PR diff, so a markdown-only PR reports
# nothing by construction. The scheduled and dispatch runs stay full-tree and
# are deliberately NOT filtered. Note: codeql-action restricts alerts to the PR diff, so a PR that
# touches no Rust reports no Rust alerts by construction (see below).
pull_request:
paths-ignore:
# A markdown-only change cannot alter any CI outcome, so running the
# pipeline on one buys nothing and costs ~95 minutes of runner time
# (Coverage alone is ~50). Verified before adding this, not assumed:
# - no `.md` is compiled in — zero `include_str!("*.md")` in the tree;
# - every gate that scans the tree excludes markdown explicitly
# (`--glob '!**/*.md'` in no_clock_and_no_json.sh and
# bridge_contracts_gate.sh, `--exclude='*.md'` in
# check_forbidden_symbols.sh);
# - the `main-branch-protect` ruleset requires deletion protection,
# non-fast-forward, a reviewed PR and signed commits, and NO status
# checks — so a skipped workflow cannot leave a PR unmergeable,
# which is the usual trap with `paths-ignore`.
# Only `**/*.md` is listed. A non-markdown file under `docs/` (none
# today: docs/ holds only audits and bench proofs) still runs the
# pipeline, because "it lives under docs/" is not evidence that nothing
# consumes it.
- '**/*.md'
# Lets a full-tree scan be forced without waiting for a push or the cron.
# Worth knowing when reading a PR check: codeql-action restricts alerts to
# the PR diff ("Persisted N diff range(s)"), so a PR that touches no Rust
# reports no Rust alerts by construction. A green PR check is not evidence
# of coverage — the push, dispatch and cron runs are the full-tree ones.
workflow_dispatch:
schedule:
# Weekly, so a language that stops analyzing surfaces as a failing
# scheduled run rather than as silence.
- cron: "27 4 * * 1"
permissions:
contents: read
jobs:
analyze:
name: CodeQL (${{ matrix.language }})
runs-on: ubuntu-latest
# Rust and Kotlin are large here; give them room rather than letting a
# timeout masquerade as a language that "doesn't work".
timeout-minutes: 90
permissions:
security-events: write
packages: read
actions: read
contents: read
strategy:
# One language failing must not hide the others' results.
fail-fast: false
matrix:
include:
# build-mode: none analyses read source directly. That matters here:
# a full --all-features workspace build takes ~30 minutes, and
# requiring one is how Rust coverage gets quietly dropped again the
# next time the build is slow or breaks.
#
# Rust coverage depends on macro expansion working, and it silently
# did not. Measured progression on this branch, full-tree runs:
#
# macro fails files w/ warnings results
# before sysroot pin 15,081 439 / 113 64
# after sysroot pin 18 48 / 504 259
#
# rule March before after
# rust/cleartext-logging 105 0 145
# rust/hard-coded-cryptographic-value 71 64 112
# rust/cleartext-transmission 2 0 2
#
# cleartext-logging's sinks — log::__private_api::log,
# std::io::stdio::_print, core::panicking::panic_fmt — exist only
# inside macro expansions, so a broken expander deleted that query
# outright instead of shrinking it. A rule returning 0 is the signal
# to check extraction health, not evidence of clean code.
#
# Two hypotheses were tried and refuted, recorded so they are not
# retried: `rustup component add rust-src` (byte-identical run — the
# extractor already installs that component itself), and a
# `build-mode` change (the extractor never reads build output, it
# re-derives everything through rust-analyzer and hits the same
# sysroot). `sysroot: None` in the extractor's config dump means the
# option is unset and discovery ran, NOT that discovery failed —
# misreading that line is what sent the first attempt wrong.
#
# Residual, tracked separately: 18 macro failures remain, all in the
# no_std embedded crates (hal::binary_info::* in dsm-anchor-pico),
# which need their thumb target to resolve. Separately, ~31 files
# under dsm_sdk/src/jni/ and platform/{android,ios} are cfg-gated to
# non-host targets and get no semantic analysis at all, so the JNI
# boundary is still invisible; the lever there is CARGO_CFG_OVERRIDES
# or a second matrix entry with a distinct category.
#
# Separately, 31 files under dsm_sdk/src/jni/ and platform/{android,
# ios} got "semantic analyzer unavailable (not included as a module)"
# because they are cfg-gated to non-host targets. The JNI boundary is
# invisible to every security query until that is addressed.
- language: rust
build-mode: none
# java-kotlin was never in the pre-2026-03-26 matrix, so the Android
# client (85 Kotlin files) has never been analyzed at all.
#
# This one CANNOT use build-mode: none. Buildless Java extracts Java
# source only — the extractor announces "Extracting Java with
# build-mode=none." and GitHub documents that Kotlin is skipped in
# that mode because "Kotlin analysis requires a build". This repo has
# 0 .java files and 85 .kt, so buildless extracts nothing at all: the
# first attempt died in `database finalize` with "CodeQL could not
# process any code written in Java/Kotlin." (exit code 32).
- language: java-kotlin
build-mode: manual
- language: javascript-typescript
build-mode: none
- language: python
build-mode: none
- language: actions
build-mode: none
# c-cpp is deliberately NOT restored. It returned 0 results on
# 2026-03-26 because there is no first-party C to extract: the repo
# tracks 3 headers and no .c/.cpp. It also cannot run build-mode:
# none. Add it back with a real build mode if firmware C lands here.
steps:
- name: Checkout
uses: actions/checkout@v7
# The CodeQL bundle's vendored rust-analyzer cannot expand std macros
# from Rust >= 1.95.0. Bundle 2.26.3 pins ra_ap_* 0.0.301; the upgrade to
# 0.328 that fixes it (github/codeql#21714) merged upstream on
# 2026-08-14 and is in no released bundle yet.
#
# rust-toolchain.toml pins channel = "1.96.0", and the extractor resolves
# its sysroot by running `rustc --print sysroot` in the checkout root —
# where rustup honours that file — so it lands on exactly the broken
# version. Both variables are required: SYSROOT_SRC alone keeps the
# discovered 1.96 binary sysroot, and SYSROOT alone skips the automatic
# rust-src install and then errors when the sources are absent.
#
# This redirects ONLY the extractor. It does not change the toolchain any
# other job builds with, and rust-toolchain.toml is untouched.
#
# Revert this once a bundle ships ra_ap >= 0.328, and confirm the numbers
# hold rather than assuming they do.
- name: Pin the Rust sysroot read by the CodeQL extractor
if: matrix.language == 'rust'
run: |
rustup toolchain install 1.94.0 --profile minimal --component rust-src
SYSROOT="$(rustup run 1.94.0 rustc --print sysroot)"
echo "CODEQL_EXTRACTOR_RUST_OPTION_SYSROOT=$SYSROOT" >> "$GITHUB_ENV"
echo "CODEQL_EXTRACTOR_RUST_OPTION_SYSROOT_SRC=$SYSROOT/lib/rustlib/src/rust/library" >> "$GITHUB_ENV"
# Mirrors the existing `android-unit-tests` job in ci.yml, which builds
# this module green on ubuntu-latest with the same JDK.
- name: Set up JDK 17
if: matrix.language == 'java-kotlin'
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"
cache: gradle
- name: Cache Gradle caches
if: matrix.language == 'java-kotlin'
uses: actions/cache@v6
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('dsm_client/android/**/*.gradle*', 'dsm_client/android/**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# No `queries:` override — this is the same default suite that ran
# before 2026-03-26. Restoring coverage and changing the query suite
# in one step would make the resulting alert delta unattributable.
# Must run AFTER init and BEFORE analyze: manual mode only captures what
# the CodeQL tracer observes between those two steps.
#
# Compile tasks, not `assembleDebug`: app/build.gradle.kts hooks
# refreshDsmJniLibs onto merge*JniLibFolders, and that task hard-fails
# when the cargo-ndk .so artifacts are absent — which they always are
# here, since this job builds no Rust. Compile tasks never touch jniLibs.
# The three tasks together cover all 85 .kt files (main 58, test 20,
# androidTest 7).
#
# --no-daemon is required for correctness, not just hygiene: a reused
# Gradle daemon starts outside the CodeQL tracer, and compilations it
# performs would go unobserved.
#
# --no-build-cache and the per-task --rerun are required for the same
# reason, and the failure they prevent is worse because it looks like
# success. The cache step above restores ~/.gradle/caches, which holds
# the Gradle build cache, so :app:compileDebugKotlin can be served
# FROM-CACHE. Gradle then reports BUILD SUCCESSFUL having run no compiler
# at all, the tracer observes nothing, and Analyze fails with "CodeQL
# could not process any code written in Java/Kotlin". Which PR that hits
# is a matter of whose cache happens to be warm, so the check is
# nondeterministic across PRs that touch no Kotlin at all. A cache hit is
# not evidence the Kotlin was analyzed; only an executed compilation is.
# Ordinary CI and build jobs keep normal caching — this treatment is for
# the traced compile alone. Note --rerun is a TASK option in Gradle 8.9,
# not a global one, so it follows each task name; as a leading flag it is
# rejected outright.
#
# androidTest is deliberately EXCLUDED. Those 7 instrumented-test files
# do not compile today — :app:compileDebugAndroidTestKotlin fails with 14
# "Unresolved reference" errors against protobuf accessors that no longer
# exist (reserveAU128, reserveBU128, setReserveAU128, setTokenId) in
# SoFiCrossDeviceOwnerTest.kt, SoFiTestHelpers.kt and SoFiTradeRealHwTest
# .kt. Nothing in CI compiles them: ci.yml runs :app:testDebugUnitTest,
# which covers main and test but never androidTest. That breakage is
# pre-existing and tracked separately — it is not this workflow's to fix,
# and blocking all Kotlin security coverage on it would be the wrong
# trade. The two tasks below cover the 78 files that matter: all 58
# production sources plus the 20 unit tests.
- name: Compile Kotlin under the CodeQL tracer
if: matrix.language == 'java-kotlin'
working-directory: dsm_client/android
run: |
./gradlew --no-daemon --stacktrace --no-build-cache \
:app:compileDebugKotlin --rerun \
:app:compileDebugUnitTestKotlin --rerun
- name: Analyze
uses: github/codeql-action/analyze@v4
with:
# Same category strings as the 2026-03-26 analyses, so alerts land in
# the existing namespace and prior triage still applies.
category: "/language:${{ matrix.language }}"