SecureChain Beta

User guide

Fix vulnerabilities

How to close the findings of a project: with an upgrade to a fixed version, or with a TuxCare build of the version that you use now. The CLI makes the change, checks it, and keeps it current.

Two ways to fix #

Upgrade the packageUse a TuxCare build
What changesThe version: you move to the lowest version that fixes the advisory.The source: the same version, built by TuxCare from source with the fix added. Its version ends in -tuxcare.N (Python: +tuxcare.N).
Use it whenThe fixed version is close, and its changes do not break your code.The fix is in a new major version, or the project that made the package no longer fixes your version.
Howsecurechain fix writes the change for you. By hand, for a direct dependency: your package manager, with the flag that pins the exact version, for example npm install --save-exact lodash@4.17.21. For pnpm the flag is also --save-exact, and for Yarn and Bun it is --exact. Without it, package.json gets a range such as ^4.17.21. A named install also sorts the dependency list of package.json again: that is your package manager, not harden. For a transitive package, see Upgrade a package.securechain harden. See TuxCare builds.
NeedsNothing more. securechain fix needs a portal token and no TuxCare registry.A subscription with TuxCare builds.

The portal tells you which fix applies:

This page is about the packages of a project. An OS package of a server is not a dependency of a project: securechain harden and securechain fix do not change it. Its How to fix card gives the update command of the package manager of the server, for example sudo yum update nettle nettle-devel. See Update an OS package.

For a language package, the portal recommends one fix in this order:

  1. A TuxCare build of the version that you use now, for example 7.29.3-tuxcare.4 for 7.29.3. A TuxCare build fixes only the version that it was built from.
  2. Else the lowest upstream version above yours that fixes the finding.
  3. Else, when no upstream version has the fix, the TuxCare build of the nearest higher version.

Upgrade a package #

  1. Open the Fix plan tab of the repository. The packages with the worst severity come first. Inside one severity, a package with a known-exploited finding comes first.
  2. Run securechain fix, or change the version of the package with your package manager. For a transitive package, upgrade the direct dependency that pulls it in, or pin it with the mechanism of your tool: overrides for npm, pnpm, and Bun, resolutions for Yarn, a constraints file for pip, dependencyManagement for Maven, a constraints block for Gradle, a direct PackageReference for NuGet, or dependency_overrides for Dart. With Go, go get moves it. With Cargo, Composer, and Bundler, cargo update, composer update, and bundle update move it inside the ranges of its parents; outside them, a parent must move. The How to fix card gives the line or the command when one fits your tool. On npm and PyPI, do not install a transitive package by name: that adds a new direct dependency, and the copy under its parent can stay vulnerable. For a TuxCare build, securechain harden writes the override.
  3. Run your tests, and commit the manifest and the lockfile.
  4. Run securechain sca, or let the CI job run it. The finding closes when the portal gets a scan that does not have it.

The fix plan marks an upgrade to a new major version with major version. Such an upgrade can break your code. A TuxCare build avoids it.

Write the upgrade: securechain fix #

securechain fix (CLI 0.1.81 or later) writes the upstream fix for each open finding, runs the install command of your package manager, and checks the result. It needs a portal token and no TuxCare registry. It also needs no .securechain.yaml. On Maven and Gradle, it needs no BOM file: the BOM file is for TuxCare builds.

securechain fix --dry-run   # print each change; write nothing
securechain fix

--dry-run prints each change that the write makes, and writes nothing in the project: your package manager runs on a temporary copy. It reads the findings as securechain sca does, so it sends the package list to your workspace when the portal has no scan of that list.

For each package, fix writes the first change that applies:

  1. The lockfile entry, when the fix fits each range that requests the package. Your package manager chooses the version, which can be above the fix. On Bun, the lock moves to the fix, not to the newest version.
  2. The range in the manifest, with the same operator, when that range refuses the fix.
  3. A new release of the parent, when a transitive range refuses the fix and a release of each parent takes it. Except on renv, fix does not move a parent to a new major version: that is your choice. Where your tool has an override, fix writes the override, and a note names the release. Where it has none, fix leaves the finding.
  4. An override, where your tool has one, scoped so that it does not move another version of the package.

The rest of what fix does:

The tools of securechain fix #

