jactionlint 2.1.0 adds an environment variable that turns on the online checks, a new workflow-secret-scope rule, and machine-readable reasons when a finding has no fix. Many security rules now produce fewer false positives, especially in reusable workflows and release pipelines. Some rules now report things they did not report before, so an existing clean run can show new findings (see Changed).
Added
JACTIONLINT_ONLINEenvironment variable. It turns on the online checks without changing the command, so CI can run them while local pre-commit hooks stay offline.1,trueoronworks like--online, andcache,strictorcache,strictselect a mode.0,false,offornoworks like--no-onlineand overridesonline: truein the config file. The--online/--no-onlineflags override the variable, and the variable overrides the config file. An invalid value exits with status 2. (#155, @jdx)- run: hk check --all env: JACTIONLINT_ONLINE: 1 GITHUB_TOKEN: ${{ github.token }}
- New
workflow-secret-scoperule (security, pedantic profile, warning, no autofix). It reports a named secret in workflow-levelenv:when steps in two or more jobs can see it. The rule skips single-job workflows,GITHUB_TOKEN, jobs or steps that override the variable, and jobs that call reusable workflows. (#136, @jdx) background-step-not-waited(correctness profile, fromparallel-steps). It reports reads of a background step'soutputs,outcomeorconclusionbefore awaitorwait-allcovers that step. Those reads evaluate to an empty string. Steps withbackground: ${{ ... }}are not tracked. (#132, @jdx)- Reasons for missing fixes. When a fixable rule has no fix for a finding, the finding now says why. This applies to
missing-timeout,dependabot-cooldown,unpinned-uses,template-injectionandartipacked. The reason appears asno_fixin JSON and template output, and asnoFixCode,noFixReasonandnoFixOptionproperties in SARIF. The codes areoption-required,option-invalid,online-required,lookup-failed,unsupported-shapeandneeds-judgment. Text output is unchanged. Whendefault-minutesordefault-daysis not set, themissing-timeoutanddependabot-cooldownmessages now say which option to set to enable--fix. (#135, #147, #154, @jdx) - Online check coverage in SARIF. When online checks are on, SARIF runs include
properties.onlineCoveragewithstatus(complete/incomplete),mode,skippedLookups,excludedRepositoriesandevidence(networkorcache). (#140, @jdx)
Changed
-
excessive-permissionsnow reports workflow-level write scopes in single-job workflows too, unless the trigger is privileged. Any job added later would inherit those scopes without notice. (#152, @jdx) -
missing-permissionsno longer reports workflows whose only trigger isworkflow_call, because the callers decide the token permissions. Workflows that haveworkflow_callplus another trigger are still reported. They now get an unsafe fix (contents: read, applied only with--fix=unsafe), and the message says the permissions must not exceed what callers grant. (#138, #152, @jdx) -
Parallel-step control keys now match what GitHub accepts. On
wait,wait-allandcancelsteps, onlyidandnameare allowed besides the keyword. Onparallelsteps, onlyparallelis allowed.wait-all: falseis now an error. To apply a condition, putif:on a step inside the parallel group instead of on the group. (#128, @navidemad) -
concurrency-limits:- Workflows that call reusable workflows now get a caller-specific concurrency group, so the caller's group no longer collides with the called workflow's group.
- Release detection now recognizes many more publish commands (for example
semantic-release,changeset publish,mvn deploy,cargo release --execute), tag pushes and release actions. Release jobs getcancel-in-progress: falseadvice and no fix. --fixnow inserts a blank line before theconcurrency:orpermissions:block it adds.
-
bot-conditionsnow uses the events of a reusable workflow's local callers. Workflows that only push-like events start are no longer reported. Mixed or non-PR events get advice that fits those events, and only PR-only workflows keep the unsafe fix. (#141, @jdx) -
pipeline-without-pipefail: copying a stage's exit code into a variable (rc=${PIPESTATUS[0]}) now counts as handling the pipeline only if a later[/[[/test,(( )),exitorreturnchecks that variable. (#145, @jdx) -
untrusted-checkout: after a step fetches a pull request revision, a later step that runs a repository script (such as./scripts/x.shorbash scripts/x.sh) is now reported if that script runs a work-tree git command onFETCH_HEADor the fetch destination. The rule only follows static, in-repo script paths. (#149, @jdx) -
cache-poisoning: a push whosebranches:filter lists only release branches (such asrelease/**) now counts as a release trigger. The built-in list of publishing actions also gained more entries, including crates.io auth, Homebrew bump actions, Azure/GCP/AWS deploys, Vercel, Netlify, Google Play and TestFlight. (#150, @jdx) -
On a first run of the default profile with 20 or more findings, the note on stderr now starts by suggesting
--baseline-write. (#152, @jdx)
Fixed
-
runs.envis no longer rejected in composite actions. JavaScript actions still reject it. (#123, @jdx) -
The
issuesevent now accepts thefield_addedandfield_removedactivity types. (#130, @jdx) -
background: trueon a step inside aparallelgroup is no longer reported. It is redundant, but GitHub accepts it. (#131, @jdx) -
pipeline-without-pipefailno longer reports pipelines inside process substitutions (< <(...),> >(...)). (#143, @jdx) -
cache-poisoningnow evaluates expressions that callers pass to a composite action's cache-switch input (for examplecache: ${{ !startsWith(github.ref, 'refs/tags/') }}) for each release scenario. Before, only literal values were evaluated. (#139, @jdx) -
use-trusted-publishing: a scoped registry (@scope:registry) now hides a publish only when the package, read frompackage.json, belongs to that scope. (#151, @jdx) -
github-env: a guard inside anif/elsebranch now protects only the writes later in that same branch. (#151, @jdx) -
template-injection:--fix=unsafeno longer quotes unquoted expressions where the shell may split words, such as command arguments, redirections, word lists and arrays. These findings now have no fix and aneeds-judgmentreason. Assignment values (NAME=${{ x }}) are still fixed. (#154, @jdx) -
artipacked:--fixnow also fixes flow-style checkout steps (- {uses: ...}) and multi-line flowwith:mappings. (#154, @jdx) -
--migrate-ignores:- With no file arguments, it now also migrates the root
action.yml/action.yaml. - Each comment it skips is listed as
file:line: not migrated: <reason>. - A version after a zizmor comment on a
uses:line (# zizmor: ignore[artipacked] v6) now stays on that line as# v6.
- With no file arguments, it now also migrates the root
-
Linting allocates about 9 to 10% less memory, because each
${{ }}expression is now parsed only once and the expression lexer no longer allocates a scanner. (#153, @jdx)
New Contributors
- @navidemad made their first contribution in #128
Full Changelog: v2.0.2...v2.1.0
💚 Sponsor jactionlint
jactionlint is a fork of rhysd/actionlint, maintained by @jdx, an open source developer at entire.io, the title sponsor of his open source work.
If jactionlint has a place in your development workflow, please consider becoming an individual or company sponsor. Your support funds ongoing development and helps keep jactionlint fast, free, and independent.