Dependency hygiene audit: what Guard checks and how the score works

A dependency tree can be free of Critical advisories and still pull unreviewed code through a mutable image, an unpinned CI action, or a build command that resolves packages on the fly. This audit maps the whole acquisition path and scores the state that exists now.

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 hygieneNeeds attention
33/100
PROBLEMS3
WATCH2
HEALTHY1
SKIPPED-

Dependency risk is accumulating across build and runtime acquisition paths. Pin the runtime image and remove unbounded build-time acquisition first.

The repository dashboard compresses the current audit into one score, four evidence counts, and the actions that belong to this audit. Fictional data.

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
PriorityWhat we foundWhere to look
HighRuntime image uses a mutable tag instead of a digest.Dockerfile:28
HighBuild step resolves an unpinned package with npx.Dockerfile:14
The current report stays a normal Guard report: score, dated run, report actions, target, summary, and evidence-backed findings. Fictional data.

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. 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. 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. 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. 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. 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. 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. 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. 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. 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.

Criteria

26 out of 45 points, scaled and rounded

58
Confirmed risk

Three deduplicated High findings at 8 points each

-24
Control gap

One Medium control finding

-1
Final score

58 - 24 - 1

33

In 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.

Runbook updated 16 Jul 2026Last run 16 Jul 2026
76/100
Overall scoreHealthy 29 vs last time
Audit score historyJul 8: 33; Jul 10: 47; Jul 12: 58; Jul 16: 760255075100Jul 8Jul 10Jul 12Jul 1633 points, Jul 847 points, Jul 1058 points, Jul 1276 points, Jul 16
The detailed audit keeps the current score beside its run history. This fictional sequence shows the same project moving from 33 to 76 across verified reruns.

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.

Improvement steps

Choose which autofixes to run right now.

Pin the runtime image by digesthigh

Replace the mutable image reference and verify the resolved runtime image.

Ready to fix.
Remove the unbounded npx fetchhigh

Declare and lock the build dependency instead of resolving it during build.

Pull request created
Pin third-party release actionshigh

Use immutable action revisions on the release path.

Ready to fix.
Improvement steps preserve the human gate: select bounded findings, see their current state, then run only the autofixes you chose. Fictional data.

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.