fix covers each package manager that securechain reads: npm, pnpm, Yarn classic, Yarn Berry, Bun, Deno, uv, PDM, Poetry, Pipenv, pip, pip-tools, conda, Maven, Gradle, Cargo, NuGet, Bundler, Composer, Stack, Cabal, CocoaPods, Julia, renv, CPAN, Pub, Mix, SwiftPM, Paket, Bazel rules_jvm_external, Go modules, GitHub Actions, and Conan.

CLI 0.1.81 covers the JavaScript tools, the Python tools, Maven, Gradle, and Cargo. Each other tool needs a CLI release after 0.1.81. CLI 0.1.81 leaves the findings of such a tool with the code no_fixer, names the tool and the file to edit by hand, and ends with exit code 1.

ToolsWhat fix writesThe override
npm, pnpm, Yarn classic, Yarn Berry, Bun, DenoThe lockfile, with the tool, and the range in package.json. On Deno: the imports of deno.json.overrides on npm and Bun, the override block that your version of pnpm reads, and resolutions on Yarn. Bun also takes resolutions when the manifest declares only that block. Deno has no override.
uv, PDM, Poetry, Pipenv, pip-toolsA lower bound on the fix, name>=version, and the tool chooses the version in its lockfile. On pip-tools, the lockfile is the compiled requirements.txt.tool.uv.override-dependencies on uv and tool.pdm.resolution.overrides on PDM. The others have no override.
pippip has no lockfile: a line in the constraints file that requirements.txt reads, or in a new constraints.txt with a new -c line.None.
condaconda has no lockfile: a line of the pip: list, also for a package that a conda spec names. fix does not edit a conda spec.The pip: list.
MavenThe <version>, the property, or the dependencyManagement entry that the report names.dependencyManagement in the pom of the module. It outranks a BOM and a parent pom outside the project, and fix moves neither of them.
GradleThe coordinate, the variable, or the key of the version catalog that the report names. Then --update-locks of each gradle.lockfile.The block upstream-fix in the settings file, before the block of harden. update and unharden leave it.
Bazel rules_jvm_externalThe version in the module file, then a repin, which writes the lock file.A new entry in the artifacts list, for a transitive artifact.
Cargo, Bundler, Composer, SwiftPM, CocoaPods, Julia, CPANThe update command of the tool for the package and its parents, for example cargo update. Else the version in the declaration, with the same operator.None: when no release of a parent on its line takes the fix, fix leaves the finding.
renvrenv::install of the fix, then renv::snapshot of the package. Else the version in DESCRIPTION, with the same operator. The package whose record refuses the fix moves to its newest release, also to a new major version.None: when such a move does not take the fix, fix leaves the finding.
NuGetThe Version of the PackageReference, its VersionOverride, or the PackageVersion of Directory.Packages.props. fix does not search the releases of a parent.A direct PackageReference at the fix. Under central package management, a PackageVersion in Directory.Packages.props, and a PackageReference with no version where the PackageVersion alone moves nothing.
Pub, Mix, PaketThe update command of the tool for the package and its parents. Else the version in pubspec.yaml, mix.exs, or paket.dependencies, with the same operator.dependency_overrides on Pub, override: true on Mix, and nuget <id> == <version> on Paket.
Stack, CabalThe extra-dep line of stack.yaml, or the line of cabal.project.freeze. fix does not widen a range of your project.None.
Go modulesgo get of the module at the fix, in each module that requires it, then the vendor step of a vendored project.None. fix does not change a replace: the reason gives the edit.
GitHub ActionsThe uses: line of the workflow or of the composite action: a release tag at or above the fix. A tag of the major or the minor stays in that form when it stands at such a release. A full commit stays a full commit.None.
Conanconan.lock, the line of conanfile.txt or conanfile.py that refuses the fix, or the line of a parent on its major version (0.x: on its minor version).override=True in conanfile.py, and the key overrides of conan.lock for conanfile.txt.

fix runs your package manager, so the tool must be on the computer, also for --dry-run. securechain sca reads many lockfiles with no tool, and fix does not. So Cargo needs cargo, NuGet needs dotnet, Conan needs Conan 2, Bazel needs bazel or bazelisk, and GitHub Actions needs git. The same holds for bundle, composer, stack, cabal, pod, julia, Rscript, carton, dart, mix, swift, paket, and go. With no such tool, the run stops with exit code 5, and the message names the tool. With no npm or uv, fix leaves the finding with the code no_file.

The findings that fix leaves #

