chore(plugins): update linter versions (security + simple bumps) - #2847
Conversation
The pinned 2.32.2 falls inside the vulnerable range of two advisories published 2026-07-27: CVE-2026-63325 (high, arbitrary code execution via Arazzo `$faker` expression, < 2.33.0) and CVE-2026-63225 (medium, path traversal in `split`, < 2.33.2). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Version-only bumps, each published at least 30 days ago and checked against GitHub Security Advisories: - checkstyle 13.5.0 -> 13.9.0 - pmd 7.25.0 -> 7.26.0 - swiftformat 0.61.1 -> 0.62.1 - swiftlint 0.63.3 -> 0.65.0 - terraform 1.15.6 -> 1.15.8 - trufflehog 3.95.5 -> 3.96.0 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Routine bumps within the majors landed by #2825, each published at least 30 days ago with no advisories: - eslint 10.5.0 -> 10.8.0 - oxlint 1.70.0 -> 1.76.0 - prisma 7.8.0 -> 7.9.1 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Coverage Impact - ubuntu-latest This PR will not change total coverage. 🚦 See full report on Qlty Cloud »🛟 Help
|
|
Coverage Impact - macos-15 This PR will not change total coverage. 🚦 See full report on Qlty Cloud »🛟 Help
|
shfmt >=3.9 rejects the basic fixture with "`then` must be followed by a statement list" and exits 1. Because the driver declares success_codes = [0, 1], qlty treated that parse failure as success, the rewrite never happened, and the linter silently reported zero issues. The fixture was invalid shell — an `if` with an empty body. Give it a body so both old and new shfmt parse it and still find it misformatted. The snapshot is unchanged: `output = "rewrite"` records only a generic formatting issue, not the file contents. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The shfmt format driver declared success_codes = [0, 1], but with `-w`
shfmt exits 0 whether or not it rewrites the file — exit 1 means only a
parse or I/O error. Accepting 1 as success meant any shell file shfmt
could not parse produced no rewrite, no error, and zero issues.
The exit-code contract is the same across versions, so this is not
version-gated: shfmt 3.8.0 also exits 1 on an unterminated quote,
unfinished `case`, or unclosed `{`. shfmt 3.9 only made the parser
stricter (an `if` with an empty body is now correctly rejected), which
is what surfaced this.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Claude Code Review
Claude Code Review is paused for this repository. To reconnect it, an admin of this repository's GitHub organization (or the account owner, for personal repositories) who can also manage your Claude organization's Code Review settings needs to re-link GitHub in Code Review settings. This is a one-time step.
Tip: disable this comment in your organization's Code Review settings.
Automated PR for release 0.644.0. Review (and edit, if needed) the changelog entry below, then merge this pull request to publish the release. ## Draft release notes ### Fixed - Bump redocly from 2.32.2 to 2.41.0, which patches CVE-2026-63325 (arbitrary code execution via Arazzo `$faker` expressions) and CVE-2026-63225 (path traversal in the `split` command) (#2847) - Fix shfmt reporting unparseable shell files as clean; files it cannot parse now surface a plugin error instead of silently passing with zero findings (#2847) Co-authored-by: qlty-releases[bot] <181762136+qlty-releases[bot]@users.noreply.github.com>

