What question does the audit answer?
Dependency hygiene is not another name for vulnerability scanning. Its job is to answer a broader operational question: are the repository's dependencies accumulating risk or disorder that makes the project harder to evolve safely?
Here, a dependency means more than a package listed inpackage.json, go.mod, or pyproject.toml. A base container image is a dependency. So is a third-party action used by a release workflow, a remote install script, or an npx command that fetches code during a build. If the project downloads or executes external code through it, it belongs in the dependency surface.
The audit evaluates the current revision, not the team's intentions. Dependabot, Renovate, scheduled scans, and a named dependency owner can be useful context, but their presence does not improve the score by itself. Guard scores what it can prove about the dependency state that is actually checked in.
Dependency risk is accumulating across build and runtime acquisition paths. Pin the runtime image and remove unbounded build-time acquisition first.
What Guard collects
The first task is to build an inventory before any scanner output is interpreted. For a monorepo or polyglot project, Guard treats each ecosystem and deployable surface separately instead of averaging them into one vague tree.
- Manifests and lockfiles: declared dependencies, resolved versions, package managers, workspaces, and dependency scope.
- Install and acquisition paths: commands in CI, Makefiles, Dockerfiles, release scripts, deploy scripts, and bootstrap tooling.
- Runtime boundaries: which dependencies belong to production, build, development, test, tooling, containers, or CI.
- Container sources: base image tags, immutable digests, and operating system packages that do not appear in a language lockfile.
- Registry and install configuration: private mirrors, registry settings, lifecycle scripts, package-age gates, and related controls.
- Supporting evidence: existing SBOM files, accepted-risk records, and dependency maintenance configuration.
Guard also records the repository, default branch, exact audited revision, commit link, and available history depth. Without that target, a report becomes ambiguous as soon as a lockfile, image, or workflow changes.
Generated and machine-local directories such as node_modules, virtual environments, build output, caches, coverage, and vendored scanner databases are excluded by default. They create duplicates and describe the audit machine more often than the tracked repository. A file from one of those locations is included only when it proves a problem that is itself checked in.
What the audit deliberately does not do
The dependency hygiene audit is analysis-only. It does not install or update packages, execute lifecycle scripts, run builds or tests, rewrite manifests or lockfiles, create a branch, or open an issue or pull request. If a conclusion requires project code to run, that check remains explicitly unverified.
Workflow files are read only where they show dependency acquisition, for examplenpm ci, npx, a container image, or a third-party action. Branch protection, release gates, and the general quality of the pipeline belong to a separate CI/CD audit.
How the audit works
1. Inventory the dependency surface
Guard fixes the target revision, detects package managers, and locates manifests, lockfiles, images, install commands, registry settings, and existing SBOM data. Each source is recorded with its ecosystem, path, scope, and resolved source when one is available.
Tool selection happens after inventory. An npm repository does not need a Rust scanner, and a JVM repository should not be forced to build only to create one more output file. If the repository already produces a trustworthy SBOM, it may offer better evidence than installing a broad toolchain during the audit.
2. Run bounded, read-only checks
Matching tools read tracked manifests, lockfiles, SBOM documents, and supported images. Guard avoids commands that change the dependency graph or execute repository code. A tool that is missing, unsupported, or unable to reach a private registry is recorded as a coverage limitation. The rest of the audit continues.
3. Normalize evidence and report the current state
Raw rows are linked to packages, versions, source files, and dependency scope. Repeated output is merged by advisory aliases, package coordinates, path, and root cause. Guard then scores nine criteria, applies deductions and hard caps, and writes two views of the same result: a compact dashboard summary and a detailed evidence report.
Reports
33 / 100July 16, 2026Current
Executive summary
Dependency risk is accumulating across build and runtime acquisition paths. The lockfile is present, but the primary build can still fetch mutable external code.
Target
- Repository
- brightyard/checkout-service
- Default branch
- main
Findings
| Priority | What we found | Where to look |
|---|---|---|
| High | Runtime image uses a mutable tag instead of a digest. | Dockerfile:28 |
| High | Build step resolves an unpinned package with npx. | Dockerfile:14 |
A scanner row is a lead, not a finding
The exact tool set depends on the repository. Running more scanners does not raise the score. A result becomes useful only when it matches the audited revision and helps establish package, version, source path, scope, and available fix.
- npm audit provides the package manager's view of the npm dependency tree, advisory data, and possible remediation.
- OSV-Scanner matches supported manifests and lockfiles to OSV advisories and helps reconcile aliases for the same vulnerability.
- Trivy adds language-package, filesystem, SBOM, and container-image evidence.
- Syft can build a software bill of materials from a filesystem or image.
- Grype matches components from directories, images, or SBOM documents against vulnerability data.
Other ecosystems use their own read-only sources, such as govulncheck,pip-audit, uv audit, cargo audit,cargo deny, bundler-audit, or composer audit.
How a row becomes a finding
Guard first links the row to a dependency source and an installed version. It records whether the package is direct or transitive, where it is used, which fixed versions are known, which tools confirmed it, and what the evidence cannot prove. Only then can the row become a report finding.
Deduplication prevents scanner volume from becoming fake severity. If npm audit, OSV-Scanner, Trivy, and Grype all report the same esbuild version from one lockfile, the report contains one finding with four evidence sources. The score is reduced once.
Why scanner severity is not copied blindly
Guard considers where the dependency is used, whether the installed version is confirmed, whether a fix exists, and which path obtains or executes the code. A confirmed runtime or release problem can be High. The same advisory in isolated tooling may be Medium or Low. A mutable runtime image or privileged install path can also be High even when no CVE describes the underlying problem.
The nine scoring criteria
Every criterion receives 0-5 points. Scores of 4-5 require strong evidence and a controlled current state. Scores of 2-3 describe partial or uneven evidence. Scores of 0-1 indicate an absent control, an unknown primary surface, or active risk.
- 1
Dependency source inventory
Guard checks whether the main manifests, lockfiles, images, and acquisition paths have been found for every deployable surface. A strong score requires a complete map tied to repository evidence. Missing primary sources score poorly and can cap the final result.
- 2
Reproducible install
The audit asks whether CI, build, and release will resolve the same dependency graph from the audited revision. Frozen installs, lock integrity, and consistent package-manager use improve this criterion. A primary path that can silently resolve new versions weakens it.
- 3
Known vulnerability posture
Affected versions, advisory aliases, fixed versions, and the actual dependency path must be understood. Scanner output alone is not enough. Guard looks for a confirmed package and version in the current repository state, plus a bounded next action when a fix exists.
- 4
Runtime, development, and test separation
A production dependency and a test-only tool do not carry the same exposure. Guard records scope so the report can distinguish runtime, deploy, build, development, test, tooling, container, and CI paths instead of assigning one severity to everything.
- 5
Safe install and acquisition paths
This criterion covers the places where build or deploy can fetch and execute third-party code. Floating container tags, unpinned CI actions, remote scripts, and build-time package resolution matter even when no package-manager advisory describes them.
- 6
Lifecycle script controls
Package installation can execute arbitrary code through lifecycle hooks. Guard checks whether that behavior is visible, necessary, and controlled. Unrestricted install scripts without a documented reason reduce the score even when no malicious package has been confirmed.
- 7
Dependency footprint control
The resolved graph should be bounded and inspectable. Guard considers lock integrity, duplicate footprint, stale components, available SBOM data, container pinning, and whether the dependency set is current enough for its role.
- 8
Managed exceptions
An accepted risk is useful only when it is explicit, narrow, owned, and still valid for the audited revision. Broad suppressions or undocumented exceptions can hide current findings and therefore lower this criterion.
- 9
Evidence quality and deduplication
The conclusion must be supported by sufficient coverage and clean evidence. Guard records skipped tools and limitations, reconciles scanner disagreement, and counts each root cause once instead of treating repeated rows as separate problems.
How the final score is calculated
The nine criteria establish the baseline. Their maximum total is 45 points, which is scaled to 100.
criteria_score = round((criteria_sum / 45) * 100)Only deduplicated findings with Medium or High confidence can reduce that baseline. Risk findings describe a confirmed vulnerable dependency or unsafe acquisition path with concrete runtime, deploy, release, or privileged-build exposure.
risk_reduction = min(
critical * 20 + high * 8 + medium * 3 + low * 1,
35
)Control findings describe a current gap without the same confirmed deployable impact, for example lifecycle-script exposure, incomplete package-age controls, or a tooling-only issue.
control_reduction = min(
high * 3 + medium * 1,
8
)
final_score = clamp(
criteria_score - risk_reduction - control_reduction,
0,
100
)Hard caps
Caps are applied after deductions. They prevent one serious stop signal from disappearing inside an otherwise healthy average.
- Any Critical finding caps the score at 39.
- Two or more High findings in runtime, deploy, or privileged acquisition paths cap it at 69.
- A non-reproducible primary CI, build, or release install caps it at 69.
- Unidentified primary dependency sources for a deployable surface cap it at 59.
- Materially incomplete scanner coverage, when repository evidence is otherwise available, caps it at 79.
A worked example: 33 out of 100
The following example is fictional. Its purpose is to make the arithmetic explicit without exposing a customer repository.
26 out of 45 points, scaled and rounded
58Three deduplicated High findings at 8 points each
-24One Medium control finding
-158 - 24 - 1
33In this example, the criteria scores are 5, 3, 2, 3, 2, 2, 3, 2, and 4. They total 26. Three High-risk findings remove 24 points, and one Medium control gap removes 1. The High finding cap does not change the result because 33 is already below 69.
Dependency hygiene
Review dependency sources, install paths, runtime scope, and current risk.
How to read the result in Guard
The project matrix shows the dependency hygiene score beside the other audit areas. Its job is orientation: it tells a team which repository has left the green zone. The repository card compresses the same result into a status, score, and small counts such as Problems, Watch, Healthy, and Skipped.
Opening the audit reveals the full evidence: the audited target, current-state answer, nine criteria, deduplicated findings, tool coverage, limitations, score inputs, and improvement checklist. The compact dashboard and detailed report do not use different formulas. They are two levels of detail for the same result.
Scores of 70 and above are in the good band. Scores from 40 through 69 need attention. Scores below 40 indicate a materially unhealthy dependency state. The score is a summary, not a substitute for the evidence that produced it.
Where the audit stops and autofix begins
The audit identifies a root cause, affected path, confidence, impact, supporting evidence, fixed versions when known, and a safe next action. It does not mutate the project. That clean boundary keeps observation separate from execution.
Findings that are bounded enough can become candidates for a separate autofix run. Current candidate types include:
- a vulnerable dependency upgrade;
- a lockfile reproducibility fix;
- removal or pinning of an unsafe install path;
- a lifecycle-script control;
- a package-age gate;
- a stale dependency review.
A person chooses which candidate runs and reviews the resulting change. If the evidence is weak, the change is too broad, or the audit cannot define a bounded candidate, Guard keeps the item in the report instead of manufacturing an autofix.
Choose which autofixes to run right now.
Replace the mutable image reference and verify the resolved runtime image.
Ready to fix.Declare and lock the build dependency instead of resolving it during build.
Pull request createdUse immutable action revisions on the release path.
Ready to fix.Limits, reruns, and maintained health
A useful score makes its evidence boundary visible:
- No install, build, or test command is executed, so claims that require project code to run remain unverified.
- Private registries and unsupported ecosystems can reduce coverage. The report states what was checked and which missing check could change the decision.
- Scanner severity does not prove reachability. Guard claims runtime impact only when repository evidence or an authorized tool supports it.
- A clean result applies to the audited revision. A new dependency, image tag, workflow, or lockfile can change the score.
Green is not permanent
After an approved fix lands, Guard reruns the audit against the new revision. Later changes can move the project out of the green zone again. The recurring audit and autofix cycle is the product, not the report in isolation.
That maintained state matters because coding agents inherit whatever condition the project is already in. When dependency hygiene stays healthy, new feature work is less likely to build on mutable, stale, vulnerable, or poorly understood external code.
Enji Guard