fix leaves a finding that has no single edit. It prints the reason for each one, and the file to edit by hand when there is one. --output json gives the same in fix.left, with a code. A left finding never reads as fixed. Each code gives exit code 1, with these exceptions. fixed_by_tuxcare, no_fix_planned, and live_patched do not change the exit code. unplaced keeps exit code 0 with a warning when it is the only open finding. The CLI release after the one that adds this code gives exit code 1.

Each finding of sca is a finding of fix, with one exception. A lockfile beside the tree of your tool can count the same package again, for example a gradle.lockfile. fix then keeps only the finding of the tree. It does this only when the tree covers the directory of the lockfile. A lockfile in a directory that the tree does not cover is a project of its own, also when the root holds the same package. For example, a website/package-lock.json below a root npm project gets the code no_file, and the reason names securechain fix --dir website. This rule needs CLI 0.1.84 or later. On a Gradle build of several projects, the finding of the gradle.lockfile of a subproject is a copy of the finding of the tree, on Gradle 8 and on Gradle 9: fix moves the package in that lockfile, and the run ends with exit code 0 (measured on Gradle 8.14.3 and 9.7.0). A subproject whose directory is outside the root is a project of its own, and its finding gets the code no_file.

CodeThe finding
no_upstream_fixThe portal names no upstream version.
several_fixesThe portal names several versions, and no comparison chooses one.
no_fileThe report names no file for the package: no lockfile, or a lockfile that no tool of this run read. For example, its tool is not on the computer, or the lockfile is a second project below the directory. The reason names the directory. Run securechain fix --dir <directory> where the tool is installed, or edit the file by hand.
pinnedThe file of the tool states the version with no range that fix can move. On NuGet: a packages.config entry.
environmentThe package is the tooling of a Python environment, or the Go toolchain. Upgrade the environment, or build with the fixed Go.
range_assumedThe change rests on a range that the CLI assumed. fix writes no edit on a guess.
manifest_rangeA declaration that fix cannot move and keep its meaning: a compound range, a tag, a URL, or an alias. On Stack and Cabal, a range of your project. On Go, a fix in another major version. On GitHub Actions, a branch or a short commit.
customer_overrideAn override that you wrote already matches the package. This includes a Go replace. fix does not replace your line.
hardenedThe project holds a TuxCare build of the package, or a pin that harden wrote. Run securechain update.
override_scopeNo key moves this copy and leaves another version as it is.
no_overrideThe tool has no override, and no parent that the project declares moves the package: Deno. On Mix and Pub: a file with no place for a new line.
no_parent_releaseNo release on the line of the parent takes the fix, and the tool has no override for it. On NuGet: the range of a parent stops below the fix.
not_movedThe tool ran, and the old version stayed. This includes a change that your package manager refused and a package that did not land: the reason keeps the line of the tool.
no_refGitHub Actions: git cannot read the repository, or the repository has no release tag at or above the fix.
residualThe new version landed, and a copy stayed on the old version. The run keeps the write.
fixed_by_tuxcareThe build in the project already closes the finding. fix has nothing to write.
no_fix_plannedTuxCare stated that it will not fix the finding on the build, so no version closes it. fix has nothing to write. This code needs a CLI release after 0.1.84.
live_patchedA live patch that is loaded on the host closes the finding. fix has nothing to write. This code needs a CLI release after 0.1.84.
unplacedNo step of the run placed the finding: no change and no other left finding names it. A defect of securechain. Move the package by hand. This code needs a CLI release after 0.1.84.

TuxCare builds #

TuxCare builds the open-source packages that your projects use from their source, and serves the builds from the TuxCare registry. When a vulnerability is found in a version, TuxCare adds the fix to that version and publishes a new build. TuxCare also fixes versions that the project that made them no longer maintains. A new upstream version reaches the registry only after TuxCare checks it, for example for malicious code.

The CLI moves a project to the builds in three steps: init one time for each repository, registry login one time for each computer, and harden each time you want to apply the builds.

If Renovate or Dependabot updates the dependencies of the repository, give it the rule of Renovate and Dependabot. Without the rule, the bot can replace a TuxCare build with an upstream release.

What you need #

Use the TuxCare registry, or your own repositories #

The setting Use the TuxCare registry says whether the CLI uses the TuxCare registry for the projects of the workspace. It is on by default.

The TuxCare registry needs a SecureChain or an ELS subscription. A workspace with neither gets no registry, whatever the setting says: the portal gives the CLI no registry address and no registry token, also for a repository with the value On. The CLI then gets the reason the TuxCare registry needs a SecureChain or an ELS subscription, and this workspace has neither, and the repository page shows Use the TuxCare registry: Not available with the same reason. The values of the setting stay as they are. When TuxCare adds a subscription, the values apply again: with the default, every repository uses the TuxCare registry. Contact TuxCare to add a subscription.

