Dependency Security
How dependencies are kept current, and how secrets are kept out of the repository.
Dependency updates are handled by Renovate,
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:
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:
{
"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 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/bunversion pinned in theDockerfileand in the CI default image. - The
turboversion 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:
include:
- template: Jobs/Secret-Detection.gitlab-ci.yml
- template: Jobs/SAST.gitlab-ci.ymlSecret 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
or Trufflehog in the
existing
lefthook.ymlcatches the secret before it is committed, which is the only point at which deleting it is sufficient. - Supply-chain review, such as Socket, 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.