The resolve()+startsWith containment check was correct but CodeQL did not
recognize it. Apply path.basename() to the name in resolveWithinPreviewDir -- a
sanitizer CodeQL recognizes -- so every id-derived preview path is provably a
single filename inside the preview dir. Behaviour is unchanged for valid ids
(already charset-validated); containment check kept as a backstop.
- svg-sanitize.ts: strip each dangerous element repeatedly until stable with
whitespace-tolerant end tags, defeating nested/overlapping tags (closes 5
incomplete-multi-character-sanitization + 1 bad-tag-filter; the prior
single-pass regex could leave a residual <script>/<iframe>).
- file-preview.ts: add a resolve()+containment barrier (the path-traversal
guard CodeQL recognizes) on top of the id charset check (closes 9
path-injection).
- metadata.ts: bound the XMP namespace:name key segments so parseXmp cannot
backtrack polynomially (closes js/polynomial-redos).
- analytics-disabled.spec.ts: match analytics by URL host, not substring
(closes 4 incomplete-url-substring-sanitization).
typecheck + lint green; svg (119), preview (22), metadata (164) tests pass.
rembg 2.0.75 requires a numpy incompatible with the pinned numpy==1.26.4
that the rest of the ML stack (onnxruntime etc.) depends on, making
pip-audit's resolution impossible. The rembg <2.0.75 advisory (medium) is
accepted as a residual: it only affects the on-demand background-removal AI
bundle (publishing currently paused) and can't be patched without a numpy
2.x migration across the whole Python sidecar.
jsdom@29.1.1 imports undici's internal lib/handler/wrap-handler.js, removed
in 8.5.0 -- so forcing undici>=8.5.0 broke ~27 jsdom unit files. Removing the
override lets pnpm resolve undici to the latest patched in-range versions
(6.27.0/7.28.0/8.5.0, all above the vulnerable ranges) while jsdom keeps
working. Unit suite restored to 188 files / 4908 tests.
The 2.0 API routes changed from /api/v1/tools/<toolId> to
/api/v1/tools/<section>/<toolId>. Updated all bench script call sites
to pass section-prefixed tool IDs (e.g. "image/resize" instead of
"resize"), matching the authoritative toolSection() mapping.
The Astro Dev Toolbar (enabled by default in dev mode) injects h1
elements ("No islands detected", "Audit", "Settings") and a
"Community" link inside its shadow DOM. Playwright CSS selectors
pierce shadow DOM, causing 6 test failures:
- 5 heading-hierarchy tests saw extra h1 elements from the toolbar
- footer "Community" column title resolved to 2 elements (footer h4
plus toolbar's Astro community chat link)
Root-cause fix: set PLAYWRIGHT=1 env var in the Playwright webServer
command; Astro config conditionally disables devToolbar when set.
Normal development retains the toolbar.
All /api/ routes are already covered by the global @fastify/rate-limit
(index.ts:283), but CodeQL doesn't model the global registration and flagged
every route without an explicit per-route limit. Added tuned config.rateLimit
to 45 routes: stricter on auth/credential routes (mfa/saml/oidc/api-keys,
15-30/min), moderate on writes (60/min), generous on reads/polls (300/min =
the global default). Real defense-in-depth on sensitive routes. 3 alerts on
non-route code (a preHandler hook, the rate-limiter's own DB lookup, a test
helper) are documented false-positives covered by the global limiter.
- file-preview.ts: validate the :id URL param against a safe charset and
restrict the original-name extension to alphanumerics before they feed
filesystem paths (closes 9 CodeQL js/path-injection; defense-in-depth on
top of the existing DB lookup).
- media-input.ts: require the SRT/VTT timecode structure to detect a subtitle
rather than a bare '-->' (closes CodeQL js/bad-tag-filter; also rejects
non-subtitle files that merely contain '-->').
- tests/e2e/helpers.ts: build the sharp script path via JSON.stringify
instead of single-quote-only replace (CodeQL js/incomplete-sanitization,
high: backslashes were not escaped). Proper fix, no suppression.
- landing CategoryCards: rename the file-modality marketing card to
"File Tools" (matches the Image/Video/Audio Tools siblings and is
unique vs the 23 "Files" tool pills, which broke the e2e locator).
Modality label stays "Files" everywhere it is the actual modality.
#280 left 10 section-prefixed custom-route files mis-indented and one
unsorted import block in the new landing section-index page. Fixed via
biome formatter (api) and manual import sort (landing). No config change
(biome.json is hook-protected); no suppression. pnpm lint + typecheck
now exit 0.
The fifth user-facing group is now Image, Video, Audio, PDF, Files
(internal modality id stays "file"; section.ts "files" was already
"Files"). Updates modality.ts label + comment, all 21 i18n locales
(categories.data "Data Files"->"Files", modalities.documentsAndFiles
"PDF & Data"->"PDF & Files", dead homePage.data), landing cards/hero
search/tools filter, docs headings, and e2e modality-tab assertions
(/^Data/ -> /^Files/, which had been failing).
#276's overhauled integration tests + QA sweep scripts hit the old
sectionless /api/v1/tools/<id> routes, which 404 after #280 made routes
section-prefixed. Ran scripts/rewrite-tool-paths.ts for literal URLs and
converted dynamic /api/v1/tools/${id} constructions to apiToolPath().
Negative-test paths (nonexistent/fake tools) intentionally left raw.
- Add || true after inference commands so set -e doesn't kill the
script before we reach the meaningful error message (exit code 3)
- Fix colorize settings: use "model" not "method" (matching colorize.py)
- Use larger font (size=40) and image (400x100) for OCR test so
PaddleOCR reliably recognizes the text
- Add || result="" fallback for stdout-capture smoke tests (OCR,
transcription) to prevent set -e on command substitution failure
The verify job runs between build and publish, gating HuggingFace
upload on per-bundle smoke tests. Each of the 14 bundle variants
(7 bundles x 2 arches) is verified independently: SHA256 checksum,
tarball extraction, Python import checks, and functional inference.
- validate models field in bundle.json
- pipe JSON via stdin instead of triple-quote embedding (injection safety)
- add PNG magic byte validation for background-removal output
- add dimension assertion for upscale-enhance output
- add fixture existence guards before smoke tests
- use --no-index for offline fixup wheel install
Verifies bundle tarballs in 4 phases: SHA256 integrity, extraction
and install into the base venv, Python import checks per bundle,
and a functional inference smoke test per bundle.
- fixture-integrity: probe media via media-engine probeMedia (resolves the
bundled static ffmpeg) instead of bare system ffprobe, which is ENOENT in CI;
gate on ffmpegAvailable() like the other media tests
- a11y: regenerate a11y-baseline.json to include the mobile device keys (the
baseline only had desktop keys, so the mobile a11y scan saw them as new)
- device-visual: tag @visual and exclude it from the PR mobile-smoke gate
(darwin-only screenshots cannot pass on linux; nightly + update-visual-baselines
still run it to seed linux goldens)