chore(deps): post-v0.2.0 dependency and Dependabot config cleanup - #131
Merged
Conversation
….lock Reapplies the bump Dependabot proposed in #127, which could not merge as opened: Dependabot edits pyproject.toml without touching backend/requirements.lock, so CI's drift gate rejects it. The lock is regenerated with the pinned command from CONTRIBUTING.md § Backend dependency lock (uv 0.8.17, --group build --generate-hashes --python-version 3.14) and moves three lines: fastapi's version and its two wheel hashes. starlette stays at the explicitly-pinned 1.3.1. All thirteen patch releases are internal dependency-tree/OpenAPI refactors (0.140.1-0.140.7) or fixes to paths this backend does not use - SSE/JSONL streaming, response_model_* on Iterable returns, jsonable_encoder's exclude_defaults, and nested-Annotated sequence params. Verified by grep: no jsonable_encoder, no StreamingResponse, no SSE helper, no response_model_* anywhere in backend/app. Confirmed against the regenerated lock rather than the version pin: a venv on CPython 3.14.6 built the way the image builds (pip install --require-hashes -r requirements.lock, then pip install --no-deps --no-build-isolation .) runs the full suite green - 666 passed, 5 skipped, identical to the pre-bump baseline.
Reapplies what Dependabot proposed in #128, SHA-pinned per the repo's Actions convention with the tag as a trailing comment. The commit SHA was resolved from upstream with git ls-remote --tags against docker/login-action rather than read off the bump description - refs/tags/v4.5.2 maps to 371161bbe7024a29a25c5e19bfcbc0804fe9ad2c. Trusting a rendered SHA is the substitution a SHA pin exists to prevent. v4.5.2 contains one substantive commit, "surface Docker Hub OIDC error responses" (docker/login-action#1058): it improves the error text when a Docker Hub OIDC login fails. All three call sites here log in to ghcr.io with the built-in GITHUB_TOKEN as username/password - not Docker Hub, not OIDC - so the changed path is never entered. No input, output, or breaking changes. This matters because these workflows run only on the tag-gated publish path and the nightly, which CI cannot exercise.
Two ignore entries, both closing a recurring failure. node majors, on the docker entry. #126 proposed node 22-bookworm-slim -> 25-bookworm-slim. Node's odd-numbered lines never become LTS: v25 reached end-of-life on 2026-06-01, before that PR was opened and years before the 22 line's 2027-04-30, so it would have moved the frontend builder onto an unsupported runtime. The wanted move is 22 -> 24 (Active LTS, 2028-04-30) and is tracked in docs/ROADMAP.md because it spans the Dockerfile, ci.yml and CONTRIBUTING.md together. The ignore is scoped to version-update:semver-major so digest refreshes of the pinned 22 tag still come through - declining a major previously left Dependabot offering nothing for this image and the digest went stale until #107 refreshed it by hand. dependency-name: scrye, on both docker entries. docker/docker-compose.yml pins image: scrye:0.2.0, Scrye's own locally-built tag; Dependabot resolves the unqualified name as Docker Hub's library/scrye, gets a 401, and fails the run with private_source_authentication_failure. Reading the files confirms the compose file carries the only such reference and docker/Dockerfile carries none, so the substantive entry is on the docker-compose ecosystem; it is repeated on the docker entry because the failing run's ecosystem is visible only in the Dependabot UI, and suppressing a name the Dockerfile never mentions costs nothing. The image tag itself is unchanged: qualifying it as ghcr.io/tyler-rich/scrye:0.2.0 would make the compose file pull a published image instead of building locally, changing what the documented quick start does.
…iagnosis A version update sitting on main is not the documented security-update case. #126, #127 and #128 were all opened against dev correctly - their head branches carry the /dev/ target-branch segment - and each records an automatic_base_change_succeeded event 2-3 seconds after the v0.2.0 promotion (#122) merged. GitHub retargets open PRs whose base branch is deleted to the merged PR's base, and auto-delete-on-merge deleted dev as the promotion's head branch. dependabot.yml was not involved, and neither was grouped security updates: all three are version updates across three different ecosystems. The remedy is already in place (auto-delete disabled on 2026-08-02); target-branch and the grouping setting stay as they are. CLAUDE.md § Dependency hygiene and a new CONTRIBUTING.md subsection carry the rule and a two-signal table for telling the two causes apart. docs/ROADMAP.md: the Node 22 -> 24 item rewritten to state that it must be its own PR with a registry-verified digest pin, and to name the three files that have to move together - bumping the Dockerfile alone would leave CI on 22 while the image builds on 24. New item for enabling GitHub code scanning (CodeQL) on Python and TypeScript: free for public repos, default setup adds a workflow running on every push and PR, and the first run surfaces a triage backlog, so it wants its own session. docs/ARCHIVE.md gains the dated §14 entry covering all of it, including the correction to #125: GHSA-mh99-v99m-4gvg was re-scoped per major on 2026-07-31 and 1.1.18 / 2.1.4 are both above their line's first patched version, so the #121 bump did clear it. Verified at the source - 1.1.17 and 2.1.3 introduce EXPANSION_MAX_LENGTH and name CVE-2026-14257 in their comments - not from the advisory metadata that produced the wrong claim in the first place. #125 closed on its own stated criterion; #123 and #124 re-verified and still accurate.
The retarget diagnosis said #110 and #120 have "no such event", which is true of automatic_base_change_succeeded but reads as though their timelines are bare. #120 carries two ordinary base_ref_changed events - the manual retarget-to-dev-and-back already documented on 2026-07-31 - so a reader checking a timeline needs the event types told apart, not just the presence of a base change.
The ignore is scoped to version-update:semver-major so digest refreshes of the pinned 22 tag keep arriving, but that rests on documented update-types semantics plus reported updater behaviour - not on an observed run in this repository. Says so, names the first scheduled docker run as the test, and records the fallback if it is wrong (refresh the digest by hand, as #107 did).
Supersedes the 4.5.2 applied earlier in this branch. 4.5.2's only substantive commit improves Docker Hub OIDC error text, a path none of the three call sites can reach - all three log in to ghcr.io with the built-in GITHUB_TOKEN as username/password. 4.6.0 lands a day later and its change is at least adjacent to what this repo does: it hardens the buildx-scoped config path used by the login -> buildx -> push chain, and carries the action's own bundled dependency bumps. SHA resolved from upstream with git ls-remote --tags, not from a changelog: refs/tags/v4.6.0 -> dbcb813823bdd20940b903addbd779551569679f. The moving v4 tag currently points at the same commit; the pin is the immutable v4.6.0 commit, not the alias. Read at the source by diffing v4.5.2...v4.6.0 rather than from the release notes, because these workflows run only on the tag-gated publish path and the nightly, which CI cannot exercise. action.yml is byte-identical, src/main.ts and src/docker.ts are unchanged, and the whole change is in src/context.ts's buildx scoped-config-dir helper - path.resolve containment on the registry and scope inputs behind a new isChildPath() helper. Behaviourally a no-op here: that helper returns early on 'if (scopeDisabled() || !scope || scope === "")', and no call site passes a scope input. Currency plus defence-in-depth, not a fix for anything reachable.
Applied by hand rather than waiting for Dependabot: it has not proposed this, and after closing #126/#127/#128 there are no open Dependabot PRs at all, so a HIGH advisory was waiting on a bot that was not going to act. Which version actually clears it was verified in the published source, not taken from the advisory range - GHSA-mh99-v99m-4gvg was re-scoped mid-flight and #125 was wrong because of it. postcss 8.5.18's lib/previous-map.js loadFile() gains the containment check the advisory describes (relative(dirname(cssFile), path) rejected when it is '..', starts with '../', or is absolute); 8.5.17 has none of it. So 8.5.18 is the real floor. The same check is still present in 8.5.25, which is what is pinned - current release on the pinned 8.5 line, per CLAUDE.md § Dependency hygiene. No overrides entry needed and no parent bumped: postcss is a direct devDependency here, and every package that also reaches it declares a peer/caret range 8.5.25 satisfies (vite's ^8.5.3 included), so raising the single pin lifts the tree and npm ls shows one deduped copy. Regenerated with npm pkg set + npm install --package-lock-only, not by editing version strings, so resolved URLs and integrity hashes moved with the version. Diff is postcss 8.5.16 -> 8.5.25, its nanoid floor ^3.3.12 -> ^3.3.16, and nanoid 3.3.15 -> 3.3.16 - all dev-only, no new packages. npm ci installs 8.5.25 with the containment check present; ESLint, Prettier, the 20-file/69-test Vitest suite and npm run build all pass; npm audit no longer reports postcss. Nothing ships either way - postcss runs during vite build and the image copies only dist/.
The §14 entry's login-action item now covers 4.6.0 and why it was taken over the 4.5.2 that #128 proposed, including the source-level diff and the honest note that the hardened path is gated on a scope input this repo never passes. The advisory-issues item gains the full postcss treatment: why it was applied by hand, the 8.5.18 fix floor verified in the published source rather than read off the advisory range, why no overrides entry or parent bump was needed, and the post-bump verification. #124 closed on its own stated criterion, the same standard applied to #125. CHANGELOG [Unreleased] gains a Security entry for the postcss advisory - and says explicitly that unlike the brace-expansion bump in 0.2.0, this one does clear its advisory - plus Changed entries for the login-action and fastapi pins.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Applies what the three Dependabot PRs closed on 2026-08-02 proposed (#126, #127, #128), correctly, plus the config changes that stop them recurring — and answers the base-branch question those closures raised. Also closes #124 by applying the
postcssbump by hand, since with the Dependabot queue empty nothing was going to propose it.Item 5 is a diagnosis with a recommendation, and it deliberately changes neither
target-branchnor the grouped-security-updates repo setting.1.
fastapi0.140.0 → 0.140.13, lock regenerated (was #127)#127 failed CI because Dependabot edits
pyproject.tomlwithout touchingbackend/requirements.lock, so the drift gate rejects it. A pip bump here is never mergeable as opened.Regenerated with the pinned command from
CONTRIBUTING.md§ Backend dependency lock (uv 0.8.17,--group build --generate-hashes --python-version 3.14). The lock moves three lines —fastapi's version and its two wheel hashes.starlettestays at the explicitly-pinned 1.3.1.Changelog read before applying; nothing in the thirteen releases touches a path Scrye uses.
lru_cacheinclude_router(),response_model_*ignored forIterable[…], SSE line-splitting,status_codeignored on SSE/JSONLresponse_model_*anywhereexclude_defaultsnot propagated to dict keys/values injsonable_encoderjsonable_encoderis never calledAnnotatedtypesAnnotated[list[str], …]ispydantic-settings'NoDecodeinapp/core/config.py, not a request paramVerified against the regenerated lock, not the version pin. A venv on CPython 3.14.6 built the way the image builds —
pip install --require-hashes -r requirements.lock, thenpip install --no-deps --no-build-isolation .— runs the full suite green: 666 passed, 5 skipped, identical to the pre-bump baseline on the same interpreter.2.
docker/login-action4.5.1 → 4.6.0, SHA-pinned (#128 proposed 4.5.2)Pinned
dbcb813823bdd20940b903addbd779551569679f # v4.6.0inpublish.yml,dev-nightly.ymlandrescan.yml.Why 4.6.0 over the 4.5.2 that #128 proposed. 4.5.2's only substantive commit improves Docker Hub OIDC error text — a path none of the three call sites can reach, since all three log in to
ghcr.iowith the built-inGITHUB_TOKENas username/password. 4.6.0 supersedes it a day later, its change is adjacent to what this repo actually does (the login → buildx-builder → push chain), and it carries the action's own bundled dependency bumps. Taking it also avoids pinning to a release that was already superseded on the day it was applied.SHA resolved from upstream with
git ls-remote --tags, not from a changelog:refs/tags/v4.6.0→dbcb813…. Note upstream's movingv4tag currently points at the same commit — the pin here is the immutablev4.6.0commit, not the alias.Read at the source, by diffing
v4.5.2...v4.6.0rather than trusting the release notes, because these workflows run only on the tag-gated publish path and the nightly, which CI cannot exercise:action.ymlis byte-identical — no input added, removed, renamed, or re-defaulted.src/main.tsandsrc/docker.tsare unchanged — the login flow itself does not move.src/context.ts's buildx scoped-config-dir helper:path.resolvecontainment on the resolved registry directory, validation of thescopeinput (at most one@, actions matching^[a-z]+(,[a-z]+)*$), and rejection of a scope path escaping the registry dir — via a newisChildPath()helper.The honest reading: behaviourally a no-op for us today. That helper returns early on
if (scopeDisabled() || !scope || scope === '') return '', and no call site passes ascopeinput (each passes exactlyregistry,username,password— noecr, nologout, no OIDC, and noDOCKER_CONFIGanywhere in the repo). Two of the three sites do go on to run buildx via.github/actions/build-image, so the hardened area is on the chain they use;rescan.ymlonly does a plaindocker pull. Recorded as currency plus defence-in-depth against a futurescope, not as a fix for anything reachable.3. Node: majors ignored, and the 22 → 24 move written up (was #126)
#126 proposed node 25. Per the Node release schedule, v25 has no LTS date at all and reached end-of-life on 2026-06-01 — before the PR was opened, and years before the 22 line's 2027-04-30. It would have moved the builder onto an unsupported runtime.
.github/dependabot.yml'sdockerentry now ignoresnodemajors, scoped toversion-update:semver-majorso digest refreshes of the pinned 22 tag still come through — the config can express this, so no workaround was needed. That scoping is load-bearing: declining a major previously left Dependabot offering nothing for this image and the digest went stale until #107 refreshed it by hand. The archive entry is explicit that this rests on documentedupdate-typessemantics rather than an observed run in this repo, and names the next scheduleddockerrun as the test.docs/ROADMAP.md's Node item is rewritten to say the target is 24 (Active LTS, 2028-04-30), that it must be its own PR with a registry-verified digest pin, and to name the three files that must move together —docker/Dockerfile's builder digest,ci.yml'snode-version, andCONTRIBUTING.md's stated Node requirement (plusREADME.md§ Requirements). Bumping the Dockerfile alone would leave CI building on 22 while the image builds on 24.4. The self-referencing image tag
dependency-name: "scrye"ignored, closing theprivate_source_authentication_failurerun.Which entry scans the compose file was verified by reading both files, not assumed:
docker/docker-compose.ymlcarries the onlyimage: scrye:0.2.0reference, anddocker/Dockerfilehas noscryeimage reference at all. So the substantive ignore is on thedocker-composeentry; it is repeated on thedockerentry because the failing run's ecosystem is visible only in the Dependabot UI/job logs, which are not reachable from a code session, and suppressing a name the Dockerfile never mentions costs nothing.The image tag itself is unchanged, as required.
5. The base-branch problem — diagnosed, no setting changed
It is not grouped security updates, and it is not
target-branch.#126/#127/#128 are all version updates across three different ecosystems, and their head branches carry the
/dev/target-branch segment — Dependabot read the config and honoured it. Each PR's timeline then records anautomatic_base_change_succeededevent:dependabot/docker/docker/dev/docker-images-0e8fc498dedependabot/pip/backend/dev/backend-dependencies-fd4d557a46dependabot/github_actions/dev/github-actions-85123b4e12The v0.2.0 promotion #122 merged at 23:56:07Z. GitHub automatically retargets open PRs whose base branch is deleted to the merged PR's base — and a promotion has
devas its head branch, so auto-delete-on-merge deleteddevand moved the entire open-PR queue ontomainin the same instant.base.shaon all three is the promotion's merge commit.This is the second blast radius of the auto-delete incident already recorded on 2026-08-02: it did not only delete a branch that had to be restored, it re-based every open PR onto the protected release branch.
#110 and #120 are the genuinely-documented security-update case: their head branches carry no
/dev/segment, and neither has anautomatic_base_change_succeededevent. Read the event type precisely — #120 does carry two plainbase_ref_changedevents, which are the manual retarget-to-dev-and-back already documented on 2026-07-31, a different thing entirely.Recommendation: nothing further to change. The remedy is already in place — auto-delete was disabled on 2026-08-02, and with no head-branch deletion there is no retarget. Specifically: do not alter
target-branch, do not trade grouped security updates for one-PR-per-advisory (grouping is not implicated), and do not re-enable auto-delete. Written intoCLAUDE.md§ Dependency hygiene and a newCONTRIBUTING.mdsubsection with a two-signal table for telling the two causes apart.6. The three advisory issues — two now closed
>=7.12.0 <8.3.0, no 7.x fix,npm auditstill offers only the 7.11.0 downgrade.postcss8.5.16 → 8.5.25.lib/previous-map.jsloadFile()gains the containment check (relative(dirname(cssFile), path)rejected when'..','../…', or absolute) that 8.5.17 lacks. 8.5.25 still carries it, and is the current release on the pinned major.overridesentry, no parent bump.postcssturns out to be a direct devDependency, and every package that also reaches it declares a peer/caret range 8.5.25 satisfies (vite's^8.5.3included), so one pin lifts the tree —npm ls postcss --allshows a single deduped copy. The "stop if a parent needs a major" condition never arose.npm pkg set+npm install --package-lock-only. Diff:postcss8.5.16 → 8.5.25, itsnanoidfloor^3.3.12→^3.3.16,nanoid3.3.15 → 3.3.16 — all dev-only, no new packages.npm auditno longer reportspostcss; the two remaining highs are the react-router pair from GHSA-qwww-vcr4-c8h2 (react-router RSC-mode CSRF bypass) — fixed by the 7.18.2 backport, not the 8.3.0 major #123. Nothing ships either way — PostCSS runs duringvite buildand the image copies onlydist/.<=5.0.7into per-major ranges (<1.1.17→ 1.1.17;>=2.0.0,<2.1.3→ 2.1.3). Scrye's 1.1.18 and 2.1.4 are both above their line's first patched version, so the chore(release): v0.2.0 release prep — CHANGELOG cut, brace-expansion bump, Dependabot security-PR routing #121 bump did clear it. Verified at the source: 1.1.17 and 2.1.3 introduceEXPANSION_MAX_LENGTHand name CVE-2026-14257 in their comments; 1.1.16 and 2.1.2'sindex.jsdo not. Closed on the issue's own stated criterion; original text preserved in a collapsed block; no duplicate opened.The shipped
[0.2.0]CHANGELOG is left as published — it was accurate against the advisory data at tag time, and §14 carries the correction.[Unreleased]gains a### Securityentry for the postcss bump that says explicitly that, unlike brace-expansion in 0.2.0, this one does clear its advisory.7. CodeQL roadmap item
New
docs/ROADMAP.md§ Near-term item for enabling GitHub code scanning on Python and TypeScript: free for public repos, default setup adds a workflow running on every push and PR, and the first run surfaces a triage backlog — so it wants its own scoped session, not a settings-page click. Includes the default-vs-advanced-setup decision and a requirement that dismissals carry written reasons.Verification
f062ceb: Backend ✅ · Frontend ✅ · Image — build + dogfood self-scan ✅ (multi-arch check skipped by design on adev-based PR)ruff✅ ·black --check✅ ·gen_env_example --check✅ · lock-drift recompile idempotent ✅ ·pytest666 passed, 5 skipped on CPython 3.14.6 against the hash-pinned lock ✅npm run build✅ ·npm auditclean of postcss ✅See
docs/ARCHIVE.md§ Deviations for the dated entry covering all of the above.