Pre-Existing Follow-Ups: Trivy Suppression Expiry & E2E Firefox Flake Scope
Discovery Date: 2026-07-29
Discovered During: QA audit of the "What's New" changelog feature (feat/changelog, see docs/reports/qa_report.md)
Status: Open
Priority: Medium
Both items below are pre-existing and unrelated to the changelog feature
— they surfaced as a side effect of that QA pass, not because of anything
in the changelog diff. Full detail/evidence is in docs/reports/qa_report.md
(§6.3 and §1b); this doc is just enough to track and pick each one up
later.
Issue 1: Stale .trivyignore Suppression for CVE-2026-32286 — RESOLVED 2026-07-29
Status: Resolved. Renewed, not removed — the CVE is still real and
still unfixed upstream, but the entry was stale documentation, not an
active CI failure.
Correction to the original framing above: Trivy's plain-text .trivyignore
format (as used by this repo, invoked via aquasecurity/trivy-action in
docker-build.yml with trivyignores: '.trivyignore') does not parse
or enforce the # exp: DATE comment at all — that annotation is a
project-only human-review convention, not a Trivy feature. Verified this
directly: ran trivy image --ignorefile .trivyignore (same CLI version,
v0.72.0, pinned by the CI action) against an exported charon:local image
with --show-suppressed, and the finding was still reported as
"Status": "ignored", "Source": ".trivyignore" despite the lapsed date —
CI's Trivy gate was not actually flagging this on unrelated PRs. The
risk was stale documentation implying a review had happened that hadn't,
not an active gate failure.
Re-investigated the underlying CVE itself on 2026-07-29: jackc/pgproto3
remains archived (still v2.3.3, no new tags). Checked upstream go.mod
directly (not just release notes) for CrowdSec v1.7.8 (Charon's current
pin, and upstream's latest stable release) and v1.8.0-rc1 (latest
including pre-releases) — both still resolve
github.com/jackc/pgx/v4 v4.18.3 → github.com/jackc/pgproto3/v2 v2.3.3.
No pgx/v5 migration has landed upstream. The original justification
(Charon defaults to SQLite; the vulnerable PostgreSQL wire-protocol path
isn't reached in a standard deployment) still holds.
Action taken: renewed the suppression rather than removing it or applying
a fix, since no upstream fix path exists yet. Updated .trivyignore and
the matching .grype.yaml entry with a fresh 2026-07-29 review note and a
new exp:/expiry of 2026-09-01 — aligned with the two sibling entries
covering the exact same underlying pgproto3/v2 bug under different
advisory IDs (GHSA-jqcq-xjh3-6g23, GHSA-x6gf-mpr2-68h6), which were
already extended to 2026-09-01 on 2026-06-02, so all three now review
together going forward instead of drifting apart. Also updated
SECURITY.md's CVE-2026-32286 entry with the same re-verification note.
Re-ran the Trivy scan after the change to confirm the finding is still
cleanly suppressed under the renewed, non-expired entry.
Issue 2: E2E Firefox Navigation-Race Flakiness Broader Than Known 3 Files
Status: Partially fixed. See below for what landed and what's still open.
Commit 7503c01a fixed a Firefox/Playwright navigation-commit race by
adding a reloadTolerant() sibling to the pre-existing gotoTolerant()
helper in tests/utils/wait-helpers.ts, and applied both to
user-lifecycle.spec.ts's navigateToLogin(). gotoTolerant() itself
already existed and was already in use in several files
(user-management.spec.ts, long-running-operations.spec.ts,
wait-helpers.spec.ts's own tests). The gap: theme-banner-userthemes.spec.ts's
goToAppearance() helper (and its sibling loginWithStoredState()) still
called raw, unprotected page.goto() — never wrapped. A full-suite local
run during the QA pass (970 tests, --project=firefox) reproduced the same
page.goto() timeout signature non-deterministically across many more
files spanning unrelated feature areas (a11y, auth, certificates, CrowdSec,
DNS providers, proxy groups/hosts, uptime monitoring, and others — see
docs/reports/qa_report.md §1b for the full file list and the re-run that
confirmed it's load-dependent, not tied to specific code paths).
Fixed (this pass)
tests/theme-banner-userthemes.spec.ts: loginWithStoredState() and the
inline page.goto('/') in the banner-persistence test now use
gotoTolerant(). goToAppearance() now uses gotoTolerant() for the
first navigation to /settings/appearance, but switches to
reloadTolerant() when the page is already on that URL — several tests
in this file call goToAppearance() a second time mid-test (e.g. to
refresh the theme list after creating a theme via the API), which is the
same "second goto() to a URL you're already on" race that 7503c01a
originally fixed for navigateToLogin()'s reload, just manifesting here
via a helper instead of an inline call. Confirmed reproducible under
2-worker load before the fix (deleting a user theme removes it from the list timed out waiting on the appearance page's radiogroup after a
same-URL goto()); 32/32 passed consistently across repeated runs after.
tests/theme.spec.ts: near-identical loginWithStoredState() /
goToAppearance() helpers (same file family, same underlying pattern,
not yet reported as flaky but structurally the same risk) updated the
same way. Its goToAppearance() is only ever called once per test here,
so no same-URL/reload case applies.
- Validated:
theme-banner-userthemes.spec.ts, user-management.spec.ts,
and wait-helpers.spec.ts together — all pass except the pre-existing,
separately-tracked reloadTolerant gap below. theme.spec.ts +
theme-banner-userthemes.spec.ts together — 32/32, repeatable.
Still open
wait-helpers.spec.ts:386 (reloadTolerant › should swallow a timeout instead of throwing) fails on a race variant reloadTolerant() doesn't
yet cover: Firefox can reject a same-URL reload() with
NS_BINDING_ABORTED, a message isExpectedNavigationRace() in
wait-helpers.ts doesn't currently match (it only checks for
'Timeout', 'interrupted by another navigation', and
'net::ERR_ABORTED'). Needs its own fix to isExpectedNavigationRace();
out of scope for this pass.
user-management.spec.ts's should show error for regular user access
(already using gotoTolerant()) failed once in a full 184-test
tests/settings/ run under 2-worker contention, but passed cleanly in an
isolated run — this is an authorization/rendering expect.poll race
unrelated to page.goto()/page.reload() at all, not something this
pass's fix touches.
- The broader sweep (a11y, auth, certificates, CrowdSec, DNS providers,
proxy groups/hosts, uptime monitoring, etc. named in the original QA
report) is still untouched — this pass covered the two confirmed
goToAppearance()-family files only, per explicit scope. Remaining
page.goto() call sites (mostly one-off, per-test navigations rather
than shared helpers reused across many tests) are lower-leverage and
higher-risk to sweep in bulk; still open for a follow-up.
whats-new-changelog.spec.ts had 7 failures in the same
tests/settings/ run (waitForModal never finding the "What's New"
dialog) — unrelated to navigation races and to this fix (the file was
not touched here); tracked separately as a changelog-feature issue, not
part of this E2E-flake follow-up.
Issue 3: "Security Enforcement" CI Job Never Actually Runs security-enforcement/ — RESOLVED 2026-07-30
Status: Resolved. Discovered 2026-07-30, while validating the
createUserViaApi/changelog-suppression fix below — unrelated to that fix,
noted in passing and tracked here per the same "pre-existing, not part of
this pass" convention as Issues 1-2. Fixed in a dedicated pass the same day.
.github/workflows/e2e-tests-split.yml's "Security Enforcement" job invoked
npx playwright test --project=chromium tests/security-enforcement/ tests/security/ tests/integration/multi-feature-workflows.spec.ts. But
playwright.config.js's chromium project has testIgnore: ['**/security-enforcement/**', '**/tests/security/**'] (mirrored on the
firefox/webkit projects) — those two directories are only ever collected
by the dedicated security-tests project (Chromium-only, sequential,
workers: 1, brings up CrowdSec/WAF via security-shard-setup), which the
CI job never selected. Net effect: the job's --project=chromium invocation
silently matched zero tests in tests/security-enforcement/ and
tests/security/ and only ever actually ran
multi-feature-workflows.spec.ts, without erroring or reporting the other
two paths as skipped/missing. Confirmed locally: npx playwright test tests/security-enforcement/multi-component-security-workflows.spec.ts --project=chromium --list → "Total: 0 tests in 0 files", while
--project=security-tests collects and runs them normally. This affected
all three per-browser security jobs (e2e-chromium-security,
e2e-firefox-security, e2e-webkit-security), each with the analogous
--project=<browser> invocation.
Fix shape chosen
Split each of the 3 jobs' single npx playwright test invocation into two,
run sequentially in the same step (Option B from the two shapes originally
sketched here) — kept the existing 3-job-per-browser topology (job count,
needs: graph in test-summary/e2e-results, and workflow_dispatch
browser-selection semantics all untouched) rather than consolidating into a
single job, since security-tests is inherently Chromium-only regardless of
which of the 3 jobs invokes it (its use: block hardcodes
devices['Desktop Chrome'] in playwright.config.js), so collapsing the
job topology was a larger, separate structural change out of scope here:
--project=<browser> (chromium/firefox/webkit, matching the job) on
tests/integration/multi-feature-workflows.spec.ts — first.
--project=security-tests on tests/security-enforcement/ +
tests/security/ — second.
Each invocation gets its own PLAYWRIGHT_HTML_REPORT and --output
subdirectory so the second invocation's HTML report doesn't overwrite the
first's before the "Upload HTML report" step runs.
Ordering matters and was not the first thing tried. Running
security-tests before the browser-project invocation (the natural
reading order matching the job's own test-path list) was tried first and
reproducibly broke the second invocation: security-tests includes
tests/security-enforcement/zzzz-break-glass-recovery.spec.ts, which
flips ACL/WAF/rate-limit modules on as part of testing enforcement, and the
project's own teardown (security-teardown in playwright.config.js) only
actually runs when PLAYWRIGHT_SKIP_SECURITY_DEPS=0 — an env var this
workflow doesn't set, so security-teardown's testMatch resolves to []
and it never executes. Within a single job, both invocations share the same
long-lived container, so the second invocation inherited whatever security
state the first left behind. Reproduced locally: running security-tests
first left the container rate-limiting its own /api/v1/health//api/v1/setup
endpoints, and the following multi-feature-workflows.spec.ts run failed
its auth.setup.ts step with GET /api/v1/setup failed with unexpected status 429. Swapping the order — browser-project invocation first, while
the container is still at its clean post-boot baseline, security-tests
second — avoids the contamination entirely, verified below. (Real CI job
runs are unaffected by cumulative state across jobs since each job starts
a brand-new container; this was purely an intra-job, cross-invocation
ordering hazard once both invocations started sharing one container.)
Verification (real re-run, not just syntax)
actionlint .github/workflows/e2e-tests-split.yml — clean.
Reproduced the fixed job's actual environment locally: brought up
.docker/compose/docker-compose.playwright-ci.yml with --profile security-tests (same CrowdSec/WAF setup the job already had, on a fresh
charon:local image built from this branch, port-remapped via a
git-ignored local override to avoid colliding with an unrelated Charon
instance already running on the host — no such conflict exists on GitHub's
ephemeral runners), waited for the health check exactly as the job does,
then ran the fixed job's exact two npx playwright test invocations
in order, back-to-back, on one freshly-started container (earlier attempts
that reused the same container across several manual runs in the same
session hit an unrelated, session-only rate-limit accumulation artifact —
not present on a fresh container, and not something a real CI job's
single-pass, fresh-per-job execution would ever hit):
--project=chromium tests/integration/multi-feature-workflows.spec.ts:
15/15 passed (39.9s) — matches the prior documented baseline exactly.
--project=security-tests tests/security-enforcement/ tests/security/:
now genuinely collects and runs (was 0 before the fix) — 407 passed, 21
failed, 3 flaky, 4 did not run, out of 435 total (34.9m). Consistent
with the earlier manual --project=security-tests verification pass
referenced in this doc's intro (409/435 baseline) — same failing test
files/categories (audit-logs, crowdsec-first-enable, rate-limiting,
security-headers, acl-waf-layering, auth-middleware-cascade,
multi-component-security-workflows, encryption-management), zero
changelog/whats-new-related failures.
Files touched: .github/workflows/e2e-tests-split.yml only (all 3 security
jobs' "Run ... Security Enforcement Tests" steps). playwright.config.js
was not modified — the fix stays entirely in the CI invocation layer.
References
- QA Report:
docs/reports/qa_report.md (§1b "The other 27 failures", §6.3)
- Prior partial fix: commit
7503c01a
- This pass's fix: commit (see git log for the
fix(e2e): extend gotoTolerant()... commit on feat/changelog)
- Issue 3:
.github/workflows/e2e-tests-split.yml "Security Enforcement" job vs. playwright.config.js project testIgnore/testMatch config
- Issue 3's fix: commit
8d797328
Auto-created from trivy-suppression-and-e2e-flake-followups.md
Pre-Existing Follow-Ups: Trivy Suppression Expiry & E2E Firefox Flake Scope
Discovery Date: 2026-07-29
Discovered During: QA audit of the "What's New" changelog feature (
feat/changelog, seedocs/reports/qa_report.md)Status: Open
Priority: Medium
Both items below are pre-existing and unrelated to the changelog feature
— they surfaced as a side effect of that QA pass, not because of anything
in the changelog diff. Full detail/evidence is in
docs/reports/qa_report.md(§6.3 and §1b); this doc is just enough to track and pick each one up
later.
Issue 1: Stale
.trivyignoreSuppression for CVE-2026-32286 — RESOLVED 2026-07-29Status: Resolved. Renewed, not removed — the CVE is still real and
still unfixed upstream, but the entry was stale documentation, not an
active CI failure.
Correction to the original framing above: Trivy's plain-text
.trivyignoreformat (as used by this repo, invoked via
aquasecurity/trivy-actionindocker-build.ymlwithtrivyignores: '.trivyignore') does not parseor enforce the
# exp: DATEcomment at all — that annotation is aproject-only human-review convention, not a Trivy feature. Verified this
directly: ran
trivy image --ignorefile .trivyignore(same CLI version,v0.72.0, pinned by the CI action) against an exported
charon:localimagewith
--show-suppressed, and the finding was still reported as"Status": "ignored", "Source": ".trivyignore"despite the lapsed date —CI's Trivy gate was not actually flagging this on unrelated PRs. The
risk was stale documentation implying a review had happened that hadn't,
not an active gate failure.
Re-investigated the underlying CVE itself on 2026-07-29:
jackc/pgproto3remains archived (still v2.3.3, no new tags). Checked upstream
go.moddirectly (not just release notes) for CrowdSec v1.7.8 (Charon's current
pin, and upstream's latest stable release) and v1.8.0-rc1 (latest
including pre-releases) — both still resolve
github.com/jackc/pgx/v4 v4.18.3→github.com/jackc/pgproto3/v2 v2.3.3.No pgx/v5 migration has landed upstream. The original justification
(Charon defaults to SQLite; the vulnerable PostgreSQL wire-protocol path
isn't reached in a standard deployment) still holds.
Action taken: renewed the suppression rather than removing it or applying
a fix, since no upstream fix path exists yet. Updated
.trivyignoreandthe matching
.grype.yamlentry with a fresh 2026-07-29 review note and anew
exp:/expiryof2026-09-01— aligned with the two sibling entriescovering the exact same underlying pgproto3/v2 bug under different
advisory IDs (
GHSA-jqcq-xjh3-6g23,GHSA-x6gf-mpr2-68h6), which werealready extended to
2026-09-01on 2026-06-02, so all three now reviewtogether going forward instead of drifting apart. Also updated
SECURITY.md'sCVE-2026-32286entry with the same re-verification note.Re-ran the Trivy scan after the change to confirm the finding is still
cleanly suppressed under the renewed, non-expired entry.
Issue 2: E2E Firefox Navigation-Race Flakiness Broader Than Known 3 Files
Status: Partially fixed. See below for what landed and what's still open.
Commit
7503c01afixed a Firefox/Playwright navigation-commit race byadding a
reloadTolerant()sibling to the pre-existinggotoTolerant()helper in
tests/utils/wait-helpers.ts, and applied both touser-lifecycle.spec.ts'snavigateToLogin().gotoTolerant()itselfalready existed and was already in use in several files
(
user-management.spec.ts,long-running-operations.spec.ts,wait-helpers.spec.ts's own tests). The gap:theme-banner-userthemes.spec.ts'sgoToAppearance()helper (and its siblingloginWithStoredState()) stillcalled raw, unprotected
page.goto()— never wrapped. A full-suite localrun during the QA pass (970 tests,
--project=firefox) reproduced the samepage.goto()timeout signature non-deterministically across many morefiles spanning unrelated feature areas (a11y, auth, certificates, CrowdSec,
DNS providers, proxy groups/hosts, uptime monitoring, and others — see
docs/reports/qa_report.md§1b for the full file list and the re-run thatconfirmed it's load-dependent, not tied to specific code paths).
Fixed (this pass)
tests/theme-banner-userthemes.spec.ts:loginWithStoredState()and theinline
page.goto('/')in the banner-persistence test now usegotoTolerant().goToAppearance()now usesgotoTolerant()for thefirst navigation to
/settings/appearance, but switches toreloadTolerant()when the page is already on that URL — several testsin this file call
goToAppearance()a second time mid-test (e.g. torefresh the theme list after creating a theme via the API), which is the
same "second goto() to a URL you're already on" race that
7503c01aoriginally fixed for
navigateToLogin()'s reload, just manifesting herevia a helper instead of an inline call. Confirmed reproducible under
2-worker load before the fix (
deleting a user theme removes it from the listtimed out waiting on the appearance page's radiogroup after asame-URL
goto()); 32/32 passed consistently across repeated runs after.tests/theme.spec.ts: near-identicalloginWithStoredState()/goToAppearance()helpers (same file family, same underlying pattern,not yet reported as flaky but structurally the same risk) updated the
same way. Its
goToAppearance()is only ever called once per test here,so no same-URL/reload case applies.
theme-banner-userthemes.spec.ts,user-management.spec.ts,and
wait-helpers.spec.tstogether — all pass except the pre-existing,separately-tracked
reloadTolerantgap below.theme.spec.ts+theme-banner-userthemes.spec.tstogether — 32/32, repeatable.Still open
wait-helpers.spec.ts:386(reloadTolerant › should swallow a timeout instead of throwing) fails on a race variantreloadTolerant()doesn'tyet cover: Firefox can reject a same-URL
reload()withNS_BINDING_ABORTED, a messageisExpectedNavigationRace()inwait-helpers.tsdoesn't currently match (it only checks for'Timeout','interrupted by another navigation', and'net::ERR_ABORTED'). Needs its own fix toisExpectedNavigationRace();out of scope for this pass.
user-management.spec.ts'sshould show error for regular user access(already using
gotoTolerant()) failed once in a full 184-testtests/settings/run under 2-worker contention, but passed cleanly in anisolated run — this is an authorization/rendering
expect.pollraceunrelated to
page.goto()/page.reload()at all, not something thispass's fix touches.
proxy groups/hosts, uptime monitoring, etc. named in the original QA
report) is still untouched — this pass covered the two confirmed
goToAppearance()-family files only, per explicit scope. Remainingpage.goto()call sites (mostly one-off, per-test navigations ratherthan shared helpers reused across many tests) are lower-leverage and
higher-risk to sweep in bulk; still open for a follow-up.
whats-new-changelog.spec.tshad 7 failures in the sametests/settings/run (waitForModalnever finding the "What's New"dialog) — unrelated to navigation races and to this fix (the file was
not touched here); tracked separately as a changelog-feature issue, not
part of this E2E-flake follow-up.
Issue 3: "Security Enforcement" CI Job Never Actually Runs
security-enforcement/— RESOLVED 2026-07-30Status: Resolved. Discovered 2026-07-30, while validating the
createUserViaApi/changelog-suppression fix below — unrelated to that fix,noted in passing and tracked here per the same "pre-existing, not part of
this pass" convention as Issues 1-2. Fixed in a dedicated pass the same day.
.github/workflows/e2e-tests-split.yml's "Security Enforcement" job invokednpx playwright test --project=chromium tests/security-enforcement/ tests/security/ tests/integration/multi-feature-workflows.spec.ts. Butplaywright.config.js'schromiumproject hastestIgnore: ['**/security-enforcement/**', '**/tests/security/**'](mirrored on thefirefox/webkitprojects) — those two directories are only ever collectedby the dedicated
security-testsproject (Chromium-only, sequential,workers: 1, brings up CrowdSec/WAF viasecurity-shard-setup), which theCI job never selected. Net effect: the job's
--project=chromiuminvocationsilently matched zero tests in
tests/security-enforcement/andtests/security/and only ever actually ranmulti-feature-workflows.spec.ts, without erroring or reporting the othertwo paths as skipped/missing. Confirmed locally:
npx playwright test tests/security-enforcement/multi-component-security-workflows.spec.ts --project=chromium --list→ "Total: 0 tests in 0 files", while--project=security-testscollects and runs them normally. This affectedall three per-browser security jobs (
e2e-chromium-security,e2e-firefox-security,e2e-webkit-security), each with the analogous--project=<browser>invocation.Fix shape chosen
Split each of the 3 jobs' single
npx playwright testinvocation into two,run sequentially in the same step (Option B from the two shapes originally
sketched here) — kept the existing 3-job-per-browser topology (job count,
needs:graph intest-summary/e2e-results, andworkflow_dispatchbrowser-selection semantics all untouched) rather than consolidating into a
single job, since
security-testsis inherently Chromium-only regardless ofwhich of the 3 jobs invokes it (its
use:block hardcodesdevices['Desktop Chrome']inplaywright.config.js), so collapsing thejob topology was a larger, separate structural change out of scope here:
--project=<browser>(chromium/firefox/webkit, matching the job) ontests/integration/multi-feature-workflows.spec.ts— first.--project=security-testsontests/security-enforcement/+tests/security/— second.Each invocation gets its own
PLAYWRIGHT_HTML_REPORTand--outputsubdirectory so the second invocation's HTML report doesn't overwrite the
first's before the "Upload HTML report" step runs.
Ordering matters and was not the first thing tried. Running
security-testsbefore the browser-project invocation (the naturalreading order matching the job's own test-path list) was tried first and
reproducibly broke the second invocation:
security-testsincludestests/security-enforcement/zzzz-break-glass-recovery.spec.ts, whichflips ACL/WAF/rate-limit modules on as part of testing enforcement, and the
project's own teardown (
security-teardowninplaywright.config.js) onlyactually runs when
PLAYWRIGHT_SKIP_SECURITY_DEPS=0— an env var thisworkflow doesn't set, so
security-teardown'stestMatchresolves to[]and it never executes. Within a single job, both invocations share the same
long-lived container, so the second invocation inherited whatever security
state the first left behind. Reproduced locally: running
security-testsfirst left the container rate-limiting its own
/api/v1/health//api/v1/setupendpoints, and the following
multi-feature-workflows.spec.tsrun failedits
auth.setup.tsstep withGET /api/v1/setup failed with unexpected status 429. Swapping the order — browser-project invocation first, whilethe container is still at its clean post-boot baseline,
security-testssecond — avoids the contamination entirely, verified below. (Real CI job
runs are unaffected by cumulative state across jobs since each job starts
a brand-new container; this was purely an intra-job, cross-invocation
ordering hazard once both invocations started sharing one container.)
Verification (real re-run, not just syntax)
actionlint .github/workflows/e2e-tests-split.yml— clean.Reproduced the fixed job's actual environment locally: brought up
.docker/compose/docker-compose.playwright-ci.ymlwith--profile security-tests(same CrowdSec/WAF setup the job already had, on a freshcharon:localimage built from this branch, port-remapped via agit-ignored local override to avoid colliding with an unrelated Charon
instance already running on the host — no such conflict exists on GitHub's
ephemeral runners), waited for the health check exactly as the job does,
then ran the fixed job's exact two
npx playwright testinvocationsin order, back-to-back, on one freshly-started container (earlier attempts
that reused the same container across several manual runs in the same
session hit an unrelated, session-only rate-limit accumulation artifact —
not present on a fresh container, and not something a real CI job's
single-pass, fresh-per-job execution would ever hit):
--project=chromium tests/integration/multi-feature-workflows.spec.ts:15/15 passed (39.9s) — matches the prior documented baseline exactly.
--project=security-tests tests/security-enforcement/ tests/security/:now genuinely collects and runs (was 0 before the fix) — 407 passed, 21
failed, 3 flaky, 4 did not run, out of 435 total (34.9m). Consistent
with the earlier manual
--project=security-testsverification passreferenced in this doc's intro (409/435 baseline) — same failing test
files/categories (
audit-logs,crowdsec-first-enable,rate-limiting,security-headers,acl-waf-layering,auth-middleware-cascade,multi-component-security-workflows,encryption-management), zerochangelog/
whats-new-related failures.Files touched:
.github/workflows/e2e-tests-split.ymlonly (all 3 securityjobs' "Run ... Security Enforcement Tests" steps).
playwright.config.jswas not modified — the fix stays entirely in the CI invocation layer.
References
docs/reports/qa_report.md(§1b "The other 27 failures", §6.3)7503c01afix(e2e): extend gotoTolerant()...commit onfeat/changelog).github/workflows/e2e-tests-split.yml"Security Enforcement" job vs.playwright.config.jsprojecttestIgnore/testMatchconfig8d797328Auto-created from trivy-suppression-and-e2e-flake-followups.md