Dependency Management

August 3, 2026 · View on GitHub

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.6 patches CVE-2026-33151 (resource exhaustion via unbounded binary attachments). 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.5.
    • @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.
    • brace-expansion: ^5.0.9 patches GHSA-rgw5-rvv9-x895 (DoS via unbounded intermediate arrays, bypassing the CVE-2026-14257 mitigation). Needed because archiverreaddir-globminimatch 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 GHSA-rgw5-rvv9-x895 (4.0.05.0.8).
    • fast-uri: ^3.1.4 patches CVE-2026-16221. Needed because secretlint's nested ajv@8.20.0 declares fast-uri: ^3.0.1, a range that still allows the unpatched 3.1.3. Dev-only (reached via npm run secretlint, never bundled into production).
    • 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.05.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.
  • @eslint/js 9.39.410.0.1 (PR #760) — blocked. Install fails on peer dependency resolution because ESLint is still on 9.x; needs a coordinated ESLint stack upgrade, not a standalone bump.
  • React 19 react 18.3.119.2.7, @types/react 18.3.1119.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.