stillwater

Les projets qui suivent les meilleures pratiques ci-dessous peuvent s'auto-certifier et montrer qu'ils ont obtenu le badge de la Open Source Security Foundation (OpenSSF).

Il n'existe aucun ensemble de pratiques qui garantissent que ce logiciel n'aura jamais de défauts ou de vulnérabilités ; même les méthodes formelles peuvent échouer si les spécifications ou les hypothèses sont fausses. Il n'y a pas non plus de pratiques qui peuvent garantir qu'un projet permettra de maintenir une communauté de développement saine et qui fonctionne bien. Toutefois, suivre les meilleures pratiques peut contribuer à améliorer les résultats des projets. Par exemple, certaines pratiques permettent la revue par plusieurs personnes avant publication, ce qui peut aider à trouver des vulnérabilités techniques difficiles à trouver autrement et à renforcer la confiance et un désir d'interaction répétée entre les développeurs de différentes entreprises. Pour gagner un badge, tous les critères DOIT et NE DOIT PAS doivent être satisfaits, tous les critères DEVRAIT doivent être satisfaits OU non satisfaits avec justification, et tous les critères PROPOSÉ doivent être satisfaits OU non satisfaits (nous voulons au moins qu'ils soient considérés). Si vous voulez entrer un texte de justification pour un commentaire générique, au lieu d'une raison justifiant que la situation est acceptable, commencez le bloc de texte avec '//' suivi d'un espace. Les commentaires sont les bienvenus via le site GitHub en tant que problèmes ou pull requests. Il existe également une liste de diffusion pour discussion générale.

