Skip to content

fix(ci): harden the AUR publish pipeline and repair its distribution CI - #297

Merged
EtienneLescot merged 5 commits into
mainfrom
claude/aur-github-actions-setup-ac865f
Aug 7, 2026
Merged

fix(ci): harden the AUR publish pipeline and repair its distribution CI#297
EtienneLescot merged 5 commits into
mainfrom
claude/aur-github-actions-setup-ac865f

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Contexte

Mise en place de la publication AUR automatisée. Les secrets et variables sont déjà configurés côté GitHub (AUR_SSH_PRIVATE_KEY en secret, AUR_KNOWN_HOSTS et AUR_PACKAGE_NAME en variables) ; cette PR ne contient que le code.

Point de départ : le job n'avait jamais tourné. Il était gardé par vars.AUR_PACKAGE_NAME, qui n'existait pas — les 10 dernières releases sont skipped. Tout ce qui suit est donc du code jamais exécuté, dont deux bugs qui l'empêchaient purement et simplement d'aboutir.

Le fait structurant : le PKGBUILD n'est pas versionné ici. Il vit sur l'AUR, appartient au mainteneur du paquet (psychosomat), et peut changer sans passer par une PR. Le workflow le clonait, le patchait et le repoussait sans jamais regarder ce qu'il contenait.

Corrections

Sécurité

Problème Correction
makepkg source le PKGBUILD tiers — il s'exécutait dans un runner détenant déjà la clé de déploiement Clone en HTTPS, clé écrite uniquement après validation
Aucune validation du contenu republié Audit du préambule + allowlist des sources sur github.com/getopenscreen/openscreen/ + refus d'un diff débordant de pkgver/pkgrel/sha256sums
Le tag de workflow_dispatch est du texte libre et finissait dans une expression sed : v1.0|e id| sortait de l'expression et exécutait du shell Regex stricte ^v[0-9]+\.[0-9]+\.[0-9]+$
Un dispatch manuel sur un tag RC publiait un pkgver invalide (pkgver interdit le tiret) Bloqué par la même regex
winget-releaser épinglé sur un tag mutable alors qu'il reçoit WINGET_ACC_TOKEN Épinglé sur le SHA de v2

Correctness

  • Un seul checksum était réécrit. Le sed ne touchait que sha256sums[0]. Celui du LICENSE est épinglé sur raw/v${pkgver}/LICENSE : il périmait dès que ce fichier changeait, et makepkg échouait alors chez tous les utilisateurs, pas chez nous. Tous les checksums sont désormais recalculés depuis les URLs réelles.
  • L'étape d'installation ne pouvait pas aboutir. pacman-package-manager ne fournit pas makepkg (seulement pacman, pacman-conf, pacman-db-upgrade, pacman-key, repo-add) mais s'installe sans erreur, ce qui rendait le || apt-get install makepkg inatteignable — l'étape mourait sur « makepkg still missing after install ». Et makepkg seul ne suffit pas non plus : il ne dépend pas du binaire pacman, qu'il résout par type -P au démarrage, et sort sinon sur « An unknown error has occurred ». Les deux paquets sont maintenant installés.
  • Vérification ajoutée que le PKGBUILD référence bien l'asset .pacman de la release.

Vérification

