Repository navigation
ci: scheduled security audit across every SDK (Mon + Wed, osv-scanner + native auditors) - #382
Conversation
… + native auditors) Adds .github/workflows/security-audit.yml (V2-1582 §3). It runs at 05:17 UTC on Mondays and Wednesdays, ahead of the release-decision meetings, on any PR or main push that changes a manifest or lockfile, and on demand. - cargo-audit: every Cargo.lock (antd, antd-rust, ffi/rust, ffi/node), honouring each .cargo/audit.toml. - govulncheck: antd-go, failing only on reachable vulnerabilities. - npm-audit: antd-js and ffi/node, dev dependencies included. - dotnet-audit: NuGet, transitive included; Examples is report-only. - resolve: generates the lockfiles the SDKs deliberately don't commit (Dart, PHP, Ruby, Python via uv, Gradle for Java/Kotlin/Android via an init script, Swift) so they can be scanned as a fresh install would resolve them today. - osv-scanner: every committed and generated lockfile. Blocking for everything except examples/; examples get a report-only pass with a warning. Results go to the job summary and to code scanning as SARIF. osv-scanner.toml holds the accepted findings, each with a reason, tracking issue and ignoreUntil date: zip (until 2026-11-16, matching #372), lru, the RustSec "unmaintained" notices, x/crypto openpgp and the Elixir cowlib advisory (V2-1637). Scanner versions are pinned. .github/security-audit/lock-all.init.gradle enables Gradle dependency locking from outside the build, so no build file changes. Refs V2-1582. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool. What Enabling Code Scanning Means:
For more information about GitHub Code Scanning, check out the documentation. |
…h a current toolchain First run on #382 found two problems in the workflow itself: - Actions runs `run:` steps with `bash -e`, and `set -uo pipefail` does not turn that off. The first osv-scanner call (the SARIF pass) exited 1 on findings and ended the step before the job summary, the findings table or the ::error:: line were written. The examples step, meant never to fail, died the same way. Exit codes are now captured with `|| code=$?`. - Swift resolved under Swift 6.0, which can't use SwiftNIO's security fixes (swift-nio >= 2.100.0, swift-nio-http2 >= 1.44.0, swift-nio-extras >= 1.34.1 all need swift-tools-version 6.1). So it scanned swift-nio 2.97.1, -http2 1.42.0 and -extras 1.33.0 and reported 5 advisories that a current toolchain doesn't resolve to. Swift now resolves in its own job in the official swift:6.4 image (setup-swift lags behind Swift releases). antd-swift's constraints are all `from:`, so 6.1+ resolves the fixed releases. The lockfile collection moves to .github/security-audit/collect-lockfiles.sh, shared by both resolve jobs. Refs V2-1582. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ainer
The swift:6.4 container runs as a different user from the one that owns
the checked-out workspace, so git refused it ("detected dubious
ownership") and collect-lockfiles.sh failed after a successful resolve.
Refs V2-1582.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dant GHSA ignore RUSTSEC-2026-0374 (basic-toml unmaintained, informational, no fix) now appears in osv's data for ffi/rust and ffi/node. basic-toml is build-time only (uniffi -> uniffi_bindgen -> askama_derive, a proc-macro), so it is not in the shipped ant-ffi library. cargo audit only warns on it. It is accepted with the same quarterly review date as the other unmaintained notices. GHSA-w4f7-4cxr-rv3c is removed: osv-scanner treats it as an alias of EEF-CVE-2026-43966, which already covers it, and reported the separate entry as unused. Refs V2-1582. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dirvine
left a comment
There was a problem hiding this comment.
REQUEST_CHANGES — two P2 false-negative defects
Reviewed all four added files (596 lines) in WithAutonomi/ant-sdk at 7dcc4b4cc1a2508723de0344929039c98e1ddcad. The exact-head CI is green, but the following gaps can produce a successful audit without the claimed coverage.
- NuGet command errors can pass as a clean audit (
.github/workflows/security-audit.yml:191–199). Callingcheckthrough||disables Basherrexitinside the function. Restore and package-list failures are lost; the final negated grep can return success. The inline finding includes the reproduced exit codes and the minimum fix. - The standalone FFI Kotlin SDK is never resolved or scanned (
.github/workflows/security-audit.yml:288–291). It has its own Gradle build and dependencies, but no committed/generated lockfile. Neither the other Gradle roots nor recursive OSV scanning resolves it. This is a coverage defect, not a claim of a known vulnerability in its current dependencies.
Verified evidence
- Exact-head security workflow: 15/15 jobs succeeded; the PR rollup contains 17 successful checks. Inspected NuGet, Gradle, Swift and OSV logs, including actual scanned lockfile paths.
- Ran the actual NuGet shell block with controlled
dotnetresponses, changing only its scratch-output path. Clean output exits 0; vulnerable output exits 1; restore failure (17), package-list failure (23), and both failing all incorrectly exit 0. A scratch-only control with explicit|| return $?guards fails all three error cases. These are shell-control tests under macOS Bash 3.2, not a claim that hosted NuGet failed. - Checked each Gradle root's
settings.gradle.kts: none of the three invoked builds includesffi/kotlin. The pinned OSV 2.7.0 scanner returns 128,No package sources found, when recursively scanning that directory with--offline --no-ignore; exact-head hosted logs also show no FFI Kotlin lockfile. - actionlint 1.7.12, Bash syntax, and
git diff --checkpassed. Local actionlint ran without shellcheck; hosted workflow lint is separately green. - Collector fixture passed for ignored/untracked lockfiles, six recognised lockfile types and paths with spaces; excluded build/cache paths stayed excluded and an empty selection failed.
- A separate seeded OSV fixture confirmed that a future
ignoreUntilsuppresses findings (exit 0) and an expired date restores them (exit 1). The fixture is not a repository dependency finding.
Panel coverage and adjudication
4/6 reviewed — partial panel; 2/3 named models. GLM 5.3 (z-ai/glm-5.3) and Qwen 3.8 (qwen38-27b) each completed correctness and adversarial reviews. All four substantive opinions request changes. Qwen confirms the NuGet defect; GLM confirms both defects. Both findings were checked independently against source and execution evidence. There is no unresolved material disagreement among the responding reviewers.
Missing: DS4 correctness and adversarial. DS4 (deepseek-v4-flash) produced no usable correctness verdict: the original and one narrowed same-model retry both ended at the 8,192-token cap (finish_reason=length). Its subsequently launched focused adversarial role was stopped after that recovery failed; it is not counted as a review. No fallback was counted as DS4. Full-panel consensus is not claimed. This verdict uses the permitted partial-panel quorum, not a claim that all six roles completed.
Tag-pinned Actions, temporary-file hardening and speculative SARIF robustness concerns are not promoted to blockers. A failed/missing SARIF upload would fail that job, not silently prove a clean scan. The FFI Python package has no runtime dependencies, so it is not asserted as a third runtime-coverage gap.
Scope limits / next steps
I did not rerun all ecosystem resolvers or the full SDK build matrix locally; the successful hosted run is the integration evidence. No repository files were changed. Fix the NuGet status propagation, add FFI Kotlin lockfile generation and confirm its runtime/test dependencies appear in the blocking scan. The planned post-merge workflow_dispatch run on main remains unrun. No merge or deployment was performed.
| check() { | ||
| dotnet restore "$1" >/dev/null | ||
| dotnet list "$1" package --vulnerable --include-transitive | tee /tmp/nuget.txt | ||
| ! grep -q "has the following vulnerable packages" /tmp/nuget.txt |
There was a problem hiding this comment.
[P2] Propagate restore and package-list errors from the NuGet check
check is called as the left side of || at line 199, so Bash disables errexit throughout the function. A failed restore or dotnet list ... | tee therefore falls through to the final negated grep. If the error output does not contain the vulnerable-package heading, that grep returns success and all four blocking projects can pass without a successful audit. I executed this exact run block with controlled dotnet responses: restore exit 17, listing exit 23, and both failing each produced workflow exit 0; clean and advisory-output controls produced 0 and 1 respectively. Explicitly propagate both restore and pipeline failures (for example, || return $?) before checking advisory output, and add regression tests for these failure paths. A scratch-only control with those guards failed all three error cases as expected. These are shell-control probes, not a claim that the hosted NuGet run failed.
| (cd antd-java && ./gradlew --no-daemon --console=plain --init-script "$INIT" resolveAndLockAll --write-locks) | ||
| (cd antd-kotlin && ../antd-java/gradlew --no-daemon --console=plain -p . --init-script "$INIT" resolveAndLockAll --write-locks) | ||
| (cd ffi/android && gradle --no-daemon --console=plain --init-script "$INIT" resolveAndLockAll --write-locks \ | ||
| -PauditConfigurations=releaseRuntimeClasspath) |
There was a problem hiding this comment.
[P2] Generate a lockfile for the standalone FFI Kotlin SDK
The Gradle job resolves antd-java, antd-kotlin and ffi/android, but never ffi/kotlin. The separate ffi/kotlin/AntFfi/build.gradle.kts project declares JVM JNA 5.17.0, coroutines 1.8.1 and test dependencies, has no committed Gradle lockfile, and is not included by any of those three Gradle builds. The collector only copies lockfiles; it cannot resolve this manifest. Exact-head OSV job logs contain no ffi/kotlin lockfile, and running the pinned OSV 2.7.0 scanner recursively over that directory with --no-ignore --offline returns 128, “No package sources found”. Thus a dependency advisory confined to that SDK will not affect this supposedly every-SDK blocking audit. Add standalone FFI Kotlin dependency resolution with a suitable Gradle runtime, upload its generated lockfile, and verify its runtime/test packages appear in the blocking scan. This is a coverage defect, not a claim that its current dependencies have a known vulnerability.
…lin; check lockfile coverage Review fixes for #382 (dirvine, both P2: ways the audit could pass without the claimed coverage): - NuGet: `check` was called as `check || ...`, and bash disables errexit inside a function called that way. A failed `dotnet restore` or `dotnet list` fell through to the final negated grep and passed as clean (reproduced: restore exit 17 -> script exit 0). The logic moves to .github/security-audit/nuget-audit.sh, which checks every exit explicitly: 0 clean, 1 vulnerable, 2 could not run. Both 1 and 2 fail for blocking projects; examples only warn. .github/security-audit/test-nuget-audit.sh runs it against a stub dotnet on every run (8 cases: clean, vulnerable, restore/list/both failing, list failing with advisories printed, two report-only cases). - ffi/kotlin: the standalone JVM UniFFI binding (JNA 5.17.0, coroutines 1.8.1, kotlin-test) is its own Gradle build, not included by any of the three that were locked, so it was never resolved or scanned. It is now locked too (36 packages locally, osv-clean), and the Gradle step fails if any of its four expected lockfiles is missing. - The osv-scanner job now checks that all 19 expected lockfiles (committed + generated) are present before scanning, listing them in the job summary, so a resolver that quietly produces less fails the run instead of leaving an SDK unscanned. The workflow header now says why ffi/python and ffi/swift have nothing to resolve: they declare no package dependencies, and their native library is ffi/rust, which is scanned. Refs V2-1582. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dirvine
left a comment
There was a problem hiding this comment.
Re-review: APPROVE (provisional: partial review panel, not full consensus)
Reviewed the complete current PR (six added files) at c277b00c2326801036133bfd3b1edfbe59d0b082, including the changes since 7dcc4b4cc1a2508723de0344929039c98e1ddcad. Both findings from review 5584736686 are fixed. No remaining demonstrated blocker was found in this scope.
Previous findings
- P2 NuGet fail-open: fixed.
.github/security-audit/nuget-audit.sh:44–56explicitly handles restore/list failures. The blocking-project loop at lines 64–72 converts both operational errors and vulnerability reports into a nonzero job result. Report-only failures still warn without blocking, as intended. The production script is called directly by the workflow and the regression tests. - P2 standalone Kotlin FFI omission: fixed.
.github/workflows/security-audit.yml:276–284resolves the separateffi/kotlinGradle root and requires all four expected Gradle lockfiles to be non-empty. The scanner coverage check also requiresffi/kotlin/AntFfi/gradle.lockfile. Exact-head CI generated that file and OSV scanned its 36 packages. I downloaded the exact-headresolved-gradleartifact (12262889296) and verified the lockfile contains JNA 5.17.0 and coroutines 1.8.1.
Verification
- Hosted security-audit run 39420175946: all 15 jobs succeeded at the reviewed head. The OSV log accounts for all 19 expected SDK lockfiles.
- Local: all 8 repository regression cases passed; 13 additional NuGet control-flow cases passed; 2 mutation checks proved the regressions reject removal of either failure-propagation branch; 3 coverage-guard cases passed, including missing/empty Kotlin FFI lockfiles. Bash syntax and actionlint passed (actionlint shellcheck integration disabled).
- Evidence limit: local NuGet failure-path tests use a stub executable, not a live vulnerable NuGet package. Hosted CI supplies real .NET/Gradle/OSV execution. These are separate forms of evidence, not interchangeable claims of end-to-end vulnerability injection.
Panel: 5/6 roles complete
- z-ai/glm-5.3 — correctness: APPROVE; adversarial: APPROVE.
- qwen38-27b — correctness: APPROVE; adversarial: APPROVE.
- deepseek-v4-flash — correctness: APPROVE; adversarial: unavailable after bounded attempts (Timed out waiting for the model lock queue; output truncated (finish_reason=length)).
The completed roles agree on approval. Missing roles are not agreement. This is a provisional partial-panel result; no substitute model or historical vote is counted.
Non-blocking watch-out
NuGet vulnerability detection still depends on the current English dotnet list output phrase. A structured-output check or a fixture captured from a vulnerable package would reduce that format risk. No failure was demonstrated with the configured .NET 8 SDK, so this is follow-up hardening, not a new blocker.
Review only. No merge or deployment performed.
Summary
A scheduled dependency-advisory audit covering every SDK in the repo (V2-1582 §3). Until now only Rust (
cargo audit) and Go (main-only,continue-on-errorgovulncheck) were scanned, and only when someone touched those paths.When it runs: Monday + Wednesday at 05:17 UTC, ahead of the release-decision meetings; on any PR or main push that changes a manifest or lockfile; and on demand (
workflow_dispatch).cargo audit×4Cargo.lock(honours.cargo/audit.toml)govulnchecknpm audit×2package-lock.jsonExamplesreport-onlyresolve×6pub get), PHP (composer update --no-install), Ruby (bundle lock), Python (uv pip compile; antd-py, antd-mcp, ant-dev), Gradle (Java, Kotlin, Android, via an init script) and Swift (package resolve)osv-scannerexamples/not accepted inosv-scanner.tomlosv-scanner also reads GitHub advisories that RustSec doesn't carry. That's how the openpgp/openssl findings fixed in #366 were found while
cargo auditreported 0.Policy
Blocking: everything except
examples/, dev and test dependencies included. Most ecosystems can't separate dev from runtime in a lockfile, and dev tooling runs in CI next to publishing credentials.Report-only:
examples//Examples/(web3j's jackson/netty/bouncycastle in the Java and Kotlin examples; Newtonsoft.Json in the C# examples). Shown in the summary with a warning, never fails.Accepted findings live in
osv-scanner.toml, each with a reason, tracking issue andignoreUntildate. When the date passes the finding fails the run again, which forces a re-review. The initial list (14 entries):extract()never called; mirrors ci(deps): ignore zip extract() advisories until ant-core moves to zip 9 (RUSTSEC-2026-0367, -0368, -0369) #372; V2-1634Ecosystems without a committed lockfile are scanned as resolved on the day, so a run can go red with no code change. That's what the schedule is for.
Not scanned: Lua, Zig, C++ (no usable advisory database).
Files
.github/workflows/security-audit.yml: the workflowosv-scanner.toml: accepted findings (reason, tracking issue,ignoreUntilfor each).github/security-audit/lock-all.init.gradle: Gradle dependency locking from outside the build (no build-file changes);-PauditConfigurationslimits ffi/android toreleaseRuntimeClasspath.github/security-audit/collect-lockfiles.sh: copies each resolve job's generated lockfiles into the artifactSwift
Swift resolves in its own job in the official
swift:6.4image, notsetup-swift, which lags behind Swift releases. The toolchain decides what gets resolved: the first run used Swift 6.0, resolved swift-nio 2.97.1 / -http2 1.42.0 / -extras 1.33.0 and correctly reported 5 SwiftNIO advisories whose fixes need Swift 6.1. That finding led to #383 (antd-swift now requires those fixes, Swift 6.1+). On 6.4 it resolves 2.104.0 / 1.46.0 / 1.35.1.Output
The job summary carries the osv table for the blocking pass, a collapsed list of accepted findings with their reasons, and the examples report. Blocking results are also uploaded as SARIF (category
osv-scanner), so they appear in the Security tab and on PRs. Scanner versions are pinned (cargo-audit 0.22.2, govulncheck v1.9.0, osv-scanner 2.7.0 with its SHA-256 checked, uv 0.13.3).Test plan
pip-audit-equivalent: Python clean; Dart, Ruby clean--x-excludeform checked:g:**/examplesworks,g:**/examples/**silently doesn't|| code=$?, because Actions'bash -eended the first scan step before it wrote the summary; (2) Swift resolves under 6.4 (above); (3)safe.directoryfor git inside the Swift container49b21c2: 40 checks passed, 2 skipped (Go race/security, which run only on pushes to main). Blocking scan passed, SARIF upload accepted, examples report ran; 0 open osv-scanner code-scanning alerts on the PRosv-scanner.toml(7dcc4b4): 17/17 checks pass, including the blocking scan, SARIF upload and examples reportworkflow_dispatchrun on main to seed the Security tabRefs V2-1582.
🤖 Generated with Claude Code