An admin sets the value of the workspace under Settings › Use the TuxCare registry. A repository can have a value of its own: open the repository, select More, and under Use the TuxCare registry select Workspace default, On, or Off. In a clone of the repository, securechain registry use on, off, or default does the same, and prints the value that applies now. When the workspace has neither SecureChain nor ELS, it prints the reason and stops with exit code 3. The value of the repository wins in both directions: a repository with On uses the TuxCare registry in a workspace with the value off. Only an admin can change the value of the workspace. A member or an admin of a repository can change the value of that repository: a person with the role viewer on the repository, a token of one repository or of one department (a CI token), and a TuxCare staff session cannot. A token of a person needs the permission write. The other people see the values on the repository page. The Audit log records each change as settings.registry_use.

For a token of one repository, the portal uses the value of that repository. For another token, it uses the value of a repository only when the CLI names the repository by its git remote. Else it uses the value of the workspace.

After an admin turns the setting off. The files that an earlier auth login, registry login, or init wrote still point your package managers at the TuxCare registry. Remove those lines. Each line names the registry host: the registry: line of .securechain.yaml names it, and on sc.tuxcare.cloud it is artifacts.tuxcare.com. git grep -n artifacts.tuxcare.com finds the lines in a repository. A registry: line that names your own mirror is your choice: keep it. While the setting is on, a new auth login writes the lines again.

The command reference lists the options of auth logout, auth login, and registry login.

Set up a project #

cd path/to/your/project
securechain init             # one time for each repository
securechain registry login   # one time for each computer

Move the project to the builds: harden #

securechain harden --dry-run   # show the plan and the changes; write nothing
securechain harden
  1. harden asks the portal which TuxCare builds your subscription has for the dependencies of the project.
  2. It writes those builds into the manifest and the lockfile.
  3. It can also change .yarnrc.yml, pnpm-workspace.yaml, or bunfig.toml, for example for the release-age excludes. Each such file gets the securechain header line as its first line. package.json and deno.json get no header, and a warning of harden names each one. unharden removes the header with the lines.
  4. It runs the install command of your package manager.
  5. It checks that each planned build is installed. If the check fails, it puts your files back and exits with code 6.
  6. It sends the new package list to your workspace.

Then run your tests, and commit the manifest, the lockfile, and each other file that harden changed.

Keep the builds current: update #

securechain update --check-only   # exit code 1 when a newer build exists; write nothing
securechain update

TuxCare publishes a new build when it fixes a new vulnerability in a version. update moves each TuxCare build of the project to the newest build of the same version, for example from 1.3.1-tuxcare.2 to 1.3.1-tuxcare.3. It installs and checks the result, as harden does. Run it on a schedule, or when the portal shows a finding that a newer build fixes.

The gate: check #

securechain check reads the installed dependencies and changes nothing. It exits with code 1 when the project is not in the state that harden made:

--explain prints the evidence and the fix for each problem.

TuxCare builds in CI #

A job that installs TuxCare builds needs the registry credential before the install step. The job does three things in this order: get the credential, install, run check.

GitHub Actions:

permissions:
  contents: read
  security-events: write
steps:
  - uses: actions/checkout@v4
  - uses: TuxCare/securechain-action@v0
    with:
      command: registry
      portal-token: ${{ secrets.SECURECHAIN_PORTAL_TOKEN }}
  - run: npm ci
  - uses: TuxCare/securechain-action@v0
    if: always()
    with:
      portal-token: ${{ secrets.SECURECHAIN_PORTAL_TOKEN }}
      registry-login: none

GitLab CI:

securechain:
  image: node:22        # any image that has curl and your package manager
  script:
    - curl -fsSL https://securechain.tuxcare.com/get/securechain | sh
    - out="$(securechain registry env --job-dir "/tmp/securechain-job-$CI_JOB_ID" --format shell)"; eval "$out"
    - env -u SECURECHAIN_PORTAL_TOKEN npm ci
    - securechain check --output junit > report.xml
  after_script:
    - rm -rf "/tmp/securechain-job-$CI_JOB_ID"
  artifacts:
    reports:
      junit: report.xml
include:
  - component: $CI_SERVER_FQDN/tuxcare/securechain-ci/check@0
    inputs:
      stage: test
      install: npm ci