Summary
Update linter plugin versions with stability and security vetting. Covers the
gaps the weekly
updateLinterVersions.tsscript leaves behind.After this PR, knip is the only linter with
latest_versionahead ofknown_good_version(blocked on #2826), and every script-unreachable linteris either current or has a filed reason below.
Updates
Security
redocly 2.32.2 was vulnerable. The pinned version falls inside the range of
two advisories published 2026-07-27:
$fakerexpression using
respect; patched in 2.33.0.splitcommand; patchedin 2.33.2.
2.41.0 clears both.
All other candidates were checked against
/repos/{owner}/{repo}/security-advisoriesand the GitHub Advisory Database, filtering withdrawn advisories. The only hits
were against versions far older than both current and target (checkstyle < 8.29,
pmd <= 7.21.0, trufflehog < 3.81.9, terraform < 0.12.17, prisma < 2.20.0).
trufflehog is a security-sensitive linter (secret detection, elevated
trust). Beyond the advisory check, its 3.96.0 release assets were verified for
expected content types and its release notes reviewed — nothing suspicious.
shfmt was reporting unparseable files as clean
The version bump surfaced a live correctness bug, fixed here.
The format driver declared
success_codes = [0, 1], but with-wshfmt exits0 whether or not it rewrites the file — exit 1 means only a parse or I/O
error. Accepting 1 as success meant any shell file shfmt could not parse
produced no rewrite, no error, and zero issues. Verified across both versions:
case{if true; then/fi(empty body)The exit-code contract is identical across versions, so this is not
version-gated — 3.8.0 silently passes an unterminated quote today, and gating
would preserve that for anyone pinning an old shfmt. shfmt 3.9 only made the
parser stricter (an
ifwith an empty body is now correctly rejected), which iswhat exposed the bug: the old fixture was invalid shell, so 3.13.1 refused it,
the rewrite never ran, and the test went green with zero findings.
The fixture now has a body, so old and new shfmt both parse it and both still
find it misformatted. Its snapshot is unchanged —
output = "rewrite"recordsonly a generic formatting issue, not file contents. An invalid fixture cannot be
kept as coverage once exit 1 is honored, since the harness asserts
success: trueper fixture.Behavior change: shell files that cannot be parsed now surface a plugin
error instead of silently reporting clean.
Worth a follow-up audit: biome, ktlint, markdownlint, rubocop, ruby-stree, and
standardrb also declare
[0, 1]on rewrite-mode drivers. For several of themexit 1 legitimately means "issues found" and the config is correct — but each
should be checked against its tool's actual contract.
Coverage of script-unreachable linters
The script can only update a linter that declares
releasesor aruntimein{java, php, ruby, python, node}; anything else hits its
Unknown runtimethrow.Because it also cannot write their
latest_version, these linters never appearin the usual "latest != known_good" gap scan either. Full enumeration:
gorustgohidden = true)clippy, rustfmt, and gofmt are runtime-coupled, not ordinary plugins.
gofmt1.22.0 is the managed Go pin (tool_builder.rs:148), andclippy/rustfmt track the Rust toolchain (pinned 1.77.2). Upstream is far ahead
— Rust 1.98.0, Go 1.27.0 — but bumping them means moving the managed runtimes,
the same class of work as the node 21→22 bump in #2814/#2825, with much wider
blast radius. Deliberately out of scope here; worth its own issue.
Stability policy
Every version here has been published for at least 30 days, no overrides.
Newer releases rejected by the gate, to pick up next run: redocly 2.48.0,
checkstyle 14.0.0, eslint 10.9.1, oxlint 1.80.0, prisma 7.10.0,
swiftlint 0.65.1, terraform 1.15.9, hadolint 2.15.1 (26d).
Node linters were pre-checked with
npm view {pkg}@{ver} enginesagainst themanaged runtime (22.23.1) — all satisfied.
Also noticed
knip/plugin.tomlandoxc/plugin.tomlstill blamenode 21.7.3. knip's real blocker is feat(check): support knip 6 JSON reporter output and bump knip to 6.17.1 #2826; oxc's is simply obsolete. Left
untouched here.
biome is already at 2.5.6 on main.
Test results
All 11 updated linters pass in plain mode, which runs every fixture at every
historical snapshot version — what CI effectively exercises. Each target was
additionally run in compare-latest-snapshot mode. Apart from shfmt, output was
byte-identical everywhere, so no snapshot changes were needed. Snapshots were
inspected for non-zero findings to rule out vacuous passes (this is what caught
shfmt).
Two fixtures fail in compare mode only — eslint
basic_8(eslintrc, ESLint 8only) and prisma
basic(prisma 5 schema). Compare mode forces the targetversion onto every fixture regardless of what it supports. Both were confirmed
to fail identically at the current promoted versions (10.5.0 / 7.8.0), so
this is pre-existing harness behaviour, not a regression.
Custom-download URL templates were HEAD-checked at the target version across
every platform variant (all 200) — no asset-name drift this round.
Test plan