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
17 changes: 16 additions & 1 deletion .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,24 @@ jobs:
# weigh. Every license that does carry obligations still has to be
# added here explicitly.
# 0BSD, Unlicense
#
# LicenseRef-scancode-google-patent-license-golang is the Go
# PATENTS file, which ships alongside the BSD-3-Clause LICENSE in
# every golang.org/x module. Newer x/* releases resolve as the
# compound expression `BSD-3-Clause AND
# LicenseRef-scancode-google-patent-license-golang`, which fails
# against BSD-3-Clause alone. It is allowed because it is an
# *additional* grant: Google licenses its patents reading on Go for
# use in implementations of Go, terminating only if the user sues
# over Go patents. It adds a right on top of BSD-3-Clause and
# imposes no obligation on GLX or on downstream consumers, so
# there is nothing for a review decision to weigh. The underlying
# copyright license is BSD-3-Clause, already allowed above.
# LicenseRef-scancode-google-patent-license-golang
allow-licenses: >-
Apache-2.0, MIT, BSD-2-Clause, BSD-3-Clause, ISC, MPL-2.0,
CC0-1.0, BlueOak-1.0.0, Python-2.0, 0BSD, Unlicense
CC0-1.0, BlueOak-1.0.0, Python-2.0, 0BSD, Unlicense,
LicenseRef-scancode-google-patent-license-golang
# Note: a dependency whose license the action cannot resolve at all
# is reported but does NOT fail the check (upstream behaviour), so
# the allow-list gates known licenses, not unknown ones — e.g.
Expand Down
2 changes: 1 addition & 1 deletion .reuse/dep5
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Files: Makefile .goreleaser.yml .gitattributes .gitignore .npmrc
Copyright: 2025 Oracynth, Inc.
License: Apache-2.0

Files: .golangci.yml .golangci-lint-version .gosec-version .markdownlint-cli2.jsonc codecov.yml lefthook.yml
Files: .golangci.yml .golangci-lint-version .gosec-version .markdownlint-cli2.jsonc codecov.yml lefthook.yml osv-scanner.toml
Copyright: 2025 Oracynth, Inc.
License: Apache-2.0

Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- **Retired the `auto-resolve-conflicts.yml` workflow in favor of git's native `union` merge driver** — The workflow re-merged conflicting PR branches on a runner and then stripped conflict markers from `CHANGELOG.md` with `sed` plus an `awk` header-dedup pass; since it landed in #82 it needed a string of follow-ups (#194, #341, #592, #601, #602, #657, #1054, #1107) to stay correct. `.gitattributes` now maps `CHANGELOG.md merge=union`, so git itself keeps both sides during the merge — no markers to strip, no second workflow, and no `workflow_run` chaining. `union` is a git built-in, so unlike the `merge=glx` driver for `.glx` files it needs no per-clone `git config`; it applies in every clone. It applies to local merges and rebases only: GitHub's server-side merges API (the "Update branch" button and `auto-update-branches.yml`) does not honor `.gitattributes` merge drivers, so a CHANGELOG-only conflict reported on GitHub is resolved by merging `main` locally and pushing. `CONTRIBUTING.md` and the root `CLAUDE.md` document both halves, plus the one case that still needs a human eye — union keeps both sides blindly, so merging a `main` that has just cut a release can land a branch entry inside the released section. `auto-update-branches.yml` is unchanged. Closes #888
- **`renovate.json` — Renovate retired, Dependabot is the sole dependency updater** — The repository ran two update bots on paper but only one in practice: `renovate.json` carried a top-level `"enabled": false`, so Renovate opened no PRs while Dependabot handled every bump. The config file is deleted and `.reuse/dep5` no longer lists it. Note that this also removes the repository's only scheduled lockfile-maintenance mechanism (Renovate's `lockFileMaintenance` block, itself disabled by the top-level switch, so nothing changes in practice) — Dependabot refreshes `go.sum` and the npm lockfiles as a side effect of the bumps it opens, and `make mod-tidy` / `make tidy-check` cover the rest. #810, which reported the disabled `lockFileMaintenance` override, is obsoleted as written by this deletion and needs closing or re-scoping to the remaining gap (an unattended periodic refresh of transitive pins no bump happens to touch); it is deliberately left open here for a maintainer to decide. `SECURITY.md` is corrected in the same change: it described Dependabot as running "daily", where `.github/dependabot.yml` actually schedules weekly staggered runs (Go Monday, npm Tuesday, GitHub Actions Wednesday) with release cooldowns that security updates bypass. (#257)

### Fixed

- **Cleared the six OSV advisories behind the OpenSSF Scorecard *Vulnerabilities* alert** — Scorecard's weekly run scored the check `4` on `GO-2026-6179`, `GO-2026-6180`, `GO-2026-5932`, `GO-2026-6303`, `GO-2026-6354`, and `GO-2026-6355` ([code-scanning alert 62](https://github.com/genealogix/glx/security/code-scanning/62)). Five are fixed by upgrading. `golang.org/x/crypto` goes `v0.53.0` → `v0.56.0` in the root module, clearing the `x/crypto/ssh` source-address bypass (`GO-2026-6303`/CVE-2026-56854, fixed in 0.55.0) and the two deadlocked-channel DoS advisories (`GO-2026-6354`/CVE-2026-78662 and `GO-2026-6355`/CVE-2026-56855, both fixed in 0.56.0), pulling `golang.org/x/net` `v0.56.0` → `v0.57.0` and `golang.org/x/sys` `v0.46.0` → `v0.47.0` with it. `golang.org/x/mod` goes `v0.35.0` → `v0.40.0` in `ci-tools/go.mod` — the module that carried the two `sumdb` advisories, the tile-verification bypass (`GO-2026-6179`/CVE-2026-56865) and the unauthenticated-hash `Lookup` issue (`GO-2026-6180`/CVE-2026-56864) — bringing `x/sync`, `x/sys`, `x/telemetry`, and `x/tools` along. Both modules stay tidy (`make tidy-check`, `make ci-tools-tidy-check`). All six are dependency bumps to test and tooling paths; no GLX code changed.

The sixth, `GO-2026-5932`, has no fixed version — it marks `golang.org/x/crypto/openpgp` unmaintained rather than patching a defect — and does not apply: `golang.org/x/crypto` is reached only through `github.com/go-git/go-git/v5`, which uses `x/crypto/hkdf` and gets OpenPGP from the maintained `github.com/ProtonMail/go-crypto/openpgp`, so `go list -deps ./...` shows no `x/crypto/openpgp` package in the build graph and `govulncheck` reports it as uncalled. Scorecard's check is version-matched rather than reachability-based and reports it anyway, so a new root `osv-scanner.toml` ignores that one ID with the reasoning and the re-verification command recorded inline. `SECURITY-POSTURE.md` gains a "Vulnerability suppression" subsection stating the rule that file operates under — suppress only what upgrading cannot clear *and* what is provably outside the build graph — so the entry is a documented exception rather than a silent mask, matching the document’s existing stance under Action pinning, where a control’s accepted consequence and known limits are written down rather than papered over. (PR [#1262](https://github.com/genealogix/glx/pull/1262))

Clearing the advisories also required one license-policy addition. GitHub’s license resolver reports the bumped `golang.org/x/*` versions as the compound expression `BSD-3-Clause AND LicenseRef-scancode-google-patent-license-golang`, which failed `dependency-review.yml`’s `allow-licenses` list from #340 even though `BSD-3-Clause` is on it. The `LicenseRef` is the Go `PATENTS` file that ships beside the BSD-3-Clause `LICENSE` in every `golang.org/x` module — an *additional* patent grant covering implementations of Go, terminating only if the user sues over Go patents. It adds a right on top of the copyright license and imposes no obligation on GLX or on downstream consumers, so it is added to the allow-list with that reasoning recorded inline, in the same form as every other entry there. No obligation-bearing license was pre-approved.

## [0.0.0-beta.12] - 2026-09-16

### Added
Expand Down
12 changes: 11 additions & 1 deletion SECURITY-POSTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ Status legend: ✓ met · ◐ partial · ☐ not yet met
| Contributors assert legal authorization (DCO/CLA) | ✓ | [Developer Certificate of Origin 1.1](https://github.com/genealogix/glx/blob/main/CONTRIBUTING.md#developer-certificate-of-origin-dco) sign-off is required on every commit and is verified for every PR during maintainer review (with direct commits to the default branch blocked by protection rules). An automated DCO check is tracked as a future enhancement to reduce reviewer toil, not as a prerequisite for enforcing this control |
| Automated test suite runs before merge | ✓ | Five workflows run unconditionally on every `pull_request` — [CI](https://github.com/genealogix/glx/blob/main/.github/workflows/validate-spec.yml), [Security](https://github.com/genealogix/glx/blob/main/.github/workflows/security.yml), [npm Audit](https://github.com/genealogix/glx/blob/main/.github/workflows/npm-audit.yml), [Dependency Review](https://github.com/genealogix/glx/blob/main/.github/workflows/dependency-review.yml), and [Lint PR Title](https://github.com/genealogix/glx/blob/main/.github/workflows/lint-pr-title.yml). Two are path-filtered to PRs that touch the relevant files: [Lint](https://github.com/genealogix/glx/blob/main/.github/workflows/lint.yml) (`**.go`, `go.mod`, `go.sum`, `.golangci.yml`) and [Lint Markdown](https://github.com/genealogix/glx/blob/main/.github/workflows/lint-markdown.yml) (`specification/**/*.md`, `docs/**/*.md`, `*.md`, `.markdownlint-cli2.jsonc`). Whichever workflows are triggered are expected to be green before merge; the Main Protection ruleset (see L1 row above) gates this |
| Static analysis of dependencies and code | ✓ | [`security.yml`](https://github.com/genealogix/glx/blob/main/.github/workflows/security.yml) runs `govulncheck` (known-vulnerable Go dependencies) and `gosec` (Go static security analysis) on every PR and weekly. gosec is version-pinned via the repo's [`.gosec-version`](https://github.com/genealogix/glx/blob/main/.gosec-version) file, and a weekly [`gosec-pin-currency.yml`](https://github.com/genealogix/glx/blob/main/.github/workflows/gosec-pin-currency.yml) workflow fails when that pin falls behind the latest gosec release, so the analyzer set cannot silently go stale. This matters because gosec's rule set maps directly onto GLX's threat surface as a parser of untrusted GEDCOM/GEDZIP archives: G122 (symlink TOCTOU in directory walks) and G120 (unbounded stream parsing) cover the archive-extraction zip-slip/symlink and decompression-bomb classes — pattern-based coverage complementary to CodeQL's dataflow analysis. [`dependency-review.yml`](https://github.com/genealogix/glx/blob/main/.github/workflows/dependency-review.yml) blocks PRs that introduce known-vulnerable dependencies, and since [#340](https://github.com/genealogix/glx/issues/340) also fails PRs that add a dependency under a resolved license outside the configured allow-list (a license the action cannot resolve is reported, not failed). **CodeQL** is configured via [GitHub Default Setup](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning/configuring-default-setup-for-code-scanning) across `actions`, `go`, `javascript-typescript`, and `python` (maintainer-verified as of 2026-05-07; Default Setup is a repository setting in the Security tab and is not represented by a workflow file in this repo). The per-PR `Analyze (…)` jobs are visible on every PR's checks list, and findings surface in Code Scanning |
| Continuous security posture scoring | ✓ | [OpenSSF Scorecard](https://github.com/genealogix/glx/blob/main/.github/workflows/scorecard.yml) runs on push to `main`, weekly on Mondays, and on `workflow_dispatch`. Results are published to the public OSSF dataset and uploaded to GitHub Code Scanning as SARIF. The workflow deliberately has **no** `pull_request` trigger: publishing requires OIDC (`id-token: write`), which fork PRs cannot safely hold, so per-PR Scorecard coverage would break the publish model rather than harden it |
| Continuous security posture scoring | ✓ | [OpenSSF Scorecard](https://github.com/genealogix/glx/blob/main/.github/workflows/scorecard.yml) runs on push to `main`, weekly on Mondays, and on `workflow_dispatch`. Results are published to the public OSSF dataset and uploaded to GitHub Code Scanning as SARIF. The workflow deliberately has **no** `pull_request` trigger: publishing requires OIDC (`id-token: write`), which fork PRs cannot safely hold, so per-PR Scorecard coverage would break the publish model rather than harden it. Advisories the *Vulnerabilities* check reports that do not apply to GLX are handled per [Vulnerability suppression](#vulnerability-suppression), not left to drift |
| GitHub Actions pinned per a documented convention | ✓ | **Every** `uses:` reference in every workflow is pinned to a full commit SHA with a trailing version comment — third-party (including `github/*`) and first-party `actions/*` alike. No floating major tags and no exact-patch-tag references remain. See [Action pinning](#action-pinning) for the rationale and the mutable-ref threat it closes |

#### Action pinning
Expand All @@ -80,6 +80,16 @@ A SHA pin is never "tidied" back down to a bare tag: upstream tags are force-rep

**Named instance of that limit — the syft installer.** `anchore/sbom-action/download-syft` in [`release.yml`](https://github.com/genealogix/glx/blob/main/.github/workflows/release.yml) is SHA-pinned per the rule above, and the action at that SHA pins syft to an exact version. It nonetheless downloads `https://raw.githubusercontent.com/anchore/syft/<version>/install.sh` and executes it with `sh` at run time. That URL resolves a **git tag**, which is a mutable ref — the same repointing primitive behind CVE-2025-30066 — and the script runs inside the release job, which holds `id-token: write` and sits upstream of cosign signing and the SLSA attestation. The SHA pin on the `uses:` line does not close that path; only upstream shipping a pinned-by-digest installer would. The risk is accepted rather than dismissed: the alternative is vendoring an installer this project would then have to keep current, and the exposure window is limited to a tagged release run. It is recorded here so the SHA pin is not read as implying more than it delivers.

#### Vulnerability suppression

<!-- Last reviewed: 2026-09-18 -->

`govulncheck` decides applicability by reachability: it reports a dependency advisory only when the vulnerable symbol is actually called. The OpenSSF Scorecard *Vulnerabilities* check does not — it runs [osv-scanner](https://github.com/google/osv-scanner) over the manifests and reports every advisory that matches a resolved module version, whether or not the affected package is in the build graph. The two tools therefore disagree on advisories that are scoped to a package GLX does not import, and that disagreement is resolved in [`osv-scanner.toml`](https://github.com/genealogix/glx/blob/main/osv-scanner.toml) at the repository root, the file osv-scanner and Scorecard both read.

The rule for that file: an advisory is suppressed only when it cannot be cleared by upgrading **and** the affected package is provably outside the build graph, and every entry states the reason and the command that re-verifies it. An advisory that applies is fixed by bumping the dependency, never by an entry here — which is the same principle as [Action pinning](#action-pinning) above, where the control’s accepted consequence and its known limits are written down rather than papered over.

One entry exists today. `GO-2026-5932` marks `golang.org/x/crypto/openpgp` unmaintained; it has no fixed version, so no upgrade clears it. `golang.org/x/crypto` is reached only through `github.com/go-git/go-git/v5`, which uses `golang.org/x/crypto/hkdf` and gets its OpenPGP support from `github.com/ProtonMail/go-crypto/openpgp` — the maintained replacement. `go list -deps ./...` returns no `golang.org/x/crypto/openpgp` package, and `govulncheck` agrees, reporting it as present in a required module but uncalled.

### Level 3 — Mature

| Control | Status | Evidence |
Expand Down
10 changes: 5 additions & 5 deletions ci-tools/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ require (
github.com/google/go-cmp v0.7.0 // indirect
github.com/hmarr/codeowners v1.2.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.44.0 // indirect
golang.org/x/telemetry v0.0.0-20260421165255-392afab6f40e // indirect
golang.org/x/tools v0.44.0 // indirect
golang.org/x/mod v0.40.0 // indirect
golang.org/x/sync v0.22.0 // indirect
golang.org/x/sys v0.47.0 // indirect
golang.org/x/telemetry v0.0.0-20260811182544-a038080d80e5 // indirect
golang.org/x/tools v0.49.0 // indirect
golang.org/x/vuln v1.3.0 // indirect
)
20 changes: 10 additions & 10 deletions ci-tools/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ require (
github.com/spf13/pflag v1.0.9 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.yaml.in/yaml/v3 v3.0.5 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/net v0.56.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/crypto v0.56.0 // indirect
golang.org/x/net v0.57.0 // indirect
golang.org/x/sys v0.47.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
Loading
Loading