Your own mirror: Artifactory or Nexus #

Many companies install packages only through their own repository manager, for example JFrog Artifactory or Sonatype Nexus Repository: a mirror. The mirror can serve the TuxCare builds. Add a remote repository of the TuxCare registry to the mirror (Nexus calls it a proxy repository), and put it first in the group that your package managers use (Artifactory calls the group a virtual repository). The mirror keeps the registry token. Your package managers keep the address of the mirror, and they need no credential of TuxCare.

The address of the TuxCare registry is artifacts.tuxcare.com. Your mirror connects to it, so allow the mirror to reach that host on port 443.

Keep the setting Use the TuxCare registry on. With the setting on, securechain registry env gives you the registry token, and securechain registry check reads the hash of each TuxCare build from the TuxCare registry. TuxCare does not publish these hashes in its feed yet. With the setting off, the check cannot compare the build of the mirror with the TuxCare build, and it reports not checked. The same applies to a portal that you install yourself and that names the mirror as its registry: the check does not compare the mirror with itself, so it reports not checked until TuxCare publishes the hashes.

The three rules for each remote repository #

npm, pnpm, Yarn, and Bun #

On Nexus:

  1. Create a repository of the type npm (proxy). Remote storage: https://artifacts.tuxcare.com/npm/. Under HTTP, set the authentication type Username, the user name to the registry token, and the password to x. Set the cache times of the third rule.
  2. Create a repository of the type npm (group), or open the group that you use. Put the new proxy first in the list of members, above the proxy of registry.npmjs.org.
  3. Point the project at the group. In .npmrc: registry=https://nexus.example.com/repository/npm-group/.

On Artifactory:

  1. Create a remote repository of the type npm. URL: https://artifacts.tuxcare.com/npm/. User name: the registry token. Password: x. Set the cache times of the third rule.
  2. Create a virtual repository of the type npm, or open the one that you use. Put the new remote repository above the remote repository of registry.npmjs.org in the list of repositories.
  3. Point the project at the virtual repository. In .npmrc: registry=https://artifactory.example.com/artifactory/api/npm/npm-virtual/.

Use the path /npm/ of the TuxCare registry, not /npm-upstream/: only /npm/ has the TuxCare builds (see the two paths of the registry). Do not add /npm to the address of the mirror: the address of the group is the registry. TuxCare did not measure an npm virtual repository of Artifactory: the free edition of Artifactory serves no npm.

pip, uv, Poetry, PDM, and Pipenv #

  1. Nexus: create a repository of the type pypi (proxy), with the Remote storage https://artifacts.tuxcare.com/pypi/. Nexus then asks https://artifacts.tuxcare.com/pypi/simple/project/ for each project. Artifactory: create a remote repository of the type PyPI with the same URL. Set the user name, the password, and the cache times as for npm.
  2. Put it first in the group (Nexus: pypi (group); Artifactory: a virtual repository of the type PyPI), above the proxy of pypi.org.
  3. Point the tools at the simple index of the group, for example index-url = https://nexus.example.com/repository/pypi-group/simple/ in pip.conf. The address ends in /simple/.

Maven and Gradle #

  1. Nexus: create a repository of the type maven2 (proxy), with the Remote storage https://artifacts.tuxcare.com/maven/. Artifactory: create a remote repository of the type Maven with the same URL. Set the user name, the password, and the cache times as for npm.
  2. Put it first in the group (Nexus: maven2 (group); Artifactory: a virtual repository of the type Maven), above the proxy of Maven Central.
  3. Point Maven at the group with a <mirror> in ~/.m2/settings.xml, and Gradle with a maven { url = uri("…") } entry in repositories. Maven and Gradle also need the BOM file of TuxCare (see What you need).

On Artifactory, delete the cached copies of the public remote repository for the packages that TuxCare rebuilds after you put the remote repository of TuxCare first: a cached copy wins over the order (the second rule). Nexus follows the order alone.

TuxCare measured the order of the Python and Maven groups on Nexus 3.98.1 and Artifactory OSS 7.161.26 with a test server in the place of the TuxCare registry. The free edition of Artifactory serves no PyPI and no npm, so the Python rules rest on Nexus alone. securechain registry check checks a Python index and a Maven or Gradle mirror (CLI 0.1.86 or later): see Check the mirror.

Check the mirror: registry check #

cd path/to/your/project
securechain registry check

