The grype ignore rule for the lefthook prebuilt binaries was scoped to
`stdlib` go-module findings only, so each new transitive dependency
statically linked into the binary (e.g. `golang.org/x/sys` flagged by
`GO-2026-5024`) surfaced as a fresh failure and had to be added by hand.
Drop the `name` and `type` constraints and match purely on the
`**/lefthook*/**` source path. Any package embedded in the lefthook
binary (stdlib, `golang.org/x/*` modules, and future transitive deps) is
now suppressed, while the path scoping still ensures the same packages
remain visible when they appear in Authelia's own runtime binaries.
Lefthook is a developer/CI git hook runner only and is never shipped in
a runtime artifact.
Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
Replace the per-CVE stdlib pin block in `.grype.yaml` with a single
location-scoped ignore that only applies to Go stdlib reported from
inside the lefthook prebuilt binaries. The previous list churned every
time the grype DB rotated, since the `lefthook-linux-x64` npm
devDependency ships an embedded Go stdlib that surfaces every new Go
vulnerability against the `public_html` SBOM even though lefthook is a
developer-only git hook runner that never reaches a runtime artifact.
The `location:` field only gates matches when the SBOM carries
source-path metadata. SPDX-JSON records this as a free-text `sourceInfo`
string that grype does not promote into the package's `locations` array,
so `location:` globs cannot match. CycloneDX preserves the path under
`syft:location:0:path`, which grype reads, so a `**/lefthook*/**` glob
reliably scopes the ignore to the lefthook source path without
over-matching a hypothetical Authelia binary that happened to ship the
same stdlib version.
Switch the grype consumers accordingly: `.buildkite/hooks/pre-command`
downloads `*.cdx.json` for the `:grype: Vulnerability Scanning` label,
and `.buildkite/steps/grypescans.sh` iterates over `*.cdx.json`.
`.goreleaser.yml` still produces both `cdx-*` and `spdx-*` SBOM IDs, and
`.buildkite/annotations/artifacts.sh` still advertises both extensions,
so the SPDX SBOM continues to ship as a release artifact.
OpenVEX was evaluated and rejected. Grype's OpenVEX matcher only checks
`products[].@id` against the matched package purl; the `subcomponents`
array is parsed and ignored. A VEX statement with `product=lefthook +
subcomponent=stdlib` does not suppress the match, and inverting it is
functionally identical to a name+version+type ignore with no genuine
source scoping.
Also added `reason:` fields to the existing busybox 1.37.0 entries
explaining that the vulnerable code paths are backported in
`authelia/base`. The reported version string is unchanged from upstream
so grype still flags it.
Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
Replaces the `pkgvalidate.sh` build-only check with a new `suites
external` namespace that spins up a project-local dev server (Hugo for
docs, react-email for templates) and drives a headless rod browser
against it to assert the rendered output is correct.
Before, CI only confirmed that `pnpm -C docs build` and `pnpm -C
internal/templates/src render` exited cleanly — a regression in a
shortcode, a broken image path, a missing search initialiser, a Tailwind
class, or a template PreviewProps drift would pass silently.
Suites are gated behind a `//go:build externalsuites` tag and registered
in their own `ExternalGlobalRegistry`, so `go test ./internal/suites`
and `authelia-scripts suites list` remain unaffected. A new
`authelia-scripts suites external [list|test]` sub-namespace runs them
via a shared `DevServer` lifecycle helper that spawns `pnpm dev` in its
own process group, polls for HTTP readiness, and tears the whole tree
down on exit.
In Buildkite, `.buildkite/steps/pkgvalidate.sh` is deleted, and the two
old package-validation steps are replaced with a single `External Tests`
step that runs `e2epackages.sh | buildkite-agent pipeline upload`,
mirroring the `e2etests.sh` dynamic pattern so new external suites
register without pipeline edits.
---------
Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
This PR contains the following updates:
| Package | Change |
[Age](https://docs.renovatebot.com/merge-confidence/) |
[Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [qs](https://redirect.github.com/ljharb/qs) | [`6.14.1` →
`6.14.2`](https://renovatebot.com/diffs/npm/qs/6.14.1/6.14.2) |

|

|
### GitHub Vulnerability Alerts
####
[CVE-2026-2391](https://redirect.github.com/ljharb/qs/security/advisories/GHSA-w7fw-mjwx-w883)
### Summary
The `arrayLimit` option in qs does not enforce limits for
comma-separated values when `comma: true` is enabled, allowing attackers
to cause denial-of-service via memory exhaustion. This is a bypass of
the array limit enforcement, similar to the bracket notation bypass
addressed in GHSA-6rw7-vpxm-498p (CVE-2025-15284).
### Details
When the `comma` option is set to `true` (not the default, but
configurable in applications), qs allows parsing comma-separated strings
as arrays (e.g., `?param=a,b,c` becomes `['a', 'b', 'c']`). However, the
limit check for `arrayLimit` (default: 20) and the optional
throwOnLimitExceeded occur after the comma-handling logic in
`parseArrayValue`, enabling a bypass. This permits creation of
arbitrarily large arrays from a single parameter, leading to excessive
memory allocation.
**Vulnerable code** (lib/parse.js: lines ~40-50):
```js
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
return val.split(',');
}
if (options.throwOnLimitExceeded && currentArrayLength >= options.arrayLimit) {
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
}
return val;
```
The `split(',')` returns the array immediately, skipping the subsequent
limit check. Downstream merging via `utils.combine` does not prevent
allocation, even if it marks overflows for sparse arrays.This
discrepancy allows attackers to send a single parameter with millions of
commas (e.g., `?param=,,,,,,,,...`), allocating massive arrays in memory
without triggering limits. It bypasses the intent of `arrayLimit`, which
is enforced correctly for indexed (`a[0]=`) and bracket (`a[]=`)
notations (the latter fixed in v6.14.1 per GHSA-6rw7-vpxm-498p).
### PoC
**Test 1 - Basic bypass:**
```
npm install qs
```
```js
const qs = require('qs');
const payload = 'a=' + ','.repeat(25); // 26 elements after split (bypasses arrayLimit: 5)
const options = { comma: true, arrayLimit: 5, throwOnLimitExceeded: true };
try {
const result = qs.parse(payload, options);
console.log(result.a.length); // Outputs: 26 (bypass successful)
} catch (e) {
console.log('Limit enforced:', e.message); // Not thrown
}
```
**Configuration:**
- `comma: true`
- `arrayLimit: 5`
- `throwOnLimitExceeded: true`
Expected: Throws "Array limit exceeded" error.
Actual: Parses successfully, creating an array of length 26.
### Impact
Denial of Service (DoS) via memory exhaustion.
### Suggested Fix
Move the `arrayLimit` check before the comma split in `parseArrayValue`,
and enforce it on the resulting array length. Use `currentArrayLength`
(already calculated upstream) for consistency with bracket notation
fixes.
**Current code** (lib/parse.js: lines ~40-50):
```js
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
return val.split(',');
}
if (options.throwOnLimitExceeded && currentArrayLength >= options.arrayLimit) {
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
}
return val;
```
**Fixed code:**
```js
if (val && typeof val === 'string' && options.comma && val.indexOf(',') > -1) {
const splitArray = val.split(',');
if (splitArray.length > options.arrayLimit - currentArrayLength) { // Check against remaining limit
if (options.throwOnLimitExceeded) {
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
} else {
// Optionally convert to object or truncate, per README
return splitArray.slice(0, options.arrayLimit - currentArrayLength);
}
}
return splitArray;
}
if (options.throwOnLimitExceeded && currentArrayLength >= options.arrayLimit) {
throw new RangeError('Array limit exceeded. Only ' + options.arrayLimit + ' element' + (options.arrayLimit === 1 ? '' : 's') + ' allowed in an array.');
}
return val;
```
This aligns behavior with indexed and bracket notations, reuses
`currentArrayLength`, and respects `throwOnLimitExceeded`. Update README
to note the consistent enforcement.
---
### Release Notes
<details>
<summary>ljharb/qs (qs)</summary>
###
[`v6.14.2`](https://redirect.github.com/ljharb/qs/blob/HEAD/CHANGELOG.md#6142)
[Compare
Source](https://redirect.github.com/ljharb/qs/compare/v6.14.1...v6.14.2)
- \[Fix] `parse`: mark overflow objects for indexed notation exceeding
`arrayLimit`
([#​546](https://redirect.github.com/ljharb/qs/issues/546))
- \[Fix] `arrayLimit` means max count, not max index, in
`combine`/`merge`/`parseArrayValue`
- \[Fix] `parse`: throw on `arrayLimit` exceeded with indexed notation
when `throwOnLimitExceeded` is true
([#​529](https://redirect.github.com/ljharb/qs/issues/529))
- \[Fix] `parse`: enforce `arrayLimit` on `comma`-parsed values
- \[Fix] `parse`: fix error message to reflect arrayLimit as max index;
remove extraneous comments
([#​545](https://redirect.github.com/ljharb/qs/issues/545))
- \[Robustness] avoid `.push`, use `void`
- \[readme] document that `addQueryPrefix` does not add `?` to empty
output ([#​418](https://redirect.github.com/ljharb/qs/issues/418))
- \[readme] clarify `parseArrays` and `arrayLimit` documentation
([#​543](https://redirect.github.com/ljharb/qs/issues/543))
- \[readme] replace runkit CI badge with shields.io check-runs badge
- \[meta] fix changelog typo (`arrayLength` → `arrayLimit`)
- \[actions] fix rebase workflow permissions
</details>
---
### Configuration
📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no
schedule defined).
🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box
---
This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/authelia/authelia).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My44LjUiLCJ1cGRhdGVkSW5WZXIiOiI0My44LjUiLCJ0YXJnZXRCcmFuY2giOiJtYXN0ZXIiLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIiwiamF2YXNjcmlwdCJdfQ==-->
---------
Signed-off-by: James Elliott <james-d-elliott@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: James Elliott <james-d-elliott@users.noreply.github.com>
This change adds a CVE exclusion for `CVE-2025-60876` as it has been
patched in the Authelia base image.
It also adds an override for `qs` to address another upstream CVE.
---------
Signed-off-by: Brynn Crowley <littlehill723@gmail.com>
Co-authored-by: Brynn Crowley <littlehill723@gmail.com>
This change no longer excludes false positive CVEs flagged within esbuild.
The dependency has now been moved to the override section to ensure that transitive deps such as Vite pull in the updated version.
Signed-off-by: Amir Zarrinkafsh <3339418+nightah@users.noreply.github.com>
This change will build artifacts for renovate deps branches and include grype scanning to prevent CVEs leaking through to `master`.
There is also an update to the ignored Grype CVEs. While esbuild updates the Go version it is built with, all the detected CVEs are not actually vulnerable/relevant, but still are flagged by Grype due to stdlib and Go versioning.
Other associated changes:
* Remove dependabot configuration for github-actions
* Update renovate to pick up github-actions
* Update probot announcements to include renovate branches
* Remove conditional ignore for Buildkite steps for `renovate/` and `dependabot/` prefixed branches
Signed-off-by: Amir Zarrinkafsh <nightah@me.com>
This change adds a step to perform vulnerability scanning with Grype against all SPDX SBOMs for the Authelia backend, frontend and also the respective Docker tag.
- New Features
- Automated vulnerability scanning added for SPDX artifacts and built Docker images.
- CI
- CI downloads SPDX JSON artifacts before scanning.
- Vulnerability scan runs after unit tests and Linux image build, with branch/commit gating.
- Scan target images based on tag, branch, or PR context.
Signed-off-by: Amir Zarrinkafsh <nightah@me.com>