diff --git a/README.md b/README.md index 04f7d5a..a9b42cb 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,20 @@ merge commit and tagged `slice/NNN` (`git tag -l 'slice/*' | wc -l` → 25, on 2 that means in practice: **Assessed against a recognised baseline.** Trinity holds the -[OpenSSF Best Practices passing badge](https://www.bestpractices.dev/projects/14772), the Open -Source Security Foundation's published bar for basics, change control, reporting, quality, -security and analysis, and the prerequisite the Linux Foundation names for a project entering -Incubation. Every required criterion is met; three *suggested* ones are recorded as unmet rather -than stretched, because a sheet with nothing on it survives a spot check less well than one that -says where the gaps are. The [assurance posture](#assurance-posture) below states what the build -enforces and what enforces it. +[OpenSSF Best Practices **silver** badge](https://www.bestpractices.dev/projects/14772), awarded +2026-09-23. Silver is the Open Source Security Foundation's second tier: it takes the passing bar +for basics, change control, reporting, quality, security and analysis, which is the prerequisite +the Linux Foundation names for a project entering Incubation, and adds documented governance and +roles, continuity of access, a published roadmap, a written test policy, 80% statement coverage, +and an assurance case carrying a threat model, its trust boundaries, an argument that secure design +principles were applied and an argument that common implementation weaknesses were countered. + +Every required criterion at both levels is met. What is *not* met is recorded rather than +stretched, because a sheet with nothing on it survives a spot check less well than one that says +where the gaps are: at silver, two suggested-tier criteria (a bus factor of two, and an +accessibility assessment) and one suggestion about signed version tags are marked unmet with the +reason. The [assurance posture](#assurance-posture) below states what the build enforces and what +enforces it. - **Talks.** Streaming chat with any provider behind one behaviour (`Trinity.LLM`), switched by configuration; the assistant's text is persisted as a draft every 500 ms or 2 KB while it @@ -188,7 +195,7 @@ rather than described by it. | Approved cryptography | A dedicated CI leg builds from source and runs the cryptographic properties inside a FIPS-mode container, so statements about approved algorithms are measured on that leg rather than asserted. | | Supply chain | Dependency and licence audits run on every commit; dependency versions are pinned in `VERSIONS.md` and verified against the lock file by the gate. A CycloneDX bill of materials is generated by the gate on every commit and ships beside every packaged binary, so whoever holds the artifact holds the list of what is in it; the bill states its own coverage inside the document, including what it does not cover. | | Provenance | Every commit carries a Developer Certificate of Origin sign-off, enforced by a hook and independently by CI. Every packaged binary carries build provenance attested through the workflow's own identity and recorded in a public transparency log, verified in the same run that produced it, so a holder can check where the bytes were built without trusting this page. | -| Independent self-certification | The project holds the [OpenSSF Best Practices passing badge](https://www.bestpractices.dev/projects/14772), assessed against the Open Source Security Foundation's published criteria for basics, change control, reporting, quality, security and analysis. Three suggested criteria are recorded as unmet rather than stretched: semantic versioning, which begins at the first supported release, and two concerning dynamic analysis tooling, which the project does not yet run. | +| Independent self-certification | The project holds the [OpenSSF Best Practices **silver** badge](https://www.bestpractices.dev/projects/14772) (awarded 2026-09-23), assessed against the Open Source Security Foundation's published criteria at two tiers. Every required criterion at both is met. What is not met is stated rather than stretched: at passing, three suggested criteria (semantic versioning, which begins at the first supported release, and two concerning dynamic analysis tooling the project does not run); at silver, a bus factor of two and an accessibility assessment, both suggested-tier, and signed version tags. | | Argued, not asserted | `docs/10-assurance-case.md` decomposes the top-level security claim into ten claims, each with its argument, the evidence a reader can check, and the limit on what it covers; the assumptions the case rests on are named rather than implied. | | Claim discipline | `docs/09-standards-register.md` records one row per control a regulated deployment might ask about, each with an evidence path and a status. No public claim about a regulation or requirement is made without a row there saying it is true. | diff --git a/docs/09-standards-register.md b/docs/09-standards-register.md index fe718bd..cd5011c 100644 --- a/docs/09-standards-register.md +++ b/docs/09-standards-register.md @@ -55,6 +55,8 @@ document, a PROOF.md line) or `none`; the status; who decides the status. Status | Control | Where Trinity satisfies it | Evidence path | Status | Decider | |---|---|---|---|---| | Recognised open-source security baseline | The OpenSSF Best Practices criteria at the passing level: basics, change control, reporting, quality, security and analysis | https://www.bestpractices.dev/projects/14772 | **passing, self-certified 2026-09-23.** Three suggested criteria are recorded unmet rather than stretched: `version_semver`, `dynamic_analysis`, `dynamic_analysis_enable_assertions` | maintainer | +| Recognised open-source security baseline, second tier | The same criteria at the **silver** level: documented governance and roles, continuity of access, a published roadmap, a written test policy, 80% statement coverage, and an assurance case carrying a threat model, trust boundaries, a secure-design argument and an implementation-weakness argument | https://www.bestpractices.dev/projects/14772 | **silver, self-certified 2026-09-23** (all 44 MUST criteria met or not applicable; `signed_releases` is not applicable only while no release is published, and becomes required at the first one). Recorded unmet with reasons: `bus_factor` and `accessibility_best_practices` (both SHOULD), `version_tags_signed` (SUGGESTED) | maintainer | +| Recognised open-source security baseline, third tier | The same criteria at the **gold** level | https://www.bestpractices.dev/projects/14772 | **30% as of 2026-09-23, not held.** Met so far: `require_2FA` (the organisation enforces two-factor authentication for write access), and the gold-level criteria inherited from the tiers below. `copyright_per_file` and `license_per_file` are met in the tree as of this row and held by `test/spdx_headers_test.exs`. Outstanding and not glossed: `bus_factor` and `contributors_unassociated` and `two_person_review` all need people other than the author in the history; `test_statement_coverage90` needs 90% against the current 80.31%; `build_reproducible` is measured and not met (docs/packaging.md); `security_review`, `code_review_standards`, `small_tasks` and `dynamic_analysis` are open work | maintainer | | Linux Foundation Incubation prerequisite | The same badge; LF Incubation requires it at passing, alongside documented technical governance and a README per repository | https://www.bestpractices.dev/projects/14772, `GOVERNANCE.md`, `MAINTAINERS.md` | badge held; neutral asset hosting is not yet in place and is a foundation-side step | owner | **Time-bound answers in that self-certification.** Three of the answers were true on the date given diff --git a/lib/trinity_web/components/layouts/root.html.heex b/lib/trinity_web/components/layouts/root.html.heex index 1228687..9fb030a 100644 --- a/lib/trinity_web/components/layouts/root.html.heex +++ b/lib/trinity_web/components/layouts/root.html.heex @@ -1,4 +1,5 @@ - +<%!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC --%> +<%!-- SPDX-License-Identifier: Apache-2.0 --%> diff --git a/lib/trinity_web/controllers/oauth_html/consent.html.heex b/lib/trinity_web/controllers/oauth_html/consent.html.heex index b609dd5..f26794c 100644 --- a/lib/trinity_web/controllers/oauth_html/consent.html.heex +++ b/lib/trinity_web/controllers/oauth_html/consent.html.heex @@ -1,3 +1,5 @@ +<%!-- SPDX-FileCopyrightText: Sudo Apt Holdings LLC --%> +<%!-- SPDX-License-Identifier: Apache-2.0 --%> <:bar> {gettext("Authorize a client")} diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 795b9b7..70f3bb5 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Sudo Apt Holdings LLC +// SPDX-License-Identifier: Apache-2.0 fn main() { tauri_build::build() } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9c3118c..0d54604 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Sudo Apt Holdings LLC +// SPDX-License-Identifier: Apache-2.0 #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 7ca49f1..fc6dfdb 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -1,3 +1,5 @@ +// SPDX-FileCopyrightText: Sudo Apt Holdings LLC +// SPDX-License-Identifier: Apache-2.0 // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use tauri_plugin_shell::process::CommandEvent; diff --git a/test/spdx_headers_test.exs b/test/spdx_headers_test.exs new file mode 100644 index 0000000..16e2931 --- /dev/null +++ b/test/spdx_headers_test.exs @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: Sudo Apt Holdings LLC +# SPDX-License-Identifier: Apache-2.0 +defmodule SpdxHeadersTest do + @moduledoc """ + Every source file this project wrote carries a copyright line and a licence line. + + These are the OpenSSF Best Practices criteria `copyright_per_file` and `license_per_file`, both + MUST at the gold level. They are held here rather than checked by hand for the reason every + population rule in this tree is held by a test: the count only stays right if adding a file that + breaks it fails something. When this test was written the tree was five files short of both + criteria, and none of the five was noticed by anyone reading the tree. + + The population is `git ls-files`, so a new source file is in scope by existing. The comment + syntax differs by language and the marker does not: SPDX defines the tag, not the comment it + sits in, which is why this checks for the tag rather than for a particular line. + """ + use ExUnit.Case, async: true + + @copyright "SPDX-FileCopyrightText" + @license "SPDX-License-Identifier" + + # How many lines from the top the tags must appear within. HEEx templates and Rust files both + # carry them first; an Elixir file may sit below a shebang or a moduledoc-free comment. + @window 6 + + defp sources do + {out, 0} = System.cmd("git", ["ls-files", "*.ex", "*.exs", "*.heex", "*.rs"]) + String.split(out, "\n", trim: true) + end + + defp head(file), do: file |> File.read!() |> String.split("\n") |> Enum.take(@window) + + test "the population is the tree's own source files, and it is not small" do + files = sources() + assert length(files) > 400, "expected the whole tree, got #{length(files)} files" + end + + test "every source file carries a copyright statement" do + missing = for f <- sources(), not Enum.any?(head(f), &String.contains?(&1, @copyright)), do: f + + assert missing == [], + "these files carry no #{@copyright} line in their first #{@window} lines" + end + + test "every source file carries a licence statement" do + missing = for f <- sources(), not Enum.any?(head(f), &String.contains?(&1, @license)), do: f + + assert missing == [], + "these files carry no #{@license} line in their first #{@window} lines" + end + + test "the licence named in each file is the one the project is under" do + for f <- sources(), line <- head(f), String.contains?(line, @license) do + assert line =~ "Apache-2.0", + "#{f} names a licence that is not the project's: #{String.trim(line)}" + end + end +end