stillwater

Проекты, которые следуют приведенным ниже лучшим практикам, могут добровольно и самостоятельно оценить себя и продемонстрировать, что они получили значок Open Source Security Foundation (OpenSSF).

Не существует набора практик, гарантирующего, что у программного обеспечения никогда не будет недостатков или уязвимостей; даже формальные методы могут не помочь, если спецификации или допущения ошибочны. Также не существует какой-либо практики, которая могла бы гарантировать, что проект будет поддерживать здоровое и хорошо функционирующее сообщество разработчиков. Однако следующие хорошие правила могут помочь улучшить результаты проектов. Например, некоторые правила описывают ревью несколькими участниками перед выпуском, что может помочь найти технические уязвимости, которые было бы сложно найти другим способом, и помочь построить доверие и желание дальнейшего взаимодействия между разработчиками из разных компаний. Чтобы получить значок, нужно выполнить все критерии с ключевыми словами "НЕОБХОДИМО"/"ОБЯЗАН"/"НЕДОПУСТИМО", все критерии со словом "СЛЕДУЕТ" либо должны удовлетворяться, либо должно быть приведено обоснование их невыполнения, и все критерии со словом "ЖЕЛАТЕЛЬНО" могут быть удовлетворены ИЛИ неудовлетворены (желательно, чтобы они были хотя бы рассмотрены). Если вы хотите ввести общий комментарий вместо объяснения, почему текущая ситуация приемлема, начните текст с '//' и пробела. Приветствуется обратная связь через сайт на GitHub в виде issues или pull requests. Существует также список рассылки для общих вопросов.

Мы с удовольствием предоставляем информацию на нескольких языках, однако, если есть какой-либо конфликт или несоответствие между переводами, английская версия является авторитетной.
Если это ваш проект, пожалуйста, покажите свой значок на странице проекта! Статус значка выглядит следующим образом: Уровень значка для проекта 14430 - in_progress Вот как вставить его:
Вы можете показать свой статус значка, вставив его в файл с разметкой Markdown:
[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/14430/badge)](https://www.bestpractices.dev/projects/14430)
- или HTML:
<a href="https://www.bestpractices.dev/projects/14430"><img src="https://www.bestpractices.dev/projects/14430/badge"></a>


Это критерии уровня Passing. Вы также можете просмотреть критерии уровня Silver или Gold.

Baseline Series: Базовый уровень 1 Базовый Уровень 2 Базовый Уровень 3

        

 Основы 13/13 ●

  • Общая

    Обратите внимание, что другие проекты могут использовать то же имя.

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

    Используйте формат выражения лицензии SPDX; примеры включают «Apache-2.0», «BSD-2-Clause», «BSD-3-Clause», «GPL-2.0+», «LGPL-3.0+», «MIT» и «(BSD-2-Clause OR Ruby)».
    Если используется более одного языка, перечислите их через запятую (пробелы необязательны), и отсортируйте их от наиболее до наименее используемого. Если список длинный, пожалуйста, перечислите по крайней мере три наиболее распространенных. Если языка нет (например, это проект только для документации или только для тестирования), используйте один символ «-» (минус). Для каждого языка используйте общепринятую капитализацию названия, например «JavaScript».
    Common Platform Enumeration (CPE) - это структурированная схема именования для информационных систем, программного обеспечения и пакетов. Она используется в ряде систем и баз данных для отчетов об уязвимостях.
  • Основная информация на веб-сайте проекта


    Веб-сайт проекта ОБЯЗАН кратко описывать, что делает программное обеспечение (какую проблему решает?). [description_good]
    Описание ОБЯЗАНО быть на языке, который могут понять потенциальные пользователи (например, с минимумом жаргона).

    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.



    Веб-сайт проекта ОБЯЗАН предоставлять информацию о том, как: получать и предоставлять обратную связь (например, отчеты об ошибках или улучшения) и вносить свой вклад в программное обеспечение. [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.



    В описании того, как сделать вклад, НЕОБХОДИМО объяснить процесс внесения вклада (например, используются ли pull request'ы). (Требуется URL) [contribution]
    Мы предполагаем, что проекты на GitHub используют issues и pull requests, если не указано иное. Описание может быть кратким, например, указывать, что проект использует pull requests, issue tracker или сообщения в список рассылки (какой?).

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



    В информацию о том, как внести вклад, СЛЕДУЕТ включать требования к приемлемым взносам (например, ссылку на любой требуемый стандарт кодирования). (Требуется URL) [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.


  • Свободная лицензия


    ПО, создаваемое проектом, ОБЯЗАНО быть выпущено под свободной лицензией. [floss_license]
    Свободное ПО (далее СПО) - это программное обеспечение, которое соответствует Определению Открытого ПО (официальный текст на англ.) или Определению Свободного Программного Обеспечения. Примеры таких лицензий включают CC0, MIT, BSD 2-Clause, BSD 3-Clause, Apache 2.0, Меньшая стандартная общественная лицензия GNU (LGPL) и Стандартная общественная лицензия GNU (GPL). Для наших целей это означает, что лицензия ОБЯЗАНА быть: ПО МОЖЕТ одновременно лицензироваться на других условиях (например, приемлема комбинация «GPLv2 или закрытая лицензия»).

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



    ЖЕЛАТЕЛЬНО, чтобы все лицензии для ПО, создаваемого проектом, были одобрены Open Source Initiative (OSI). [floss_license_osi]
    Для одобрения OSI используется строгий процесс, чтобы определить, какие лицензии соответствуют Открытому ПО.

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



    Проект ОБЯЗАН публиковать лицензию или лицензии своих результатов в стандартном расположении в своем репозитории исходного кода. (Требуется URL) [license_location]
    Например, в качестве файла верхнего уровня с именем LICENSE или COPYING. Имена файлов лицензии МОГУТ сопровождаться расширением, таким как «.txt» или «.md». Другим соглашением может быть наличие каталога с именем LICENSES, содержащего файлы лицензий; имена этих файлов обычно соответствуют SPDX-идентификатору лицензии, за которым следует соответствующее расширение файла, как описано в спецификации REUSE . Обратите внимание, что этот критерий является обязательным только для репозитория с исходным кодом. Вам НЕ нужно включать файл лицензии при генерации чего-либо из исходного кода (например, исполняемого файла, пакета или контейнера). Например, при создании пакета R для Comprehensive R Archive Network (CRAN) рекомендуется следовать стандартной практике CRAN: если лицензия является стандартной, используйте стандартную короткую спецификацию лицензии (чтобы избежать установки еще одной копии текста) и добавьте файл LICENSE в списке исключений, например .Rbuildignore. Аналогично, при создании пакета Debian вы можете поместить в файл copyright ссылку на текст лицензии в /usr/share/common-licenses и исключить файл лицензии из созданного пакета (например, удаляя файл после вызова dh_auto_install). Мы рекомендуем включать машиночитаемую информацию о лицензии в сгенерированных форматах, где это возможно.

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


  • Документация


    Проект ОБЯЗАН предоставлять базовую документацию для программного обеспечения, создаваемого проектом. [documentation_basics]
    Эта документация должна быть в некоторых формах (таких как текст или видео), которые включают в себя: как установить программное обеспечение, как его запустить, как его использовать (возможно, с помощью учебника с примерами) и как использовать его безопасно (например, что делать и чего не делать), если эти темы применимы для данного программного обеспечения. Документация по безопасности не обязательно должна быть длинной. Проект МОЖЕТ использовать гипертекстовые ссылки для не-проектных материалов в качестве документации. Если проект не создает программное обеспечение, выберите «неприменимо» (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.



    Проект ОБЯЗАН предоставлять справочную документацию, описывающую внешний интерфейс (как входной, так и выходной) программного обеспечения, создаваемого проектом. [documentation_interface]
    Документация внешнего интерфейса объясняет конечному пользователю или разработчику, как его использовать. Это может включать в себя интерфейс прикладного программирования (API), если программное обеспечение его имеет. Если это библиотека, документируйте основные классы/типы и методы/функции, которые можно вызвать. Если это веб-приложение, определите его URL-интерфейс (часто его интерфейс REST). Если это интерфейс командной строки, документируйте параметры и настройки, которые он поддерживает. Во многих случаях лучше всего, если большая часть этой документации будет автоматически сгенерирована, чтобы эта документация была синхронизирована с программным обеспечением по мере его изменения, но это не требуется. Проект МОЖЕТ использовать гипертекстовые ссылки для не-проектных материалов в качестве документации. Документация МОЖЕТ быть автоматически сгенерирована (там, где это применимо, это часто наилучший способ создания документации). Документация интерфейса REST может быть сгенерирована с использованием Swagger/OpenAPI. Документация по интерфейсу кода МОЖЕТ быть сгенерирована с использованием таких инструментов, как JSDoc (JavaScript), ESDoc (JavaScript), pydoc (Python), devtools (R), pkgdown (R) и Doxygen (многие языки). Просто иметь комментарии в коде реализации недостаточно для выполнения этого критерия; должен быть простой способ увидеть информацию без чтения всего исходного кода. Если проект не создает программное обеспечение, выберите «неприменимо» (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.


  • Другое


    Сайты проекта (веб-сайт, репозиторий и URL-адреса для загрузки) ОБЯЗАНЫ поддерживать HTTPS с использованием TLS. [sites_https]
    Для выполнения этого критерия требуется, чтобы URL домашней страницы проекта начинался с "https:", а не "http:". Вы можете получить бесплатные сертификаты от проекта Let's Encrypt. Проекты МОГУТ выполнить этот критерий, используя (например) GitHub Pages, GitLab Pages или проектные страницы SourceForge. Если вы поддерживаете HTTP, мы настоятельно рекомендуем перенаправить HTTP-трафик на 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.



    Проект ОБЯЗАН иметь один или несколько механизмов для обсуждения (включая предлагаемые изменения и проблемы), которые доступны для поиска, позволяют ссылаться на сообщения и темы по URL, позволяют новым людям участвовать в некоторых обсуждениях и не требуют установки на стороне клиента проприетарного программного обеспечения. [discussion]
    Примерами приемлемых механизмов являются архивируемые списки рассылки, обсуждения в GitHub issues и pull requests, Bugzilla, Mantis и Trac. Асинхронные механизмы обсуждения (например, IRC) приемлемы, если они отвечают этим критериям; убедитесь, что есть механизм архивирования URL-адресов. Разрешено, хотя и не рекомендуется, использовать проприетарный JavaScript.

    GitHub supports discussions on issues and pull requests.



    Проекту СЛЕДУЕТ предоставлять документацию на английском языке и иметь возможность принимать отчеты об ошибках и комментарии о коде на английском языке. [english]
    Английский в настоящее время является лингва франка компьютерной техники; Поддержка английского языка увеличивает число потенциальных разработчиков и рецензентов во всем мире. Проект может соответствовать этому критерию, даже если английский не является основным языком его ключевых разработчиков.

    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.



    НЕОБХОДИМО, чтобы проект поддерживался. [maintained]
    Как минимум, проект должен пытаться реагировать на сообщения о серьезных проблемах и уязвимостях. Проект, который активно добивается получения значка, вероятно, и поддерживается тоже. Ресурсы любого проекта и человека ограничены, и обычно проекты будут отклонять некоторые предлагаемые изменения; поэтому ограниченность ресурсов и отклонение предложений сами по себе не указывают на то, что проект не поддерживается.

    Если известно, что проект больше не будет поддерживаться, следует установить для этого критерия значение «Не соответствует» и использовать подходящие механизмы, чтобы указать другим, что он не поддерживается. Например, используйте “DEPRECATED” («УСТАРЕЛ») в качестве первого заголовка в файле README, добавьте “DEPRECATED” в начале его домашней страницы, добавьте “DEPRECATED” в начало описания проекта репозитория кода, добавьте значок об отсутствии поддержки в README проекта и/или домашнюю страницу, пометьте его как устаревший в любых репозиториях пакетов (напр., npm deprecate ) и/или используйте механизм, предоставленный репозиторием кода для его архивирования (например, параметр "archive" у GitHub или пометка "archive" у GitLab, статус «только для чтения» у Gerrit или статус «брошенного» проекта у SourceForge). Дополнительное обсуждение можно найти здесь .

    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


 Управление изменениями 9/9 ●

  • Публичное хранилище исходного кода с поддержкой версий


    Проект ОБЯЗАН иметь репозиторий (хранилище) исходного кода с управлением версиями, который является общедоступным и имеет URL. [repo_public]
    URL МОЖЕТ быть таким же, как URL проекта. Проект МОЖЕТ использовать частные (непубличные) ветви в конкретных случаях, когда изменение не выпускается публично (например, для устранения уязвимости до того, как она будет открыта для публики).

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



    Проектный репозиторий исходного кода ОБЯЗАН отслеживать, какие изменения были внесены, кто внес изменения и когда изменения были сделаны. [repo_track]

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



    Чтобы обеспечить возможность для проверки другими участниками, проектный репозиторий исходного кода ОБЯЗАН включать промежуточные версии для проверки между релизами; НЕДОПУСТИМО хранить в репозитории лишь финальные версии. [repo_interim]
    Проекты МОГУТ опускать отдельные промежуточные версии из своих публичных репозиториев (например, те, которые фиксируют отдельные не обнародованные уязвимости, никогда не будут публично выпущены или включают материалы, которые не могут быть опубликованы на законных основаниях и не находятся в финальной версии).

    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



    Для хранилища проектного исходного кода ЖЕЛАТЕЛЬНО использовать типовое ПО для распределенного управления версиями (например, git). [repo_distributed]
    Не требуется именно git, и проекты могут использовать централизованное программное обеспечение для управления версиями (например, Subversion) с обоснованием.

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


  • Уникальная нумерация версий


    Результаты проекта ОБЯЗАНЫ иметь уникальный идентификатор версии для каждой версии, предназначенной для конечных пользователей. [version_unique]
    Это МОЖНО выполнить различными способами, включая идентификаторы коммита (например, идентификатор коммита git или идентификатор набора изменений mercurial) или номер версии (включая номера версий, которые используют семантическое версионирование или схемы на основе даты, такие как 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.



    Для выпусков ЖЕЛАТЕЛЬНО использовать семантическую либо календарную нумерацию версий. При использовании календарной нумерации к версии ЖЕЛАТЕЛЬНО добавлять микро-компоненту. [version_semver]
    МОЖНО использовать в качестве номеров версий другие схемы нумерации версий, такие как идентификаторы коммитов (например, идентификатор коммита в git или идентификатор набора изменений в mercurial) или схемы на основе даты, такие как YYYYMMDD, поскольку они уникальны. Некоторые альтернативы могут вызвать трудности, поскольку пользователи могут быть не в состоянии легко определить, используют ли они последнюю версию. SemVer может оказаться менее полезным для идентификации версий программного обеспечения, если все получатели используют только последнюю версию (например, это код для одного веб-сайта или интернет-сервиса, который постоянно обновляется с помощью непрерывной доставки).


    Проектам ЖЕЛАТЕЛЬНО идентифицировать каждый выпуск в своей системе управления версиями. Например, при использовании git ЖЕЛАТЕЛЬНО идентифицировать каждую версию, используя теги 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.


  • Примечания к выпуску


    Проект ОБЯЗАН предоставлять с каждой выпускаемой версией замечания к выпуску - удобочитаемые человеком сведения об основных изменениях в этом выпуске, помогающие пользователям определить, должны ли они обновляться и какими будут последствия обновления. НЕДОПУСТИМО делать замечания к выпуску сырым выводом журнала управления версиями (например, результаты команды «git log» не являются замечаниями к выпуску). Проекты, результаты которых не предназначены для повторного использования в нескольких местах (например, программное обеспечение для одного веб-сайта или службы) И выдаются через непрерывную доставку (continuous delivery) МОГУТ выбрать «неприменимо» (N/A). (Требуется URL) [release_notes]
    Замечания к выпуску МОГУТ быть реализованы различными способами. Многие проекты предоставляют их в файле с именем «NEWS», «CHANGELOG» или «ChangeLog», возможно с расширениями, такими как «.txt», «.md» или «.html». Исторически термин «журнал изменений» означал журнал для каждого изменения, но для соответствия этим критериям требуется человекочитаемая сводка. Замечания к выпуску МОГУТ вместо этого быть предоставлены механизмами системы контроля версий, такими как процесс GitHub Releases.

    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.



    В замечаниях о выпуске НЕОБХОДИМО упоминать каждую общеизвестную уязвимость, исправленную ​​в каждой новой версии, для которой существует CVE или аналогичная публичная запись. Критерий может быть отмечен как неприменимый (N/A), если у пользователей обычно нет практической возможности обновить данное ПО самостоятельно (это часто относится к, например, обновлениям ядра операционной системы). Если замечаний о выпуске не публиковалось или не было обнародованных уязвимостей, отвечайте "неприменимо". [release_notes_vulns]
    Этот критерий помогает пользователям определить, исправит ли данное обновление общеизвестную уязвимость, чтобы помочь пользователям принять обоснованное решение об обновлении. Если пользователи обычно не могут практически самостоятельно обновлять программное обеспечение на своих компьютерах, а вместо этого должны полагаться на одного или нескольких посредников для выполнения обновления (как это часто бывает с ядром и низкоуровневым программным обеспечением, которое взаимосвязано с ядром), проект вместо этого можно выбрать «Неприменимо», поскольку эта дополнительная информация будет бесполезна для таких пользователей. Аналогично, проект может выбрать «неприменимо» (N/A), если все получатели используют только последнюю версию (например, это код для одного веб-сайта или интернет-службы, который постоянно обновлен при помощи непрерывной доставки). Этот критерий применим только к результатам проекта, а не к его зависимостям. Перечисление уязвимостей всех транзитивных зависимостей проекта становится громоздким, поскольку зависимости увеличиваются и изменяются; и в этом нет необходимости, поскольку инструменты, которые исследуют и отслеживают зависимости, могут делать это более масштабируемым способом.

    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.


 Отчеты о проблемах 8/8 ●

  • Процесс сообщения об ошибках


    Проект ОБЯЗАН предоставить пользователям возможность отправлять сообщения об ошибках (например, используя систему отслеживания ошибок или список рассылки). (Требуется URL) [report_process]

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



    СЛЕДУЕТ использовать трекер вопросов (issue tracker) для отслеживания отдельных вопросов. [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



    Проект ОБЯЗАН подтверждать получение большинства сообщений об ошибках, отправленных за последние 2-12 месяцев (включительно); подтверждение не обязательно включает исправление. [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



    Проекту СЛЕДУЕТ реагировать на большинство (>50%) запросов на улучшения в течение последних 2-12 месяцев (включительно). [enhancement_responses]
    В качестве ответа МОЖЕТ быть «нет» или обсуждение выгод от данного улучшения. Цель состоит в том, чтобы по крайней мере на некоторые запросы был какой-то ответ, что указывает на то, что проект все еще жив. Для целей этого критерия не нужно учитывать поддельные запросы (например, от спамеров или автоматизированных систем). Если проект больше не принимает улучшения, выберите «не соответствует» и укажите URL, проясняющий ситуацию для пользователей. Если проект большую часть времени перегружен количеством запросов на улучшения, выберите «не cоответствует» и объясните.

    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



    Проект ОБЯЗАН иметь общедоступный архив для отчетов и ответов для последующего поиска. (Требуется URL) [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


  • Процесс отчета об уязвимостях


    Проект ОБЯЗАН публиковать процесс уведомления об уязвимостях на сайте проекта. (Требуется URL) [vulnerability_report_process]
    Например, четко обозначенный почтовый адрес на https://PROJECTSITE/security, часто в форме security@example.org. Процесс МОЖЕТ быть таким же, как и процесс для отчетов об ошибках. Отчеты об уязвимостях МОГУТ быть всегда общедоступными, но многие проекты имеют приватный механизм для отправки отчетов об уязвимостях.

    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.



    Если поддерживаются приватные отчеты об уязвимости, проект ОБЯЗАН включить описание того, как отправлять сведения конфиденциальным способом. (Требуется URL) [vulnerability_report_private]
    Примеры включают приватный отчет о дефектах, отправленный в Интернете с использованием HTTPS (TLS) или электронной почты, зашифрованной с использованием OpenPGP. Если отчеты об уязвимостях всегда являются общедоступными (поэтому нет приватных отчетов об уязвимостях), выберите «неприменимо» (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.



    Проект ОБЯЗАН обеспечивать время первоначального отклика на любой отчет об уязвимости, полученный за последние 6 месяцев, в пределах 14 дней или меньше. [vulnerability_report_response]
    Если за последние 6 месяцев не было обнаружено никаких уязвимостей, выберите «неприменимо» (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


 Качество 13/13 ●

  • Рабочая система сборки


    Если программное обеспечение, создаваемое проектом, требует сборки для использования, проект ОБЯЗАН предоставить рабочую систему сборки, которая может автоматически пересобирать программное обеспечение из исходного кода. [build]
    Система сборки определяет, какие действия необходимо предпринять для пересборки программного обеспечения (и в каком порядке), а затем выполняет эти действия. Например, она может вызывать компилятор для компиляции исходного кода. Если исполняемый файл создается из исходного кода, должна иметься возможность изменить исходный код проекта, а затем сгенерировать обновленный исполняемый файл с этими изменениями. Если программное обеспечение, создаваемое проектом, зависит от внешних библиотек, система сборки не обязана пересобирать эти внешние библиотеки. Если для использования программного обеспечения после изменения его исходного кода пересборка не требуется, выберите «неприменимо» (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



    ЖЕЛАТЕЛЬНО использовать общеупотребительные инструменты для сборки программного обеспечения. [build_common_tools]
    Например, Maven, Ant, cmake, autotools, make, rake или devtools (R).

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



    Для сборки проекта СЛЕДУЕТ использовать только инструменты со свободными лицензиями. [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


  • Набор автотестов


    Проект ОБЯЗАН использовать по крайней мере один автоматизированный набор тестов, опубликованный как свободное ПО (этот набор тестов может поддерживаться как отдельный проект свободного ПО). Проект ОБЯЗАН ясно показывать или иметь документацию о том, как запускать наборы тестов (например, через непрерывную интеграцию (CI) или используя файлы документации, такие как BUILD.md, README.md или CONTRIBUTING.md). [test]
    Проект МОЖЕТ использовать несколько автоматизированных наборов тестов (например, один, который работает быстро, а другой - более тщательный, но требует специального оборудования). Существует множество каркасов (frameworks) и систем поддержки тестирования, включая Selenium (автоматизация веб-браузера), 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.



    Запуск набора тестов СЛЕДУЕТ реализовывать стандартным способом для этого языка. [test_invocation]
    Например, «make check», «mvn test» или «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.



    ЖЕЛАТЕЛЬНО охватывать набором тестов большинство (а в идеале все) ветви кода, поля ввода и функциональные возможности. [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



    ЖЕЛАТЕЛЬНО реализовать непрерывную интеграцию (Continuous Integration - частая интеграция нового или измененного кода в центральное хранилище кода, и запуск автоматических тестов на получившейся базе кода). [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


  • Тестирование новых функций


    Проект ОБЯЗАН иметь общую политику (формальную или нет), обязывающую добавлять тесты в набор автоматических тестов по мере добавления новых функциональных возможностей к программному обеспечению, создаваемому проектом. [test_policy]
    Если есть действующая политика, хотя бы «из уст в уста», которая говорит, что разработчики должны добавлять тесты в набор автотестов для новой функциональности, указывайте «соответствует».

    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.



    Проект ОБЯЗАН иметь доказательства того, что критерий test_policy о добавлении тестов соблюдался при недавних крупных изменениях ПО, создаваемого проектом. [tests_are_added]
    Крупная функциональность обычно упоминается в замечаниях к выпуску. Совершенство не требуется, просто доказательство того, что на практике тесты обычно добавляются в набор автотестов, когда к ПО, создаваемому проектом, добавляются новые крупные функции.

    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.



    ЖЕЛАТЕЛЬНО задокументировать эту политику добавления тестов (см. критерий test_policy) в инструкции к предложениям об изменениях. [tests_documented_added]
    Однако даже неформальное правило приемлемо, если тесты добавляются на практике.

    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.


  • Флаги предупреждений


    Проект ОБЯЗАН включать один или несколько предупреждающих флагов компилятора, «безопасный» языковой режим или использовать отдельный инструмент «linter» для поиска ошибок качества кода или типовых простых ошибок, если есть хотя бы один инструмент на свободном ПО, который может реализовать этот критерий на выбранном языке. [warnings]
    Примером предупреждающего флага компилятора может служить "-Wall" для gcc/clang. Примеры «безопасного» языкового режима включают «use strict» в JavaScript и «use warnings» в perl5. Отдельный инструмент «linter» - это просто инструмент, который исследует исходный код для поиска ошибок качества кода или типовых простых ошибок. Всё это обычно включается в исходный код или инструкции сборки.

    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



    Проект ОБЯЗАН обращать внимание на предупреждения. [warnings_fixed]
    Речь о предупреждениях, найденных при выполнении критерия warnings. Проект должен исправлять предупреждения или отмечать их в исходном коде как ложные срабатывания. В идеале не должно быть никаких предупреждений, но проект МОЖЕТ принимать существование каких-то предупреждений (обычно менее 1 предупреждения на 100 строк или менее 10 предупреждений).

    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



    ЖЕЛАТЕЛЬНО, чтобы проекты использовали самый строгий режим предупреждений в производимом ПО, где это целесообразно. [warnings_strict]
    Некоторые предупреждения не могут быть эффективно задействованы в некоторых проектах. Что необходимо в этом критерии - это доказательства того, что проект стремится включать флаги предупреждений там, где это возможно, чтобы ошибки обнаруживались на ранней стадии.

    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


 Безопасность 14/16 ●

  • Знание безопасной разработки


    По крайней мере один основной разработчик на проекте ОБЯЗАН знать, как проектировать безопасное программное обеспечение (точные требования описаны в подробностях к критерию). [know_secure_design]
    Это требует понимания следующих принципов проектирования, в том числе 8 принципов из Saltzer and Schroeder:
    • экономичность механизма (поддерживать дизайн ПО настолько простым и компактным, насколько практически возможно, например, с помощью массовых упрощений)
    • отказобезопасные значения по умолчанию (доступ по умолчанию должен быть запрещен, а установка проектов по умолчанию должна быть в защищенной конфигурации)
    • полное разграничение (любой доступ, который может быть ограничен, должен проверяться на достаточность прав доступа и не иметь обходных путей)
    • открытый дизайн (механизмы безопасности должны полагаться не на незнание их злоумышленником, а на данные типа ключей и паролей, которые проще защищать и менять)
    • разделение привилегий (в идеале доступ к важным объектам должен зависеть от более чем одного условия, так чтобы взлом одной системы защиты не приводил к полному доступу; напр., многофакторная аутентификация с требованием и пароля, и аппаратного токена сильнее однофакторной)
    • минимальные привилегии (процессы должны работать с минимальными привилегиями, необходимыми для выполнения ими своих функций)
    • наименьший общий механизм (дизайн должен минимизировать механизмы, общие для нескольких пользователей и следовательно зависящие от всех этих пользователей, например, каталоги для временных файлов)
    • психологическая приемлемость (интерфейс для человека должен быть спроектирован с учетом удобства использования - может быть полезным проектирование для «наименьшего удивления»)
    • ограничение периметра атаки (периметр атаки - множество разных точек, в которых злоумышленник может попытаться ввести или извлечь данные - должен быть ограничен)
    • проверка входных данных с помощью списков на допуск (входы обычно должны проверяться на корректность до их принятия; эта проверка должна использовать списки на допуск, содержащие только заведомо хорошие значения, а не списки на запрет, пытающиеся перечислить заведомо плохие значения).
    «Основной разработчик» в проекте - это любой, кто знаком с базой кода проекта, без затруднений может вносить в него изменения и признан таковым большинством других участников проекта. Основной разработчик, как правило, неоднократно вносит вклад в течение последнего года (через код, документацию или ответы на вопросы). Разработчики обычно считаются основными разработчиками, если это они начали проект (и не покинули проект более трех лет назад), имеют возможность получать информацию по закрытому каналу для отчетов об уязвимостях (если он есть), могут принимать коммиты от имени проекта или делать финальные выпуски программного обеспечения проекта. Если есть только один разработчик, этот человек является основным разработчиком. Есть много книг и курсов, помогающих понять, как разрабатывать более безопасное ПО, с обсуждением вопросов проектирования. Например, Secure Software Development Fundamentals - это бесплатный набор из трех курсов, объясняющих, как разрабатывать более безопасное ПО (бесплатный для обучения; за отдельную плату вы можете получить сертификат для подтверждения, что вы освоили материал).

    ...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.



    По крайней мере, один из основных разработчиков проекта ОБЯЗАН знать об общих видах ошибок, которые приводят к уязвимостям в этом виде программного обеспечения, а также по крайней мере одному методу противодействия или смягчения каждого из них. [know_common_errors]
    Примеры (в зависимости от типа ПО) включают внедрение SQL-кода (injection), внедрение на уровне ОС, классическое переполнение буфера, межсайтовый скриптинг, отсутствие проверки подлинности и отсутствие авторизации. Обычно используемые списки уязвимостей можно найти в CWE/SANS top 25 или OWASP Top 10. Есть много книг и обучающих курсов, помогающих понять, как разрабатывается безопасное программное обеспечение, и обсуждающих типичные ошибки в реализации, ведущие к уязвимостям. К примеру, Secure Software Development Fundamentals - это набор из трех курсов, объясняющих, как сделать разрабатываемое ПО более безопасным (бесплатный для прослушивания; за дополнительную плату вы можете получить справку о том, что прошли его).

    ...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.


  • Основы правильного использования криптографии

    Обратите внимание, что некоторое ПО не нуждается в использовании криптографических механизмов.

    Программное обеспечение, созданное проектом, ОБЯЗАНО использовать по умолчанию только публикуемые криптографические протоколы и алгоритмы, которые анализируются экспертами (если используются криптографические протоколы и алгоритмы). [crypto_published]
    Эти криптографические критерии не всегда применяются, поскольку некоторые программы не нуждаются в прямом использовании криптографических возможностей.

    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.



    Если программное обеспечение, создаваемое проектом, является приложением или библиотекой, и его основной целью является не внедрение криптографии, тогда для реализации криптографических функций СЛЕДУЕТ обращаться к программному обеспечению, специально предназначенному для этого; НЕ СЛЕДУЕТ повторно реализовывать свои собственные функции. [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.



    Вся функциональность программного обеспечения, создаваемого проектом, которая зависит от криптографии, ОБЯЗАНА быть реализована с использованием свободного ПО. [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



    Механизмы безопасности в программном обеспечении, создаваемом проектом, ОБЯЗАНЫ использовать стандартные длины криптографических ключей, которые, по крайней мере, соответствуют минимальным требованиям NIST до 2030 года (как указано в 2012 году). Проект ОБЯЗАН предоставлять возможность настройки ПО таким образом, чтобы уменьшенные длины ключей были полностью отключены. [crypto_keylength]
    Эти минимальные длины в битах перечислены далее: симметричный ключ - 112, модуль факторизации - 2048, дискретный логарифмический ключ - 224, дискретная логарифмическая группа - 2048, эллиптическая кривая - 224 и хеш - 224 (хеширование пароля не покрывается этой длиной, больше информации о хешировании пароля можно найти в описании критерия crypto_password_storage). См. http://www.keylength.com для сравнения рекомендаций по длинам криптографических ключей от различных организаций. Программное обеспечение МОЖЕТ допускать меньшие длины ключей в некоторых конфигурациях (в идеале не должно, поскольку это позволяет атаки через понижение длины ключа, но иногда требуется более короткая длина ключа для обеспечения взаимодействия с другими системами).

    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.



    Механизмы безопасности по умолчанию в программном обеспечении, создаваемом проектом, НЕДОПУСТИМО делать зависимыми от взломанных криптографических алгоритмов (например, MD4, MD5, single DES, RC4, Dual_EC_DRBG) или использовать режимы шифрования, которые не подходят для контекста, если только они не требуются для интероперабельности протокола (поддерживающего самую новую версию стандарта на этот протокол, широко распространенного в сетевой экосистеме, причем эта экосистема требует использования данного алгоритма или режима, не предлагая более безопасных альтернатив). В документации НЕОБХОДИМО описать все связанные с этим риски безопасности и все известные способы смягчения рисков, если данные алгоритмы или режимы действительно нужны для совместимости с другими реализациями этого протокола. [crypto_working]
    Режим ECB почти никогда не подходит, потому что внутри зашифрованного ECB текста обнаруживаются идентичные блоки, как можно видеть на примере «пингвина ECB», а режим CTR часто неприемлем, поскольку не выполняет аутентификацию и приводит к дубликатам, контекста, если состояние ввода повторяется. Во многих случаях лучше всего выбирать режим алгоритма с блочным шифром, предназначенный для сочетания секретности и аутентификации, например, Galois / Counter Mode (GCM) и EAX. Проекты МОГУТ разрешать пользователям включать сломанные механизмы, где это необходимо для совместимости, но в таких случаях пользователи знают, что они это делают.

    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.



    Механизмы безопасности по умолчанию в программном обеспечении, создаваемом проектом, НЕ СЛЕДУЕТ делать зависимыми от криптографических алгоритмов или режимов с известными серьезными слабостями (например, криптографический алгоритм хеширования SHA-1 или режим CBC в SSH). [crypto_weaknesses]
    Проблемы, связанные с режимом CBC в SSH, обсуждаются в описании уязвимости CERT: 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.



    В механизмах безопасности в программном обеспечении, создаваемом проектом, СЛЕДУЕТ реализовать совершенную прямую секретность для протоколов соглашений о ключах, чтобы ключ сеанса, произведенный из набора долгосрочных ключей, не мог быть скомпрометирован, если один из долгосрочных ключей скомпрометирован в будущем. [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.



    Если ПО, создаваемое проектом, требует хранить пароли для аутентификации внешних пользователей, НЕОБХОДИМО хранить пароли как итерированные хеши с солью для каждого пользователя с использованием алгоритма (итерированного) растяжения ключа (например, PBKDF2, Bcrypt или Scrypt). См. также: OWASP Password Storage Cheat Sheet (на англ.). [crypto_password_storage]
    Этот критерий применяется только тогда, когда программное обеспечение требует проверки внешних пользователей с использованием паролей (так называемая входящая аутентификация), таких как серверные веб-приложения. Он не применяется в тех случаях, когда программное обеспечение хранит пароли для аутентификации в других системах (исходящая аутентификация; например, программное обеспечение реализует клиент для какой-либо другой системы), поскольку по крайней мере части этого программного обеспечения должны часто обращаться к нехешированному паролю.

    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.



    Механизмы безопасности в программном обеспечении, создаваемом проектом, ОБЯЗАНЫ генерировать все криптографические ключи и временные значения с использованием криптографически безопасного генератора случайных чисел; НЕДОПУСТИМО делать это с использованием генераторов, которые криптографически небезопасны. [crypto_random]
    Криптографически безопасный генератор случайных чисел может быть аппаратным генератором случайных чисел или криптографически безопасным генератором псевдослучайных чисел (CSPRNG), использующим такие алгоритмы как Hash_DRBG, HMAC_DRBG, CTR_DRBG, Yarrow или Fortuna. Примеры вызовов защищенных генераторов случайных чисел включают java.security.SecureRandom в Java и window.crypto.getRandomValues в JavaScript. Примеры вызовов небезопасных генераторов случайных чисел включают java.util.Random в Java и Math.random в 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.


  • Доставка, защищенная от атак посредника (MITM)


    Проект ОБЯЗАН использовать механизм доставки, устойчивый против атак посредника (MITM). Приемлемо использование https или ssh + scp. [delivery_mitm]
    Еще более сильным механизмом является выпуск программного обеспечения в виде пакетов, подписанных цифровой подписью, поскольку это смягчает атаки на систему распространения, но это работает только в том случае, если пользователи могут быть уверены, что открытые ключи для подписей верны и если пользователи действительно проверяют подпись.

    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



    НЕДОПУСТИМО получать криптографические контрольные суммы (например, sha1sum) по HTTP и использовать их без проверки криптографической подписи. [delivery_unsigned]
    Эти хеши могут быть изменены при передаче.

    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.


  • Исправление обнародованных уязвимостей


    НЕДОПУСТИМО оставлять незакрытыми уязвимости со степенью серьезности средней или выше, опубликованные более 60 дней назад. [vulnerabilities_fixed_60_days]
    Уязвимость должна быть исправлена ​​и выпущена самим проектом (патчи могут быть разработаны в другом месте). Уязвимость считается опубликованной (для цели данного критерия) после того, как она имеет CVE с описанием, бесплатно доступным для общественности, (например, в National Vulnerability Database) или когда проект был проинформирован, и информация была опубликована для общественности (возможно, самим проектом). Уязвимость имеет среднюю и высокую степень серьезности, если ее базовая оценка по CVSS 2.0 равна 4 или выше. Примечание. Это означает, что пользователи могут оставаться уязвимыми для всех злоумышленников по всему миру на срок до 60 дней. Этот критерий часто намного легче выполнить, чем рекомендует Google в Rebooting responsible disclosure, поскольку Google рекомендует, чтобы 60-дневный период начинался, когда проект был уведомлен, даже если отчет не является общедоступным.

    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



    Проектам СЛЕДУЕТ оперативно исправлять критические уязвимости после сообщения о них. [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.


  • Другие вопросы безопасности


    НЕДОПУСТИМА утечка действующих частных учетных данных (например, рабочий пароль или закрытый ключ), предназначенных для ограничения общего доступа, из публичных репозиториев. [no_leaked_credentials]
    Проект МОЖЕТ пропускать «шаблонные» учетные данные для тестирования и несущественные базы данных, при условии что они не предназначены для ограничения общего доступа.

    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.


 Анализ 8/8 ●

  • Статический анализ кода


    НЕОБХОДИМО применять по крайней мере, один инструмент анализа статического кода (помимо предупреждений компилятора и "безопасных" режимов языка) к любой предлагаемой основной версии создаваемого ПО до ее выпуска, если есть хотя бы один инструмент на свободном ПО, который реализует этот критерий на выбранном языке. [static_analysis]
    Средство анализа статического кода анализирует программный код (как исходный код, промежуточный код или исполняемый файл), не выполняя его с конкретными входами. Для целей этого критерия предупреждения компилятора и «безопасные» языковые режимы не считаются инструментами анализа статического кода (они обычно избегают глубокого анализа, поскольку скорость имеет жизненно важное значение). Примеры таких статических инструментов анализа кода включают cppcheck (C, C++), статический анализатор Clang (C, C++), SpotBugs (Java), FindBugs (Java; включая FindSecurityBugs), PMD (Java), Brakeman (Ruby on Rails), lintr (R), goodpractice (R), Анализатор качества Coverity, SonarQube, Codacy и статический анализатор кода HP Enterprise Fortify. Более крупные списки инструментов можно найти в таких местах, как Wikipedia list of tools for static code analysis, OWASP information on static code analysis, NIST list of source code security analyzers и Wheeler's list of static analysis tools. Если для используемого языка(ов) реализации нет доступных инструментов статического анализа на свободном ПО, выберите «неприменимо» (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.



    ЖЕЛАТЕЛЬНО включать по крайней мере в один из инструментов статического анализа, используемых для критерия static_analysis, правила или подходы для поиска распространенных уязвимостей в анализируемом языке или среде. [static_analysis_common_vulnerabilities]
    Инструменты статического анализа, специально предназначенные для поиска распространенных уязвимостей, с большей вероятностью найдут их. Тем не менее, использование любых статических инструментов обычно помогает найти какие-то проблемы, поэтому мы предлагаем, но не требуем этого для получения базового значка.

    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.



    Все уязвимости со средней и высокой степенью серьезности, обнаруженные при статическом анализе кода, НЕОБХОДИМО своевременно исправлять после их подтверждения. [static_analysis_fixed]
    Уязвимость имеет среднюю и высокую степень серьезности, если ее оценка по CVSS 2.0 - 4 или выше.

    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



    ЖЕЛАТЕЛЬНО выполнять анализ статического исходного кода при каждом коммите или по крайней мере ежедневно. [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.


  • Динамический анализ кода


    ЖЕЛАТЕЛЬНО применять по крайней мере один инструмент динамического анализа к любой предлагаемой основной (major) версии программного обеспечения перед ее выпуском . [dynamic_analysis]
    Инструмент динамического анализа проверяет программное обеспечение, выполняя его с конкретными входными данными. Например, проект МОЖЕТ использовать инструмент фаззинг-тестирования (например, American Fuzzy Lop) или сканер веб-приложений (например, OWASP ZAP или w3af). В некоторых случаях проект OSS-Fuzz может быть готов применить фаззинг-тестирование к вашему проекту. Для целей этого критерия инструмент динамического анализа должен каким-то образом варьировать исходные данные, чтобы искать проблемы разного рода или быть автоматическим набором тестов с покрытием веток исполнения не менее 80%. Страница Википедии о динамическом анализе и cтраница OWASP о фаззинг-тестировании указывают некоторые инструменты динамического анализа. Использование инструмента/ов анализа МОЖЕТ, но не обязано быть сосредоточено на поиске уязвимостей в безопасности.

    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.



    ЖЕЛАТЕЛЬНО регулярно использовать по меньшей мере один динамический инструмент (например, fuzzer или сканер веб-приложения) в сочетании с механизмом для обнаружения проблем безопасности памяти, таких как перезапись буфера, если программное обеспечение, создаваемое проектом, включает части, написанные на небезопасном языке (например, C или C++). Если проект не создает программное обеспечение, написанное на небезопасном языке, выберите «неприменимо» (N/A). [dynamic_analysis_unsafe]
    Примерами механизмов обнаружения проблем безопасности памяти являются Address Sanitizer (ASAN) (доступен в GCC и LLVM), Memory Sanitizer и valgrind. Другие потенциально используемые инструменты включают Thread Sanitizer и Undefined Behavior Sanitizer. Достаточно широкое использование утверждений (assertions) тоже может быть приемлемо.

    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.



    ЖЕЛАТЕЛЬНО включать в ПО, создаваемое проектом, достаточно много утверждений (assertions) времени выполнения, проверяемых при динамическом анализе. Во многих случаях эти утверждения не должны попадать в сборки под эксплуатацию (production). [dynamic_analysis_enable_assertions]
    Этот критерий не предполагает включения утверждений на этапе эксплуатации; решение об этом полностью лежит на проекте и его пользователях. Вместо этого критерий направлен на улучшение обнаружения ошибок во время динамического анализа перед развертыванием. Использование утверждений при эксплуатации полностью отличается от такового во время динамического анализа (например, при тестировании). В некоторых случаях включать утверждения при эксплуатации крайне неразумно (особенно в компонентах с высокой степенью целостности). Существует множество аргументов против включения утверждений в выпускаемых сборках: например, библиотеки не должны вызывать сбой при вызове, присутствие утверждений может привести к отклонению магазинами приложений и/или активация их при рабочем использовании может привести к раскрытию частных данных, таких как закрытые ключи. Помните, что во многих дистрибутивах Linux NDEBUG не определен, поэтому C/C++assert() в таких рабочих средах по умолчанию будет включен. Может быть важно использовать другой механизм утверждений или определить NDEBUG для эксплуатации в этих средах.

    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.



    Проект ОБЯЗАН своевременно исправлять все уязвимости средней и выше степени серьезности, обнаруженные при динамическом анализе кода, после их подтверждения. [dynamic_analysis_fixed]
    Если вы не используете динамический анализ кода и, следовательно, не обнаружили уязвимостей таким способом, выберите «неприменимо» (N/A). Степень серьезности уязвимости считается средней или выше, если уязвимость имеет среднюю или выше базовую оценку по Common Vulnerability Scoring System (CVSS). В версиях CVSS с 2.0 по 3.1 это соответствует оценке 4.0 и выше. Проекты могут использовать оценку CVSS опубликованную в любой широко используемой базе данных по уязвимостям (такой как National Vulnerability Database) используя самую новую версию CVSS доступную в этой базе данных. Вместо этого проекты могут сами вычислять серьезность используя последнюю версию CVSS на момент раскрытия уязвимости, если вводные для вычисления раскрываются вместе с публикацией уязвимости.

    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.



Вы можете использовать инструменты и системы ИИ для предложения изменений через простой URL, например https://www.bestpractices.dev/ru/projects/14430/choose/edit?osps_ac_01_01_status=Met&osps_ac_01_01_justification=GitHub+enforced. Смотрите нашу систему автоматизированных предложений о том, как это сделать. Эти данные доступны по лицензии Community Data License Agreement – Permissive, Version 2.0 (CDLA-Permissive-2.0). Это означает, что получатель данных может распространять данные с изменениями или без них, при условии, что получатель данных предоставляет текст данного соглашения вместе с распространяемыми данными. Пожалуйста, укажите в качестве источника Jesse Slaton и участников OpenSSF Best Practices badge.

Владелец анкеты на значок проекта: Jesse Slaton.
2026-09-03 00:15:58 UTC, последнее изменение сделано 2026-09-03 01:56:47 UTC.