Skip to main content

Dependency Management

This document describes how Questarr selects, obtains, and tracks its dependencies.

Selection

Dependencies are added deliberately as part of normal development, via npm install, and land in package.json alongside the feature or fix that needs them. Preference is given to actively maintained, widely used packages already common in the Node/React ecosystem. New dependencies go through the same pull request review process as any other code change (see .github/CONTRIBUTING.md) before merging to main.

Obtaining dependencies

  • Packages are installed from the public npm registry.
  • package-lock.json is committed to the repository and used for reproducible installs — the exact resolved version of every direct and transitive dependency is pinned.
  • The packageManager field in package.json pins the npm version used to install and build the project.
  • The allowScripts field in package.json explicitly allowlists which packages are permitted to run install-time (postinstall) scripts. It’s npm’s own native field (npm ≥ 11.16.0), managed via npm approve-scripts / npm deny-scripts, not a third-party tool — today it’s advisory (npm flags unreviewed scripts but still runs them), with a future npm release expected to block unapproved scripts by default. Any package added here should have a concrete reason (e.g. a native module that needs to compile a binary during install).
  • The overrides field forces a specific version of a transitive dependency when a direct dependency’s own declared range still permits a vulnerable release:
    • socket.io-parser: 4.2.7 patches CVE-2026-33151 (resource exhaustion via unbounded binary attachments, fixed in 4.2.6) and CVE-2026-69185 (zero-attachment memory exhaustion, fixed in 4.2.7). Needed because socket.io/socket.io-client declare socket.io-parser: ~4.2.4, a range that still allows the unpatched 4.2.4–4.2.6.
    • engine.io: ^6.6.10 patches GHSA-2gc4-cqfq-p2gv (DoS via an Engine.IO protocol revision mismatch). Needed because socket.io@4.8.3 declares engine.io: ~6.6.0, a range that still allows the unpatched 6.6.0–6.6.9.
    • @esbuild-kit/core-utils’s esbuild dependency is bumped to ^0.25.0 to patch the esbuild dev-server request-forwarding issue (GHSA-67mh-4wv8-2f99). Needed because drizzle-kit pulls in @esbuild-kit/esm-loader → @esbuild-kit/core-utils, which pins esbuild: ~0.18.20.
    • body-parser: 1.20.6 patches CVE-2026-12590 (invalid limit values silently disabling size enforcement). Needed because express bundles body-parser: ~1.20.5.
    • qs: ^6.16.0 patches CVE-2026-82417 (DoS via attacker-controlled isBuffer, affecting >=2.2.5 <6.16.0) and CVE-2026-82562 (array-limit bypass via bracket-key comma parsing, affecting >=6.14.2 <=6.15.3). Needed because express and body-parser both pin qs: ~6.15.1, which excludes the patched 6.16.0 — npm’s only other remedy was a semver-major bump to express@5. The override also covers openid, steam-web and superagent, whose own ranges likewise still permit a vulnerable release.
    • brace-expansion: ^5.0.12 patches GHSA-6j4f-fj2g-mc7p and GHSA-qhr7-859c-m2p7 (stack exhaustion via uncontrolled recursion, fixed in 5.0.10 and 5.0.11) and GHSA-q2hr-2g5m-vwhr (quadratic-time {a},b} rewrite, fixed in 5.0.12). It was previously pinned at ^5.0.9, which patched CVE-2026-69152 (DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation). Needed because archiver → readdir-glob → minimatch would resolve to a version below 5.0.9 without this override; the override was previously pinned at ^5.0.8, which patched CVE-2026-14257 but was itself within the range vulnerable to CVE-2026-69152 (4.0.0–5.0.8). The same floor also covers CVE-2026-13149 (exponential-time expansion of consecutive {} groups, fixed in 5.0.8).
    • fast-uri: ^3.1.8 also patches GHSA-hrr3-gc8f-f4qj (inconsistent host case normalization via percent-encoded octets, fixed in 3.1.8). The earlier ^3.1.7 floor patched seven HIGH advisories: CVE-2026-16221 (fixed in 3.1.4), CVE-2026-18446 (backslash authority introducer, fixed in 3.1.5), CVE-2026-75931 and CVE-2026-76172 (host confusion via skipped IDN/percent-encoded scheme normalization), CVE-2026-75975 and CVE-2026-75899 (SSRF via malformed IPv6/repeated hostname percent-decoding normalization), and CVE-2026-84292 (authority injection via an unvalidated port in serialize). Needed because ajv@8.20.0’s own declared range (^3.0.1, then ^3.1.5) kept allowing an unpatched release. ajv is a shared transitive dependency of both secretlint (dev-only) and the production @hookform/resolvers (as a peerOptional dependency), so despite earlier notes here, fast-uri is reachable in production — confirmed by npm audit --omit=dev flagging it directly.
    • proxy-addr: ^2.0.8 patches CVE-2026-90711 (AIKIDO-2026-101201, critical: accepts undersized IPv4-mapped IPv6 trust subnets, letting unauthenticated clients spoof X-Forwarded-For and bypass IP-based access controls, rate limiting, and audit logging), affecting >=1.1.0 <=2.0.7. Needed because express pins proxy-addr: ~2.0.7, which had not yet bumped its declared range to include the patched 2.0.8.
    • ip-address: ^10.7.2 patches CVE-2026-101913 and CVE-2026-101910 (affecting <=10.5.0). Needed because express-rate-limit (^10.2.0) and socks (^10.1.1) still allow a vulnerable release.
    • node-gyp’s undici dependency is bumped to ^6.29.0 to patch CVE-2026-85024 (affecting 6.25.0–6.28.0). Scoped to node-gyp (reached via the dev-only @lizenz/checker) because the app’s own direct undici dependency already carries the same fix on the 8.x line (8.10.2). Needed because node-gyp declares undici: ^6.25.0.
    • eslint-plugin-react’s eslint dependency is bumped to ^10.9.1 to allow ESLint v10 support. Needed because eslint-plugin-react@7.37.2 officially supports only up to eslint@^9.7, but the linting rules in ESLint v10 are stricter and require updating the codebase to comply. This override is temporary — once eslint-plugin-react releases a new major version with official ESLint v10 support, it can be removed.
    • All of the above should be revisited (and likely removed) once the upstream packages bump their own internal dependency ranges past the vulnerable versions.
    • The check-overrides CI job (npm run check:overrides, see scripts/check-overrides.mjs) checks this automatically on every PR and fails once an override is no longer needed, so there’s no need to track removal manually.