Le code réel des blocs run: est extrait du YAML et exécuté contre le vrai dépôt AUR — aucune logique n'est recopiée dans le harnais, il ne peut donc pas diverger.

  • 20 assertions, dont 12 négatives : exfiltration par $(), backticks, pkgname détourné, sha256sums multi-lignes, source détournée vers un autre hôte, backdoor injectée dans package(), et 5 tags malveillants.
  • makepkg --printsrcinfo réellement exécuté (6.0.2, l'image de ubuntu-latest) : .SRCINFO correct pour 1.9.0-1, 2 sources / 2 checksums.
  • Les 11 blocs run: passent bash -n après dédentation YAML.

Deux bugs ont été trouvés dans mon propre code par ces tests et corrigés : \bsource\b rejetait la déclaration source=() légitime, et makepkg --version | head -1 prenait un SIGPIPE que pipefail remontait en échec d'étape.

Ce que ça ne couvre pas

  • La première exécution réelle reste devant nous : un workflow_dispatch sur v1.9.0 (l'AUR est à 1.7.0-2).
  • Rien ne partira tant que la clé publique n'est pas autorisée côté AUR.
  • Le contrôle de diff protège les utilisateurs d'un package() modifié, mais il bloque dans ce cas au lieu de publier : un changement légitime en amont demandera une intervention manuelle. C'est le compromis assumé.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Security & Reliability

    • Strengthened AUR publishing validation for versions, package metadata, source URLs, release assets, and checksums.
    • Added safeguards to prevent unexpected package definition changes before publication.
    • Pinned the WinGet publishing workflow to a specific action revision for more predictable releases.
  • Maintenance

    • Excluded local AUR publishing credentials and setup artifacts from version control.

Le dépôt est public et la génération de la clé de déploiement AUR laisse
aur_ci, aur_ci.pub et aur_known_hosts à la racine. La privée est détruite
après enregistrement dans les secrets, mais rien n'empêchait un git add -A
de la committer entre-temps.
Un tag git est mutable et cette action tierce reçoit WINGET_ACC_TOKEN : un
tag repointé exfiltrerait le token sans qu'aucun diff n'apparaisse ici.
v2 resolves to 4ffc7888bffd451b357355dc214d43bb9f23917e.
Le PKGBUILD publié sur l'AUR n'est pas versionné ici : il appartient au
mainteneur du paquet et peut changer sans passer par une PR. Le workflow le
clonait, le patchait et le repoussait sans jamais regarder ce qu'il contenait.

- makepkg *source* le PKGBUILD ; il s'exécutait donc dans un runner qui
  détenait déjà la clé de déploiement. Clone en HTTPS, clé écrite seulement
  après validation.
- Audit du préambule, allowlist des sources sur notre dépôt, et refus de
  pousser un diff qui déborde de pkgver/pkgrel/sha256sums.
- Le tag de workflow_dispatch est du texte libre et finissait dans une
  expression sed : v1.0|e id| donnait une exécution de shell. Regex stricte,
  qui bloque aussi la publication d'une RC (pkgver interdit le tiret).
- Un seul checksum était réécrit ; celui du LICENSE, épinglé sur le tag,
  périmait en silence et cassait makepkg chez les utilisateurs.
- L'étape d'installation ne pouvait pas aboutir : pacman-package-manager ne
  fournit pas makepkg mais s'installe sans erreur, rendant le repli mort.
  makepkg seul ne suffit pas non plus, il résout pacman par type -P.

Jamais détecté car le job n'avait jamais dépassé son if: — vars.AUR_PACKAGE_NAME
n'existait pas, les 10 dernières releases sont skipped.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@EtienneLescot, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 1 minute

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 55330c40-8e14-46f0-86b6-d1019854939c

📥 Commits

Reviewing files that changed from the base of the PR and between ba273bf and cecc90a.

📒 Files selected for processing (1)
  • .github/workflows/aur-publish.yml
📝 Walkthrough

Walkthrough

The AUR workflow now validates tags, PKGBUILD declarations, source URLs, checksums, generated metadata, and publication diffs before pushing. SSH setup occurs after validation. The WinGet workflow pins its action to a commit SHA, and .gitignore excludes local AUR credentials and setup files.

Changes

AUR publication validation

Layer / File(s) Summary
Tag, repository, and PKGBUILD validation
.github/workflows/aur-publish.yml
The workflow validates stable tags, clones the AUR repository over HTTPS, and audits PKGBUILD declarations before running makepkg.
Source verification and package metadata
.github/workflows/aur-publish.yml
The workflow safely updates package versions, validates allowlisted source URLs, hashes every source, installs required tools, and regenerates .SRCINFO.
Publication diff and SSH push
.github/workflows/aur-publish.yml, .gitignore
The workflow limits PKGBUILD changes to expected metadata, delays SSH setup until validation completes, uses the package-specific AUR remote, and ignores local AUR setup artifacts.

WinGet action pinning

Layer / File(s) Summary
Pinned publishing action
.github/workflows/publish-winget.yml
The workflow replaces the mutable WinGet releaser tag with an explicit commit SHA and documents the update process.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: siddharthvaddem

Sequence Diagram(s)

sequenceDiagram
  participant GitHubActions
  participant GitHubReleaseAssets
  participant Makepkg
  participant AURSSHRemote
  GitHubActions->>GitHubReleaseAssets: download and hash all declared sources
  GitHubActions->>Makepkg: build package and regenerate .SRCINFO
  GitHubActions->>AURSSHRemote: push validated package changes
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main changes: hardening the AUR publish pipeline and repairing distribution CI.
Description check ✅ Passed The description clearly covers the changes, security rationale, testing, limitations, and pending release prerequisites, despite not using every template heading.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/aur-github-actions-setup-ac865f

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
.github/workflows/aur-publish.yml (1)

118-131: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add process substitution to the rejected preamble constructs.

The pattern list catches $(, backticks, and a set of command names. It does not catch <( or >(. Bash evaluates process substitution in the preamble the same way as command substitution, so source=(<(curl ...)) or similar top-level use would pass this audit.

The audit is defense in depth, and makepkg still runs after it, so this is not a blocker. Adding the two tokens keeps the stated guarantee intact.

🛡️ Proposed addition to the pattern list
           done <<'PATTERNS'
           \$\(
           `
+          [<>]\(
           (^|[;&|[:space:]])(eval|source|curl|wget|bash|sh|python3?)[[:space:]]
           PATTERNS
🤖 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/aur-publish.yml around lines 118 - 131, Update the
PATTERNS list in the PKGBUILD preamble audit to reject both Bash
process-substitution tokens, &lt;( and &gt;(. Keep the existing
command-substitution, backtick, and command-name checks unchanged.
🤖 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/aur-publish.yml:
- Around line 202-214: Harden the URL validation in the source-fetch loop around
SUMS and URLS: reject URLs containing dot-segment path components before
fetching, and prevent redirects from bypassing the allowlist by removing curl’s
redirect-following behavior or validating the final effective URL against
ALLOWED_SOURCE_PREFIX. Ensure only the validated repository origin is used to
calculate checksums.
- Around line 193-200: Before writing the regenerated sha256sums array in the
workflow’s URL/SUMS processing, compare the source-entry count with the SUMS
count and fail with an error if they differ. Ensure local or non-URL source
entries are represented so the generated sha256sums array preserves the source
array length and prevents publishing an invalid PKGBUILD.

---

Nitpick comments:
In @.github/workflows/aur-publish.yml:
- Around line 118-131: Update the PATTERNS list in the PKGBUILD preamble audit
to reject both Bash process-substitution tokens, &lt;( and &gt;(. Keep the
existing command-substitution, backtick, and command-name checks unchanged.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bccc6a5f-40ca-48f4-8cd1-55e487b899a0

📥 Commits

Reviewing files that changed from the base of the PR and between e3dbb4c and ba273bf.

📒 Files selected for processing (3)
  • .github/workflows/aur-publish.yml
  • .github/workflows/publish-winget.yml
  • .gitignore

Comment thread .github/workflows/aur-publish.yml
Comment thread .github/workflows/aur-publish.yml
Permet d'exercer tout le pipeline sur un vrai runner — install de makepkg,
clone, audit, bump, .SRCINFO, contrôle de diff — en s'arrêtant avant le push.
La clé de déploiement n'est alors jamais écrite sur le disque.

Nécessaire pour valider ce workflow avant sa première publication réelle :
il n'a jamais dépassé son if: en dix releases.
- sha256sums pouvait devenir plus court que source=(). Une entrée locale et
  légitime (.install, .desktop) ne produit aucune URL ; on écrivait alors un
  tableau désaligné que makepkg génère sans broncher et que le contrôle de
  diff laisse passer, pour finir en échec de vérification chez chaque
  utilisateur. On compte les entrées et on refuse un formatage non reconnu.

- Le test de préfixe de l'allowlist ne contraignait pas l'origine réellement
  contactée : curl normalise les segments .. avant la requête, donc
  .../getopenscreen/openscreen/../../attacker/repo/x passait et allait
  chercher le dépôt d'un tiers. Reproduit — l'URL effective devient bien
  github.com/attacker/repo. Segments .., pourcent, arobase et antislash
  refusés, et l'hôte d'arrivée est vérifié après redirection.

- La liste de motifs du préambule ignorait la substitution de processus.

23 assertions passent, dont 15 négatives.
@EtienneLescot
EtienneLescot merged commit 267435f into main Aug 7, 2026
16 checks passed
@EtienneLescot
EtienneLescot deleted the claude/aur-github-actions-setup-ac865f branch August 7, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant