diff --git a/.pql/changelog/ticket_history/2026-06.sql b/.pql/changelog/ticket_history/2026-06.sql index 3e0e9ad1..ff314c97 100644 --- a/.pql/changelog/ticket_history/2026-06.sql +++ b/.pql/changelog/ticket_history/2026-06.sql @@ -3514,3 +3514,13 @@ INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, chang INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB5HMYDXP62RKH3HP55T6AYG', 'status', 'backlog', 'review', NULL, '2026-06-10 18:24:41', '2026-06-10 18:24:41', '2026-06-10 18:24:41', NULL, 'c5a88f9594c44896a6a3d1a4b2418ed2', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB5HMYDXP62RKH3HP55T6AYG', 'status', 'review', 'done', NULL, '2026-06-10 18:27:58', '2026-06-10 18:27:58', '2026-06-10 18:27:58', NULL, 'a9c72ab53b69f5ca6bf1fa4dd0ddfa05', 2) ON CONFLICT(hash) DO NOTHING; INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FB5M14B76B31654D959XM5AC', 'status', 'backlog', 'done', NULL, '2026-06-10 18:38:53', '2026-06-10 18:38:53', '2026-06-10 18:38:53', NULL, 'b94cfe8ba315b3be6775474c681b4e80', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBAWHM1SQ1686ZJ8JQCFQ1ZW', 'description', 'flutter pub outdated reports 16 packages behind latest (3 direct: ffi 2.1.3→2.2.0, jovial_svg 1.1.26→1.1.30, markdown 7.2.2→7.3.1; dev: alchemist 0.12.1→0.14.0, mocktail 1.0.4→1.0.5, test 1.31.0→1.31.1; plus transitive incl. xml 6.6.1→7.0.1 major). Per the prefer-zero-deps + exact-pin + advisory-review guardrail (D-42, CLAUDE.md supply chain), evaluate each pinned/direct dep: review CVEs/advisories (OSV.dev + pub.dev) for the current pin AND the candidate version, then bump the safe ones (artefact + assets/licenses.yaml in the same commit) and document any deliberately-held pins. Transitive deps move with the resolver/Flutter SDK; note but don''t force. Triggered by repeated ''N packages have newer versions'' noise on every build.', 'flutter pub outdated reports 16 packages behind latest (3 direct: ffi 2.1.3→2.2.0, jovial_svg 1.1.26→1.1.30, markdown 7.2.2→7.3.1; dev: alchemist 0.12.1→0.14.0, mocktail 1.0.4→1.0.5, test 1.31.0→1.31.1; plus transitive incl. xml 6.6.1→7.0.1 major). Per the prefer-zero-deps + exact-pin + advisory-review guardrail (D-42, CLAUDE.md supply chain), evaluate each pinned/direct dep: review CVEs/advisories (OSV.dev + pub.dev) for the current pin AND the candidate version, then bump the safe ones (artefact + assets/licenses.yaml in the same commit) and document any deliberately-held pins. Transitive deps move with the resolver/Flutter SDK; note but don''t force. Triggered by repeated ''N packages have newer versions'' noise on every build. + +FOLLOW-UP SCOPE (folded in 2026-06-11): + +1. DONE: osv-scanner supply-chain gate added to `make push-check` (ci/osv_scan.sh; fail-closed on any pubspec.lock advisory). Requires osv-scanner on PATH (brew install osv-scanner). + +2. TODO — tighten env floors to reality. pubspec.yaml `environment` currently declares Dart >=3.5.0 / Flutter >=3.19.0, but we actually require more (alchemist 0.12 needs Flutter 3.32; the held markdown 7.3.1 needs Dart 3.9). Raise floors to ~Dart >=3.9.0 / Flutter >=3.32.0 — honest minimums. Raising Dart to 3.9 also unblocks the held markdown 7.2.2 -> 7.3.1 bump. + +3. TODO — exact toolchain pin for reproducible builds. No FVM .fvmrc / .tool-versions / .flutter-version exists; a fresh clone builds with whatever Flutter the dev has (>= floor). Add an exact pin (FVM .fvmrc or asdf/mise .tool-versions) targeting the current toolchain (Dart 3.12.1 / Flutter 3.44.1).', NULL, '2026-06-11 07:05:54', '2026-06-11 07:05:54', '2026-06-11 07:05:54', NULL, 'f6d9c657c6987f7927bc3ba0bc02b3a4', 2) ON CONFLICT(hash) DO NOTHING; +INSERT INTO ticket_history (ticket_record_id, field, old_value, new_value, changed_by, changed_at, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBAWHM1SQ1686ZJ8JQCFQ1ZW', 'status', 'backlog', 'ready', NULL, '2026-06-11 07:05:58', '2026-06-11 07:05:58', '2026-06-11 07:05:58', NULL, '1553f134361839180feffa625a88c06d', 2) ON CONFLICT(hash) DO NOTHING; diff --git a/.pql/changelog/tickets/2026-06.sql b/.pql/changelog/tickets/2026-06.sql index 378edcb5..8e62733c 100644 --- a/.pql/changelog/tickets/2026-06.sql +++ b/.pql/changelog/tickets/2026-06.sql @@ -3241,3 +3241,21 @@ INSERT INTO tickets (record_id, type, parent_record_id, title, description, stat This is a wrong-workDir timing issue, not db-busy (so the T-350 retry doesn''t catch it). Fix: the pql-backed panes refetch on ProjectOpened (which fires after the IPC server swaps to the project workRoot). Implemented for the tickets pane; the decisions/pql/search panes share the latent bug and should get the same refetch.', 'done', 'high', NULL, NULL, NULL, '2026-06-10 18:34:05', '2026-06-10 18:38:53', NULL, '3891f832c1d930d8fefca2fae7ffea86', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBAWHM1SQ1686ZJ8JQCFQ1ZW', 'task', NULL, 'Evaluate pinned dependency versions + full CVE/advisory check', 'flutter pub outdated reports 16 packages behind latest (3 direct: ffi 2.1.3→2.2.0, jovial_svg 1.1.26→1.1.30, markdown 7.2.2→7.3.1; dev: alchemist 0.12.1→0.14.0, mocktail 1.0.4→1.0.5, test 1.31.0→1.31.1; plus transitive incl. xml 6.6.1→7.0.1 major). Per the prefer-zero-deps + exact-pin + advisory-review guardrail (D-42, CLAUDE.md supply chain), evaluate each pinned/direct dep: review CVEs/advisories (OSV.dev + pub.dev) for the current pin AND the candidate version, then bump the safe ones (artefact + assets/licenses.yaml in the same commit) and document any deliberately-held pins. Transitive deps move with the resolver/Flutter SDK; note but don''t force. Triggered by repeated ''N packages have newer versions'' noise on every build.', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-11 06:50:21', '2026-06-11 06:50:21', NULL, 'd5de0d7f9b1c365f7a35c6d0aefa178e', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBAWHM1SQ1686ZJ8JQCFQ1ZW', 'task', NULL, 'Evaluate pinned dependency versions + full CVE/advisory check', 'flutter pub outdated reports 16 packages behind latest (3 direct: ffi 2.1.3→2.2.0, jovial_svg 1.1.26→1.1.30, markdown 7.2.2→7.3.1; dev: alchemist 0.12.1→0.14.0, mocktail 1.0.4→1.0.5, test 1.31.0→1.31.1; plus transitive incl. xml 6.6.1→7.0.1 major). Per the prefer-zero-deps + exact-pin + advisory-review guardrail (D-42, CLAUDE.md supply chain), evaluate each pinned/direct dep: review CVEs/advisories (OSV.dev + pub.dev) for the current pin AND the candidate version, then bump the safe ones (artefact + assets/licenses.yaml in the same commit) and document any deliberately-held pins. Transitive deps move with the resolver/Flutter SDK; note but don''t force. Triggered by repeated ''N packages have newer versions'' noise on every build. + +FOLLOW-UP SCOPE (folded in 2026-06-11): + +1. DONE: osv-scanner supply-chain gate added to `make push-check` (ci/osv_scan.sh; fail-closed on any pubspec.lock advisory). Requires osv-scanner on PATH (brew install osv-scanner). + +2. TODO — tighten env floors to reality. pubspec.yaml `environment` currently declares Dart >=3.5.0 / Flutter >=3.19.0, but we actually require more (alchemist 0.12 needs Flutter 3.32; the held markdown 7.3.1 needs Dart 3.9). Raise floors to ~Dart >=3.9.0 / Flutter >=3.32.0 — honest minimums. Raising Dart to 3.9 also unblocks the held markdown 7.2.2 -> 7.3.1 bump. + +3. TODO — exact toolchain pin for reproducible builds. No FVM .fvmrc / .tool-versions / .flutter-version exists; a fresh clone builds with whatever Flutter the dev has (>= floor). Add an exact pin (FVM .fvmrc or asdf/mise .tool-versions) targeting the current toolchain (Dart 3.12.1 / Flutter 3.44.1).', 'backlog', 'medium', NULL, NULL, NULL, '2026-06-11 06:50:21', '2026-06-11 07:05:54', NULL, 'd81c595ab22359cc6c75d490d17d8c5d', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); +INSERT INTO tickets (record_id, type, parent_record_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('06FBAWHM1SQ1686ZJ8JQCFQ1ZW', 'task', NULL, 'Evaluate pinned dependency versions + full CVE/advisory check', 'flutter pub outdated reports 16 packages behind latest (3 direct: ffi 2.1.3→2.2.0, jovial_svg 1.1.26→1.1.30, markdown 7.2.2→7.3.1; dev: alchemist 0.12.1→0.14.0, mocktail 1.0.4→1.0.5, test 1.31.0→1.31.1; plus transitive incl. xml 6.6.1→7.0.1 major). Per the prefer-zero-deps + exact-pin + advisory-review guardrail (D-42, CLAUDE.md supply chain), evaluate each pinned/direct dep: review CVEs/advisories (OSV.dev + pub.dev) for the current pin AND the candidate version, then bump the safe ones (artefact + assets/licenses.yaml in the same commit) and document any deliberately-held pins. Transitive deps move with the resolver/Flutter SDK; note but don''t force. Triggered by repeated ''N packages have newer versions'' noise on every build. + +FOLLOW-UP SCOPE (folded in 2026-06-11): + +1. DONE: osv-scanner supply-chain gate added to `make push-check` (ci/osv_scan.sh; fail-closed on any pubspec.lock advisory). Requires osv-scanner on PATH (brew install osv-scanner). + +2. TODO — tighten env floors to reality. pubspec.yaml `environment` currently declares Dart >=3.5.0 / Flutter >=3.19.0, but we actually require more (alchemist 0.12 needs Flutter 3.32; the held markdown 7.3.1 needs Dart 3.9). Raise floors to ~Dart >=3.9.0 / Flutter >=3.32.0 — honest minimums. Raising Dart to 3.9 also unblocks the held markdown 7.2.2 -> 7.3.1 bump. + +3. TODO — exact toolchain pin for reproducible builds. No FVM .fvmrc / .tool-versions / .flutter-version exists; a fresh clone builds with whatever Flutter the dev has (>= floor). Add an exact pin (FVM .fvmrc or asdf/mise .tool-versions) targeting the current toolchain (Dart 3.12.1 / Flutter 3.44.1).', 'ready', 'medium', NULL, NULL, NULL, '2026-06-11 06:50:21', '2026-06-11 07:05:58', NULL, '352f3ceb8714f14bd12515a9b4542794', 2) ON CONFLICT(record_id) DO UPDATE SET type=excluded.type, parent_record_id=excluded.parent_record_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash); diff --git a/CHANGELOG.md b/CHANGELOG.md index 003c90f0..16b6c64d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,11 @@ heading, and (b) bumping `pubspec.yaml` `version:` in the same commit. `xml` 7.0.1), `mocktail` 1.0.4→1.0.5. Deliberately held with reasons in `pubspec.yaml`: `markdown` (7.3.1 needs Dart 3.9), `alchemist` (0.13 golden churn), `test` (Flutter-SDK locked). (T-353) +- **Supply-chain gate in `make push-check`.** A new `security` step runs + `osv-scanner` over `pubspec.lock` and fails the push if any resolved + dependency has a known advisory — a hard gate on top of `dart pub`'s passive, + non-failing advisory print. Requires `osv-scanner` on PATH + (`brew install osv-scanner`). (T-353) ## [2.3.2] — 2026-06-11 diff --git a/Makefile b/Makefile index 43ba366e..f9757514 100644 --- a/Makefile +++ b/Makefile @@ -309,8 +309,8 @@ clide-cli-clean: ## Remove the compiled C `clide` client. # -- security ------------------------------------------------------------- .PHONY: security -security: ## Dart advisory review. - @echo "security: Dart advisories reviewed manually before pubspec.yaml bumps." +security: ## Supply-chain gate — osv-scanner over pubspec.lock (fails on a known advisory). + ci/osv_scan.sh # -- pre-push gate -------------------------------------------------------- @@ -319,7 +319,7 @@ decisions-validate: ## Parser dry-run over governance/{decisions,questions,rejec pql decisions validate .PHONY: push-check -push-check: decisions-validate changelog-gate test-coverage coverage-gate test-core ## Pre-push gate (fast — <2 min target). Order is fail-fast: instant gates (decisions, changelog) first, then the coverage suite + gate (the expensive, most-likely-to-fail stage) BEFORE test-core — a coverage miss aborts here instead of after running everything, so a fix doesn't force a full re-run of the rest. test-coverage already runs the a11y suite (test/a11y), so no separate test-a11y pass. +push-check: decisions-validate changelog-gate security test-coverage coverage-gate test-core ## Pre-push gate (fast — <2 min target). Order is fail-fast: instant gates (decisions, changelog, security/osv) first, then the coverage suite + gate (the expensive, most-likely-to-fail stage) BEFORE test-core — a coverage miss aborts here instead of after running everything, so a fix doesn't force a full re-run of the rest. test-coverage already runs the a11y suite (test/a11y), so no separate test-a11y pass. .PHONY: push-check-full push-check-full: push-check test-integration smoke-bundle ## Pre-release gate (push-check + integration + smoke; slower). diff --git a/ci/osv_scan.sh b/ci/osv_scan.sh new file mode 100755 index 00000000..bc8e6bb4 --- /dev/null +++ b/ci/osv_scan.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# Supply-chain gate — fails the push if any resolved dependency in +# pubspec.lock has a known advisory (OSV / GitHub Advisory Database). +# +# Complements `dart pub get`'s passive advisory print (informational, +# non-failing) with a hard, fail-closed gate. Native deps (dugite, +# tree-sitter, wasmtime) are vendored by SHA and not in a lockfile OSV +# reads — they're reviewed separately on bump (D-42, CLAUDE.md supply chain). +set -euo pipefail +cd "$(dirname "$0")/.." + +# Resolve osv-scanner: PATH first, then a brew prefix (the git pre-push hook +# may run with a leaner PATH than the dev's interactive shell). +OSV="$(command -v osv-scanner || true)" +if [[ -z "$OSV" ]] && command -v brew >/dev/null 2>&1; then + cand="$(brew --prefix 2>/dev/null)/bin/osv-scanner" + [[ -x "$cand" ]] && OSV="$cand" +fi +if [[ -z "$OSV" ]]; then + echo "==> osv gate: osv-scanner not found on PATH." >&2 + echo " Install it: brew install osv-scanner" >&2 + echo " (or: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest)" >&2 + exit 2 +fi + +echo "==> osv gate: scanning pubspec.lock for known advisories" +if "$OSV" scan source --lockfile=pubspec.lock; then + echo "==> osv gate OK: no known advisories" +else + echo "==> osv gate FAIL: a dependency has a known advisory (see above)." >&2 + echo " Bump the affected package (+ its assets/licenses.yaml entry), or" >&2 + echo " document an explicit, justified exception before pushing." >&2 + exit 1 +fi