Skip to content

Preparing Your Project

Syft and Trivy read the dependencies already resolved on disk. A project with a committed lock file is ready as it is. Four stacks need one command first.

Your stack What to do
npm / yarn / pnpm, Go, Rust, Ruby, PHP, Python (Poetry / Pipenv / uv) Nothing. Commit the lock file.
Maven mvn dependency:go-offline -fae
Gradle ./gradlew dependencies --write-locks
.NET dotnet restore --use-lock-file
Python, bare requirements.txt pip-compile or pip freeze

Run these once, on a machine that can build the project, with internet. The scan itself is always offline.

Maven

mvn dependency:go-offline -fae     # or: mvn install -DskipTests

Fills the local cache at ~/.m2/repository. Nothing to commit, and nothing to do at all if this machine already builds the project regularly.

The cache must be on the machine that runs the scan

~/.m2 lives in a home directory, not in the repo, so it does not travel with the code. Run the prep and the scan on the same machine, or copy the warmed ~/.m2 across. Syft also honours SYFT_JAVA_MAVEN_LOCAL_REPOSITORY_DIR for a project-local cache.

Why, and what to do when modules fail to resolve

A pom.xml records only direct dependencies, so offline the scanners read ~/.m2 instead.

-fae (fail at end) keeps Maven going after a module fails, instead of stopping at the first one and leaving the rest of the cache cold. -fn (fail never) is more permissive still.

The usual cause of a failing module is a JDK mismatch: older projects declare JDK-8-era artifacts such as jdk.tools:jdk.tools or com.sun:tools, which do not exist on modern JDKs. Those modules will not be cached, every other one still is. Prep failures never break the scan, they only limit how much of the tree it sees.

Gradle

# once, in build.gradle or settings.gradle:
#   dependencyLocking { lockAllConfigurations() }
./gradlew dependencies --write-locks

Writes a gradle.lockfile per module. Commit them. They live in the repo, so they travel with the code and need no cache at scan time.

Why the lock file is not optional here

Trivy's only Gradle input is *.gradle.lockfile; it never reads build.gradle. Without the lock file the Gradle build contributes nothing at all.

The lock file also covers the test configurations, so it recovers dependencies the pom parser structurally cannot see: that parser skips test and optional scope, and --include-dev-deps covers npm/yarn/gradle but not pom.

.NET

dotnet restore YourSolution.sln --use-lock-file
# no solution file? per project:
# dotnet restore src/YourProject/YourProject.csproj --use-lock-file

Writes a packages.lock.json per project. Commit them. Like Gradle's, they live in the repo.

Why a plain dotnet restore is not enough

A plain restore writes obj/project.assets.json, which neither scanner reads: 0 components in both. packages.lock.json is the only .NET file either tool reads that carries a transitive graph, and it is opt-in.

Without it the scan falls back to Directory.Packages.props or the .csproj files, which are flat lists of declared versions with no graph at all.

Python (bare requirements.txt)

pip-compile                      # pip-tools; installs nothing
pip freeze > requirements.txt    # or, from a virtualenv where the app is installed

Only needed for a hand-written requirements.txt. Poetry, Pipenv and uv need nothing: commit poetry.lock / Pipfile.lock / uv.lock and you already have the full tree.

Everything else

Nothing beyond committing the lock file, which is normal practice anyway:

Stack Lock file
npm / yarn / pnpm package-lock.json / yarn.lock / pnpm-lock.yaml
Go (1.17+) go.mod + go.sum
Rust Cargo.lock
PHP composer.lock
Ruby Gemfile.lock

Missing one? Run the ecosystem's install once (npm ci, composer install, bundle lock) and commit the result.

Why any of this is needed

A lock file lists the full transitive tree, every direct and indirect dependency, pinned. A bare manifest (pom.xml, build.gradle, a hand-written requirements.txt) lists only the direct ones. Syft and Trivy are static readers: they do not build your project and do not reach the network, so they report exactly what is written down. Prep means making the resolved state exist before the scan.

To check it worked, compare the component count against "just the direct dependencies you wrote down". A Maven project reporting only a few dozen components probably had a cold ~/.m2.

A stack neither scanner catalogs, such as Bazel, Scala SBT or Perl/CPAN, will not appear no matter how you prep.