--- title: Dependency Security description: How dependencies are kept current, and how secrets are kept out of the repository. type: reference related: - /en/docs/packages/security/application - /en/docs/deployment/gitlab --- # Dependency Security Dependency updates are handled by [Renovate](https://docs.renovatebot.com), configured in `renovate.json` at the repository root. It opens merge requests against GitLab. There is no `.github/` directory and no Dependabot. GitLab has no Dependabot equivalent — its dependency scanning reports vulnerabilities but never opens update MRs — which is the reason Renovate is here at all. ## How it runs Renovate is a job in the `setup` stage of `.gitlab-ci.yml`, gated on a scheduled pipeline: ```yaml rules: - if: '$CI_PIPELINE_SOURCE == "schedule" && $RENOVATE == "true"' ``` To turn it on, create a scheduled pipeline in **Build → Pipeline schedules**, set `RENOVATE=true` as a pipeline variable, and add a `RENOVATE_TOKEN` — a project or group access token with `api` and `write_repository` scope. Without the token the job runs and finds nothing to do, which is a quiet failure worth checking for on the first scheduled run. Running it on a schedule rather than as a hosted app is deliberate: the token stays in your own project, and self-hosted GitLab works identically to gitlab.com. ## What it groups Updating `next` without `react` produces an install that resolves but does not run. Related packages are therefore raised as one MR: | Group | Packages | | ------------------- | ---------------------------------------------------------------- | | next and react | `next`, `react`, `react-dom`, `@types/react`, `@types/react-dom` | | turbo | `turbo`, `@turbo/gen` | | prisma | `prisma`, `@prisma/client`, `@prisma/adapter-pg` | | biome and ultracite | `@biomejs/biome`, `ultracite` | | better-auth | `better-auth` | Prisma is grouped because the client, the CLI and the adapter share a runtime protocol, and a mismatched trio fails at query time rather than at install. ## What it automerges Minor and patch updates automerge — but only for packages whose current version is not `0.x`: ```json { "matchUpdateTypes": ["minor", "patch"], "matchCurrentVersion": "!/^0/", "automerge": true, "automergeType": "branch" } ``` Semver only promises compatibility above 1.0. Below it a minor bump is allowed to break anything, and a great deal of this dependency tree is still on `0.x`. Those updates wait for a human. `automergeType: "branch"` merges without opening an MR when the pipeline passes, which keeps the MR list readable. The pipeline is still the gate — a failing build blocks the merge exactly as it would for a person. Patch updates to `devDependencies` automerge regardless of version, since a broken linter fails CI rather than production. Both automerge rules carry `minimumReleaseAge: "3 days"`. A compromised or simply broken release is usually yanked within hours, and nothing in an automerged patch bump is urgent enough to be the first install of it. Lockfile maintenance runs weekly, before 5am on Monday, so the refresh is already merged or already failing by the time anyone starts work. ## Vulnerability alerts `osvVulnerabilityAlerts` is on, so Renovate raises updates for advisories in the [OSV](https://osv.dev) database. Those are labelled `security` and **never** automerge, regardless of the rules above — a security update is exactly the one a human should look at, and the rules that make routine bumps safe say nothing about whether a fix changes behaviour. ## Versions that are not in a manifest Several versions matter but live in ordinary source files, where no dependency manager would ever look. `customManagers` in `renovate.json` picks them up with regexes: * The bun, pnpm and npm versions the CLI writes into generated projects, in `packages/cli/src/bootstrap/constants.ts` — one manager each, since each resolves against a different package. Without these, every project generated from the template pins a package manager that is quietly a year out of date. * The `oven/bun` version pinned in the `Dockerfile` and in the CI default image. * The `turbo` version the Dockerfile fetches for its prune stage. If you move either of those, move the matching `managerFilePatterns` entry with it. A regex manager that matches nothing does not fail — it just stops working. ## Secret scanning GitLab's own templates are included in `.gitlab-ci.yml` and run on every pipeline: ```yaml include: - template: Jobs/Secret-Detection.gitlab-ci.yml - template: Jobs/SAST.gitlab-ci.yml ``` Secret Detection scans the diff for credentials; SAST runs static analysis over the source. Both report into the merge request. They are a backstop, not a control: a secret that reaches a pipeline has already been pushed, so treat any finding as a rotation rather than a deletion. ## Worth adding per project * **A pre-commit secret scan.** [Gitleaks](https://github.com/gitleaks/gitleaks) or [Trufflehog](https://github.com/trufflesecurity/trufflehog) in the existing `lefthook.yml` catches the secret before it is committed, which is the only point at which deleting it is sufficient. * **Supply-chain review**, such as [Socket](https://socket.dev), if the project pulls in dependencies from outside the well-known set. Renovate keeps versions current; it says nothing about whether a package should be trusted. --- For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md) For an index of all available documentation, see [/llms.txt](/llms.txt)