Forgejo's October looks calm only if you read it from the release list. Read it from the trust boundaries instead.

The review that carries an unvalidated commit

The headline fix in 17.0.1 / 16.0.7 / 15.0.11, shipped October 26 across all three maintained branches, is a CI trust bug in the same family as the Gitea batch documented in last week's correction:

When a review is submitted to Forgejo on a PR, the commit_id is never validated. This can allow someone to submit a review on a pull request that was authored by a trusted author, but, provide a malicious review commit_id, and run a workflow from an arbitrary commit (including one from a forked pull request).

The trigger event for CI is pull_request_review. The workflow environment (forgejo.ref) did not point at the pull request's own reference — it pointed at whatever commit the reviewer's event carried. The fix aligns forgejo.ref with refs/pull/<n>/head, which is GitHub's documented behavior for the same event. The consequence before the fix: the "trusted" determination for Actions — the gate that decides whether a workflow gets secrets and write tokens — evaluated a commit the platform never checked the provenance of. A review event authored on a trusted pull request, carrying a commit from a fork, was trusted.

The operational framing of this site's audience is: your community forge, Actions on, volunteers submitting reviews. Any workflow that gives privileges to "runs from trusted branches" was, until October 26, extending that trust across a review event without validating which tree the event pointed at. The fix is not optional anywhere Actions runs; on 15.0.11 LTS, 16.0.7 and 17.0.1 alike.

The 15.0.11 notes also carry two more fixes worth naming, because they were not in the security-blog summary anyone quoted: a NuGet package-API authentication bypass — authenticating with X-NuGet-ApiKey using an access token did not respect the token's scopes, so a token without packages scope could read/write packages (limited to the package APIs, but a scope check that doesn't work is a scope check the token system promises and the package API ignores); and a visibility bypass on watched and starred repositories — the exact bug class Gitea disclosed the week before as its Low, where API routes under users/{username} skipped the visibility checker because a middleware was missing. Same class, two projects, two weeks, one of them carrying CVE IDs and one carrying release-notes prose and no CVE at all. That is what cross-vendor disclosure asymmetry looks like from the outside, and it is worth remembering when someone hands you a "clean advisories record" as a quality signal.

One more item in the same release, easy to misread in the opposite direction: a bug fix that workflows running from a trusted PR base sometimes had secrets omitted. The forge's secret-injection was too conservative in a code path adjacent to the one that was too permissive. Trust logic gets patched in both directions, and neither direction is optional.

The EOL cliff, three days later

Forgejo 16.0 was released July 16, described as a "non-LTS" release "supported until 29 October 2026." That day has passed. 16.0.7 — October 26 — was its last security release. Every Forgejo operator on a 16.0 tag is now running an unsupported branch, three days after the branch last received a patch, in the same week the branch needed one.

The supported paths, and their trade-offs stated plainly:

  • 17.0.x — the current line, released October 15, with 17.0.0's rough edges (blocked pushes and merges; the project itself advised holding off) fixed in 17.0.1. One-week-old major, one-week-old patch history, actively maintained.
  • 15.0.11 LTS — the previous LTS branch, which received the same three security fixes on October 26 and continues on the LTS clock. Older, boring, still supported — the classic community choice.

On Gitea instead: Gitea's 28.2.0 covers the same week's ledger, and the correction above covers its accounting, which is larger than any release note states.

The 17.0 upgrade carries breaking changes worth the read before either move — one affects integrations concretely: action run webhook events are deprecated for removal in favor of workflow run events, the header names (X-*-Event, X-*-Event-Type) and the action key changed, no automatic migration exists, and completion notifications now fire only after all jobs of a run terminate. Any webhook receiver wired to Action run events — the chat notifications half the community forges run through Matrix or Discord — breaks quietly at upgrade unless migrated by hand first. Absolute links in rendered documents resolving against the repository root rather than the instance root is the other behavior change to grep your docs for. The org-invitations option (ADD_MEMBERS_BY_INVITATIONS) lands in the opposite column — it is a governance feature community forges will want: adds become reviewable invitations with a 14-day expiry instead of instant membership.

The clone path — Git LFS 3.8.1

Git LFS 3.8.1 (October 22) fixes four CVEs, and the split between them matters more than the count:

| CVE | What | Trigger | |---|---|---| | CVE-2026-69260 | SSH remote commands sent with unquoted arguments | server-side, on LFS-over-SSH | | CVE-2026-69261 | Cookie headers forwarded to a different host after an HTTP redirect | credentials to the wrong host | | CVE-2026-105802 | Line feed in a submodule's URL → arbitrary program execution | config the clone supplies | | CVE-2026-105803 | Custom transfer adapter defined in .lfsconfig → arbitrary program execution | config the clone supplies | | — | custom transfer commands restricted to local configuration sources (the fix) | — |

Four CVEs, and the client's attack surface is the configuration the clone supplies and the redirects it follows: two execute arbitrary programs from files the hostile repository carries (the submodule URL, the .lfsconfig transfer adapter — that is not memory corruption and it is not a chain, it is the repository's own configuration being executed by the client that cloned it), one injects an unquoted argument into an SSH remote command derived from the remote URL, and one forwards cookies across a redirect to a host that did not earn them. It is the same trust shape git-annex just closed — clone-time execution of config the clone supplies. The fix restricts custom transfer adapter definitions to local sources.

For community operators the threat model statement is short: your CI clones repositories you do not control. Public community forges accept pushes from anonymous accounts; the same machine that mirrors your production repo on schedule also clones strangers' code on schedule. Four CVEs that enter in and around the clone — in the LFS client every one of those clones shares — move "clone and run" from a policy for humans to a policy for runners, with the patched client as the floor.

The patched clients are where they should be: PackageCloud debs (bullseye through trixie, amd64 and arm64), Homebrew, and release tarballs with the SHA-256 set published alongside — and recorded somewhere the release cannot touch, per the artifact-channel lesson from the OVHcloud CLI.

What to actually do

Forgejo 16.x: choose now, not at the next security release — 17.0.1 (read the webhook deprecation and grep your receivers first) or 15.0.11 LTS. Both carry the review-trigger fix; neither is an upgrade you can defer on a branch with no maintainer.

Forgejo 15.x LTS: 15.0.11, no drama, no deadline yet.

Gitea: 28.2.0 remains the answer for the 28 line, and the corrected ledger behind it is the reason to stop reading release notes as the accounting.

Git LFS fleet: 3.8.1 everywhere CI clones, then audit which of your automated paths clone untrusted repositories — every one of them was in the blast radius, and the .lfsconfig class may not be finished in other clients.

Cross-vendor note, one sentence, no editorializing: the same review-and-fork trust class produced two Criticals and a High on one forge's disclosure ledger, a silent fix on the other's release notes, and the correction above documents how uneven the accounting is between them. The pull path is the perimeter. Treat pull_request_review, action run, and .lfsconfig as untrusted input channels in your own threat model, whichever forge is between you and the next bad week.