Repository navigation
Conversation
Corrige les défauts relevés par l'audit du 2026-08-04 (9 dimensions,
contre-expertise adversariale). 12 lots, périmètres de fichiers disjoints.
Bloquants
- MFA: la connexion à deux étapes était cassée pour TOUS les comptes MFA.
Le template rendait {{.Password}}, clé absente de la map de données, donc
un mot de passe vide au second POST -> "Identifiants invalides" + compteur
de rate-limit. Remplacé par un état de pré-authentification côté serveur
(mfa_pending_user + expiration 5 min), sans credential dans le HTML.
- CI: les PR d'un dépôt public s'exécutaient sur le runner self-hosted
(go test = exécution de code arbitraire). Bascule sur ubuntu-latest pour
les PR. Injection shell via le titre de PR corrigée (passage par env).
- Publication GHCR enchaînée à la release (l'image latest avait 4 versions
de retard sur le dépôt).
- Restauration N3: le verdict "application répond" était rendu sans exécuter
de healthcheck. Le niveau prouvé est désormais calculé avant restauration.
- Canal GoaBackup: la commande d'installation téléchargeait la page de login
chez un tiers. Route publique + jeton à usage unique.
- Audit: aucune action privilégiée n'était tracée (shell root, clés SSH,
playbooks, MFA). Middleware AuditTrail + appels explicites.
- XSS stockée dans la vue Wazuh (nom d'agent, IP, OS non échappés).
- Ansible contournait l'épinglage TOFU appliqué par le reste du produit.
Sécurité
- Rate-limiter: X-Forwarded-For n'est plus cru sans TRUSTED_PROXIES; purge
des entrées.
- Révocation de session par session_epoch; secours MFA par reset admin.
- RBAC: les mutations du catalogue d'applications passent en AdminOnly.
- SSRF du worker de health-check fermée au niveau du dialer.
- /setup: limitation de débit et création de compte atomique.
Données
- Migrations versionnées, erreurs réelles distinguées par code MySQL,
Migrate() retourne une erreur et peut faire échouer le démarrage.
- retention_count effectivement appliqué; rpo_hours configurable;
l'archive restaurée est enregistrée (preuve auditable).
Frontend
- 497 classes de palette brute -> tokens sémantiques (thème clair illisible).
- Config Tailwind <alpha-value>: 61 utilitaires d'opacité étaient omis du
build. CSS régénéré et compilé dans l'image.
- 31 alert() -> composant toast partagé; modales accessibles; mobile.
Exploitabilité
- /healthz et /readyz; WriteTimeout neutralisé sur les endpoints streamés;
certificat TLS persisté avec SAN configurables; support des secrets _FILE;
durcissement des conteneurs.
Dépendances
- x/crypto v0.31.0 -> v0.52.0 (6 vulns, dont GO-2026-5020 atteignable depuis
la console SSH), chi v5.2.1 -> v5.2.2.
- govulncheck: 42 -> 26 vulnérabilités appelées, plus aucune hors stdlib.
Couverture de tests: 14,3% -> 24,1%. go build, go vet, gofmt et
go test -race passent sur l'intégralité du dépôt.
Le premier commit a été relu par 5 relecteurs adversariaux (5 axes, 18
contre-expertises). 47 défauts retenus, dont 2 critiques : ils venaient
tous du même angle mort — du code correct JAMAIS CÂBLÉ, parce que
cmd/server/main.go et le routeur n'appartenaient à aucun périmètre.
Critiques
- Le canal Proxmox était mort : SetDefaultChannelHostKeys n'était appelé
nulle part, donc la vérification TOFU fail-closed refusait toute
connexion — tests N1/N2/N3, push off-site, healthcheck et sonde disque
compris. Idem SetDefaultHostKeyStore pour l'exécution Ansible manuelle.
- L'image ne se construisait plus : le builder était épinglé sur Go 1.23.12
(GOTOOLCHAIN=local) face à un go.mod monté à 1.25.0 par un autre lot.
Builder repris sur golang:1.25-alpine (1.25.12) et directive `toolchain`
ajoutée pour que le local, la CI et l'image parlent de la même version.
Câblage manquant
- L'erreur de Migrate() était jetée : le refus de démarrer sur schéma
cassé était du code mort.
- BackupService.Wait() n'était appelé nulle part : la fuite de sandbox
qu'il corrige subsistait. Drainé à l'arrêt, budget dédié.
- {{.InstallCommand}}/{{.InstallerSHA}} n'étaient utilisés par aucun
template : la commande affichée restait un curl -k sans vérification
d'empreinte, toute la machinerie sha256 était morte à l'écran.
- Le worker SOAR ignorait GetRecentAlertsWindow : le curseur dépassait
encore la troncature. Clé de dédup passée à l'ID d'alerte (l'ancienne
collisionnait pendant un pic).
- retention_enabled n'était exposé ni par le handler ni par la vue : le
champ « conserver N archives » était devenu inerte, l'UI mentait.
- Les briques d'épinglage TOFU n'avaient aucun point d'entrée : trois
routes Admin (scan/pin/delete) + parcours dans l'écran Clés SSH.
- TRUSTED_PROXIES n'était dans aucun compose ni .env.example.
Régressions introduites par la remédiation
- /api/mfa/verify contournait le durcissement de /api/mfa/disable : une
session volée pouvait remplacer le second facteur au lieu de l'enlever.
- Le bouton « Désactiver 2FA » envoyait une requête sans corps -> 400.
- session_epoch n'était pas incrémenté au changement de mot de passe.
- La rotation de rétention s'armait rétroactivement sur les installations
existantes (défaut de schéma à 3) et purgeait des archives que GoaCore
n'a pas produites. Neutralisée derrière un interrupteur explicite.
- Le TOFU Ansible n'avait aucun chemin d'amorçage : toutes les
planifications existantes cessaient de fonctionner.
- Le certificat auto-signé était émis en CA, avec sa clé privée dans un
volume, et la doc invitait à l'importer dans le magasin de confiance.
Aussi : healthcheck MySQL compatible _FILE, HEALTHCHECK conteneur sur
/readyz, install/backup.sh (intégrité avant restauration, plancher de
rétention, dump en 0600), Échap ne jette plus une saisie non enregistrée,
palette Ctrl+K dans la pile de modales partagée, dernières couleurs brutes
migrées, job CI qui exerce enfin les migrations sur un vrai MySQL.
govulncheck : 42 -> 0 vulnérabilité appelée. L'étape CI devient bloquante.
Couverture : 14,3% -> 26,5%. build, vet, gofmt et go test -race verts.
…blic Le nettoyage du Jalon 0 avait purgé l'infrastructure personnelle de l'auteur du dépôt public ; les agents de remédiation l'avaient réintroduite dans 10 fichiers (29 occurrences), sans que le build ni les tests ne s'en plaignent — un dépôt public ne fait pas d'erreur de compilation sur une fuite de cartographie réseau. - Exemples de documentation (.env.example, config.go, ratelimit.go, tls.go, ssh_keys.html) : plages RFC5737 192.0.2.0/24, comme le reste du dépôt. - Tests : bascule sur 172.16.0.0/12, qui reste du RFC1918 — la sémantique « plage privée » des tests SSRF et TRUSTED_PROXIES en dépend — mais hors du plan d'adressage réel. - docker-compose-dev.yml : TLS_HOSTS sans valeur par défaut, en-tête sans nom de domaine. - Fixture de test : grafana.example.com. Restent les 4 occurrences de ci-deploy.yml, déjà présentes sur origin/dev : ce sont les cibles de déploiement de l'auteur, pas une régression de ce diff. Elles sont désormais surchargeables par les variables de dépôt DEV_PROBE_URL / PROD_PROBE_URL.
Le transfert AbrahamOP/GoaCore -> GoaCloud/GoaCore a détaché le paquet GHCR : `goacore` reste la propriété du compte personnel et son champ `repository` est désormais null (vérifié via l'API). Or les deux workflows codaient en dur `ghcr.io/abrahamop/goacore` : le GITHUB_TOKEN d'un dépôt de l'organisation n'a pas le droit d'écrire dans un paquet d'un compte personnel, donc la prochaine release aurait échoué à publier. - Image renommée en `ghcr.io/goacloud/goacore` (workflows, install/docker-compose.yml, README, ROADMAP) : l'espace de nommage suit le propriétaire du dépôt, et GITHUB_TOKEN retrouve ses droits sans jeton personnel à long terme. - URL du dépôt mises à jour vers GoaCloud/GoaCore dans la documentation. L'ancien paquet `ghcr.io/abrahamop/goacore` reste en place, figé sur 0.7.7 — il n'a jamais été publié au-delà (cf. audit). À supprimer une fois la première image publiée sous l'organisation.
Le transfert AbrahamOP/GoaCore -> GoaCloud/GoaCore a détaché le paquet GHCR : `goacore` reste la propriété du compte personnel et son champ `repository` est désormais null. Or publish-image.yml codait en dur `ghcr.io/abrahamop/goacore`, et le GITHUB_TOKEN d'un dépôt d'organisation n'a pas le droit d'écrire dans le paquet d'un compte personnel : la prochaine release aurait échoué à publier. - Image renommée en `ghcr.io/goacloud/goacore` : l'espace de nommage suit le propriétaire du dépôt, GITHUB_TOKEN retrouve ses droits sans jeton personnel. - URL du dépôt mises à jour (README, ROADMAP, install/docker-compose.yml, package.json). Les liens raw.githubusercontent.com ne suivent pas la redirection de transfert, donc ils devaient être corrigés. Correctif isolé, extrait de la branche fix/audit-2026-08 pour ne pas laisser la publication cassée en attendant la revue du reste.
# Conflicts: # .github/workflows/publish-image.yml # install/docker-compose.yml
fix: remédiation de l'audit de sécurité et de qualité (govulncheck 42→0, couverture 14→26 %)
`go test -race` exige cgo, donc un compilateur C. Le runner self-hosted n'en a pas (ni gcc ni cc sur le CT), CGO_ENABLED retombe donc à 0 et l'étape échoue avant même de lancer un test : « -race requires cgo ». C'est ce qui a fait rougir la CI au merge de la remédiation, sans qu'aucune data race soit en cause. Plutôt que d'installer une chaîne C sur le CT du runner pour un job qui n'a besoin d'aucun accès au LAN, l'étape devient un job `race` sur ubuntu-latest, où gcc est fourni — même raisonnement que pour le job de migrations MySQL. Les déploiements et la release en dépendent (`needs: [build, race, helper, db-tests]`) : une data race doit bloquer la mise en production, pas seulement teinter un log.
`main` avait divergé de 18 commits (Dependabot + correctifs CI) jamais redescendus dans `dev`. Plusieurs faisaient la même chose que la remédiation, en plus récent : fusionner sans arbitrer aurait fait REGRESSER leurs mises à jour. Arbitrages : - Dockerfile : alpine 3.21 (moi) -> 3.24 (main), mais épinglé par DIGEST comme le reste du fichier. On prend la version de main sans perdre la reproductibilité du build. - go.mod : les dépendances de main sont plus récentes (x/crypto v0.54.0 contre v0.52.0, chi v5.3.1 contre v5.2.2) et sont retenues telles quelles. Seule la directive `toolchain go1.25.12` est conservée de mon côté : elle est ce qui rend cohérents le builder du Dockerfile et le toolchain local, et ce qui solde les vulnérabilités de la bibliothèque standard. - Workflows : versions d'actions de main (checkout v7, setup-go v7, qemu/buildx/ login v4, metadata v6, build-push v7) RÉ-ÉPINGLÉES par SHA. Un tag d'action reste mouvant, et ces actions voient le GITHUB_TOKEN. - notify-pr : les deux côtés corrigeaient le même bloc pour des raisons différentes. Garde de main sur un DISCORD_WEBHOOK vide (les PR Dependabot utilisent un autre magasin de secrets, `curl -sf` sortait en 3 et faisait échouer PR Check) ET désinfection des valeurs contrôlées par l'auteur de la PR (anti-injection). La garde passe en premier : inutile d'assainir pour ne rien envoyer. Vérifié après réconciliation : go build, go vet, gofmt, go test -race et govulncheck (0 vulnérabilité) tous verts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Passage en production de la remédiation de l'audit du 2026-08-04, déjà fusionnée dans
dev(PR #21) et vérifiée sur l'instance DEV déployée (
/readyz→{"status":"ready","database":"ok"},donc migrations appliquées sur une base existante).
govulncheck— vulnérabilités appeléesLes défauts bloquants corrigés
POST, l'utilisateur récoltait « Identifiants invalides » et un compteur de rate-limit. État de
pré-authentification côté serveur, plus aucun credential dans le HTML.
go test).Les jobs de PR basculent sur des runners GitHub jetables. Injection shell via le titre de PR corrigée.
auto-découvertes — le moteur rendait « l'application répond » alors que seul le boot était prouvé.
curlrécupérait la pagede login et
sudo bashl'exécutait. Route publique + jeton à usage unique.désactivation MFA ne laissaient rien dans le journal d'audit.
Plus : rate-limiter non contournable par en-tête forgé, révocation de session, RBAC du catalogue
d'applications, SSRF du health-check fermée, migrations versionnées bloquantes, rétention réellement
appliquée derrière un interrupteur explicite,
/healthzet/readyz, certificat TLS persisté,secrets
_FILE, durcissement des conteneurs, thème clair réparé, 31alert()remplacés,modales accessibles.
Réconciliation avec
mainmainavait divergé de 18 commits jamais redescendus dansdev, dont plusieurs faisaient la mêmechose que la remédiation en plus récent. Fusionner sans arbitrer aurait fait régresser ces mises
à jour. Ce qui a été tranché, commit
350f4e7:alpinemain(3.24) retenue, mais épinglée par digest comme le reste du fichiermainretenues (x/cryptov0.54.0,chiv5.3.1 — plus récentes que les miennes)toolchain go1.25.12main(checkout v7, setup-go v7, qemu/buildx/login v4, metadata v6, build-push v7) ré-épinglées par SHAnotify-prmainsur unDISCORD_WEBHOOKvide (PR #18) et la désinfection anti-injection des valeurs d'auteur de PRCe qui se déclenche au merge
deploy-prod→release(bump sémantique + tag + release GitHub) →publish(image multi-arch).Deux gestes manuels ensuite :
ghcr.io/goacloud/goacore. Un paquet créé par Actions naît privé, etle
docker pullanonyme du README en dépend. C'est la première publication sous l'organisation :l'image change d'espace de nommage suite au transfert du dépôt.
ghcr.io/abrahamop/goacore, figé sur 0.7.7 et désormais rattaché àaucun dépôt.
Vérification
go build,go vet,gofmt,go test ./... -raceetgovulncheck(0 vulnérabilité) verts aprèsréconciliation. Contrairement à la PR vers
dev, celle-ci déclenche la CI complète — build,détecteur de course, migrations sur MySQL réel, validation du helper goabackup.
Réserve de relecture
15 000 lignes écrites en grande partie par des agents, sur un produit destiné à des clients, et le
prochain merge part en production. Le morceau à relire en priorité reste
internal/services/backup.go: la rotation des archives est la seule opération destructiveajoutée. Elle est désarmée par défaut — un interrupteur
retention_enabledque la migration laisseà faux sur tout l'existant — et un test vérifie qu'une installation existante ne supprime rien.