Tracking and updates

Dependabot is configured in .github/dependabot.yml to check for updates weekly (Monday) for both npm dependencies and GitHub Actions used in CI:
  • Updates are opened as grouped pull requests (e.g. React-related packages, Radix UI components, dev vs. production dependencies, and all GitHub Actions bumps) to keep the PR volume manageable.
  • Semver-major bumps are proposed automatically like any other update rather than excluded, since silently skipping them meant a major-version-only security fix could go unnoticed; they aren’t folded into the minor/patch groups, so they still land as their own PR and get individual review.
  • Every dependency-update PR runs through the same CI gate as any other change — lint, type check, the full test suite, and a Docker build (see .github/workflows/ci.yml) — before it can be merged.

Release-time visibility

Every published Docker image ships with a generated Software Bill of Materials listing the exact versions of every dependency included in that release. See docs/SBOM.md for how to inspect it.

Currently blocked updates

Tracked here so a blocked Dependabot PR doesn’t get silently re-proposed and re-investigated from scratch. Remove an entry once its update is unblocked and merged. As of 2026-07-04, release/1.4.0:
  • @hookform/resolvers 3.10.0 → 5.4.0 (PR #756) — blocked. Installs, but the TypeScript check fails in form resolver usage (client/src/pages/downloaders.tsx, client/src/pages/indexers.tsx). The project is on Zod 3 (zod: ^3.25.0); this upgrade likely needs resolver/schema compatibility adjustments first.
  • React 19 react 18.3.1 → 19.2.7, @types/react 18.3.11 → 19.2.17 (PR #761) — blocked. Install fails on peer dependency resolution across UI dependencies; needs a broader compatibility pass across the React ecosystem packages first.