Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .github/workflows/gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ jobs:
with:
version-file: .tool-versions
version-type: strict
install-hex: false

# Hex is pinned to the archive the image pins (ci/ironbank/hardening_manifest.yaml), by the
# SHA-512 builds.hex.pm publishes for it in installs/hex.csv. setup-beam's own install takes
# the newest Hex, and the bill of materials names the running Hex, so a Hex release turned
# `trinity.third_party_licenses --check` red on every branch (2.5.3, 2026-10-14) while
# nothing in the tree had changed.
- name: Hex 2.5.1, pinned as the image pins it
run: |
curl -fsSL -o /tmp/hex-2.5.1-otp-28.ez https://builds.hex.pm/installs/1.20.0/hex-2.5.1-otp-28.ez
echo "690f12f6933088bb32f1d7125485fb13426318c03cfeabcbd3cce55824cddf2a701104b09b7f4cd7092fe7f76d5b669e391a072428ec4526879b36f62820ae5d /tmp/hex-2.5.1-otp-28.ez" | sha512sum -c -
mix archive.install /tmp/hex-2.5.1-otp-28.ez --force
mix hex.info | head -1

- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
Expand Down Expand Up @@ -138,6 +151,19 @@ jobs:
with:
version-file: .tool-versions
version-type: strict
install-hex: false

# Hex is pinned to the archive the image pins (ci/ironbank/hardening_manifest.yaml), by the
# SHA-512 builds.hex.pm publishes for it in installs/hex.csv. setup-beam's own install takes
# the newest Hex, and the bill of materials names the running Hex, so a Hex release turned
# `trinity.third_party_licenses --check` red on every branch (2.5.3, 2026-10-14) while
# nothing in the tree had changed.
- name: Hex 2.5.1, pinned as the image pins it
run: |
curl -fsSL -o /tmp/hex-2.5.1-otp-28.ez https://builds.hex.pm/installs/1.20.0/hex-2.5.1-otp-28.ez
echo "690f12f6933088bb32f1d7125485fb13426318c03cfeabcbd3cce55824cddf2a701104b09b7f4cd7092fe7f76d5b669e391a072428ec4526879b36f62820ae5d /tmp/hex-2.5.1-otp-28.ez" | sha512sum -c -
mix archive.install /tmp/hex-2.5.1-otp-28.ez --force
mix hex.info | head -1

- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/otp-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,20 @@ jobs:
with:
version-file: .tool-versions
version-type: strict
install-hex: false

# Hex is pinned to the archive the image pins (ci/ironbank/hardening_manifest.yaml), by the
# SHA-512 builds.hex.pm publishes for it in installs/hex.csv. setup-beam's own install takes
# the newest Hex, and the bill of materials names the running Hex, so a Hex release turned
# `trinity.third_party_licenses --check` red on every branch (2.5.3, 2026-10-14) while
# nothing in the tree had changed.
- name: Hex 2.5.1, pinned as the image pins it
if: steps.seen.outputs.seen == 'false'
run: |
curl -fsSL -o /tmp/hex-2.5.1-otp-28.ez https://builds.hex.pm/installs/1.20.0/hex-2.5.1-otp-28.ez
echo "690f12f6933088bb32f1d7125485fb13426318c03cfeabcbd3cce55824cddf2a701104b09b7f4cd7092fe7f76d5b669e391a072428ec4526879b36f62820ae5d /tmp/hex-2.5.1-otp-28.ez" | sha512sum -c -
mix archive.install /tmp/hex-2.5.1-otp-28.ez --force
mix hex.info | head -1

- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
if: steps.seen.outputs.seen == 'false'
Expand Down
12 changes: 6 additions & 6 deletions THIRD_PARTY_LICENSES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ it, and a reader should not infer from an entry here that the component is in a
| `abnf_parsec` | `2.1.0` | MIT | bom |
| `asn1` | `5.4.3` | Apache-2.0 | bom |
| `axon` | `0.8.1` | Apache-2.0 | bom |
| `bandit` | `1.12.5` | MIT | bom |
| `bandit` | `1.13.1` | MIT | bom |
| `beam_mcp` | `0.10.1` | Apache-2.0 | bom |
| `boundary` | `0.10.4` | MIT | bom |
| `bumblebee` | `0.7.1` | Apache-2.0 | bom |
Expand All @@ -36,7 +36,7 @@ it, and a reader should not infer from an entry here that the component is in a
| `crypto` | `5.8.3.3` | Apache-2.0 | bom |
| `daisyui` | `22ecff57f2c3` | MIT | declared |
| `db_connection` | `2.10.2` | Apache-2.0 | bom |
| `decimal` | `3.1.1` | Apache-2.0 | bom |
| `decimal` | `3.2.0` | Apache-2.0 | bom |
| `dns_cluster` | `0.2.0` | MIT | bom |
| `dotenvy` | `1.2.1` | Apache-2.0 | bom |
| `earmark_parser` | `1.4.46` | Apache-2.0 | bom |
Expand All @@ -62,7 +62,7 @@ it, and a reader should not infer from an entry here that the component is in a
| `glob_ex` | `0.1.12` | MIT | bom |
| `heroicons` | `0435d4ca364a` | MIT | declared |
| `hex` | `2.5.1` | Apache-2.0 | bom |
| `hex_core` | `0.19.0` | Apache-2.0 | bom |
| `hex_core` | `0.20.1` | Apache-2.0 | bom |
| `hpax` | `1.1.0` | Apache-2.0 | bom |
| `idna` | `7.1.0` | MIT | bom |
| `igniter` | `0.8.4` | MIT | bom |
Expand Down Expand Up @@ -114,7 +114,7 @@ it, and a reader should not infer from an entry here that the component is in a
| `plug_crypto` | `2.2.0` | Apache-2.0 | bom |
| `polaris` | `0.1.0` | Apache-2.0 | bom |
| `postgrex` | `0.22.4` | Apache-2.0 | bom |
| `protobuf` | `0.17.0` | MIT | bom |
| `protobuf` | `0.17.1` | MIT | bom |
| `public_key` | `1.20.3.4` | Apache-2.0 | bom |
| `purl` | `0.5.0` | Apache-2.0 | bom |
| `req` | `0.7.4` | Apache-2.0 | bom |
Expand All @@ -125,7 +125,7 @@ it, and a reader should not infer from an entry here that the component is in a
| `rustler_precompiled` | `0.9.0` | Apache-2.0 | bom |
| `safetensors` | `0.1.3` | Apache-2.0 | bom |
| `sasl` | `4.3.2` | Apache-2.0 | bom |
| `sbom` | `0.11.0` | BSD-3-Clause AND CC0-1.0 AND MIT AND Apache-2.0 | bom |
| `sbom` | `0.12.0` | BSD-3-Clause AND CC0-1.0 AND MIT AND Apache-2.0 | bom |
| `server_sent_events` | `1.1.0` | MIT | bom |
| `sobelow` | `0.15.0` | Apache-2.0 | bom |
| `sourceror` | `1.12.3` | Apache-2.0 | bom |
Expand All @@ -142,7 +142,7 @@ it, and a reader should not infer from an entry here that the component is in a
| `telemetry_registry` | `0.3.2` | Apache-2.0 | bom |
| `text_diff` | `0.1.0` | MIT | bom |
| `texture` | `1.2.1` | Apache-2.0 | bom |
| `thousand_island` | `1.5.0` | MIT | bom |
| `thousand_island` | `1.6.0` | MIT | bom |
| `tokenizers` | `0.5.1` | Apache-2.0 | bom |
| `toml` | `0.7.0` | Apache-2.0 | bom |
| `typed_struct` | `0.3.0` | MIT | bom |
Expand Down
86 changes: 10 additions & 76 deletions ci/headless/justifications.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,71 +21,16 @@
# include no pcre2_jit_*.c and no pcre2_dfa_match.c (`tar -tzf otp_src_28.5.0.7.tar.gz`).
---
justifications:
# pcre2: inherited from ubi9-micro 9.8, whose own scan reports the same three CVEs on the same
# two packages. Red Hat lists no fix (grype fix state `not-fixed`, Red Hat's namespace).
- finding: "CVE-2026-86145"
package: "pcre2-10.40-6.el9"
packagePath: null
severity: "high"
scanSource: "grype"
justification: >-
Inherited from the ubi9-micro 9.8 base image, which reports the same finding; Red Hat lists
no fix. The flaw is in pcre2_jit_match. In this image libpcre2-8 is linked only by the base's
libselinux and libpcre2-posix (readelf -d over every executable and shared object); the
Erlang runtime does not link it, and the PCRE2 copy ERTS compiles in (erts/emulator/pcre,
10.47) contains no JIT source (no pcre2_jit_*.c in OTP 28.5.0.7's tree).
- finding: "CVE-2026-86145"
package: "pcre2-syntax-10.40-6.el9"
packagePath: null
severity: "high"
scanSource: "grype"
justification: >-
The syntax-data subpackage of the pcre2 finding of the same identifier: inherited from the
ubi9-micro 9.8 base image, Red Hat lists no fix, and the JIT code the flaw is in is not
loaded by anything Trinity runs (see the pcre2 row).
- finding: "CVE-2026-89161"
package: "pcre2-10.40-6.el9"
packagePath: null
severity: "high"
scanSource: "grype"
justification: >-
Inherited from the ubi9-micro 9.8 base image, which reports the same finding; Red Hat lists
no fix. The flaw needs an attacker-controlled pattern and the JIT API. libpcre2-8 is linked
only by the base's libselinux and libpcre2-posix; the Erlang runtime does not link it, and
ERTS's own PCRE2 10.47 copy contains no JIT source.
- finding: "CVE-2026-89161"
package: "pcre2-syntax-10.40-6.el9"
packagePath: null
severity: "high"
scanSource: "grype"
justification: >-
The syntax-data subpackage of the pcre2 finding of the same identifier: inherited from the
ubi9-micro 9.8 base image, Red Hat lists no fix, and the JIT code the flaw is in is not
loaded by anything Trinity runs (see the pcre2 row).
- finding: "CVE-2026-103111"
package: "pcre2-10.40-6.el9"
packagePath: null
severity: "high"
scanSource: "grype"
justification: >-
Inherited from the ubi9-micro 9.8 base image, which reports the same finding; Red Hat lists
no fix. The flaw is in pcre2_dfa_match. libpcre2-8 is linked only by the base's libselinux
and libpcre2-posix; the Erlang runtime does not link it, and ERTS's own PCRE2 10.47 copy has
no pcre2_dfa_match.c (erts/emulator/pcre in OTP 28.5.0.7).
- finding: "CVE-2026-103111"
package: "pcre2-syntax-10.40-6.el9"
packagePath: null
severity: "high"
scanSource: "grype"
justification: >-
The syntax-data subpackage of the pcre2 finding of the same identifier: inherited from the
ubi9-micro 9.8 base image, Red Hat lists no fix, and the DFA matcher the flaw is in is not
loaded by anything Trinity runs (see the pcre2 row).

# openssl-libs: added by this image (the crypto NIF links libcrypto). Red Hat lists no fix for
# either; `dnf upgrade` ran against the installroot at build time, so this is the newest build
# UBI's repositories carried. Re-pinned 2026-10-08 from 3.5.8-1 to Red Hat's 3.5.8-2 rebuild, which
# the scan still reports as not fixed for both.
# Retired 2026-10-19, from the scan of run 39257269071 (grype database built 2026-10-19T06:39Z):
# the six pcre2 and pcre2-syntax rows, because the base now carries 10.40-7.el9_8, against which
# none of CVE-2026-86145, CVE-2026-89161 or CVE-2026-103111 is reported; and the
# CVE-2026-84782 row, because Red Hat's security data names openssl-1:3.5.8-2.el9_8, the build in
# this image, as the fixed release (RHSA-2026:77396).
#
# openssl-libs: added by this image (the crypto NIF links libcrypto). Red Hat lists no fix for the
# finding below; `dnf upgrade` ran against the installroot at build time, so this is the newest
# build UBI's repositories carried. Re-pinned 2026-10-08 from 3.5.8-1 to Red Hat's 3.5.8-2
# rebuild.
- finding: "CVE-2026-75804"
package: "openssl-libs-1:3.5.8-2.el9_8"
packagePath: null
Expand All @@ -97,14 +42,3 @@ justifications:
(readelf -d over all of them); Erlang's crypto NIF links libcrypto alone, and OTP's ssl
application implements TLS in Erlang without libssl. Red Hat lists no fix; the build ran dnf
upgrade, so this is the newest openssl-libs UBI carried.
- finding: "CVE-2026-84782"
package: "openssl-libs-1:3.5.8-2.el9_8"
packagePath: null
severity: "high"
scanSource: "grype"
justification: >-
The flaw is in OpenSSL's DTLS retransmission, which lives in libssl. No executable or shared
object in this image declares libssl.so.3 as a dependency (readelf -d over all of them);
Erlang's crypto NIF links libcrypto alone, and OTP's ssl application implements DTLS in
Erlang without libssl. Red Hat lists no fix; the build ran dnf upgrade, so this is the
newest openssl-libs UBI carried.
2 changes: 1 addition & 1 deletion docs/regulated/control-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ register does not claim is never Trinity's.
| `IA-5(7)` | The gate fails on a credential-shaped string anywhere in the tracked tree | `lib/mix/tasks/trinity.secrets.scan.ex` | `mix trinity.secrets.scan`; `test/secrets_scan_test.exs` "RED: a planted fake key of each shape is found" | Trinity's | none | none |
| `IA-11` | An expired token is refused, and with introspection enabled the issuer is asked about each token; a client then goes back to the identity provider | `lib/trinity/mcp/auth/token.ex`, `config/runtime.exs` | `test/trinity/mcp/auth/token_test.exs` "introspection: an opaque token is asked about at the issuer (RFC 7662), the answer's claims checked as a JWT's are" | shared | Token lifetime, revocation and re-authentication policy, at the identity provider | CRM "Token revocation and session lifetime" |
| `IR-6` | Trinity reports nothing to anyone; `incident.md` sets out the clocks that may apply and what the record gives an investigator | `docs/regulated/incident.md` | none | the deployment's | Reporting, within whatever clock applies | CRM "Reporting to a government customer within a clock" |
| `RA-5` | The gate audits every locked dependency on every push; the built image is scanned with grype, and a High or Critical finding without a justification fails. **The image's scan fails today**: OTP findings fixed in a later OTP release have no justification until the toolchain moves | `lib/mix/tasks/trinity.image.findings.ex`, `ci/headless/justifications.yaml` | `mix deps.audit`; `mix trinity.image.findings`; `test/mix/tasks/trinity_image_findings_test.exs` "AC3: a planted High finding with no row fails, and passes once justified" | shared | Scanning what is actually deployed, on the deployment's schedule | CRM "Scanning for known vulnerabilities" |
| `RA-5` | The gate audits every locked dependency on every push; the built image is scanned with grype, and a High or Critical finding without a justification fails. A finding with a fix available is fixed, never justified: the OTP findings fixed in 28.5.0.7 were closed by moving the image to it (ADR-0014) | `lib/mix/tasks/trinity.image.findings.ex`, `ci/headless/justifications.yaml` | `mix deps.audit`; `mix trinity.image.findings`; `test/mix/tasks/trinity_image_findings_test.exs` "AC3: a planted High finding with no row fails, and passes once justified" | shared | Scanning what is actually deployed, on the deployment's schedule | CRM "Scanning for known vulnerabilities" |
| `RA-5(11)` | A published process for reporting a vulnerability in this software. It is a document, and no check in the tree holds it | `SECURITY.md` | none | shared | Reporting to it | CRM "A published vulnerability disclosure process for this software" |
| `SA-4` | Nothing in the tree is a contract with a model provider | `config/llm.exs` | none | the deployment's | Whether the provider may train on or keep the data, settled in the contract | CRM "Whether the model provider trains on your data" |
| `SA-9` | The model in force is configuration: Trinity calls the endpoint it is pointed at, and under the regulated profile only one on the allow-list (the `AC-4` row above) | `config/llm.exs` | none | shared | Choosing the provider and the endpoint | CRM "Which model is called" |
Expand Down
Loading
Loading