Skip to content

feat: add pre-commit hook for local dependency scanning (closes #443) - #745

Closed
DevamShah wants to merge 1 commit into
safedep:mainfrom
DevamShah:feat/pre-commit-hook
Closed

DevamShah wants to merge 1 commit into
safedep:mainfrom
DevamShah:feat/pre-commit-hook

Conversation

@DevamShah

Copy link
Copy Markdown

Summary

Adds a top-level .pre-commit-hooks.yaml exposing a vet-scan hook so downstream repositories can shift dependency vetting left and catch malicious or vulnerable packages locally, before they are committed.

Problem / motivation

vet already guards the PR boundary via vet-action (GitHub) and the GitLab CI component, but there is no first-class way to run it at the git commit boundary on a developer's machine (#443). The earlier the signal, the cheaper the fix: a malicious or slop-squatted dependency that reaches CI has already been committed and pushed, and a compromised lockfile that lands on main widens the blast radius. The pre-commit framework is the de-facto standard for local Git hooks, but consuming vet through it today requires every team to hand-roll a local hook. Shipping a maintained manifest in this repo makes adoption a two-line .pre-commit-config.yaml change.

Change

  • New .pre-commit-hooks.yaml at the repository root with two hooks:
    • vet-scan — language: golang, builds vet from source via the Go toolchain so no prior installation is required (mirrors how gitleaks/golangci-lint ship their own hooks).
    • vet-scan-system — language: system, reuses an already-installed vet (Homebrew/npm/release binary) for a faster path.
  • Both hooks run vet scan -D . with pass_filenames: false (vet scans the directory tree, not a file list) and require_serial: true.
  • A files: regex scopes execution to dependency manifests and lockfiles across the ecosystems vet supports (npm, PyPI, Maven, Go, Ruby, Rust, PHP). The hook therefore stays quiet on ordinary commits and only fires when dependencies actually change — keeping developer friction and false positives low.
  • README gains a Pre-commit Hook subsection under Production Ready Integrations, documenting install, the system-binary variant, and how to make the hook blocking via a CEL --filter ... --filter-fail.

Default behavior is report-only so the hook never surprises a developer by blocking a commit; teams opt into hard-fail with an explicit policy filter.

Security rationale

This pulls software-supply-chain enforcement to the earliest practical control point in the SDLC.

  • Malicious package introduction (MITRE ATT&CK T1195.001, Supply Chain Compromise — Software Dependencies): vet scan queries known-malicious package intelligence by default (--malware-query is on), so typosquatted / slop-squatted dependencies are flagged at commit time rather than after distribution.
  • CWE-1357 (Reliance on Insufficiently Trustworthy Component) and CWE-1395 (Dependency on Vulnerable Third-Party Component): policy-as-code via CEL filters lets teams fail the commit on critical CVEs (e.g. vulns.critical.exists(p, true)), aligning with OWASP Top 10 A06:2021 — Vulnerable and Outdated Components.
  • Building from source in the golang hook keeps the toolchain pinned to the rev the consumer selects, avoiding an unpinned download in the developer's hook path.

The hook narrows the exposure window described in #443: a flagged dependency is surfaced before the working tree change becomes a commit, reducing the likelihood of incident-response on a developer machine.

Testing / validation

Validated locally with pre-commit 4.6.0 and Go 1.26.2 (matches the repo's go 1.26.2):

  • pre-commit validate-manifest .pre-commit-hooks.yaml → exit 0 (manifest conforms to the pre-commit schema).
  • pre-commit try-repo <repo> vet-scan --all-files against a sample consumer repo containing a go.mod → the golang hook compiled vet from source, ran vet scan -D ., scanned the discovered manifest, queried malware intelligence, and reported vet scan (build from source) ... Passed.
  • files: regex unit-checked in Python re (the engine pre-commit uses): asserted positive matches for package-lock.json, app/go.mod, src/requirements-dev.txt, poetry.lock, Cargo.toml, frontend/yarn.lock, pom.xml, composer.lock, Pipfile, pnpm-lock.yaml, and negative matches for README.md, main.go, src/index.ts, config.yaml, LICENSE, go.work — confirming the hook does not fire on non-dependency files.

No live external target or API key is required: vet scan -D . resolves manifests and queries known-malicious package data with default flags.

Notes for maintainers

  • The rev: v1.x.x in the README/manifest comments is a placeholder; consumers pin to a released tag.
  • Two hooks are provided by design (build-from-source vs. system binary, mirroring real CLI-hook repos). Happy to drop one if you prefer a single canonical hook.
  • This addresses "scan on dependency change" via the files: filter; it does not implement true per-file incremental/cached scanning (a vet CLI concern, out of scope for a hook manifest).

Closes #443

Add a top-level .pre-commit-hooks.yaml exposing `vet-scan` (build from
source via the Go toolchain) and `vet-scan-system` (use an installed vet
binary) so downstream repositories can run `vet scan` at the git commit
boundary via the pre-commit framework.

Both hooks run `vet scan -D .` with pass_filenames disabled and are
scoped via a files regex to dependency manifests and lockfiles across
npm, PyPI, Maven, Go, Ruby, Rust and PHP, so they stay quiet on commits
that do not change dependencies. Default behavior is report-only; teams
opt into blocking via a CEL --filter ... --filter-fail.

Document usage in the README under Production Ready Integrations.

Closes safedep#443

Signed-off-by: Devam Shah <devamshah91@gmail.com>
@safedep

safedep Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

SafeDep Report Summary

Green Malicious Packages Badge Green Vulnerable Packages Badge Green Risky License Badge

No dependency changes detected. Nothing to scan.

View complete scan results →

This report is generated by SafeDep Github App

@KunalSin9h

Copy link
Copy Markdown
Member

@abhisek do we need it?

@abhisek

abhisek commented Jul 6, 2026

Copy link
Copy Markdown
Member

@KunalSin9h No. I think this is more of a docs thing and not part of vet repo.

@KunalSin9h KunalSin9h closed this Jul 22, 2026
@DevamShah

Copy link
Copy Markdown
Author

Agreed, it's a docs thing. A hook is integration guidance, not something the binary's repo should carry.

Concrete home, if it's useful: safedep/docs → new governance/integrations/pre-commit.mdx, listed in the "CI/CD & Platform Integrations" group in docs.json (lines 143-147, alongside github/gitlab/bitbucket) with a card on governance/integrations/overview.mdx. I grepped that repo and there is no pre-commit or git-hook content in it today, and CONTRIBUTING.md's Diátaxis split puts this squarely in how-to guides.

I'd frame the page as defense-in-depth rather than a primary control: by pre-commit time the package is already on the machine, which is exactly the point you made on #443. PMG is the install-time answer; the hook is just local parity with the CI policy gate for teams that want the same verdict before a push.

Happy to send that docs PR if you want it. Should #443 stay open to track the docs page, or be closed now that PMG covers the install-time case?

@DevamShah

Copy link
Copy Markdown
Author

Following up here — rather than leave this as a proposal, I can just open the docs PR against safedep/docs so there's a concrete diff to merge or close on sight. It'd be the one governance/integrations/pre-commit.mdx page framed as defense-in-depth (PMG stays the install-time answer; the hook is just local parity with the CI policy gate), plus the entry in the CI/CD integrations group in docs.json. Say the word and I'll send it — otherwise I'll leave #443 with you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add pre-commit Hook for scanning

3 participants