ci: add signed fail-closed release supply chain - #38
Conversation
|
Warning Review limit reached
Next review available in: 4 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughO workflow executa releases Android assinados para tags ChangesPipeline de release assinado
Revisão de dependências
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/release.yml:
- Around line 16-24: Adicionar comentários explicativos nos blocos de
permissions de .github/workflows/release.yml#L16-L24 e `#L170-L178`: no job
build-validate, documentar que id-token: write e attestations: write são
necessários para assinatura e attestation via Sigstore; no job da linha 170,
documentar que contents: write é necessário para criar a GitHub Release.
- Around line 105-133: Update the “Collect and verify signed APK” step to
extract the APK certificate fingerprint using apksigner and compare it with a
configured expected fingerprint from a repository secret or variable. Fail the
job when the reference value is missing or does not match, while preserving the
existing signature verification and checksum generation.
- Around line 30-35: Remove cache: gradle from the actions/setup-java step in
the release workflow, leaving the Temurin Java 17 configuration unchanged.
Ensure this release job does not restore or save a Gradle cache shared with
less-trusted workflow contexts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b8e0e1a6-b825-450a-8d4a-1e033d5c7368
📒 Files selected for processing (3)
.github/workflows/release.ymlapp/build.gradle.ktsdocs/RELEASE_SECURITY.md
| jobs: | ||
| release-disabled: | ||
| build-validate: | ||
| name: Build and validate release candidate | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 60 | ||
| permissions: | ||
| contents: read | ||
| id-token: write | ||
| attestations: write |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Blocos de permissions sem comentário explicativo (zizmor). Ambos os jobs concedem permissões elevadas e mínimas corretamente escopadas, mas o zizmor sinaliza a falta de comentários justificando cada uma — mesma causa raiz nos dois locais.
.github/workflows/release.yml#L16-L24: adicionar comentário explicando por queid-token: writeeattestations: writesão necessários (assinatura/attestation via Sigstore)..github/workflows/release.yml#L170-L178: adicionar comentário explicando por quecontents: writeé necessário apenas neste job (criação da GitHub Release).
🧰 Tools
🪛 zizmor (1.26.1)
[warning] 23-23: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
📍 Affects 1 file
.github/workflows/release.yml#L16-L24(this comment).github/workflows/release.yml#L170-L178
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 16 - 24, Adicionar comentários
explicativos nos blocos de permissions de .github/workflows/release.yml#L16-L24
e `#L170-L178`: no job build-validate, documentar que id-token: write e
attestations: write são necessários para assinatura e attestation via Sigstore;
no job da linha 170, documentar que contents: write é necessário para criar a
GitHub Release.
Source: Linters/SAST tools
| - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 | ||
| with: | ||
| distribution: temurin | ||
| java-version: "17" | ||
| cache: gradle | ||
|
|
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Risco de cache poisoning no setup-java com cache: gradle.
O zizmor sinaliza este passo como potencialmente vulnerável a cache poisoning. Num pipeline de release fail-closed cujo objetivo é garantir a integridade da cadeia de assinatura, restaurar um cache Gradle possivelmente escrito por execuções menos confiáveis (ex.: workflows de PR/branches) permitiria que dependências adulteradas sejam reutilizadas no build que será assinado, atestado e publicado — comprometendo justamente as garantias que o SBOM/provenance deveriam oferecer.
Recomenda-se desabilitar o cache neste job de release (ou usar uma chave de cache isolada de qualquer contexto menos confiável).
🔒 Correção sugerida
- uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
with:
distribution: temurin
java-version: "17"
- cache: gradle📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 | |
| with: | |
| distribution: temurin | |
| java-version: "17" | |
| cache: gradle | |
| - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 | |
| with: | |
| distribution: temurin | |
| java-version: "17" |
🧰 Tools
🪛 zizmor (1.26.1)
[error] 30-30: runtime artifacts potentially vulnerable to a cache poisoning attack (cache-poisoning): this step
(cache-poisoning)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 30 - 35, Remove cache: gradle
from the actions/setup-java step in the release workflow, leaving the Temurin
Java 17 configuration unchanged. Ensure this release job does not restore or
save a Gradle cache shared with less-trusted workflow contexts.
Source: Linters/SAST tools
| - name: Collect and verify signed APK | ||
| id: artifact | ||
| env: | ||
| RELEASE_BASENAME: ${{ steps.naming.outputs.basename }} | ||
| run: | | ||
| set -euo pipefail | ||
| mapfile -t apks < <(find app/build/outputs/apk/release -maxdepth 1 -type f -name '*.apk' -print) | ||
| if [ "${#apks[@]}" -ne 1 ]; then | ||
| echo "::error title=Unexpected release outputs::Expected exactly one release APK, found ${#apks[@]}." | ||
| printf '%s\n' "${apks[@]:-}" | ||
| exit 1 | ||
| fi | ||
|
|
||
| mkdir -p dist | ||
| apk="dist/${RELEASE_BASENAME}.apk" | ||
| cp "${apks[0]}" "$apk" | ||
|
|
||
| apksigner_bin="$(command -v apksigner || true)" | ||
| if [ -z "$apksigner_bin" ]; then | ||
| apksigner_bin="$(find "$ANDROID_HOME/build-tools" -type f -name apksigner -print | sort -V | tail -n 1)" | ||
| fi | ||
| test -n "$apksigner_bin" | ||
| "$apksigner_bin" verify --verbose --print-certs "$apk" | ||
|
|
||
| ( | ||
| cd dist | ||
| sha256sum "${RELEASE_BASENAME}.apk" > "${RELEASE_BASENAME}.apk.sha256" | ||
| ) | ||
| echo "apk=$apk" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win
apksigner verify confirma assinatura, mas não a identidade da chave.
O passo garante que o APK está assinado e íntegro, mas não compara o fingerprint do certificado com um valor esperado conhecido. Em caso de erro operacional (ex.: keystore/alias trocados por engano no secret), o pipeline continuaria "verde" mesmo assinando com a chave errada — o próprio docs/RELEASE_SECURITY.md (linha 80) menciona registrar o fingerprint do certificado, mas isso não é validado automaticamente aqui.
Considere extrair o fingerprint com apksigner verify --print-certs e compará-lo a um valor de referência armazenado em secret/variável do repositório, falhando o job em caso de divergência.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/release.yml around lines 105 - 133, Update the “Collect
and verify signed APK” step to extract the APK certificate fingerprint using
apksigner and compare it with a configured expected fingerprint from a
repository secret or variable. Fail the job when the reference value is missing
or does not match, while preserving the existing signature verification and
checksum generation.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/security.yml:
- Around line 53-55: Atualize a coleta de arquivos em changed_files no workflow
para detectar truncamento: compare a quantidade obtida com
pulls/$PR_NUMBER.changed_files e encerre o job com erro quando houver
divergência, antes de executar a Dependency Review. Como alternativa, derive a
lista usando git diff entre os SHAs base e head, garantindo que nenhum manifesto
omitido permita pular a verificação.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 95703b35-3940-4da3-89e6-6e628920e1ce
📒 Files selected for processing (1)
.github/workflows/security.yml
| changed_files="$(gh api --paginate \ | ||
| --jq '.[].filename' \ | ||
| "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files")" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "## .github/workflows/security.yml"
wc -l .github/workflows/security.yml
sed -n '1,220p' .github/workflows/security.yml | cat -n
echo
echo "## Search for changed_files / dependency review / pulls/$PR_NUMBER usage"
rg -n "changed_files|dependency review|changed_files|pulls/\\$PR_NUMBER|changed_files" .github/workflows/security.yml .github/workflows -g '*.yml' -g '*.yaml' || trueRepository: itayson/Rockservice-mobile
Length of output: 4177
🌐 Web query:
GitHub REST API pull requests list files maximum 3000 files truncation changed_files documentation
💡 Result:
The GitHub REST API endpoint for listing files in a pull request (GET /repos/{owner}/{repo}/pulls/{pull_number}/files) is documented to return a maximum of 3,000 files [1][2][3]. When a pull request contains more than 3,000 files, the API will truncate the results [4]. Crucially, the API does not provide a warning or error message when this truncation occurs; it simply stops returning further pages [4]. Because the API silently caps the total number of files returned, you cannot rely on standard pagination (using the Link header) to retrieve files beyond this 3,000-file limit [4]. Developers encountering this limit are often advised to check the changed_files property available in the main Pull Request object (GET /repos/{owner}/{repo}/pulls/{pull_number}) [4]. The changed_files property provides an accurate count of all files changed in the pull request, which can be compared against the number of files actually returned by the list files endpoint to detect if truncation has occurred [4]. If you need to access files beyond the 3,000-file limit, common workarounds include fetching the files commit-by-commit using the List pull request commits endpoint, although this approach has its own limitations regarding total commit counts [4].
Citations:
- 1: https://docs.github.com/en/rest/pulls/pulls
- 2: https://docs.github.com/en/rest/pulls/pulls?apiVersion=2026-03-10
- 3: https://docs.github.com/en/enterprise-server@3.17/rest/pulls/pulls
- 4: approve: fix silent approval bypass when PR exceeds GitHub file list API limit kubernetes-sigs/prow#707
Falhe fechado quando a lista de arquivos do PR for truncada. O endpoint GET /pulls/{pull_number}/files pode cortar resultados acima de 3.000 arquivos; nesse caso, um manifesto pode ficar fora de changed_files e a Dependency Review ser pulada indevidamente. Compare com pulls/$PR_NUMBER.changed_files e encerre o job com erro se houver divergência; outra opção é usar git diff a partir dos SHAs base e head.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/security.yml around lines 53 - 55, Atualize a coleta de
arquivos em changed_files no workflow para detectar truncamento: compare a
quantidade obtida com pulls/$PR_NUMBER.changed_files e encerre o job com erro
quando houver divergência, antes de executar a Dependency Review. Como
alternativa, derive a lista usando git diff entre os SHAs base e head,
garantindo que nenhum manifesto omitido permita pular a verificação.
Source: MCP tools
Escopo
Implementa a infraestrutura de release rastreada em #20 sem ativar publicação insegura.
Este PR:
ROCKSERVICE_REQUIRE_RELEASE_SIGNING=true, fazendo o Gradle falhar antes de gerar release sem configuração completa;$RUNNER_TEMPcom permissões restritas e remoçãoif: always();assembleRelease;apksigner;contents: writeexclusivamente no job final de publicação;A execução manual valida release candidates mas não publica releases. A publicação automática ocorre somente em evento de tag compatível com
v*.*.*e após todos os gates do job de build/validação.Refs #20.
Risco
Moderado. A mudança afeta a infraestrutura de distribuição, mas mantém comportamento fail-closed. Sem secrets válidos, o release falha antes do build. O pipeline ainda não deve ser considerado operacionalmente concluído até os secrets reais, a proteção administrativa de tags e um release candidate assinado serem validados.
Testes
app/build.gradle.ktsé alterado;Evidência de hardware
Não aplicável diretamente. O workflow não altera transporte USB nem habilita operações destrutivas. Releases destinados a hardware continuam sujeitos aos gates físicos definidos no roadmap.
Segurança
0600if: always()apksigner verifybloqueantecontents: writeSummary by CodeRabbit
v*.*.*), mantendoworkflow_dispatch.