Nous fournissons volontiers l'information dans plusieurs langues, cependant, s'il existe un conflit ou une contradiction entre les traductions, la version anglaise est la version qui fait autorité.
Si c'est votre projet, veuillez indiquer votre statut de badge sur votre page de projet ! Le statut du badge ressemble à ceci : Le niveau de badge pour le projet 14430 est in_progress Voici comment l'intégrer :
Vous pouvez afficher votre statut de badge en incorporant ceci dans votre fichier markdown :
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14430/badge)](https://www.bestpractices.dev/projects/14430)
ou en incorporant ceci dans votre HTML :
<a href="https://www.bestpractices.dev/projects/14430"><img src="https://www.bestpractices.dev/projects/14430/badge"></a>


Ce sont les critères du niveau Basique. Vous pouvez également afficher les critères des niveaux Argent ou Or.

Baseline Series: Niveau de référence 1 Niveau de référence 2 Niveau de référence 3

        

 Notions de base 13/13 ●

  • Général

    Notez que d'autres projets peuvent utiliser le même nom.

    Self-hosted artist/composer metadata and image manager for Emby, Jellyfin, and Kodi

    Utilisez un format d'expression de licence SPDX ; des exemples sont « Apache-2.0 », « BSD-2-Clause », « BSD-3-Clause », « GPL-2.0+ », « LGPL-3.0+ », « MIT » et « (BSD-2-Clause OU Ruby) ». Ne pas inclure des guillemets simples ou doubles.
    S'il y a plus d'un langage, listez-les en tant que valeurs séparées par des virgules (espaces facultatifs) et triez-les du plus au moins utilisé. S'il y a une longue liste, veuillez lister au moins les trois premiers. S'il n'y a pas de langage (par exemple, il s'agit d'un projet uniquement de documentation ou de test), utilisez le caractère unique « - ». Utilisez une capitalisation conventionnelle pour chaque langage, par exemple « JavaScript ».
    La plate-forme commune d'énumération (CPE) est un schéma de dénomination structuré pour les systèmes, les logiciels et les paquetages des technologies de l'information. Il est utilisé dans un certain nombre de systèmes et de bases de données pour signaler des vulnérabilités.
  • Contenu basique du site Web du projet


    Le site du projet DOIT décrire succinctement ce que le logiciel fait (quel problème résout-il ?). [description_good]
    Cela DOIT être dans un langage que les utilisateurs potentiels peuvent comprendre (par exemple, il utilise un jargon minimal).

    The README's opening paragraph states what Stillwater does and the problem it solves, in language aimed at self-hosted media-server users rather than developers: https://github.com/sydlexius/stillwater#readme

    It names the software (a self-hosted web app), what it operates on (artist and composer metadata for a music library), where the results go (artist.nfo files, or pushed to Emby/Jellyfin over their APIs), and the two problems it exists to solve: artists that other tools leave blank, and manual edits being overwritten the next time a server refreshes. The same description appears on the documentation site at https://sydlexius.github.io/stillwater/ and as the repository's one-line summary.



    Le site Web du projet DOIT fournir des informations sur la façon d'obtenir, de fournir des commentaires (comme des signalements de bogues ou des demandes d'amélioration) et de contribuer au logiciel. [interact]

    All three are covered, reachable from the README:

    OBTAIN -- the README's "Quick start" section gives a working docker-compose file, and the documentation site carries full install guides for Docker, prebuilt binaries, Unraid Community Applications, and reverse-proxy setups: https://github.com/sydlexius/stillwater#quick-start and https://sydlexius.github.io/stillwater/

    FEEDBACK -- bug reports and enhancement requests go through GitHub Issues, with three templates (bug, feature, task) offered at https://github.com/sydlexius/stillwater/issues/new/choose . CONTRIBUTING.md documents which template to use and asks that larger ideas start with a short scope sketch in the issue body; questions are directed to the question label.

    CONTRIBUTE -- the README's "Contributing" section links CONTRIBUTING.md, which covers development environment setup, code style, and the pull-request workflow: https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md . Participants are asked to follow the Code of Conduct: https://github.com/sydlexius/stillwater/blob/main/CODE_OF_CONDUCT.md

    Security vulnerabilities are deliberately routed away from public issues to a private disclosure process documented in SECURITY.md.



    L'information sur la façon de contribuer DOIT expliquer le processus de contribution (par exemple, les pull requests sont-ils utilisés ?) (URL requise) [contribution]
    Nous supposons que les projets sur GitHub utilisent les problèmes et les pull requests, sauf indication contraire. Cette information peut être courte, par exemple, en indiquant que le projet utilise les pull requests, un suivi des problèmes ou des messages dans une liste de diffusion (laquelle ?)

    Non-trivial contribution file in repository: https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md.



    Les informations sur la façon de contribuer DEVRAIENT inclure les exigences pour des contributions acceptables (par exemple, une référence à toute norme de codage requise). (URL requise) [contribution_requirements]

    https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md#code-style

    CONTRIBUTING.md's "Code style" section states the requirements for an acceptable contribution and links the enforcing configuration:

    The "Pull request workflow" section adds the process requirements a contribution must satisfy: branch from main (direct commits are refused by branch protection), a conventional-commit prefix on the squash commit, and a passing local pre-push gate before pushing. These are enforced rather than advisory -- every PR must pass required CI checks including tests, a patch-coverage floor, CodeQL, and code review before it can merge.


  • Licence FLOSS


    Le logiciel produit par le projet DOIT être distribué en tant que FLOSS. [floss_license]
    FLOSS est un logiciel distribué d'une manière qui répond à la Définition de l'Open Source ou à la Définition du Logiciel Libre. Des exemples de ces licences sont CC0, MIT, BSD 2-clause, BSD 3-clause révisée, Apache 2.0, Lesser GNU General Public License (LGPL), et GNU General Public License (GPL). Pour nos besoins, cela signifie que la licence DOIT être : Le logiciel PEUT également être distribué avec d'autres licences (par exemple, « GPLv2 ou propriétaire » est acceptable).

    The GPL-3.0 license is approved by the Open Source Initiative (OSI).



    Il est PROPOSÉ que toute licence requise pour le logiciel produit par le projet soit approuvée par l'Open Source Initiative (OSI). [floss_license_osi]
    L'OSI utilise un processus d'approbation rigoureux pour déterminer quelles licences sont OSS.

    The GPL-3.0 license is approved by the Open Source Initiative (OSI).



    Le projet DOIT afficher la ou les licences de ses résultats dans un emplacement standard dans leur dépôt source. (URL requise) [license_location]
    Une convention est de publier la licence sous la forme d'un fichier à la racine du dépôt appelé LICENSE ou COPYING, qui PEUT être suivi d'une extension telle que « .txt » ou « .md ». Une autre convention est d'avoir un réportoire nommé LICENSES contenant le(s) fichier(s) de licence ; ces fichiers sont généralement nommés comme leur identifiant de licence SPDX suivi d'une extension de fichier appropriée, comme décrit dans la Spécification REUSE. Notez que ce critère est requis uniquement pour le dépôt de sources. Vous n'avez PAS besoin d'inclure le fichier de licence lors de la génération d'un élément à partir du code source (tel qu'un exécutable, un paquet ou un conteneur). Par exemple, lors de la génération d'un paquet R pour le réseau d'archives complet R (CRAN), suivez la procédure standard CRAN : si la licence est une licence standard, utilisez la spécification de standard courte (pour éviter d'installer une autre copie du texte) et listez le fichier LICENSE dans un fichier d'exclusion tel que .Rbuildignore. De même, lors de la création d'un paquet Debian, vous pouvez mettre un lien dans le fichier de copyright vers le fichier de licence dans /usr/share/common-licenses, et exclure le fichier de licence du paquet créé (par exemple, en supprimant le fichier après avoir appelé dh_auto_install). Nous encourageons fortement l'inclusion d'informations de licence lisibles automatiquement dans des formats générés lorsque cela est possible.

    Non-trivial license location file in repository: https://github.com/sydlexius/stillwater/blob/main/LICENSE.


  • Documentation


    Le projet DOIT fournir une documentation de base pour le logiciel produit par le projet. [documentation_basics]
    Cette documentation doit se trouver dans un certain format (comme le texte ou la vidéo) qui comprend : comment l'installer, comment le démarrer, comment l'utiliser (éventuellement avec un tutoriel à l'aide d'exemples) et comment l'utiliser en toute sécurité (par exemple, quoi faire et ne pas faire) si c'est un sujet approprié pour le logiciel. La documentation de sécurité n'a pas besoin d'être longue. Le projet PEUT utiliser des liens hypertextes vers du matériel hors projet en tant que documentation. Si le projet ne produit pas de logiciel, choisissez « non applicable » (N/A).

    The documentation site at https://sydlexius.github.io/stillwater/ carries 75 pages covering installation through reference, organized by task:

    GETTING STARTED (8 pages) -- first-run setup walkthrough plus install guides for Docker Compose, prebuilt binary, and Unraid Community Applications, and connection guides for Emby, Jellyfin, and Lidarr: https://sydlexius.github.io/stillwater/getting-started/

    CORE CONCEPTS (10 pages) -- how the software actually behaves: metadata providers and per-field fallback, field locking, image handling, NFO write-back: https://sydlexius.github.io/stillwater/core-concepts/

    HOW-TO (27 pages) -- task-oriented guides for the operations a user actually performs.

    REFERENCE (8 pages) -- the CLI, environment variables, every setting by tab, the rules catalogue, providers, preferences, and UI labels: https://sydlexius.github.io/stillwater/reference/

    TROUBLESHOOTING, plus a documented REST API. The API is additionally covered by an OpenAPI specification and an executable Bruno collection in the repository, and make check-openapi gates the spec against the implementation so it cannot drift silently.

    The README gives the short version -- what the software does, a working docker-compose quick start, and the feature summary -- and links the full site: https://github.com/sydlexius/stillwater#readme

    Several reference pages are GENERATED from the source of truth rather than hand-maintained (the rules catalogue, the settings reference, and the doc anchors), so they cannot drift from the implementation.



    Le projet DOIT fournir une documentation de référence qui décrit l'interface externe (entrée et sortie) du logiciel produit par le projet. [documentation_interface]
    La documentation d'une interface externe explique à un utilisateur final ou un développeur comment l'utiliser. Cela inclut son interface de programmation (API) si le logiciel en possède une. S'il s'agit d'une bibliothèque, documentez les principales classes / types et méthodes / fonctions pouvant être appelés. S'il s'agit d'une application Web, définissez son interface URL (souvent son interface REST). S'il s'agit d'une interface de ligne de commande, documentez les paramètres et les options qu'elle supporte. Dans de nombreux cas, il est préférable que la majorité de cette documentation soit générée automatiquement, de sorte que cette documentation reste synchronisée avec le logiciel au fur et à mesure qu'il change, mais cela n'est pas nécessaire. Le projet PEUT utiliser des liens hypertextes vers du matériel hors projet en tant que documentation. La documentation PEUT être générée automatiquement (quand c'est possible, c'est souvent la meilleure façon de le faire). La documentation d'une interface REST peut être générée à l'aide de Swagger / OpenAPI. La documentation de l'interface de code PEUT être générée à l'aide d'outils tels que JSDoc (JavaScript), ESDoc (JavaScript), pydoc (Python), devtools (R), pkgdown (R) et Doxygen (plusieurs). Le simple fait d'avoir des commentaires dans le code source n'est pas suffisant pour satisfaire ce critère ; il doit y avoir un moyen simple de voir l'information sans lire l'intégralité du code source. Si le projet ne produit pas de logiciel, choisissez « non applicable » (N/A).

    Every external interface the software exposes is documented, and most of the reference material is generated from the source of truth rather than maintained by hand.

    REST API. The project ships an OpenAPI specification covering 234 paths and 274 operations, describing request and response shapes for the whole /api/v1/ surface: https://github.com/sydlexius/stillwater/blob/main/internal/api/openapi.yaml . The API is documented on the documentation site at https://sydlexius.github.io/stillwater/api/

    The specification is verified against the implementation rather than trusted. make check-openapi compares the spec to the actual handlers, and it runs as a gate -- so a change to a request or response shape that is not reflected in the spec fails rather than silently leaving the documentation wrong. An executable Bruno collection additionally exercises the live API end to end in CI, which is a second, behavioural check that the documented interface is the one the server actually serves.

    COMMAND-LINE INTERFACE. Documented at https://sydlexius.github.io/stillwater/reference/cli/ , covering the flags and subcommands, including the operational one-shot passes (for example the pre-guard lock-damage dry-run and repair, with their approval-digest arguments).

    CONFIGURATION INTERFACE. Environment variables are documented at https://sydlexius.github.io/stillwater/reference/environment-variables/ , and every user-facing setting is catalogued by tab at https://sydlexius.github.io/stillwater/reference/settings-by-tab/ . The settings reference and the rules catalogue are GENERATED from the code that defines them, so they cannot drift from the implementation; a documentation-drift check runs in CI and fails when a shipped page no longer matches the code it describes.

    OTHER SURFACES. The provider set and their per-field behaviour are documented at https://sydlexius.github.io/stillwater/reference/providers/ , user preferences at .../reference/preferences/ , and UI labels at .../reference/ui-labels/ . The reference section runs to 8 pages, within a documentation site of 75 pages total.

    Interface documentation is therefore readable without opening the source, which is what this criterion asks: a consumer can learn the REST API, the CLI, and the configuration surface entirely from published pages.


  • Autre


    Les sites du projet (site Web, dépôt et URLs de téléchargement) DOIVENT supporter HTTPS en utilisant TLS. [sites_https]
    Cela nécessite que l'URL de la page d'accueil du projet et l'URL du référentiel de contrôle de version commencent par « https: » et non par « http: ». Vous pouvez obtenir des certificats gratuits de Let's Encrypt. Les projets PEUVENT mettre en œuvre ce critère en utilisant (par exemple) des pages GitHub, des pages GitLab ou des pages de projet SourceForge. Si vous prenez en charge HTTP, nous vous invitons à rediriger le trafic HTTP vers HTTPS.

    All three project URLs are HTTPS, verified live.

    Repository: https://github.com/sydlexius/stillwater -- 200 over TLS.

    Documentation site: https://sydlexius.github.io/stillwater/ -- 200 over TLS, served by GitHub Pages with its GitHub-managed certificate. Plain HTTP is not merely supported, it redirects: http://sydlexius.github.io/stillwater/ returns 301 to the HTTPS URL.

    Downloads: releases are served from GitHub over HTTPS at https://github.com/sydlexius/stillwater/releases , and container images from GitHub Container Registry (ghcr.io) over HTTPS. Release artifacts are additionally signed with Sigstore, so their provenance is verifiable independently of the transport.



    Le projet DOIT avoir un ou plusieurs mécanismes de discussion (y compris les changements et les problèmes proposés) qui peuvent être recherchés, permettent de désigner les messages et les sujets par une URL, permettent aux nouvelles personnes de participer à certaines des discussions et ne nécessitent pas d'installation côté client de logiciels propriétaires. [discussion]
    Parmi les exemples de mécanismes acceptables figurent les listes de diffusion archivées, les problèmes de GitHub et les discussions sur les pull requests, Bugzilla, Mantis et Trac. Les mécanismes de discussion asynchrones (comme IRC) sont acceptables s'ils répondent à ces critères ; assurez-vous qu'il existe un mécanisme d'archivage adressable par URL. Une solution propriétaire en JavaScript, tout en étant découragée, est autorisée.

    GitHub supports discussions on issues and pull requests.



    Le projet DEVRAIT fournir de la documentation en anglais et être en mesure d'accepter les signalements de bogues et les commentaires sur le code en anglais. [english]
    L'anglais est actuellement la langue véhiculaire des technologies informatiques ; l'utilisation de l'anglais augmente le nombre de développeurs et de relecteurs potentiels dans le monde entier. Un projet peut répondre à ce critère même si la langue principale de ses principaux développeurs n'est pas l'anglais.

    The project is developed and documented entirely in English. The README, CONTRIBUTING.md, CODE_OF_CONDUCT.md, SECURITY.md, the full documentation site at https://sydlexius.github.io/stillwater/ , all in-repo design and reference docs, the three GitHub issue templates, code comments, and commit messages are all written in English. Issues, pull request discussion, and code review are conducted in English: https://github.com/sydlexius/stillwater/issues and https://github.com/sydlexius/stillwater/pulls. The application's own user interface is written in US English.



    Le projet DOIT être maintenu. [maintained]
    Au minimum, le projet doit tenter de répondre aux rapports de problèmes et de vulnérabilités importants. Un projet qui poursuit activement un badge est probablement maintenu. Tous les projets et tous les individus ont des ressources limitées, et les projets typiques doivent rejeter certaines modifications proposées, de sorte que les ressources limitées et les rejets de propositions n'indiquent pas en eux-mêmes un projet non maintenu.

    Lorsqu'un projet sait qu'il ne sera plus maintenu, il doit définir ce critère comme « Non satisfait » et utiliser le(s) mécanisme(s) approprié(s) pour indiquer aux autres qu'il n'est pas maintenu. Par exemple, utiliser « DEPRECATED » comme premier en-tête de son fichier README, ajouter « DEPRECATED » au début de sa page d'accueil, ajouter « DEPRECATED » au début de la description de projet de son dépôt de code, ajouter un badge sans maintenance dans son README et/ou sa page d'accueil, le marquer comme obsolète dans tous les dépôts de paquets (par exemple, npm deprecate), et/ou utiliser le système de marquage du dépôt de code pour l'archiver (par exemple, le paramètre « archive » de GitHub, le marquage « archivé » de GitLab, le statut « lecture seule » de Gerrit ou le statut de projet « abandonné » de SourceForge). Une discussion supplémentaire peut être trouvée ici.

    The project is under active development, with the most recent commit today.

    COMMITS: 648 in the last 90 days, the latest on 2026-09-02.

    RELEASES: four stable releases in the past month -- v1.6.1 (2026-08-04), v1.6.2 (08-19), v1.6.3 (08-22), v1.6.4 (08-27) -- plus automated nightly pre-releases, the most recent on 2026-09-02. Release artifacts are signed with Sigstore.

    ISSUE ACTIVITY: 94 issues closed in the last 30 days, against 295 open. The open count reflects a tracked and milestoned backlog rather than neglect: issues are filed with a milestone and labels at creation, work is scheduled through release milestones, and defects found during review are captured as tracked issues rather than dropped.

    REVIEW AND CI: every change lands through a pull request with required CI checks (tests, a patch-coverage floor, CodeQL, lint, and a generated-file drift check) and required code review; direct commits to the default branch are refused by branch protection. Dependabot is configured for dependency updates.

    Repository activity is visible at https://github.com/sydlexius/stillwater/commits/main and https://github.com/sydlexius/stillwater/releases


 Contrôle des modifications 9/9 ●

  • Dépôt source public sous contrôle de version


    Le projet DOIT avoir un dépôt source sous contrôle de version qui est publiquement lisible et possède une URL. [repo_public]
    L'URL PEUT être identique à l'URL du projet. Le projet PEUT utiliser des branches privées (non publiques) dans des cas spécifiques alors que la modification n'est pas diffusée publiquement (par exemple, pour la correction d'une vulnérabilité avant qu'elle ne soit révélée au public).

    Repository on GitHub, which provides public git repositories with URLs.



    Le dépôt source du projet DOIT suivre les changements apportés, qui a effectué les changements et quand les changements ont été effectués. [repo_track]

    Repository on GitHub, which uses git. git can track the changes, who made them, and when they were made.



    Pour permettre une analyse collaborative, le dépôt source du projet DOIT inclure des versions provisoires pour examen entre versions officielles ; Il NE DOIT PAS inclure que les dernières versions. [repo_interim]
    Les projets PEUVENT choisir d'omettre des versions intermédiaires spécifiques dans leurs dépôts source publics (par exemple, celles qui corrigent des vulnérabilités de sécurité non publiques spécifiques, ne peuvent jamais être rendues publiques ou incluent des éléments qui ne peuvent être légalement publiés et ne sont pas dans la version finale).

    Met. The repository is the live development history, not a release drop.

    https://github.com/sydlexius/stillwater/commits/main -- 648 commits in the last 90 days, 160 merges to the default branch in the last 30 days alone, against four stable releases in that period. Every intermediate state between releases is public.

    Development happens in the open through pull requests, each reviewable before it lands: https://github.com/sydlexius/stillwater/pulls . Direct commits to the default branch are refused by branch protection, so no change reaches main without an interim reviewable state existing first. Review comments and their resolutions stay attached to the PR after merge.

    Automated nightly pre-releases are additionally published from the current development head, so an interim build is available to testers between stable releases: https://github.com/sydlexius/stillwater/releases

    Work in progress is also visible before any code exists, as milestoned and labelled issues: https://github.com/sydlexius/stillwater/issues



    Il est PROPOSÉ qu'un logiciel reconnu de contrôle de version distribué soit utilisé (par exemple, git) pour le dépôt source du projet. [repo_distributed]
    Git n'est pas spécifiquement requis et les projets peuvent utiliser un logiciel de contrôle de version centralisé (comme subversion) avec justification.

    Repository on GitHub, which uses git. git is distributed.


  • Numérotation unique de la version


    Les résultats du projet DOIVENT avoir un identifiant de version unique pour chaque version destinée à être utilisée par les utilisateurs. [version_unique]
    Cela PEUT être satisfait de diverses façons, y compris les identifiants de commit (comme git commit id ou mercure changeset id) ou un numéro de version (y compris les numéros de version qui utilisent la version sémantique ou les systèmes basés sur la date comme YYYYMMDD).

    Met. Every release carries a unique semantic-version identifier.

    Stable releases are tagged vMAJOR.MINOR.PATCH -- v1.6.4 (2026-08-27), v1.6.3 (08-22), v1.6.2 (08-19), v1.6.1 (08-04): https://github.com/sydlexius/stillwater/releases

    Automated nightly builds are separately and unambiguously identified as nightly-YYYYMMDD and are marked as pre-releases, so they cannot be confused with a stable version.

    The identifier is not only a tag. It is compiled into the binary at build time via linker flags -- internal/version.Version, alongside the exact commit SHA and build date -- so a user can determine precisely which build they are running rather than inferring it from a filename. A CI guard in the release workflow specifically checks that the ldflags symbol path has not silently drifted, since a renamed package would otherwise leave the version unset without failing the build.

    Container images are tagged with the same version identifiers on GitHub Container Registry, and release artifacts are signed with Sigstore, so a given version is verifiable as well as identifiable.



    Il est PROPOSÉ d'utiliser le format de numérotation de version appelé Versionage Sémantique (SemVer) ou Versionage Calendaire (CalVer). Il est PROPOSÉ que ceux qui utilisent CalVer incluent une valeur de niveau micro. [version_semver]
    Les projets devraient généralement préférer le format attendu par leurs utilisateurs, par exemple, parce que c'est le format normal utilisé par leur écosystème. De nombreux écosystèmes préfèrent SemVer, et SemVer est généralement préféré pour les interfaces de programmation d'applications (API) et les kits de développement logiciel (SDK). CalVer a tendance à être utilisé par des projets de grande envergure, ayant un nombre inhabituellement élevé de dépendances développées indépendamment, ayant une portée en constante évolution ou étant sensibles au temps. Il est PROPOSÉ que ceux qui utilisent CalVer incluent une valeur de niveau micro, car l'inclusion d'un niveau micro prend en charge les branches maintenues simultanément chaque fois que cela devient nécessaire. D'autres formats de numérotation de version peuvent être utilisés, y compris les ID de commit git ou les ID de jeu de modifications mercurial, à condition qu'ils identifient de manière unique les versions. Cependant, certaines alternatives (telles que les ID de commit git) peuvent poser des problèmes en tant qu'identificateurs de version, car les utilisateurs peuvent ne pas être en mesure de déterminer facilement s'ils sont à jour. Le format de l'ID de version peut être sans importance pour l'identification des versions logicielles si tous les destinataires n'exécutent que la dernière version (par exemple, il s'agit du code d'un site Web ou d'un service Internet unique qui est constamment mis à jour par livraison continue).


    Il est PROPOSÉ que les projets identifient chaque version dans leur système de contrôle de version. Par exemple, il est PROPOSÉ que ceux qui utilisent git identifient chaque version à l'aide des tags de git. [version_tags]

    Met. Every release is identified by a git tag in the repository: https://github.com/sydlexius/stillwater/tags

    62 version tags are present, following vMAJOR.MINOR.PATCH -- for example v1.6.4, v1.6.3, v1.6.2, v1.6.1 -- with release-candidate tags (v1.6.0-rc0, v1.6.0-rc1) where a release was staged. The tags are ANNOTATED rather than lightweight, so each carries its own tagger and date.

    Nightly pre-release builds are tagged separately as nightly-YYYYMMDD, keeping them distinguishable from stable releases in the tag list as well as on the releases page.

    Each tag is what drives the release: the release workflow builds from the tag and stamps that same identifier into the binary via linker flags, so a tag, a GitHub release, and the version a running binary reports all refer to the same commit.


  • Notes de version


    Le projet DOIT fournir, avec chaque distribution, des notes de version qui sont un résumé lisible par les humains des changements majeurs dans cette version afin d'aider les utilisateurs à déterminer s'ils doivent se mettre à niveau et quel sera l'impact de la mise à niveau. Les notes de version NE DOIVENT PAS être la sortie brute d'un journal de contrôle de version (par exemple, les résultats de la commande « git log » ne sont pas des notes de version). Les projets dont les résultats ne sont pas destinés à être réutilisés dans plusieurs emplacements (tels que le logiciel pour un site Web ou un service unique) ET qui utilisent la livraison continue PEUVENT sélectionner « N/A ». (URL requise) [release_notes]
    Les notes de version PEUVENT être mises en œuvre de différentes façons. De nombreux projets les fournissent dans un fichier nommé « NEWS », « CHANGELOG » ou « ChangeLog », éventuellement avec des extensions telles que « .txt », « .md » ou « .html ». Historiquement, le terme « journal des modifications » signifiait un enregistrement de chaque changement, mais pour répondre à ces critères, il faut un résumé lisible par un humain. Les notes de version PEUVENT être fournies à la place par des mécanismes de système de contrôle de version tels que le GitHub Releases workflow.

    https://github.com/sydlexius/stillwater/releases

    Every release ships hand-written release notes, structured as a plain-language summary of what changed and what it means for the user -- not a git log dump.

    The most recent, v1.6.4 (https://github.com/sydlexius/stillwater/releases/tag/v1.6.4), is representative. It opens with a one-sentence statement of what the release delivers, then a Highlights section explaining each significant change in terms of the user-visible symptom rather than the code: what went wrong, what it caused, and what now happens instead. Issue numbers are cited so a reader can follow any item to its full history.

    Upgrade impact is addressed directly where it exists. v1.6.4 shipped a repair for damage caused before an earlier guard existed, so the notes carry a dedicated section explaining that the repair is a deliberate one-time pass rather than something that runs at startup, instructing the user to stop the server and back up the database first, giving the exact commands for the dry-run preview and the approved repair, and explaining why the preview requires human judgment that no automatic test can substitute for.

    Earlier releases follow the same structure: v1.6.3, v1.6.2, v1.6.1. The project ships small, frequent, semver-correct releases, which keeps each set of notes specific enough to be read rather than skimmed.



    Les notes de version DOIVENT identifier toutes les vulnérabilités connues du public corrigées dans cette version qui avaient déjà une affectation CVE ou similaire lors de la création de la version. Ce critère peut être marqué comme non applicable (N/A) si les utilisateurs ne peuvent pas en général mettre à jour le logiciel eux-mêmes (par exemple, comme c'est souvent le cas pour les mises à jour du noyau). Ce critère s'applique uniquement aux résultats du projet, pas à ses dépendances. S'il n'y a pas de notes de version ou qu'il n'y a pas eu de vulnérabilité publiquement connue, choisissez N/A. [release_notes_vulns]
    Ce critère aide les utilisateurs à déterminer si une mise à jour donnée corrigera une vulnérabilité connue publiquement, pour aider les utilisateurs à prendre une décision éclairée concernant la mise à jour. Si les utilisateurs ne peuvent généralement pas mettre à jour le logiciel eux-mêmes sur leur ordinateur, mais doivent à la place dépendre d'un ou plusieurs intermédiaires pour effectuer la mise à niveau (comme c'est souvent le cas pour un noyau et un logiciel de bas niveau associé à un noyau), le projet peut choisir « non applicable » (N/A) à la place, car ces informations supplémentaires ne seront pas utiles à ces utilisateurs. De même, un projet peut choisir N/A si tous les destinataires n'exécutent que la dernière version (par exemple, il s'agit du code d'un site Web ou d'un service Internet unique qui est constamment mis à jour par livraison continue). Ce critère s'applique uniquement aux résultats du projet, pas à ses dépendances. Énumérer les vulnérabilités de toutes les dépendances transitives d'un projet devient ingérable à mesure que les dépendances augmentent et varient, et n'est pas nécessaire car les outils qui examinent et suivent les dépendances peuvent le faire de manière plus évolutive.

    N/A -- there have been no publicly known vulnerabilities in Stillwater itself.

    No CVE has been assigned against this project. Verified against NIST's CPE dictionary: neither the product nor the vendor name returns any entry, which is expected for a project with no published CVE. The repository has published zero GitHub security advisories: https://github.com/sydlexius/stillwater/security/advisories

    The criterion applies to the project's own results rather than its dependencies. Dependency advisories are handled separately and continuously -- Dependabot is configured, govulncheck runs in CI, and CodeQL scans every pull request -- but none of those are vulnerabilities in Stillwater's own code.

    A private disclosure process is in place for when one does arise, documented in SECURITY.md: https://github.com/sydlexius/stillwater/blob/main/SECURITY.md . Release notes are hand-written per release and would carry such a fix.


 Compte-rendu 8/8 ●

  • Procédure de signalement des bogues


    Le projet DOIT fournir un processus permettant aux utilisateurs de soumettre des signalements de bogue (par exemple, en utilisant un suivi des problèmes ou une liste de diffusion). (URL requise) [report_process]

    Non-trivial SECURITY[.md] file found file in repository: https://github.com/sydlexius/stillwater/blob/main/SECURITY.md. [osps_do_02_01]



    Le projet DEVRAIT utiliser un suivi des problèmes pour le suivi des problèmes individuels. [report_tracker]

    Met. The project uses GitHub Issues: https://github.com/sydlexius/stillwater/issues

    It is used as a working tracker rather than an inbox. Three issue templates route reports to the right shape (bug, feature, task): https://github.com/sydlexius/stillwater/issues/new/choose

    Issues are triaged at creation: every issue is assigned a milestone and labels when it is filed, drawn from 24 milestones and a 45-label taxonomy covering type (bug, enhancement, chore, technical-debt), scope, and affected area (images, rules, ui, providers, database, and so on). An unmilestoned issue does not appear in any release view, so the convention exists to keep work schedulable rather than invisible.

    Recent throughput: 94 issues closed in the last 30 days. Defects surfaced during code review are captured as tracked issues with the milestone of the work that surfaced them, rather than dropped or left in a comment thread.

    Security vulnerabilities are deliberately routed away from the public tracker to a private disclosure process: https://github.com/sydlexius/stillwater/blob/main/SECURITY.md



    Le projet DOIT confirmer une majorité des signalements de bogues soumis au cours des 2 à 12 derniers mois (inclus) ; la réponse ne doit pas nécessairement inclure une correction. [report_responses]

    Met. Every bug report is triaged, and the great majority are resolved.

    Of the 400 issues labelled bug filed in the last 12 months, 394 carry a milestone -- 98.5%. Assigning a milestone and labels at filing time is the project's convention, and it is the acknowledgement: it places the report in a release view where it can be scheduled, rather than leaving it unrouted. The 6 without one are a rounding error, not a backlog of ignored reports.

    Resolution rate is high as well: two thirds of the bug reports in that window are already closed, and 94 issues of all types were closed in the last 30 days alone.

    Reports are also acted on rather than merely filed. Defects surfaced during code review are captured as tracked issues carrying the milestone of the work that surfaced them, and issues frequently accumulate investigation comments -- measurement results, refuted hypotheses, design decisions -- before any code is written.

    The tracker is public: https://github.com/sydlexius/stillwater/issues



    Le projet DEVRAIT répondre à une majorité (>50%) des demandes d'amélioration au cours des 2 à 12 derniers mois (inclus). [enhancement_responses]
    La réponse PEUT être « non » ou une discussion sur ses mérites. Le but est simplement qu'il y ait une réponse à certaines demandes, ce qui indique que le projet est toujours en vie. Aux fins de ce critère, les projets ne doivent pas compter les fausses demandes (par exemple, provenant de spammeurs ou de systèmes automatisés). Si un projet ne fait plus d'améliorations, sélectionnez « non satisfait » et incluez l'URL qui rend cette situation claire pour les utilisateurs. Si un projet tend à être submergé par le nombre de demandes d'amélioration, sélectionnez « non satisfait » et expliquez.

    Met. Of the 400 issues labelled enhancement filed in the last 12 months, 391 carry a milestone -- 97.75% -- and 231 are already closed, so well over half have been both responded to and resolved.

    Enhancement requests are triaged at filing: a milestone and labels are assigned when the issue is created, which places the request in a release view where it can be scheduled or explicitly deferred, rather than left unrouted. An unmilestoned issue appears in no release view, so the convention exists precisely to keep proposals from going silently unanswered.

    The feature template asks that larger ideas open with a short scope sketch in the issue body so the design can be discussed before any code is written, and CONTRIBUTING.md directs contributors to raise a feature that way rather than starting with a pull request: https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md#suggesting-a-feature

    Proposals that are declined or reshaped are answered in the issue with the reasoning, and larger ones are decomposed into epics with tracked children rather than closed as too big.

    Tracker: https://github.com/sydlexius/stillwater/issues?q=is%3Aissue+label%3Aenhancement



    Le projet DOIT avoir une archive publique pour les signalements et les réponses pour une recherche ultérieure. (URL requise) [report_archive]

    https://github.com/sydlexius/stillwater/issues?q=is%%3Aissue

    All bug reports, enhancement requests, and their responses are permanently archived in the public GitHub issue tracker, open and closed alike, and are full-text searchable without an account. Closed issues are never deleted, so the reasoning behind a decision remains readable long after the work ships.

    Pull request discussion is archived alongside it and is equally searchable: https://github.com/sydlexius/stillwater/pulls?q=is%%3Apr . Review comments and their resolutions stay attached to the pull request after merge, so the argument that produced a change is recoverable from the change itself.

    The two are cross-linked in both directions -- pull requests reference the issues they address, and merged work appears in the issue's timeline -- so a search that lands on either finds the other.

    Releases carry hand-written notes citing the issue numbers they resolve, giving a third searchable path from a shipped version back to the discussion behind it: https://github.com/sydlexius/stillwater/releases


  • Processus de signalement de vulnérabilité


    Le projet DOIT publier le processus de signalement des vulnérabilités sur le site du projet. (URL requise) [vulnerability_report_process]
    Les projets hébergés sur GitHub DEVRAIENT envisager d'activer le signalement privé d'une vulnérabilité de sécurité . Les projets sur GitLab DEVRAIENT envisager d'utiliser sa capacité à signaler une vulnérabilité en privé . Les projets PEUVENT identifier une adresse postale sur https://PROJECTSITE/security, souvent sous la forme security@example.org. Ce processus de rapport de vulnérabilité PEUT être le même que son processus de rapport de bogue. Les rapports de vulnérabilité PEUVENT toujours être publics, mais de nombreux projets ont un mécanisme de rapport de vulnérabilité privé.

    https://github.com/sydlexius/stillwater/blob/main/SECURITY.md

    SECURITY.md publishes the full reporting process, and GitHub surfaces it automatically in the repository's Security tab and on the "Report a vulnerability" flow.

    The policy states plainly that vulnerabilities must NOT be reported through public GitHub issues, and directs reporters to a private channel -- GitHub Security Advisories: https://github.com/sydlexius/stillwater/security/advisories/new -- so an issue can be triaged and a fix prepared before public disclosure.

    It specifies what a report should contain: a description of the vulnerability and its impact, steps to reproduce or a proof of concept, the affected versions, and a suggested fix if the reporter has one.

    It sets expectations both ways. An initial acknowledgement is promised within 72 hours of receipt; once confirmed, a fix is developed and released as soon as practical, typically within 14 days for critical issues.

    It also defines scope explicitly, which keeps a reporter from spending effort on something that will be declined. In scope: the application binary and Docker image, the REST API, authentication and session management, encryption of stored secrets, and file system operations. Out of scope: upstream dependency vulnerabilities (directed to the upstream project), issues requiring physical host access, and denial of service through expected resource usage.

    Supported versions are stated as well -- only the latest release receives security updates.



    Si les signalements de vulnérabilités privés sont pris en charge, le projet DOIT inclure la façon d'envoyer l'information de manière confidentielle. (URL requise) [vulnerability_report_private]
    Des exemples incluent un signalement de défaut privé envoyé sur le Web en utilisant HTTPS (TLS) ou un courrier électronique chiffré à l'aide d'OpenPGP. Si les signalements de vulnérabilités sont toujours publics (donc il n'y a jamais de signalements de vulnérabilités privés), choisissez « non applicable » (N/A).

    https://github.com/sydlexius/stillwater/security/advisories/new

    Yes, private reporting is supported and is the required channel. SECURITY.md (https://github.com/sydlexius/stillwater/blob/main/SECURITY.md) states that vulnerabilities must NOT be reported through public GitHub issues, and directs reporters to GitHub Security Advisories at the URL above.

    That channel is private by construction: a draft advisory is visible only to the reporter and the repository maintainers until it is deliberately published, so the report, the discussion, and any proof-of-concept stay confidential while a fix is prepared. It requires no email exchange and no key management by the reporter, and it is reachable from the repository's Security tab as well as from the link in SECURITY.md.

    The policy also tells a reporter what to expect after sending: an initial acknowledgement within 72 hours, and a fix released as soon as practical -- typically within 14 days for critical issues -- before public disclosure.



    Le temps de réponse initial du projet pour tout signalement de vulnérabilité reçu au cours des 6 derniers mois DOIT être inférieur ou égal à 14 jours. [vulnerability_report_response]
    S'il n'y a pas eu de vulnérabilité signalée au cours des 6 derniers mois, choisissez « non applicable » (N/A).

    N/A -- no vulnerability reports have been received in the last 6 months, or at any point.

    The repository has published zero security advisories (https://github.com/sydlexius/stillwater/security/advisories) and no CVE has been assigned against the project, so there is no response time to measure.

    The commitment is published in advance for when one does arrive: SECURITY.md promises an initial acknowledgement within 72 hours of receipt -- well inside the 14-day bar -- with a fix released as soon as practical, typically within 14 days for critical issues. https://github.com/sydlexius/stillwater/blob/main/SECURITY.md

    Reports arrive through GitHub Security Advisories, which notifies maintainers directly rather than relying on a monitored mailbox: https://github.com/sydlexius/stillwater/security/advisories/new


 Qualité 13/13 ●

  • Système de construction opérationnel


    Si le logiciel produit par le projet nécessite d'être construit pour être utilisé, le projet DOIT fournir un système de construction fonctionnel qui peut reconstruire automatiquement le logiciel à partir du code source. [build]
    Un système de construction détermine quelles actions doivent se produire pour reconstruire le logiciel (et dans quel ordre), puis exécute ces étapes. Par exemple, il peut invoquer un compilateur pour compiler le code source. Si un exécutable est créé à partir du code source, il doit être possible de modifier le code source du projet, puis de générer un exécutable mis à jour avec ces modifications. Si le logiciel produit par le projet dépend de bibliothèques externes, le système de construction n'a pas besoin de construire ces bibliothèques externes. S'il n'est pas nécessaire de construire quoi que ce soit pour utiliser le logiciel après la modification de son code source, sélectionnez « non applicable » (N/A).

    The project builds from source with a single command.

    make build compiles the binary, and it is not a bare go build -- it depends on the templ and tailwind targets, so generated Go template code and the compiled stylesheet are regenerated first and cannot silently fall out of date with their sources. The Makefile documents 37 targets, discoverable via make help: https://github.com/sydlexius/stillwater/blob/main/Makefile

    Version information is injected at build time through linker flags (version, commit SHA, build date), so a binary reports exactly which source it came from.

    Container builds are equally reproducible from source: https://github.com/sydlexius/stillwater/tree/main/build/docker

    The build is verified continuously rather than assumed to work. Every pull request runs the full build in CI on a clean runner, and a required generated-file drift check fails the build if committed generated output does not match what regenerating from source produces -- so "it builds on the maintainer's machine" is not sufficient to merge. The release workflow builds the shipped artifacts from the tagged source in CI, and it carries its own guard against the version-injection symbol path drifting silently.

    Build prerequisites and the local development workflow are documented at https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md#development-environment



    Il est PROPOSÉ d'utiliser des outils courants pour la construction du logiciel. [build_common_tools]
    Par exemple, Maven, Ant, cmake, autotools, make, rake (Ruby) ou devtools (R).

    Non-trivial build file in repository: https://github.com/sydlexius/stillwater/blob/main/Makefile.



    Le projet DEVRAIT être constructible en utilisant uniquement des outils FLOSS. [build_floss_tools]

    Every tool required to build Stillwater from source is FLOSS.

    • Go toolchain (Go 1.27) -- BSD-3-Clause
    • templ (github.com/a-h/templ), the HTML templating generator -- MIT. Invoked as go tool templ, so it is pinned as a module dependency rather than an ambient install.
    • Tailwind CSS, for the stylesheet build -- MIT
    • Node.js, which Tailwind runs under -- MIT
    • GNU Make, driving the build -- GPL
    • git -- GPL-2.0
    • Docker or any OCI-compatible builder for container images; the Dockerfiles are plain and buildable with Podman or Buildah as well: https://github.com/sydlexius/stillwater/tree/main/build/docker

    No proprietary compiler, SDK, IDE, or paid service is needed at any point. A contributor on Linux, macOS, or Windows can clone the repository and run make build with nothing but the above.

    Development and CI tooling is FLOSS as well -- golangci-lint (GPL-3.0), govulncheck (BSD-3-Clause), and Go's built-in test and fuzz tooling. The CI workflows run on GitHub-hosted runners, but nothing in the build depends on GitHub specifically: the same Makefile targets CI invokes are the ones a contributor runs locally.

    Build prerequisites are documented at https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md#development-environment


  • Suite de tests automatisée


    Le projet DOIT utiliser au moins une suite de tests automatisée publiée publiquement comme FLOSS (cette suite de tests peut être maintenue sous la forme d'un projet FLOSS distinct). Le projet DOIT clairement montrer ou documenter comment exécuter la ou les suites de tests (par exemple, via un script d'intégration continue (CI) ou via la documentation dans des fichiers tels que BUILD.md, README.md ou CONTRIBUTING.md). [test]
    Le projet PEUT utiliser plusieurs suites de tests automatisées (par exemple, une qui s'exécute rapidement, par rapport à une autre qui est plus approfondie, mais nécessite un équipement spécial). De nombreuses plate-formes de tests et environnements de tests sont disponibles, tels que Selenium (automatisation de navigateur Web), Junit (JVM, Java), RUnit (R), testthat (R).

    The project has multiple automated test suites, all built on FLOSS tooling, and how to run them is documented.

    HOW TO RUN -- CONTRIBUTING.md's "Running tests" section: https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md#running-tests

    go test -race ./...
    # or, equivalent
    make test
    

    THE SUITES:

    • Go unit and integration tests -- 754 test files across the module, using Go's standard testing package (BSD-3-Clause). Integration tests run against a real SQLite database via modernc.org/sqlite, not a mock.
    • Native Go fuzzing across roughly 15 packages, with a CI-enforced matrix so a new fuzz target cannot be added without being exercised.
    • Accessibility tests -- Playwright (Apache-2.0) driving axe-core (MPL-2.0) against both Chromium and Firefox.
    • API tests -- an executable Bruno collection (MIT), exercised end to end by make bruno-ci, which builds the binary, starts an ephemeral server, and runs the collection against it.
    • Responsive-layout and load test suites under the same tests/ tree.

    Every suite is FLOSS and none requires a paid service.

    The suites are RUN, not merely present. Every pull request runs them in CI as required status checks, so a change cannot land while they fail: https://github.com/sydlexius/stillwater/blob/main/.github/workflows/ci.yml -- and a patch-coverage floor is itself a required check, so new code arriving without tests fails the build rather than slipping through quietly.



    Une suite de tests DEVRAIT être invocable d'une manière standard pour ce langage. [test_invocation]
    Par exemple, « make check », « mvn test » ou « rake test » (Ruby).

    The test suite is invoked the standard way for Go:

    go test ./...
    

    with the race detector, which is how this project runs it:

    go test -race ./...
    

    No wrapper, no bespoke runner, no environment fixture required -- a contributor who knows Go needs no project-specific knowledge to run the tests. make test is provided as a convenience alias for exactly that command, not as a substitute for it, and CONTRIBUTING.md presents the two as equivalent: https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md#running-tests

    The auxiliary suites follow their own ecosystems' conventions in the same way -- Go's built-in fuzzing via go test -fuzz, Playwright through its standard runner, and the Bruno API collection through the Bruno CLI. Each is wrapped in a named Makefile target for discoverability (make help lists them), but every one remains runnable directly with its native command.



    Il est PROPOSÉ que la suite de tests couvre la plupart (ou idéalement toutes) les branches du code, les champs de saisie et les fonctionnalités. [test_most]

    Coverage is enforced rather than aspirational, at two levels.

    PER-PACKAGE FLOORS. 54 packages carry an individual coverage floor, checked as a required CI status. Floors range from 58% to 100%, with 31 packages at 80% or above; the exact figures are committed at https://github.com/sydlexius/stillwater/blob/main/testdata/coverage-floor.json and mirrored in https://github.com/sydlexius/stillwater/blob/main/codecov.yml so each package is an independent check. A package that regresses below its own floor fails, so coverage cannot quietly erode in one corner while the overall number holds steady.

    PATCH COVERAGE. New and changed code must reach 78%, as a required check. This is the load-bearing one: it means a change arriving without tests fails the build rather than merging and diluting the average later.

    BEYOND LINE COVERAGE. The project treats line coverage as necessary but not sufficient, and tests for behaviour rather than for the metric:

    • Native Go fuzzing across roughly 15 packages exercises input fields with generated input, and a CI-enforced matrix means a fuzz target cannot be added without being run.
    • New tests guarding a bug fix are required to be shown failing without the fix -- revert it, confirm red, restore, confirm green -- so a test that guards nothing is caught before it lands.
    • Accessibility tests run real axe-core through Playwright against both Chromium and Firefox, against a live page rather than static analysis.
    • An executable Bruno collection exercises the REST API end to end against a real server started for the run.
    • Integration tests use a real SQLite database rather than mocks.

    754 test files across the module: https://github.com/sydlexius/stillwater/blob/main/.github/workflows/ci.yml



    Il est PROPOSÉ que le projet utilise une intégration continue (où le code nouveau ou modifié est fréquemment intégré dans un dépôt de code central et des tests automatisés sont exécutés sur le résultat). [test_continuous_integration]

    The project runs continuous integration on GitHub Actions, across 22 workflows: https://github.com/sydlexius/stillwater/tree/main/.github/workflows

    FREQUENT INTEGRATION. 648 commits in the last 90 days and 160 merges to the default branch in the last 30, against four stable releases in that period. Every change lands through a pull request; direct commits to the default branch are refused by branch protection, so nothing reaches the trunk without passing through CI first.

    TESTS RUN ON EVERY CHANGE, AND BLOCK. The main workflow (ci.yml) runs the full Go suite with the race detector, lint, a whole-module build, and a generated-file drift check that fails if committed generated output does not match a fresh regeneration. These are required status checks, so a red result stops the change rather than merely reporting.

    SPECIALIZED WORKFLOWS beyond the main suite:

    • bruno-ci.yml -- builds the binary, starts an ephemeral server, and runs the REST API collection against it
    • fuzz.yml -- native Go fuzzing, with a matrix check so a new fuzz target cannot be added without being run
    • codeql.yml -- static analysis on every pull request
    • mutation.yml -- mutation testing, to check the tests themselves have teeth
    • gate.yml -- provider-failure smoke tests and gate-invariant enforcement
    • docs-drift.yml -- fails when the documentation no longer matches the code it describes
    • scorecard.yml -- OpenSSF Scorecard on a schedule
    • nightly.yml / release-nightly.yml -- a nightly build published as a pre-release, so an interim artifact exists between stable releases

    A patch-coverage floor is itself a required check, so new code arriving without tests fails rather than landing quietly. All GitHub Actions are pinned to commit SHAs rather than floating tags, and workflow tokens follow least privilege -- both verified independently by OpenSSF Scorecard, which scores this project 10/10 on CI-Tests, Token-Permissions, and Dangerous-Workflow: https://securityscorecards.dev/viewer/?uri=github.com/sydlexius/stillwater


  • Nouveau test de fonctionnalité


    Le projet DOIT avoir une politique générale (formelle ou non) qui spécifie que, dès qu'une nouvelle fonctionnalité majeure est ajoutée au logiciel produit par le projet, des tests de cette fonctionnalité devraient être ajoutés à une suite de tests automatisée. [test_policy]
    Dès qu'une politique est en place, même par le bouche à oreille, qui spécifie que les développeurs devraient ajouter des tests à une suite de tests automatisée pour toute nouvelle fonctionnalité importante, sélectionnez « Atteint ».

    Met, and the policy is mechanically enforced rather than left to discipline.

    THE POLICY. New and changed code must reach 78% patch coverage. This is a REQUIRED status check, so a change that adds functionality without tests fails and cannot land -- the policy is not an intention a reviewer has to remember to raise. Configuration: https://github.com/sydlexius/stillwater/blob/main/codecov.yml

    Per-package floors back it up: 54 packages each carry their own committed minimum, checked independently, so coverage cannot erode in one area while the overall figure holds steady. https://github.com/sydlexius/stillwater/blob/main/testdata/coverage-floor.json

    WHAT THE POLICY ASKS FOR BEYOND A NUMBER. The project treats coverage as necessary but not sufficient, and documents that in its contributor guidance:

    • A test written for a bug fix must be shown to FAIL without the fix -- revert it, confirm red, restore, confirm green -- so a test that guards nothing is caught before it lands rather than providing false assurance.
    • Tests must assert their own preconditions, so a fixture that silently stops exercising the intended path fails instead of passing vacuously.
    • Tests must not depend on the developer's environment; a dependency on an external tool must be stubbed, because a green local run does not prove a test will pass on a bare CI runner.
    • Concurrent code is tested under the race detector.
    • Mutation testing runs in CI against the test suite itself, checking that the tests actually detect changed behaviour.

    Contributor-facing documentation: https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md and the per-package testing guidance in https://github.com/sydlexius/stillwater/tree/main/.github/instructions

    The result is visible in the history: 754 test files across the module, and features routinely ship in the same pull request as the tests that exercise them.



    Le projet DOIT avoir la preuve que la politique de test pour l'ajout de tests a été respectée dans les dernières modifications majeures apportées au logiciel produit par le projet. [tests_are_added]
    Les principales fonctionnalités sont généralement mentionnées dans les notes de version. La perfection n'est pas nécessaire, il suffit de prouver que les tests sont généralement ajoutés en pratique à la suite de tests automatisée lorsque de nouvelles fonctionnalités majeures sont ajoutées au logiciel produit par le projet.

    The four most recently merged pull requests each shipped tests with the functionality, and the pattern holds because CI enforces it rather than relying on discipline.

    • #3191 (feat, new enumeration primitive) -- 2 of 3 changed files were tests. Patch coverage 100%.
    • #3192 (fix, delete-intent recorded on an API path, plus a shared test helper) -- 3 of 6 files tests. Patch coverage 100%.
    • #3193 (fix, new operator warning on the push path) -- new dedicated test file alongside the feature. Patch coverage 100%.
    • #3195 (guards for the ordering properties #3193 depends on) -- 3 of 3 files tests, an entire PR of tests deliberately split out to keep it reviewable.

    https://github.com/sydlexius/stillwater/pulls?q=is%3Apr+is%3Amerged

    The evidence goes past file counts. These changes were reviewed adversarially, and the review found and rejected tests that passed for reasons unrelated to what they claimed -- a test whose assertion held whether or not the fix was present, an assertion anchored so that real network latency would mask the defect it existed to catch, a comment claiming a drift check the code could not perform. Each was corrected before the change landed, and each new guard was demonstrated red without its fix and green with it.

    #3195 is the clearest illustration of the policy in practice: it exists solely because the ordering properties a merged fix depends on could still be broken silently. Six mutations reproducing prior defects were applied one at a time and each confirmed to make the right test fail. Nothing in it adds functionality; it exists so the functionality cannot regress unnoticed.

    Enforcement rather than intention: patch coverage at 78% is a required status check, and 54 packages carry individual coverage floors, so a change adding functionality without tests fails the build.



    Il est PROPOSÉ que cette politique sur l'ajout de tests (voir la politique de test) soit documentée dans les instructions pour les propositions de modification. [tests_documented_added]
    Cependant, même une règle informelle est acceptable tant que les tests sont ajoutés dans la pratique.

    The expectation is stated in the contributor instructions and in the pull request template, and it is enforced mechanically rather than left to discipline.

    CONTRIBUTING.md states that new or changed functionality is expected to arrive with tests in the same pull request, and that this is enforced: patch coverage at 78% is a required status check, so a change adding functionality without tests fails rather than landing. https://github.com/sydlexius/stillwater/blob/main/CONTRIBUTING.md

    The pull request template's test plan asks the author to confirm that new or changed functionality is covered by tests in that pull request: https://github.com/sydlexius/stillwater/blob/main/.github/pull_request_template.md

    Per-package testing standards live in https://github.com/sydlexius/stillwater/tree/main/.github/instructions and apply automatically to any *_test.go file under review, covering what a good test looks like in this codebase rather than only that one should exist.

    The documented standard goes past a coverage percentage. A test guarding a bug fix is expected to be shown failing without the fix -- revert it, confirm red, restore, confirm green -- so a test that guards nothing is caught before it lands. Tests are expected to assert their own preconditions, so a fixture that stops exercising the intended path fails rather than passing vacuously.

    Enforcement backs the documentation: 54 packages carry individual committed coverage floors (https://github.com/sydlexius/stillwater/blob/main/testdata/coverage-floor.json), each checked independently, and mutation testing runs in CI against the test suite itself.


  • Options d'avertissement


    Le projet DOIT activer une ou plusieurs options d'avertissement du compilateur, un mode du langage « sûr » ou utiliser un outil « linter » séparé pour rechercher des erreurs de qualité de code ou des erreurs simples courantes, s'il existe au moins un outil FLOSS qui peut implémenter ce critère dans le langage sélectionné. [warnings]
    Des exemples d'options d'avertissement du compilateur incluent « -Wall » pour gcc/clang. Des exemples d'un mode de langage « sûr » incluent « use strict » en JavaScript et « use warnings » de perl5. Un outil « linter » distinct est simplement un outil qui examine le code source pour rechercher des erreurs de qualité de code ou des erreurs simples courantes. Ceux-ci sont généralement activés par le code source ou par les instructions de construction.

    The project runs golangci-lint (GPL-3.0, FLOSS) with 23 linters enabled, configured at https://github.com/sydlexius/stillwater/blob/main/.golangci.yml

    The enabled set is deliberately broader than the default:

    • Correctness: errcheck, errorlint, nilerr, exhaustive, sqlclosecheck, bodyclose, contextcheck, noctx, musttag
    • Security: gosec
    • Static analysis: govet, staticcheck, unused, unparam, unconvert, wastedassign
    • Quality and idiom: gocritic (with style and performance checks enabled beyond its defaults), revive, gocognit for cognitive complexity, prealloc, usestdlibvars, misspell
    • nolintlint, which polices the suppressions themselves so a //nolint cannot be added without a reason

    Suppressions are specific and carry their rationale inline rather than being blanket disables -- for example gosec G104, G117 and G120 are each excluded with a written reason naming why the rule misfires here.

    Beyond the linter: Go's compiler and go vet run over the whole module on every change, and CodeQL performs static analysis on every pull request. Hadolint lints the Dockerfiles, actionlint the GitHub Actions workflows, and stylelint the CSS.

    These are enforced, not advisory. make lint is documented in CONTRIBUTING.md, the pre-commit hook runs golangci-lint on staged packages so a failing change cannot be committed, and lint is a required CI status check. A cognitive-complexity violation blocked a commit during recent work and had to be resolved by extraction rather than suppressed, which is the enforcement working as intended.

    OpenSSF Scorecard independently scores this project 10/10 on its SAST check: https://securityscorecards.dev/viewer/?uri=github.com/sydlexius/stillwater



    Le projet DOIT résoudre les avertissements. [warnings_fixed]
    Ce sont les avertissements identifiés par la mise en œuvre du critère warnings. Le projet doit corriger les avertissements ou les marquer dans le code source comme faux positifs. Idéalement, il n'y aurait pas d'avertissement, mais un projet PEUT accepter certains avertissements (généralement moins de 1 avertissement pour 100 lignes ou moins de 10 avertissements).

    The codebase is clean: golangci-lint run ./... across the whole module reports 0 issues, measured against current main.

    Warnings are addressed rather than accumulated, because they cannot accumulate. Lint is a required CI status check, so a change introducing a warning fails and does not land, and a pre-commit hook runs golangci-lint on staged packages so a failing change cannot be committed locally either. The gate is not advisory: during recent work a cognitive-complexity violation blocked a commit and had to be resolved by extracting functions, not by suppressing the check.

    Where a rule genuinely misfires, the suppression is specific and carries its reasoning inline rather than being a blanket disable. Every one of the 443 //nolint directives in the codebase names both the specific linter it suppresses and a written reason, for example:

    defer rows.Close() //nolint:errcheck // Close error not actionable on cleanup
    

    That consistency is itself enforced: nolintlint is among the enabled linters, so a suppression cannot be added without naming its linter and giving an explanation. A directive that suppresses nothing is also flagged, which keeps stale suppressions from surviving the code they were written for.

    The three project-level exclusions in .golangci.yml (gosec G104, G117, G120) each carry a written rationale naming why the rule misfires in this codebase, rather than being disabled silently.

    Configuration: https://github.com/sydlexius/stillwater/blob/main/.golangci.yml



    Il est PROPOSÉ que les projets soient maximalement stricts avec les avertissements dans le logiciel produit par le projet, quand cela est approprié. [warnings_strict]
    Certains avertissements ne peuvent être efficacement activés sur certains projets. Ce qui est nécessaire est la preuve que le projet s'efforce d'activer les options d'avertissements où il peut, de sorte que les erreurs soient détectées tôt.

    The configuration is deliberately stricter than golangci-lint's defaults, and every relaxation is individually justified in the file rather than applied as a blanket disable.

    STRICTER THAN DEFAULT. 23 linters are enabled, well beyond the default set, including several most projects leave off: contextcheck, exhaustive, musttag, nilerr, noctx, sqlclosecheck, unparam, wastedassign, and gocognit for cognitive complexity. gocritic runs with its style and performance tag bundles enabled on top of its defaults.

    RELAXATIONS ARE ARGUED, NOT ASSUMED. Each disabled gocritic check carries a written rationale in .golangci.yml explaining why it does not fit this codebase -- for example octalLiteral is off because the project uses 0o-prefixed file modes and the check also objects to a literal 0; commentedOutCode is off because it false-positives on Go's semantic // Deprecated: markers. The gocritic diagnostic tag is omitted with its reasoning stated: every check in it overlaps something govet or staticcheck already reports, so enabling it would produce duplicate findings rather than new ones. The three gosec exclusions (G104, G117, G120) each name the specific misfire.

    That is the evidence the criterion asks for: the decisions are visible and defensible, so a future contributor can re-litigate any of them against the reasoning rather than guessing why a check is off.

    SUPPRESSIONS ARE POLICED. nolintlint is enabled, so an inline suppression cannot be added without naming its linter and giving a reason, and a directive that suppresses nothing is flagged. All 443 //nolint directives in the codebase carry both.

    BEYOND THE LINTER. The whole module is built and vetted on every change; CodeQL analyses every pull request; the race detector runs over the suite in CI; mutation testing checks the tests themselves detect changed behaviour; Hadolint, actionlint and stylelint cover the Dockerfiles, workflows and CSS. Lint is a required status check and a pre-commit hook runs it on staged packages, so strictness is enforced at both ends.

    Current result: golangci-lint run ./... reports 0 issues across the module.

    https://github.com/sydlexius/stillwater/blob/main/.golangci.yml


 Sécurité 14/16 ●

  • Connaissance du développement sécurisé


    Le projet DOIT avoir au moins un développeur principal qui sait comment concevoir un logiciel sécurisé. (Voir les « détails » pour les exigences exactes.) [know_secure_design]
    Cela nécessite de comprendre les principes de conception suivants, y compris les 8 principes de Saltzer et Schroeder :
    • économie de moyens (maintenez la conception aussi simple et petite que pratique, par exemple en adoptant des simplifications conséquentes)
    • valeurs sûres par défaut (les décisions d'accès par défaut devraient être de refuser l'accès et l'installation des projets devrait être sécurisée par défaut)
    • médiation complète (tous les accès qui pourraient être limités doivent être vérifiés pour l'autorité et ne pas être contournables)
    • conception ouverte (les mécanismes de sécurité ne doivent pas dépendre de l'ignorance par l'attaquant de sa conception, mais plutôt d'informations plus facilement protégées et modifiées comme des clés et des mots de passe)
    • séparation des privilèges (idéalement, l'accès aux objets importants devrait dépendre de plus d'une condition, de sorte que la défaillance d'un système de protection n'autorisera pas l'accès complet. Par exemple, l'authentification multi-facteurs, comme l'exigence d'un mot de passe et d'un jeton matériel, est plus forte qu'une authentification à un seul facteur)
    • principe de plus faible privilège (les processus doivent fonctionner avec le minimum de privilège requis)
    • mécanisme de partage minimal (la conception devrait minimiser les mécanismes communs à plus d'un utilisateur et nécessaires à tous les utilisateurs, par exemple, les répertoires pour les fichiers temporaires)
    • acceptabilité psychologique (l'interface humaine doit être conçue pour faciliter l'utilisation - la conception pour « l'étonnement minimal » peut aider)
    • surface d'attaque limitée (la surface d'attaque - l'ensemble des différents points où un attaquant peut essayer d'entrer ou d'extraire des données - devrait être limitée)
    • validation d'entrée avec des listes blanches (les entrées devraient généralement être vérifiées pour déterminer si elles sont valides avant qu'elles ne soient acceptées ; cette validation devrait utiliser des listes blanches (qui n'acceptent que des bonnes valeurs connues), et non des listes noires (qui tentent de répertorier les valeurs mauvaises connues)).
    Un « développeur principal » dans un projet est celui qui connaît la base de code du projet, est à l'aise pour faire des modifications et est reconnu comme tel par la plupart des autres participants au projet. Un développeur principal a effectué généralement un certain nombre de contributions au cours de l'année écoulée (du code, de la documentation ou des réponses aux questions). Des développeurs sont généralement considérés comme des développeurs principaux s'ils ont lancé le projet (et n'ont pas quitté le projet il y a plus de trois ans), ont la possibilité de recevoir des informations sur un canal privé de déclaration de vulnérabilités (s'il y en a un), peuvent accepter des contributions au nom du projet, ou effectuer les distributions finales du logiciel du projet. S'il n'y a qu'un seul développeur, cette personne est le développeur principal. De nombreux livres et cours sont disponibles pour vous aider à comprendre comment développer des logiciels plus sûrs et pour discuter de leur conception. Par exemple, le cours Bases du développement logiciel sécurisé est un ensemble gratuit de trois cours qui expliquent comment développer des logiciels plus sûrs (il est possible de le suivre gratuitement comme auditeur ; il est aussi possible de payer pour obtenir un certificat prouvant que vous avez compris les enseignements du cours).

    ...pending completion of the OpenSSF Secure Software Development Fundamentals course.

    The project has a single primary developer, who initiated it, accepts all commits, performs releases, and receives private vulnerability reports. That developer has not completed formal secure-design training and does not claim familiarity with the Saltzer and Schroeder principles by name. Since this criterion asks what a developer understands rather than what the codebase contains, answering Met on the strength of the implementation would be asserting the knowledge rather than evidencing it. It is recorded honestly as unmet.

    The plan to meet it is the course this criterion names -- the OpenSSF Secure Software Development Fundamentals, audited free -- which also addresses know_common_errors.

    For context, several of the principles are applied in the codebase today, though a number of them were arrived at through adversarial review and automated analysis rather than from first principles:

    • Fail-safe defaults: destructive checks are written as positive allowlists rather than negated safe-lists, and an image-repair path that cannot positively identify the slot it means to overwrite adds rather than overwrites, on the reasoning that one extra copy is recoverable and a destroyed image is not.
    • Complete mediation: authentication, CSRF protection and rate limiting are router middleware, so a new endpoint is covered by construction rather than by the author remembering.
    • Open design: provider API keys are encrypted at rest with AES-256-GCM, the key is a 0600 file rather than an environment value, and the whole design is public under GPL-3.0.
    • Least privilege: CI workflow tokens are scoped per job, since job-level permissions replace rather than extend workflow-level ones, and checkouts run with persist-credentials disabled.
    • Input validation with allowlists: path inputs are validated against an allowlist of a single path element rather than a denylist of traversal forms.
    • Limited attack surface: SECURITY.md defines scope explicitly; the application is self-hosted with no outbound telemetry and a minimal vendored JavaScript footprint rather than an npm dependency tree.

    External analysis supports the implementation -- CodeQL on every pull request, gosec among 23 enabled linters, govulncheck in CI, Sigstore-signed releases, and an OpenSSF Scorecard of 9.0/10 -- but external analysis is not the developer knowledge this criterion asks about, so the criterion is answered on its own terms.



    Au moins l'un des principaux développeurs du projet DOIT connaître les types courants d'erreurs qui conduisent à des vulnérabilités dans ce genre de logiciel, ainsi qu'au moins une méthode pour contrer ou atténuer chacun d'eux. [know_common_errors]
    Des exemples (selon le type de logiciel) incluent l'injection SQL, l'injection OS, le débordement mémoire classique, le cross-site scripting, l'authentification manquante et l'autorisation manquante. Voir CWE/SANS top 25 ou OWASP Top 10 pour les listes couramment utilisées. De nombreux livres et cours sont disponibles pour vous aider à comprendre comment développer des logiciels plus sûrs et discuter des erreurs courantes de mise en œuvre qui conduisent à des vulnérabilités. Par exemple, le cours Bases du développement logiciel sécurisé est un ensemble gratuit de trois cours qui expliquent comment développer des logiciels plus sûrs (gratuitement ; vous pouvez payer pour obtenir un certificat démontrant que vous avez assimilé le matériel présenté).

    ...pending completion of the OpenSSF Secure Software Development Fundamentals course.

    The project is developed by a single primary developer, who has not completed formal secure-development training, and this criterion asks what a developer knows rather than what the codebase contains. Answering Met on the strength of the implemented mitigations would be asserting knowledge rather than evidencing it, so it is recorded honestly as unmet.

    The plan to meet it is the course this criterion names -- the OpenSSF Secure Software Development Fundamentals, audited free -- which also addresses know_secure_design.

    For context, mitigations for the common classes are implemented and enforced in the project today, several of them structurally rather than per-site: authentication, CSRF protection, and rate limiting applied as router middleware so a new endpoint is covered by construction; provider API keys encrypted at rest with AES-256-GCM under a 0600 key file rather than an environment value; input validated at the API boundary, with path inputs checked against an allowlist of one path element rather than a denylist of traversal forms; parameterized SQL throughout.

    These are backed by automated checks rather than vigilance: CodeQL on every pull request, gosec among 23 enabled linters, govulncheck in CI, and Dependabot for dependency advisories. That is why the codebase scores well on external analysis -- OpenSSF Scorecard rates it 9.0/10 -- but external analysis is not the same as the developer knowledge this criterion asks about, and the criterion is answered on its own terms.


  • Utiliser de bonnes pratiques de base de cryptographie

    Notez que certains logiciels n'ont pas besoin d'utiliser des mécanismes cryptographiques. Si votre projet produit un logiciel qui (1) inclut ou active la fonctionnalité de chiffrement, et (2) peut être publié des États-Unis (US) vers l'extérieur des États-Unis ou vers un citoyen autre qu'américain, vous pouvez être légalement obligé à faire quelques étapes supplémentaires. En règle générale, cela implique simplement l'envoi d'un email. Pour plus d'informations, consultez la section sur le chiffrement de Comprendre la technologie Open Source et les contrôles à l'exportation américains .

    Le logiciel produit par le projet DOIT utiliser, par défaut, uniquement les protocoles cryptographiques et les algorithmes publiés publiquement et revus par des experts (si des protocoles et algorithmes cryptographiques sont utilisés). [crypto_published]
    Ces critères cryptographiques ne s'appliquent pas toujours car certains logiciels n'ont pas besoin d'utiliser directement de capacités cryptographiques.

    Every cryptographic algorithm the project uses is publicly published, standardised and expert-reviewed, and all of them come from Go's standard library or the Go project's supplementary x/crypto module. Nothing is home-grown.

    • Secrets at rest: AES-256-GCM (FIPS 197 for AES, NIST SP 800-38D for GCM) via crypto/aes and crypto/cipher, used to encrypt provider API keys. Implementation: https://github.com/sydlexius/stillwater/blob/main/internal/encryption/encryption.go
    • Password storage: bcrypt (Provos and Mazieres, USENIX 1999) via golang.org/x/crypto/bcrypt, at the library's default cost. Passwords are pre-hashed before bcrypt to avoid its 72-byte input truncation.
    • Transport: TLS via crypto/tls, with certificate handling through crypto/x509.
    • Signatures and keys: ECDSA (crypto/ecdsa with crypto/elliptic) and RSA (crypto/rsa), as required for OIDC token verification.
    • Hashing and message authentication: SHA-256 (FIPS 180-4) via crypto/sha256, and HMAC (RFC 2104, FIPS 198-1) via crypto/hmac.
    • Randomness: crypto/rand, the cryptographically secure source, rather than math/rand.
    • Constant-time comparison: crypto/subtle, so secret comparisons do not leak by timing.

    No custom cipher, construction, mode, or key-derivation scheme is defined anywhere in the project. Where a cryptographic operation is needed, the standard library primitive is used directly.

    Supporting controls: the encryption key is stored as a file with 0600 permissions rather than an environment variable, so it does not appear in a process listing or in inherited environments. gosec is among the 23 linters enabled in CI, CodeQL analyses every pull request, and govulncheck runs against the dependency graph -- so a weak or deprecated primitive would be flagged rather than lingering unnoticed.



    Si le logiciel produit par le projet est une application ou une bibliothèque, et si son objectif principal n'est pas d'implémenter de la cryptographie, alors il DEVRAIT simplement appeler un logiciel spécialement conçu pour implémenter des fonctions cryptographiques ; il ne DEVRAIT PAS ré-implémenter les siennes. [crypto_call]

    Stillwater is a metadata and image manager for self-hosted media servers; implementing cryptography is not its purpose, and it does not attempt to.

    Every cryptographic operation calls into software written specifically to implement it -- Go's standard library (crypto/aes, crypto/cipher, crypto/tls, crypto/x509, crypto/sha256, crypto/hmac, crypto/rand, crypto/subtle, crypto/ecdsa, crypto/rsa) and the Go project's supplementary golang.org/x/crypto/bcrypt.

    No cipher, mode, hash, MAC, key-derivation function, or random-number generator is implemented in this codebase. The encryption path composes standard primitives in the documented way -- aes.NewCipher into cipher.NewGCM, with the nonce drawn from crypto/rand -- rather than assembling anything bespoke: https://github.com/sydlexius/stillwater/blob/main/internal/encryption/encryption.go

    Where the project had a choice between using a primitive correctly and working around one of its properties, it used the primitive: passwords are pre-hashed before bcrypt to avoid its 72-byte input truncation, rather than substituting a hand-rolled scheme.

    This is enforced as well as intended. gosec is among the 23 linters enabled in CI and flags weak or misused cryptographic primitives, CodeQL analyses every pull request, and govulncheck runs against the dependency graph -- so an advisory affecting x/crypto surfaces as a CI finding rather than going unnoticed. Two such advisories were resolved this way by upgrading the dependency rather than patching around it.



    Toutes les fonctionnalités du logiciel produit par le projet qui dépendent de la cryptographie DOIVENT être réalisables à l'aide de FLOSS. [crypto_floss]

    Every cryptographic dependency is FLOSS, and none is optional or substitutable for a proprietary alternative.

    • Go standard library crypto (crypto/aes, crypto/cipher, crypto/tls, crypto/x509, crypto/sha256, crypto/hmac, crypto/rand, crypto/subtle, crypto/ecdsa, crypto/rsa) -- BSD-3-Clause.
    • golang.org/x/crypto/bcrypt, from the Go project -- BSD-3-Clause.

    Both ship with, or are fetched as, ordinary module dependencies. There is no proprietary cryptographic library, no hardware security module requirement, no vendor SDK, and no paid service anywhere in the cryptographic path.

    The algorithms themselves are open standards with multiple independent FLOSS implementations, so nothing here is tied to a single provider: AES (FIPS 197) and GCM (NIST SP 800-38D), bcrypt (published by Provos and Mazieres, USENIX 1999), TLS and X.509 (IETF), SHA-256 (FIPS 180-4), HMAC (RFC 2104), ECDSA and RSA. A reimplementation against any conforming FLOSS crypto library would interoperate with data this project has already written.

    The build reflects that: the whole project builds from source with FLOSS tooling only (Go, templ, Tailwind, Node, Make, git), so a user can compile and run every cryptographic path themselves without acquiring anything proprietary. Encrypted data is readable by any AES-256-GCM implementation given the key, and the key is a plain file rather than something sealed to a proprietary keystore -- so a user is not locked in by the encryption at rest.

    The project is licensed GPL-3.0: https://github.com/sydlexius/stillwater/blob/main/LICENSE



    Les mécanismes de sécurité dans le logiciel produit par le projet DOIVENT utiliser des longueurs de clés par défaut qui satisfont au moins aux exigences minimales du NIST jusqu'à l'année 2030 (comme indiqué en 2012). Il DOIT être possible de configurer le logiciel afin que les plus petites longueurs de clés soient complètement désactivées. [crypto_keylength]
    Ces longueurs de bit minimales sont : pour une clé symétrique 112, pour un modulo de factorisation 2048, pour une clé de logarithme discret 224, pour un groupe du logarithmique discret 2048, pour une courbe elliptique 224 et pour un hachage 224 (le hachage de mot de passe n'est pas couvert par cette longueur de bit, plus d'informations sur le hachage de mot de passe peuvent être trouvées dans le critère crypto_password_storage). Voir https://www.keylength.com pour une comparaison des recommandations sur les longueurs de clés de diverses organisations. Le logiciel PEUT permettre de plus petites longueurs de clés dans certaines configurations (idéalement non, car cela permet des attaques de dégradation, mais des longueurs de clés plus courtes sont parfois nécessaires pour l'interopérabilité).

    Every default exceeds the stated 2030 minimums, and shorter lengths are not configurable -- they are rejected outright rather than merely discouraged.

    SYMMETRIC (minimum 112). AES-256, so 256 bits. The key length is not a setting: the encryptor requires exactly 32 bytes and returns an error otherwise, so a shorter key cannot be configured, supplied, or fallen back to. A generated key is drawn from crypto/rand at the full 32 bytes. https://github.com/sydlexius/stillwater/blob/main/internal/encryption/encryption.go

    ELLIPTIC CURVE (minimum 224). P-256, so 256 bits, used for the ACME account and certificate keys. The curve is fixed in code rather than exposed as an option.

    TRANSPORT. The HTTPS listener sets MinVersion: tls.VersionTLS13, which is stronger than this criterion requires: TLS 1.3 removes the weak cipher suites and short-key key-exchange groups entirely, so a downgrade to a smaller keylength is not reachable through negotiation. Older protocol versions are refused rather than deprioritised.

    FACTORING MODULUS AND DISCRETE LOG (minimum 2048). The project does not generate RSA or finite-field Diffie-Hellman keys of its own. crypto/rsa appears only for verifying OIDC provider tokens, where the key is the identity provider's, and crypto/x509 for certificate handling. Where the project chooses, it chooses ECDSA P-256.

    HASH (minimum 224). SHA-256, so 256 bits, via crypto/sha256. No shorter digest is used anywhere; SHA-1 and MD5 do not appear in the cryptographic path.

    CONFIGURABILITY OF SMALLER LENGTHS. There is none, by design. No setting, environment variable, or flag lowers any of the above. The AES key length is enforced with an explicit error, the curve and hash are compile-time choices, and the TLS floor is set at the listener. Since the project has no interoperability requirement that would justify a weaker option, the downgrade path this criterion permits simply does not exist.

    gosec, which flags weak cryptographic parameters, is among the 23 linters enabled in CI, and CodeQL analyses every pull request.



    Les mécanismes de sécurité par défaut dans le logiciel produit par le projet NE DOIVENT PAS dépendre d'algorithmes cryptographiques cassés (par exemple, MD4, MD5, DES unique, RC4, Dual_EC_DRBG) ou utiliser des modes de chiffrement inappropriés dans le contexte, sauf si ils sont nécessaires pour implémenter un protocole d'interopérabilité (où le protocole implémenté est la version la plus récente du standard supporté largement par l'écosystème du réseau, l'écosystème requiert l'utilisation de cet algorithme ou mode, et cet écosystème n'offre pas d'alternative plus sûre). La documentation DOIT décrire tous les risques de sécurité appropriés et les parades connues si ces algorithmes ou modes cassés sont nécessaires pour un protocole d'interopérabilité. [crypto_working]
    Le mode ECB n'est presque jamais approprié car il révèle des blocs identiques dans le texte chiffré, comme le montre le pingouin ECB, et le mode CTR est souvent inapproprié car il n'effectue pas d'authentification et provoque des doublons si l'état d'entrée est dupliqué. Dans de nombreux cas, il est préférable de choisir un mode d'algorithme de chiffrement de bloc conçu pour combiner le secret et l'authentification, par exemple Galois/Counter Mode (GCM) et EAX. Les projets PEUVENT permettre aux utilisateurs d'activer les mécanismes cassés (par exemple pendant la configuration) si nécessaire pour la compatibilité, mais les utilisateurs savent alors qu'ils le font.

    No broken algorithm and no inappropriate cipher mode is used anywhere in the project's security mechanisms, and none is needed for interoperability.

    VERIFIED ABSENT. A scan of the codebase for MD4, MD5, SHA-1, single DES, RC4, and Dual_EC_DRBG returns nothing outside of test files -- none of them appears in the cryptographic path at all. There is consequently no interoperability exception to document, and no configuration option that would enable one.

    CIPHER MODE. Secrets at rest use AES-256-GCM, an AEAD mode that provides authentication alongside confidentiality, which is the combination this criterion recommends. ECB, CBC, and raw CTR are not used anywhere: the codebase contains no call to cipher.NewCBCEncrypter, NewCTR, or any ECB construction. The nonce is drawn from crypto/rand per encryption rather than reused or derived, so GCM's requirement for nonce uniqueness is met by construction. https://github.com/sydlexius/stillwater/blob/main/internal/encryption/encryption.go

    THE REST OF THE PATH. SHA-256 for hashing, HMAC for message authentication, bcrypt for password storage, ECDSA on P-256 for certificate keys, and TLS 1.3 as the enforced transport floor -- TLS 1.3 having removed the weak cipher suites, RC4, and CBC-mode suites from negotiation entirely, so a downgrade to a broken algorithm is not reachable.

    ONE CLARIFICATION, TO PRE-EMPT A FALSE POSITIVE. The project computes a 64-bit perceptual hash (dHash) over images to detect visually duplicate artwork: https://github.com/sydlexius/stillwater/blob/main/internal/image/hash.go . That is an image-similarity function, not a cryptographic one. It protects nothing, guards no security decision, and is never used for authentication, integrity, or as a security boundary -- comparing artwork for near-duplicates is its only purpose. Its short digest is therefore not a cryptographic weakness.

    ENFORCEMENT. gosec, which flags weak and deprecated cryptographic primitives, is among the 23 linters enabled in CI and runs on every change; CodeQL analyses every pull request; govulncheck runs against the dependency graph. A broken primitive introduced later would fail the build rather than reach a release.



    Les mécanismes de sécurité par défaut dans le logiciel produit par le projet NE DEVRAIENT PAS dépendre d'algorithmes ou de modes cryptographiques avec des faiblesses sérieuses connues (par exemple, l'algorithme de hachage cryptographique SHA-1 ou le mode CBC en SSH). [crypto_weaknesses]
    Les préoccupations concernant le mode CBC en SSH sont discutées dans CERT : vulnérabilité SSH CBC.

    Nothing in the project's security mechanisms depends on an algorithm or mode with a known serious weakness.

    SHA-1 does not appear in the cryptographic path at all -- verified by scanning the codebase. Hashing is SHA-256 throughout, and message authentication is HMAC.

    CBC is not used in any form. Secrets at rest use AES-256-GCM, an authenticated mode, so the padding-oracle and malleability problems that motivate the concern about CBC do not arise: a ciphertext that has been tampered with fails authentication rather than decrypting into attacker-influenced plaintext.

    The transport floor is TLS 1.3, set explicitly at the listener rather than left to negotiation. This is stronger than avoiding weak choices individually: TLS 1.3 removed CBC-mode cipher suites, RC4, static RSA key exchange, and renegotiation from the protocol, so the weaknesses this criterion is concerned with are not reachable by downgrade even if a client requests them. Earlier protocol versions are refused, not merely deprioritised.

    Elliptic-curve work uses P-256 rather than any of the curves with questioned provenance, and randomness comes from crypto/rand -- never math/rand, and never a DRBG of uncertain design.

    Passwords are stored with bcrypt at the library's default cost, and are pre-hashed before bcrypt so that its 72-byte input truncation cannot silently discard part of a long passphrase.

    The project does not implement or expose SSH, so the CBC-in-SSH case this criterion cites does not apply.

    Enforcement rather than intent: gosec, which flags weak and deprecated primitives, is among the 23 linters enabled in CI; CodeQL analyses every pull request; govulncheck runs against the dependency graph, and two golang.org/x/crypto advisories were resolved by upgrading the dependency rather than working around them.



    Les mécanismes de sécurité dans le logiciel produit par le projet DEVRAIENT implémenter la confidentialité persistante pour les protocoles d'échange de clés afin qu'une clé de session dérivée d'un ensemble de clés à long terme ne soit pas compromise si l'une des clés à long terme est compromise dans le futur. [crypto_pfs]

    The only key-agreement protocol the project uses is TLS, and it is pinned to TLS 1.3, which provides forward secrecy unconditionally.

    The HTTPS listener sets MinVersion: tls.VersionTLS13 explicitly rather than leaving the floor to negotiation: https://github.com/sydlexius/stillwater/blob/main/internal/server/listeners.go

    This is stronger than selecting forward-secret cipher suites within an older protocol. TLS 1.3 removed static RSA and static Diffie-Hellman key exchange from the protocol entirely, so every handshake is an ephemeral (EC)DHE exchange by construction. There is no cipher-suite configuration, downgrade path, or interoperability option through which a non-forward-secret handshake could be negotiated -- a client offering only older mechanisms is refused rather than accommodated.

    Compromise of the server's long-term private key at some future point therefore does not expose previously recorded sessions: the per-session keys derive from ephemeral key shares that are discarded when the session ends and are never transmitted.

    The HTTP/3 listener shares the same TLS material and the same floor; QUIC mandates TLS 1.3 in any case, and quic-go enforces that internally.

    The project implements no other key-agreement protocol. Secrets at rest are encrypted with a locally held symmetric key (AES-256-GCM) rather than through a key-agreement exchange, so forward secrecy is not applicable to that path -- there are no session keys and no long-term keys negotiated between parties.

    Outbound connections to metadata providers and to Emby, Jellyfin, and Lidarr use Go's standard net/http client, whose default TLS configuration prefers forward-secret suites and negotiates TLS 1.3 with any server that supports it.



    Si le logiciel produit par le projet entraîne la sauvegarde de mots de passe pour l'authentification d'utilisateurs externes, les mots de passe DOIVENT être sauvegardés comme hachages itérés avec un salage par utilisateur en utilisant un algorithme d'étirement de clé (itéré) (par exemple Argon2id, Bcrypt, Scrypt, ou PBKDF2). Voir également le pense-bête sur le stockage des clés d'OWASP. [crypto_password_storage]
    Ce critère s'applique uniquement lorsque le logiciel applique l'authentification des utilisateurs utilisant des mots de passe pour les utilisateurs extérieurs (càd l'authentification entrante), telles que des applications Web côté serveur. Il ne s'applique pas dans les cas où le logiciel sauvegarde des mots de passe pour l'authentification dans d'autres systèmes (càd l'authentification sortante, par exemple, le logiciel implémente un client pour un autre système), car au moins certaines parties de ce logiciel doivent avoir souvent accès au mot de passe en clair.

    Local user passwords are stored as bcrypt hashes -- an iterated, salted, key-stretching algorithm, and one of the algorithms this criterion names.

    Implementation: bcrypt.GenerateFromPassword(PrehashPassword(password), bcrypt.DefaultCost) via golang.org/x/crypto/bcrypt. https://github.com/sydlexius/stillwater/blob/main/internal/auth/user.go

    • ITERATED: bcrypt is a deliberately slow key-stretching function; the work factor is the library's default cost rather than a lowered value.
    • PER-USER SALT: bcrypt generates a random salt per invocation and stores it within the resulting hash, so two users with the same password produce different stored values.
    • The plaintext password is never stored, and never logged.

    PRE-HASHING, AND WHY. The password is passed through SHA-256 and hex-encoded before bcrypt, rather than handed to bcrypt directly. bcrypt silently truncates its input at 72 bytes, so a long passphrase would otherwise have its tail discarded without any error -- two distinct passphrases sharing a 72-byte prefix would authenticate interchangeably. Pre-hashing to a fixed-length 64-character hex digest removes that truncation entirely, so the full passphrase contributes to the stored hash. This is the construction the OWASP Password Storage Cheat Sheet recommends for bcrypt, and it does not weaken the stretching: bcrypt still performs its full cost-factor work over the digest, and the per-invocation salt is unchanged.

    TIMING. Login compares against a bcrypt hash computed once at default cost for the unknown-user case, so an unregistered username costs the same as a registered one and does not leak account existence by response time.

    SCOPE. This applies to local accounts. The project also supports OIDC and federated authentication against a media server, where no password is stored at all -- the identity provider or media server holds the credential. Credentials the project stores for OUTBOUND authentication to third-party services (provider API keys) are encrypted with AES-256-GCM rather than hashed, since they must be recoverable to be used, which is the outbound case this criterion explicitly excludes.



    Les mécanismes de sécurité dans le logiciel produit par le projet DOIVENT générer toutes les clés cryptographiques et les nonces en utilisant un générateur de nombres aléatoires cryptographiquement sécurisé, et NE DOIVENT PAS le faire en utilisant des générateurs qui ne seraient pas cryptographiquement sécurisés. [crypto_random]
    Un générateur de nombres aléatoires cryptographiquement sécurisé peut être un générateur de nombres aléatoires matériel ou un générateur de nombres pseudo-aléatoires cryptographiquement sécurisé (CSPRNG) utilisant un algorithme tel que Hash_DRBG, HMAC_DRBG, CTR_DRBG, Yarrow ou Fortuna. Des exemples d'appels de générateurs de nombres aléatoires sûrs incluent java.security.SecureRandom en Java et window.crypto.getRandomValues ​​de JavaScript. Des exemples d'appels de générateurs de nombres aléatoires non sûrs incluent java.util.Random en Java et Math.random en JavaScript.

    Every cryptographic key, nonce, token, and secret is generated from crypto/rand, Go's cryptographically secure random source, which draws from the operating system CSPRNG.

    Security-relevant generation sites all use it:

    • The AES-256 encryption key, when generated (internal/encryption/encryption.go)
    • The GCM nonce, drawn fresh per encryption
    • Session and authentication tokens (internal/auth/)
    • CSRF tokens (internal/api/middleware/csrf.go)
    • OIDC state and nonce values (internal/auth/provider_oidc.go)
    • Invitation tokens (internal/auth/invite.go)
    • ACME account and certificate keys (internal/server/cert_manager_lego.go)
    • Settings export material (internal/settingsio/export.go)

    math/rand is used in exactly two places, and neither is a security mechanism. Both are annotated inline with the reason:

    • internal/watcher/probe.go generates a filename suffix for a filesystem write-probe -- a temporary file the watcher creates to test whether a directory is writable. Unpredictability is not a property it needs.
    • internal/provider/retry.go computes jitter for retry backoff against third-party metadata APIs, so concurrent retries do not synchronise. Jitter does not need to be unguessable.

    Neither generates a key, nonce, token, or any value an attacker could exploit by predicting. Both carry a //nolint:gosec directive naming the linter and stating why the rule does not apply, which is the project's convention for every suppression.

    That distinction is enforced rather than trusted: gosec's G404 rule flags every use of a non-cryptographic RNG, and gosec is among the 23 linters enabled in CI running on every change. A new math/rand call in a security path would fail the build unless someone explicitly suppressed it with a written justification, which is visible in review.


  • Livraison sécurisée contre les attaques man-in-the-middle (MITM)


    Le projet DOIT utiliser un mécanisme de livraison qui contrecarre les attaques MITM. L'utilisation de https ou ssh+scp est acceptable. [delivery_mitm]
    Un mécanisme encore plus fort distribue le logiciel sous forme de paquetages signés numériquement, car cela atténue les attaques sur le système de distribution, mais cela ne fonctionne que si les utilisateurs peuvent être convaincus que les clés publiques pour les signatures sont correctes et si les utilisateurs vérifient la signature.

    HTTPS is used throughout and additionally by digital signatures on every release artifact.

    TRANSPORT. Every distribution channel is HTTPS-only:

    Go module dependencies are fetched over HTTPS and verified against go.sum, so a tampered dependency fails the build rather than being silently accepted.

    SIGNATURES, the stronger mechanism this criterion describes. Release artifacts are signed with cosign (Sigstore) as part of the release workflow, and SLSA provenance attestation is generated at release level: https://github.com/sydlexius/stillwater/blob/main/.github/workflows/release.yml

    Sigstore is keyless -- signatures are bound to the GitHub Actions workflow identity that produced them and recorded in the public Rekor transparency log. That addresses the caveat in this criterion's own wording: a user does not need to obtain and trust a maintainer's public key out of band, since verification checks the signing identity against the transparency log rather than against a key the user must first acquire securely. Checksums are published alongside the artifacts, and the multi-architecture container manifest is signed as well as the individual images.

    OpenSSF Scorecard independently confirms this, scoring the project 10/10 on Signed-Releases: "5 out of the last 5 releases have a total of 10 signed artifacts" -- https://securityscorecards.dev/viewer/?uri=github.com/sydlexius/stillwater



    Un hachage cryptographique (par exemple, un sha1sum) NE DOIT PAS être récupéré par http et utilisé sans vérifier une signature cryptographique. [delivery_unsigned]
    Ces hachages peuvent être modifiés en transit.

    Nothing in the project retrieves a cryptographic hash over plaintext HTTP, in the build, in CI, or at runtime.

    BUILD AND CI. A scan of the workflows finds no plaintext http:// fetch of any kind. Everything the build retrieves comes over HTTPS, and each class is verified by a mechanism stronger than a bare downloaded checksum:

    • Go module dependencies are fetched over HTTPS and verified against the committed go.sum, which is itself in version control rather than downloaded alongside the artifact. A tampered module fails the build.
    • npm dependencies are pinned in package-lock.json with integrity hashes, likewise committed.
    • All 155 GitHub Actions references are pinned to commit SHAs rather than mutable tags, so a compromised or moved tag cannot substitute different code. OpenSSF Scorecard confirms this independently, scoring 10/10 on Pinned-Dependencies for Actions.
    • Container base images are pinned in the Dockerfiles.

    RUNTIME. The application fetches metadata and artwork from third-party providers over HTTPS, and connects to Emby, Jellyfin and Lidarr over the scheme the operator configures. It does not download a checksum or hash file from any source and act on it, so the situation this criterion describes does not arise.

    The one place the project handles checksums is in the opposite direction: the release workflow PUBLISHES checksums alongside its artifacts, and those artifacts are additionally signed with cosign (Sigstore) with SLSA provenance attestation. A consumer verifying a release therefore checks a signature bound to the producing workflow identity and recorded in the public Rekor transparency log, rather than trusting a hash retrieved over the wire.


  • Vulnérabilités publiquement identifiées et corrigées


    Il ne DOIT pas y avoir de vulnérabilités non corrigées de gravité moyenne ou supérieure connues publiquement depuis plus de 60 jours. [vulnerabilities_fixed_60_days]
    La vulnérabilité doit être corrigée et diffusée par le projet lui-même (les correctifs peuvent être développés ailleurs). Une vulnérabilité devient publique (à cet effet) une fois qu'elle a un CVE avec des informations non payantes publiquement publiées (signalée, par exemple, dans la Base de données Nationale des Vulnérabilités) ou lorsque le projet a été informé et que l'information a été diffusée au public (éventuellement par le projet). Une vulnérabilité est considérée de gravité moyenne ou supérieure si son score de base qualitatif du Système Commun d'Évaluation des Vulnérabilités (CVSS) est moyen ou supérieur. Dans les versions CVSS 2.0 à 3.1, cela équivaut à un score CVSS de 4.0 ou supérieur. Les projets peuvent utiliser le score CVSS publié dans une base de données de vulnérabilité largement utilisée (telle que la base de données nationale des vulnérabilités) en utilisant la version la plus récente de CVSS rapportée dans cette base de données. Les projets peuvent aussi calculer eux-mêmes la gravité à l'aide de la dernière version de CVSS au moment de la divulgation de la vulnérabilité, si les entrées de calcul sont révélées publiquement une fois que la vulnérabilité est connue du public.Note : cela signifie que les utilisateurs peuvent être laissés vulnérables à tous les attaquants du monde entier jusqu'à 60 jours. Ce critère est souvent beaucoup plus facile à atteindre que ce que Google recommande dans son Redémarrage de la divulgation responsable, car Google recommande que la période de 60 jours commence lorsque le projet est notifié même si le rapport n'est pas public. Notez que ce critère de badge, comme d'autres critères, s'applique à un projet individuel. Certains projets font parti d'organisations ou de projets englobants, parfois à plusieurs niveaux, et de nombreux projets fournissent leurs résultats à d'autres organisations et projets au sein d'une chaîne approvisionnement potentiellement complexe. Un projet individuel ne peut souvent pas contrôler le reste, mais un projet individuel peut travailler à fournir un correctif de vulnérabilité à temps. Pour cette raison, nous nous concentrons seulement sur le temps de réponse des projets individuels. Une fois qu'un correctif est disponible de la part d'un projet individuel, les autres projets peuvent déterminer comment appliquer le correctif (par exemple, ils peuvent mettre à jour la dernière version ou ils peuvent appliquer uniquement le correctif).

    There are no unpatched vulnerabilities of any severity, publicly known or otherwise.

    Verified against the live repository:

    • Published security advisories: 0 (https://github.com/sydlexius/stillwater/security/advisories)
    • Open Dependabot alerts: 0
    • Open code-scanning (CodeQL) alerts: 0
    • No CVE has been assigned against the project; NIST's CPE dictionary returns no entry for either the product or the vendor name, as expected for a project with no published CVE.

    There has consequently never been a 60-day window to exceed.

    Vulnerabilities are surfaced continuously rather than waiting on an external report: CodeQL analyses every pull request, govulncheck runs in CI against the dependency graph, gosec is among the 23 linters enabled on every change, and Dependabot is configured for dependency updates.

    Dependency advisories are acted on promptly rather than accumulating. Two golang.org/x/crypto advisories surfaced by govulncheck were resolved by upgrading the dependency the same day they were identified, ahead of any external scanner flagging them. One further advisory affecting a transitive dependency has no fixed version published upstream; the affected subpackage is never imported by this project, so it is unreachable here, and that determination was made by call-path analysis rather than by version range alone.

    If a vulnerability is reported, SECURITY.md commits to an initial acknowledgement within 72 hours and a fix released as soon as practical, typically within 14 days for critical issues -- well inside the 60-day bound this criterion sets: https://github.com/sydlexius/stillwater/blob/main/SECURITY.md



    Les projets DEVRAIENT corriger rapidement toutes les vulnérabilités critiques après leur signalement. [vulnerabilities_critical_fixed]

    No critical vulnerability has been reported against the project, so there is no remediation record to point to. Verified live: zero published security advisories, zero open Dependabot alerts, zero open CodeQL alerts, and no CVE assigned.

    What can be evidenced is the response speed on the security findings the project has actually had.

    Dependency advisories are fixed the day they surface. Two golang.org/x/crypto advisories were identified by govulncheck and resolved by upgrading the dependency within hours, ahead of any external scanner flagging them. A third advisory affecting a transitive dependency has no fixed version published upstream; call-path analysis confirmed the affected subpackage is never imported here, so it is unreachable rather than unaddressed, and that determination is recorded rather than assumed.

    Findings from code review are treated the same way. A recent change introduced a data-loss window -- a cleanup sweep could delete another concurrent operation's in-progress staging file, turning a recoverable failure into permanent loss. It was found in review, fixed the same session with a proof that reproduces the loss when the fix is removed, and the fix itself was then re-reviewed rather than trusted.

    The commitment for a reported vulnerability is published in advance: an initial acknowledgement within 72 hours, and a fix released as soon as practical, typically within 14 days for critical issues. https://github.com/sydlexius/stillwater/blob/main/SECURITY.md

    Detection is continuous rather than waiting on a report -- CodeQL on every pull request, govulncheck in CI, gosec among 23 enabled linters, and Dependabot for dependency updates -- and releases are frequent (four stable releases in the past month), so a fix does not wait on a distant release train to reach users.


  • Autres problèmes de sécurité


    Les dépôts publics NE DOIVENT PAS fuiter un certificat privé valide (par exemple, un mot de passe ou une clé privée) qui est destiné à limiter l'accès public. [no_leaked_credentials]
    Un projet PEUT fuiter des « échantillons » de certificats pour les tests et pour des bases de données sans importance, pour autant qu'ils ne soient pas destinés à limiter l'accès public.

    No valid private credential is present in the repository.

    GitHub secret scanning is ENABLED on the repository, so a committed credential matching a known provider pattern is detected and reported automatically rather than depending on anyone noticing. Push protection and scanning run against the full history, not just new commits.

    Nothing sensitive is tracked. No .pem, .key, .p12, SSH private key, or encryption key file exists in version control -- the only match for a key-shaped filename is a Go test that asserts the encryption key file is created with 0600 permissions, which contains no key material. .gitignore excludes .env and .env.*, all SQLite database files including WAL and shared-memory sidecars, and the UAT staging directory, which holds a cloned database and an encryption key and is deliberately kept out of git so that material never enters history.

    The encryption key is generated at runtime into a 0600 file beside the database rather than being shipped, embedded, or held in an environment variable -- which also keeps it out of process listings and inherited environments. Provider API keys are supplied by the operator at runtime and stored encrypted with AES-256-GCM; none are distributed with the software.

    Test fixtures use obviously non-functional placeholder values, and where a test needs key material it is generated at test time rather than committed.

    The project also treats credentials on the command line as a leak risk in their own right, not just credentials in files: passing a secret as a command-line argument is avoided because it is visible in the process table and can be recorded in shell and tooling history. Secrets are passed by environment variable or stdin instead.

    CI reinforces this -- gosec is among the 23 enabled linters and flags hardcoded credential patterns, and CodeQL analyses every pull request.


 Analyse 8/8 ●

  • Analyse statique de code


    Au moins un outil d'analyse statique de code (au-delà des avertissements du compilateur et des modes « sûrs » des languages) DOIT être appliqué à toute distribution majeure proposée avant sa sortie s'il existe au moins un outil FLOSS qui implémente ce critère dans le langage sélectionné. [static_analysis]
    Un outil d'analyse statique de code examine le code logiciel (au niveau du code source, du code intermédiaire ou de l'exécutable) sans l'exécuter avec des entrées spécifiques. Aux fins de ce critère, les avertissements du compilateur et les modes de langage « sûrs » ne comptent pas comme des outils d'analyse statique de code (ceux-ci évitent généralement une analyse approfondie car la rapidité est vitale). Certains outils d'analyse statique se concentrent sur la détection de défauts génériques, d'autres se concentrent sur la détection de défauts spécifiques (tels que les vulnérabilités) et d'autres encore proposent une combinaison de ces deux types d'outils. Des exemples de tels outils d'analyse statique de code incluent cppcheck (C, C++), clang static analyzer (C, C++), SpotBugs (Java), FindBugs (Java) (y compris FindSecurityBugs), PMD (Java), Brakeman (Ruby on Rails), lintr (R), goodpractice (R), Coverity Quality Analyzer, SonarQube, Codacy et HP Enterprise Fortify Static Code Analyzer. Des listes plus vastes d'outils peuvent être trouvées dans des endroits tels que la liste Wikipedia d'outils pour l'analyse statique de code, l'information OWASP sur l'analyse statique de code, la liste NIST des analyseurs de sécurité du code source et la liste des outils d'analyse statique de Wheeler. S'il n'y a pas d'outil d'analyse statique FLOSS disponible pour le(s) langage(s) d'implémentation utilisé(s), sélectionnez « N/A ».

    Multiple FLOSS static analysis tools run on every change, so every release is analysed by construction rather than as a pre-release step someone has to remember.

    CodeQL (MIT-licensed CLI) is the primary one. It runs on every push and every pull request, plus a weekly scheduled scan, analysing two languages: go for the application itself and actions for the GitHub Actions workflows -- the latter catching workflow-level issues such as untrusted input reaching a run step. Findings surface as code-scanning alerts on the pull request that introduced them, and there are currently zero open alerts. https://github.com/sydlexius/stillwater/blob/main/.github/workflows/codeql.yml

    Alongside it, and also on every change:

    • gosec, the Go security-focused analyser, among the 23 linters enabled via golangci-lint (GPL-3.0)
    • staticcheck, which performs deep interprocedural analysis well beyond compiler diagnostics
    • govulncheck (BSD-3-Clause), which is call-path aware rather than version-range based: it reports a known vulnerability only when the affected code is actually reachable from this project
    • gocritic with its style and performance analysis bundles enabled
    • Hadolint for the Dockerfiles, actionlint for workflow syntax, stylelint for CSS

    These are required status checks, so a finding blocks the change rather than being filed for later. Because they gate every merge, the state of the default branch at any tag is analysis-clean -- a release cannot contain code that has not passed them.

    Beyond static analysis, the project runs mutation testing in CI against its own test suite, checking that the tests actually detect changed behaviour rather than merely executing the lines.

    Current result: golangci-lint run ./... reports 0 issues across the module, and OpenSSF Scorecard independently scores the project 10/10 on its SAST check.



    Il est PROPOSÉ qu'au moins l'un des outils d'analyse statique utilisés pour le critère d'analyse statique inclue des règles ou des approches pour rechercher des vulnérabilités courantes dans le langage ou l'environnement analysé. [static_analysis_common_vulnerabilities]
    Les outils d'analyse statique spécialement conçus pour détecter les vulnérabilités les plus courantes sont plus susceptibles de les détecter. Cela dit, l'utilisation d'outils statiques aidera généralement à trouver des problèmes, nous suggérons donc, sans l'exiger, de le faire pour le badge de niveau « passant ».

    Three of the static analysis tools in use are specifically designed to find vulnerabilities rather than general defects.

    CodeQL runs on every push and pull request with its security query suites for Go, covering the injection, path traversal, SSRF, insecure randomness, and sensitive-data-exposure classes among others. It also analyses the GitHub Actions workflows as a separate language, which catches supply-chain issues such as untrusted input reaching a run step -- a class general-purpose linters do not look for at all. There are currently zero open code-scanning alerts. https://github.com/sydlexius/stillwater/blob/main/.github/workflows/codeql.yml

    gosec is the Go ecosystem's dedicated security analyser, and its rule set maps directly onto the common vulnerability classes: hardcoded credentials, SQL injection, command injection, path traversal, weak or deprecated cryptographic primitives, insecure random number generation, unsafe file permissions, and unhandled errors in security-relevant positions. It is enabled among the 23 linters that run on every change.

    gosec's value here is demonstrable rather than theoretical. Its G404 rule -- non-cryptographic random number generator -- is what forces every use of math/rand in this codebase to be justified in writing: the two non-security uses that exist (a filesystem probe filename and retry jitter) each carry an inline suppression naming the rule and stating why it does not apply. A new math/rand call in a security path would fail the build.

    govulncheck completes the set from a different angle: rather than pattern-matching source, it checks the dependency graph against the Go vulnerability database and reports a known CVE only when the affected function is actually reachable from this project's call paths. That distinction is load-bearing -- one advisory currently affecting a transitive dependency was determined unreachable here by exactly that analysis, rather than by comparing version ranges.

    Supporting these, staticcheck performs deep interprocedural analysis, Hadolint checks the Dockerfiles for insecure container practices, and actionlint validates the workflows.

    OpenSSF Scorecard independently scores the project 10/10 on its SAST check.



    Toutes les vulnérabilités exploitables de gravité moyenne ou plus découvertes avec une analyse statique de code DOIVENT être corrigées en temps approprié après leur confirmation. [static_analysis_fixed]
    Une vulnérabilité est considérée comme étant de gravité moyenne ou supérieure si son score qualitatif de base du Système Commun d'Évaluation des Vulnérabilités (CVSS) est moyen ou supérieur. Dans les versions CVSS 2.0 à 3.1, cela équivaut à un score CVSS de 4.0 ou supérieur. Les projets peuvent utiliser le score CVSS publié dans une base de données de vulnérabilité largement utilisée (telle que la base de données nationale des vulnérabilités) en utilisant la version la plus récente de CVSS rapportée dans cette base de données. Les projets peuvent aussi calculer eux-mêmes la gravité à l'aide de la dernière version de CVSS au moment de la divulgation de la vulnérabilité, si les entrées de calcul sont révélées publiquement une fois que la vulnérabilité est connue du public. Remarquez que le critère vulnerabilities_fixed_60_days nécessite que de telles vulnérabilités soient corrigées dans les 60 jours de leur divulgation publique.

    There are currently zero open code-scanning alerts of any severity, verified live against the repository.

    Findings are not allowed to accumulate. Static analysis runs as required status checks on every pull request -- CodeQL, gosec, staticcheck and govulncheck among them -- so a finding blocks the change that introduced it rather than being filed for later triage. A vulnerability therefore cannot reach the default branch and start ageing.

    Where a finding is genuinely a false positive or a deliberate acceptance, it is dismissed with its reasoning recorded rather than silently closed. Of the historical dismissals, all 16 marked "won't fix" and 2 marked "used in tests" carry a written dismissal comment, as do 61 of 62 marked "false positive". The project's standing rule is that a dismissal must state the MECHANISM that makes the finding inapplicable, with file or configuration evidence, never a bare "false positive" -- and that the reasoning is recorded on both surfaces a future reader might consult: the alert itself, and the scanner's inline review thread on the pull request. A dismissal without that reasoning reads, six months later, as someone having waved the finding away.

    The single dismissal missing a comment is a documentation gap in the record rather than an unfixed vulnerability, and the standing rule exists to prevent exactly that.

    Response speed is evidenced elsewhere too: two golang.org/x/crypto advisories surfaced by govulncheck were resolved by upgrading the dependency within hours of being identified, ahead of any external scanner reporting them. One further advisory affecting a transitive dependency has no upstream fix published; call-path analysis confirmed the affected subpackage is never imported here, so it is unreachable rather than outstanding.

    No CVE has been assigned against the project, and no security advisory has been published: https://github.com/sydlexius/stillwater/security



    Il est PROPOSÉ que l'analyse statique du code source se produise à chaque commit ou au moins quotidiennement. [static_analysis_often]

    Static analysis runs on every commit, not merely daily.

    CodeQL is triggered on every push and every pull request, with an additional weekly scheduled scan so the default branch is re-analysed against updated query packs even when nothing has changed. It analyses two languages: go for the application and actions for the GitHub Actions workflows. https://github.com/sydlexius/stillwater/blob/main/.github/workflows/codeql.yml

    Running alongside it on every change, as required status checks:

    • golangci-lint with 23 linters enabled, including gosec for security-specific rules, staticcheck for deep interprocedural analysis, and gocritic with its style and performance bundles
    • govulncheck against the dependency graph, call-path aware rather than version-range based
    • Hadolint for the Dockerfiles, actionlint for workflow syntax, stylelint for CSS

    Analysis also runs before a commit exists: a pre-commit hook runs golangci-lint over the staged packages, so a finding is caught locally rather than after a push. The gate is real -- a cognitive-complexity violation blocked a commit during recent work and had to be resolved by extracting functions rather than suppressed.

    Because these are required checks on every pull request, and direct commits to the default branch are refused by branch protection, no commit reaches the trunk without having been analysed. The frequency is therefore structural rather than scheduled: there is no window in which unanalysed code can sit on the default branch.

    Recent cadence for scale: 648 commits in the last 90 days, 160 merges to the default branch in the last 30, each analyzed.


  • Analyse dynamique de code


    Il est PROPOSÉ qu'au moins un outil d'analyse dynamique soit appliqué à tout candidat pour une version majeure du logiciel avant sa distribution. [dynamic_analysis]
    Un outil d'analyse dynamique examine le logiciel en l'exécutant avec des entrées spécifiques. Par exemple, le projet PEUT utiliser un outil de fuzzing (par exemple, American Fuzzy Lop) ou un scanner d'application Web (par exemple, OWASP ZAP ou w3af). Dans certains cas, le projet OSS-Fuzz peut être prêt à appliquer des tests de fuzzing à votre projet. Aux fins de ce critère, l'outil d'analyse dynamique doit varier les entrées d'une manière ou d'une autre pour rechercher différents types de problèmes ou être une suite de test automatisée avec au moins 80% de couverture de branche. La page Wikipedia sur l'analyse dynamique et la page OWASP sur le fuzzing identifient certains outils d'analyse dynamique. Le ou les outils d'analyse PEUVENT être axés sur la recherche de vulnérabilités de sécurité, mais cela n'est pas nécessaire.

    Several dynamic analysis mechanisms run against the software, and each executes it with varying or generated inputs rather than inspecting source.

    FUZZING. 30 native Go fuzz targets across 11 packages, run in CI on a weekly schedule and on demand, each with a time budget. Go's fuzzing engine is coverage-guided: it mutates inputs and retains those that reach new code paths, and any crash or failing input is written to testdata as a permanent regression case, so a discovered defect becomes a standing test rather than a one-off. A crash fails the workflow. The matrix is enforced, so a new fuzz target cannot be added without being exercised. https://github.com/sydlexius/stillwater/blob/main/.github/workflows/fuzz.yml

    END-TO-END API EXERCISE. make bruno-ci builds the binary, starts an ephemeral server, and runs an executable Bruno collection against the live REST API -- a genuine running instance, not a mock.

    ACCESSIBILITY SCANNING. Playwright drives real Chromium and Firefox browsers against rendered pages, with axe-core evaluating the live DOM. This is a browser-driven scan of the running application rather than static template inspection.

    RACE DETECTION. The whole test suite runs under Go's race detector in CI, which is a runtime instrumentation: it observes actual memory access ordering during execution and reports data races that no static pass can see.

    PROVIDER-FAILURE SMOKE TESTS. A dedicated CI tier exercises the application's behaviour when third-party metadata providers fail, so error paths are executed rather than assumed.

    MUTATION TESTING. CI mutates the source and re-runs the suite, checking the tests actually detect changed behaviour. This is a check on the dynamic analysis itself.

    Because these run in CI on every change or on a weekly schedule against the default branch, any tagged release has been dynamically analysed before it is cut, rather than analysis being a step someone must remember at release time.



    Il est PROPOSÉ que, si le logiciel produit par le projet comprend un logiciel écrit à l'aide d'un langage non sûr pour les accès mémoire (par exemple, C ou C ++), au moins un outil dynamique (par exemple, un fuzzer ou un scanner d'application Web) soit utilisé de façon routinière en combinaison avec un mécanisme pour détecter des problèmes de sécurité mémoire tels que les dépassements de zone mémoire. Si le projet ne produit pas de logiciel écrit dans un langage non sûr pour les accès mémoire, choisissez « non applicable » (N/A). [dynamic_analysis_unsafe]
    Des exemples de mécanismes pour détecter les problèmes de sécurité de la mémoire comprennent Address Sanitizer (ASAN) (disponible dans GCC et LLVM), Memory Sanitizer et valgrind. D'autres outils potentiellement utilisés incluent thread sanitizer et undefined behavior sanitizer. La généralisation de l'utilisation des assertions fonctionnera également.

    The project produces no software written in a memory-unsafe language.

    Stillwater is written entirely in Go, which is memory-safe: it is garbage-collected, bounds-checks slice and array access, and does not permit pointer arithmetic. Buffer overwrites and use-after-free of the kind this criterion targets are not expressible.

    There is no C or C++ in the project, and no cgo. Release binaries are built with CGO_ENABLED=0, so the shipped artifacts are pure Go with no linked C libraries at all -- verified in both the release workflow and the goreleaser configuration. Even the SQLite driver is modernc.org/sqlite, a pure-Go transliteration rather than a cgo binding to the C library, which is a deliberate choice that keeps the memory-unsafe surface at zero rather than merely small.

    The browser-facing code is HTML, CSS, and a small amount of vendored JavaScript, none of it memory-unsafe either.

    For completeness, runtime instrumentation is used regardless: the test suite runs under Go's race detector in CI, which is a dynamic memory-access analysis in the same family as ThreadSanitizer -- it observes actual access ordering during execution rather than reasoning about the source. That runs alongside 30 fuzz targets across 11 packages, an executable API collection against a live server, and browser-driven accessibility scanning.



    Il est PROPOSÉ que le projet utilise une configuration pour au moins une analyse dynamique (comme le test ou le fuzzing) qui active de nombreuses assertions. Dans de nombreux cas, ces assertions ne doivent pas être activées dans les versions de production. [dynamic_analysis_enable_assertions]
    Ce critère ne suggère pas d'activer les assertions en production ; c'est entièrement au projet et à ses utilisateurs de le décider. L'objectif de ce critère est plutôt d'améliorer la détection des défauts lors de l'analyse dynamique avant le déploiement. L'activation des assertions en production est complètement différente de l'activation des assertions pendant l'analyse dynamique (comme les tests). Dans certains cas, il est extrêmement imprudent d'activer les assertions en production (en particulier dans les composants à haute intégrité). Il existe de nombreux arguments contre l'activation des assertions en production, par exemple, les bibliothèques ne devraient pas faire échouer les appelants, leur présence peut provoquer le rejet par les magasins d'applications et/ou l'activation d'une assertion en production peut exposer des données privées telles que des clés privées. Attention, dans de nombreuses distributions Linux, NDEBUG n'est pas défini, donc assert() sera activé par défaut en C/C++ pour la production dans ces environnements. Il peut être important d'utiliser un mécanisme d'assertion différent ou de définir NDEBUG pour la production dans ces environnements.

    Go has no assert() construct and no NDEBUG-style compile-time toggle, so the C/C++ shape of this criterion does not translate directly. The equivalent -- extra checking enabled during dynamic analysis but not compiled into production builds -- is used extensively.

    RACE DETECTOR. The test suite runs under go test -race in CI, which is exactly this pattern: CGO_ENABLED=1 go test -race instruments every memory access to check ordering at runtime, while release binaries are built with CGO_ENABLED=0 and carry none of that instrumentation. It is a large body of runtime checking that exists only during analysis.

    FUZZING WITH PERSISTED FAILURES. 30 fuzz targets across 11 packages run coverage-guided, and any input that trips a check is written to testdata as a permanent regression case. A crash fails the workflow rather than being reported and forgotten.

    ASSERTIONS AS TEST DISCIPLINE. The project's testing standard requires assertions that most suites omit, and these are the substance of the criterion rather than a syntax question:

    • A test must assert its own PRECONDITIONS, not only its outcome, so a fixture that silently stops exercising the intended path fails instead of passing vacuously. Where a test seeds a directory, it reads the directory back and asserts the count before exercising the code under test.
    • A test guarding a bug fix must be shown to FAIL without the fix -- revert it, confirm red, restore, confirm green -- so a test that asserts nothing meaningful is caught before it lands.
    • Structural assertions are used where a behavioural one cannot see the property: one test parses the source of a function and asserts a statement ordering invariant that no runtime check could observe.

    MUTATION TESTING. CI mutates the source and re-runs the suite, verifying the assertions actually detect changed behaviour rather than merely executing lines.

    None of this instrumentation ships. Production builds are CGO_ENABLED=0, with no race detector, no fuzzing harness, and no test-only checking compiled in.



    Toutes les vulnérabilités exploitables de gravité moyenne ou plus découvertes avec une analyse de code dynamique DOIVENT être corrigées en un temps approprié après leur confirmation. [dynamic_analysis_fixed]
    Si vous n'utilisez pas d'analyse de code dynamique et n'avez donc trouvé aucune vulnérabilité de cette manière, choisissez « non applicable » (N/A). Une vulnérabilité est considérée comme étant de gravité moyenne ou supérieure si son score qualitatif de base du Système Commun d'Évaluation des Vulnérabilités (CVSS) est moyen ou supérieur. Dans les versions CVSS 2.0 à 3.1, cela équivaut à un score CVSS de 4.0 ou supérieur. Les projets peuvent utiliser le score CVSS publié dans une base de données de vulnérabilité largement utilisée (telle que la base de données nationale des vulnérabilités) en utilisant la version la plus récente de CVSS rapportée dans cette base de données. Les projets peuvent aussi calculer eux-mêmes la gravité à l'aide de la dernière version de CVSS au moment de la divulgation de la vulnérabilité, si les entrées de calcul sont révélées publiquement une fois que la vulnérabilité est connue du public.

    Dynamic analysis is run routinely, and it has not discovered a security vulnerability of medium or higher severity. There is consequently nothing outstanding.

    The mechanisms running are 30 native Go fuzz targets across 11 packages on a weekly schedule, the full test suite under Go's race detector, an executable API collection exercised against a live server, browser-driven accessibility scanning with axe-core, and a provider-failure smoke tier. No fuzz crash, data race, or comparable finding is currently open; a crash fails the fuzzing workflow rather than being reported and forgotten, and any input that trips a check is written to testdata as a permanent regression case.

    What dynamic analysis has found here is correctness defects rather than exploitable vulnerabilities, and those are fixed at the point of discovery rather than deferred. A recent example: a concurrency defect where a cleanup sweep could delete another in-flight operation's staging file, converting a recoverable failure into permanent data loss. It was found during review, fixed in the same session, and the fix was verified by a two-goroutine test that reproduces the loss when the fix is removed -- then re-reviewed rather than trusted. That is a data-integrity bug, not a security vulnerability, but it illustrates the response pattern.

    Verified live against the repository: zero open code-scanning alerts, zero open Dependabot alerts, zero published security advisories, and no CVE assigned. https://github.com/sydlexius/stillwater/security

    If a dynamic analysis run does surface a vulnerability, SECURITY.md commits to an initial acknowledgement within 72 hours and a fix released as soon as practical, typically within 14 days for critical issues. Releases are frequent -- four stable releases in the past month -- so a fix reaches users promptly rather than waiting on a distant release train.



Vous pouvez utiliser des outils et des systèmes d'IA pour proposer des modifications via une URL simple, par exemple https://www.bestpractices.dev/fr/projects/14430/choose/edit?osps_ac_01_01_status=Met&osps_ac_01_01_justification=GitHub+enforced. Consultez notre système de propositions d'automatisation pour savoir comment procéder. Ces données sont disponibles sous la licence Community Data License Agreement – Permissive, Version 2.0 (CDLA-Permissive-2.0). Cela signifie qu'un destinataire de données peut partager les données, avec ou sans modifications, à condition que le destinataire de données rende disponible le texte de cet accord avec les données partagées. Veuillez créditer Jesse Slaton et les contributeurs du badge des meilleures pratiques de la OpenSSF.

Soumission du badge du projet appartenant à : Jesse Slaton.
Soumission créée le 2026-09-03 00:15:58 UTC, dernière mise à jour le 2026-09-03 01:56:47 UTC.