registry check asks the registry that the files of the project name whether it serves the TuxCare builds. It changes no file. It takes one package of the project that has a TuxCare build at the same version as the public package, and asks the registry for the hash of that version. Then it compares the hash with the hash of the TuxCare build. It asks with your own tools and your own credential for the mirror: npm with the .npmrc for npm, pnpm, Yarn 1, and Bun, Maven with ~/.m2/settings.xml, Gradle with the credentials of the repository, and pip for pip, uv, Poetry, and PDM, with the configuration of pip, ~/.netrc, or the keyring. pip does not read a credential that only uv, Poetry, or PDM holds: when the mirror then refuses pip in such a project, the check reports not checked. The mirror holds the token of TuxCare. Run it after you set up the mirror, and after each change of the group. It prints one block for each package manager:

npm: registry https://nexus.example.com/repository/npm-group/
  ✖ it serves upstream's build of braces@3.0.2, not TuxCare's (sha512-b8um+L1RzM3W…, TuxCare's is sha512-xdS7zoiSHyo3…)
  cause: the group lists the TuxCare remote repository below the public proxy, so the public build wins
  fix: in Nexus or Artifactory, move the remote repository of artifacts.tuxcare.com above the public proxy in the group or the virtual repository, then run securechain registry check again
The lineWhat it meansWhat to doExit code
✓ it serves the TuxCare buildThe mirror serves the TuxCare build.Nothing.0
✖ it serves upstream's build, and the cause names the orderThe group has the TuxCare builds, but the proxy of the public registry is above the remote repository of TuxCare. On Maven and Gradle, the cause also names a cached copy of the public remote repository on Artifactory.Move the remote repository of TuxCare above the public proxy (the second rule). On Artifactory, also delete the cached copies of the public remote repository.5
✖ it serves upstream's build, and the cause names no remote repositoryThe group has no TuxCare version: it has no remote repository of TuxCare, or the TuxCare registry refuses the token of that remote repository. A client of the mirror cannot see which of the two it is. On a subscription with no ELS, the cause can also be the order: TuxCare did not measure whether the mirror then lists TuxCare versions, and the cause on the line names the order too.Add the remote repository of the format of the tool at the top of the group (https://artifacts.tuxcare.com/npm/, https://artifacts.tuxcare.com/maven/, or https://artifacts.tuxcare.com/pypi/), with the registry token as the user name, or put the new token into it (the first rule). On a subscription with no ELS, also check the order (the second rule).5
✖, and the cause says that the registry has no copy of the versionThe mirror answers that it does not have the version: no remote repository of TuxCare and no public proxy that holds it, or a refused token.The same as in the row above.5
✖ it did not accept the credential of this machineThe mirror refused your own credential for the mirror. This is not the token of TuxCare.Sign in to the mirror: npm: npm login --registry address of the mirror; Maven: a <server> with the id of the mirror in ~/.m2/settings.xml; Gradle: a credentials block of the repository; pip: ~/.netrc, the keyring, or the index URL. Or ask the administrator of the mirror for read access.5
✖, and the cause says that the files name the public registryThe project installs from the public registry (registry.npmjs.org, registry.yarnpkg.com, Maven Central, the Gradle Plugin Portal, or pypi.org), not from a mirror or the TuxCare registry.Point the files of the project at the mirror, or run securechain init.5
✖ could not askThe registry did not answer in time, two times, or it gave another error.Run the check again when the registry answers.5
· not checkedThe check could not judge this package manager: the project has no package with such a TuxCare build, the tree did not resolve, the package manager is Yarn 2 or later, Pipenv, or a tool of another ecosystem, the Python files name an index that the check does not judge (for example PyPI beside the mirror, more than one index, or a find-links), the mirror refused pip in a uv, Poetry, or PDM project, the index serves only the source archive of the version and no wheel, a Gradle build lists no Maven repository, or the check has no TuxCare build to compare with (the setting off, or a portal of your own that names the mirror).Read the reason on the line.2

The exit code is the highest code of the blocks, and 5 is above 2. It is 0 only when the mirror served the TuxCare build to each package manager. With no portal token, or with no registry token in the workspace, the command stops with exit code 3. When the portal does not answer, it stops with exit code 4. The command needs CLI 0.1.85 or later. The check of Maven, Gradle, pip, uv, Poetry, and PDM needs CLI 0.1.86 or later.

Rules for a project with TuxCare builds #

npm: the two paths of the registry #

For npm, the TuxCare registry has two paths on one host:

So a new TuxCare build never changes what an existing lockfile installs, and the install does not fail with EINTEGRITY. pnpm and Yarn 2 or later also use /npm-upstream/ as the source: harden writes /npm/ into the lockfile entry of each build. A project that a CLI before 0.1.21 set up names /npm/ as the source of pnpm and Yarn 2 or later. The next securechain init changes it, and keeps the lockfile.

A tool that selects a version for a TuxCare build against /npm-upstream/ stops, because that path has no TuxCare builds: npm update and an install with no lockfile stop with ETARGET. On Bun, an edit of the override block has the same effect: see the rule for Bun.

Renovate and Dependabot #

An update bot opens pull requests that move your dependencies to newer versions. A TuxCare build has the version of the upstream release with -tuxcare.N added, for example 3.3.1-tuxcare.1. Without a rule, a bot can replace the build with an upstream release that does not have the TuxCare fix. The finding then comes back.

What the bots propose with no rule #

We gave each bot a project with TuxCare builds and no rule. The table shows the update that each bot proposed.

Botnpm: jquery 3.3.1-tuxcare.1Maven: commons-lang3 3.12.0-tuxcare.1
Renovate3.7.1 and 4.0.0: upstream releases3.21.0: an upstream release
Dependabot4.0.0: an upstream release3.14.0-tuxcare.1: a TuxCare build of a later version

Renovate reads -tuxcare.1 as a prerelease (npm) or a qualifier (Maven), and moves to the newest release. Dependabot for Maven stays on TuxCare builds, but it moves to a later version. Dependabot for npm reads the versions from the registry of the project, which has no TuxCare builds, and moves to the newest release.

Renovate #

Add the rule of your package manager to the Renovate configuration of the repository, for example renovate.json. For a repository with npm and Maven projects, put both rules into one file. Renovate updates the other dependencies as before.

From CLI 0.1.73, securechain init writes the rule for npm and the rule for Maven into renovate.json, and unharden removes them. init and unharden find each rule by its description, so keep the description when you change a rule. A second init does not add a rule again. If your project keeps its Renovate configuration in another file, for example .renovaterc.json or the renovate key of package.json, init does not write renovate.json: Renovate does not run when it finds two configuration files. init then prints the rule, and you add it to your file. With a CLI before 0.1.73, see the steps below for each package manager.

npm (also pnpm, Yarn, and Bun): Renovate opens no pull request for a package on a TuxCare build. Move these packages with securechain update, for example in a scheduled CI job.

{
  "packageRules": [
    {
      "description": "TuxCare hardened builds: stay in the -tuxcare series, never jump to upstream",
      "matchDatasources": ["npm"],
      "matchCurrentValue": "/-tuxcare\\./",
      "enabled": false
    }
  ]
}

Renovate can find a newer build, but it cannot update the lockfile for it. To update the lockfile, Renovate runs npm install with the registry of the project, /npm-upstream/ (see npm: the two paths of the registry). That path has no TuxCare builds, so npm stops with ETARGET. securechain update writes the new build and its lockfile line together.

The npm rule of an init before 0.1.73. An init before 0.1.73 wrote a rule with the same description and the keys ignoreUnstable, respectLatest, allowedVersions, and registryUrls in place of enabled. With that rule, Renovate proposes a newer TuxCare build, also of a later version, for example 3.4.1-tuxcare.2 for 3.3.1-tuxcare.1, and the lockfile update of the pull request fails. Run securechain init of CLI 0.1.73 or later: it replaces that rule with the rule above, and keeps the rest of the file. If you changed that rule, init keeps your rule, and prints the rule above. If you changed that rule, or if you keep a CLI before 0.1.73, replace those four keys with the line "enabled": false yourself, and keep the description.

Maven: a Maven project has no lockfile, so Renovate can move the builds. With this rule, Renovate proposes only a newer TuxCare build of the same version: 3.12.0-tuxcare.2 for 3.12.0-tuxcare.1. It proposes no upstream release and no build of another version. securechain update moves the builds in the same way. init of CLI 0.1.73 or later writes this rule. With a CLI before 0.1.73, add the rule yourself, and remove it yourself when you stop using TuxCare builds.

{
  "packageRules": [
    {
      "description": "TuxCare builds for Maven: propose only a newer build of the same version",
      "matchDatasources": ["maven"],
      "matchCurrentValue": "/-tuxcare\\./",
      "versioning": "regex:^(?<compatibility>.+)-tuxcare\\.(?<patch>\\d+)$",
      "registryUrls": ["https://artifacts.tuxcare.com/maven/"]
    }
  ]
}

The registry token for Maven. Renovate reads the Maven builds with the registry token, from a hostRules entry. init does not write this entry: when the configuration names a secret that Renovate does not have, Renovate stops for the whole repository. init prints the entry in a note. Do these steps in this order:

  1. Save the token as the Renovate secret TUXCARE_TOKEN. Use the registry token that TuxCare gave you, the token that an admin saves under Settings › Registry token. For the Mend Renovate app, save it in the settings of the organization or the repository in the Mend developer portal. For a Renovate that you run yourself, save it in secrets of its configuration. Do not write the token into the file.
  2. Add the hostRules entry to the file that holds the rule. The Maven registry takes the token as the user name and as the password.
{
  "hostRules": [
    {
      "hostType": "maven",
      "matchHost": "artifacts.tuxcare.com",
      "password": "{{ secrets.TUXCARE_TOKEN }}",
      "username": "{{ secrets.TUXCARE_TOKEN }}"
    }
  ]
}

Without the entry, Renovate cannot read the TuxCare builds: it proposes no update for them, and it updates the other Maven dependencies as before.

Dependabot #

Dependabot has no setting that keeps a package on the builds of one version. So keep Dependabot away from the packages on TuxCare builds with an ignore entry for each package, and move these packages with securechain update, for example in a scheduled CI job. Dependabot updates the other dependencies as before. In our test, Dependabot opened no pull request for a package in the ignore list, for npm and for Maven.

npm: securechain init writes the TuxCare registry into .github/dependabot.yml, and harden adds an ignore entry for each package that it moves to a TuxCare build. Add the registry token as the Dependabot secret TUXCARE_TOKEN in the settings of the repository. For a project on npm, the file is then:

version: 2
registries:
  tuxcare:
    type: npm-registry
    url: https://artifacts.tuxcare.com/npm
    token: ${{ secrets.TUXCARE_TOKEN }}
  tuxcare-upstream:
    type: npm-registry
    url: https://artifacts.tuxcare.com/npm-upstream
    token: ${{ secrets.TUXCARE_TOKEN }}
updates:
  - package-ecosystem: npm
    directory: /
    registries: [tuxcare, tuxcare-upstream]
    schedule: {interval: weekly}
    # One entry per package `securechain harden` pinned.
    ignore:
      - dependency-name: jquery
      - dependency-name: lodash

Dependabot sends a token only to the URL of its registry entry, so each of the two npm paths has an entry. This is also true for pnpm and Yarn 2 or later. A project that a CLI before 0.1.21 set up for these tools has only the entry tuxcare: the next securechain init adds the entry tuxcare-upstream.

Maven: from CLI 0.1.74, harden adds an ignore entry for each package on a TuxCare build to each package-ecosystem: maven block of .github/dependabot.yml. Each entry has the comment # securechain harden. harden keeps the entries that you wrote, and writes no registry, no new block, and no new file: without a maven block, Dependabot does not update the Maven project. init writes nothing for Maven into this file. For a project on Maven, the file is then:

version: 2
updates:
  - package-ecosystem: maven
    directory: /
    schedule: {interval: weekly}
    ignore:
      - dependency-name: org.springframework:spring-core
        versions: ["7.x"]
      - dependency-name: org.apache.commons:commons-lang3 # securechain harden

Here the entry for spring-core is your entry, and harden wrote the entry for commons-lang3. harden also puts a comment block at the top of the file. unharden removes only the entries with the comment # securechain harden. securechain check reports each package on a TuxCare build that has no entry in a maven block (maven-dependabot-ignore). An entry with versions or update-types stops only some updates, so harden adds its own entry beside it.

The entries keep the builds also when Dependabot reads Maven Central: the file needs no TuxCare registry. If your file has a TuxCare registry with replaces-base: true, harden and unharden keep it.

With a CLI before 0.1.74, harden writes nothing for Maven. Add an ignore entry for each package on a TuxCare build yourself, and add an entry each time that harden moves another package to a TuxCare build.

Dependabot security updates may not follow the ignore list. Do not merge a pull request that replaces a -tuxcare version with an upstream release.

Remove TuxCare builds: unharden #

securechain unharden --dry-run   # show the changes; write nothing
securechain unharden

unharden undoes init and harden. Use it when you stop the subscription, or to go back to the public packages.

Then run your tests, and commit the changed files.

Next: Reference