Diff-aware supply-chain scanning for pull requests. The action checks npm and PyPI dependencies added or changed by a PR against Hacktron's public malware feed, posts a summary of dependency changes, and fails the workflow when malicious packages are introduced.
The default scan is free to run, fast-mode only, and does not require an API key.
Create .github/workflows/hacktron-dependency-scan.yml:
name: Hacktron Dependency Scan
on:
pull_request:
paths:
- "**/package-lock.json"
- "**/pnpm-lock.yaml"
- "**/npm-shrinkwrap.json"
- "**/pyproject.toml"
- "**/uv.lock"
- "**/requirements*.txt"
- "**/requirements/*.txt"
permissions:
contents: read
pull-requests: write
jobs:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: HacktronAI/dependency-scan@v1The action compares the base branch manifest to the PR manifest and scans only packages that were added or changed by the PR. Supported manifests:
| Ecosystem | Files |
|---|---|
| npm | package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml |
| PyPI | pyproject.toml, uv.lock, requirements*.txt, requirements/*.txt |
If no lockfile or lockfiles input is set, supported manifests are
auto-discovered from tracked files.
SUCCEEDED: no malicious packages were introduced.FAILED: one or more malicious packages were introduced andfail-on-maliciousis enabled.ERROR: the action could not parse manifests, call the public API, or post the PR comment.
Suspicious package signals are shown in the PR comment, but only malicious packages fail the workflow by default.
| Input | Default | Description |
|---|---|---|
lockfile |
empty | Single manifest to scan. Mutually exclusive in practice with lockfiles. |
lockfiles |
empty | Newline-separated manifest paths or bash-style path patterns. |
ignore-file |
.hfwignore |
Optional allowlist file, one name@version per line. |
fail-on-malicious |
true |
Fail the check when malicious packages are introduced. Allowed: true, false. |
| Output | Description |
|---|---|
malicious_count |
Number of malicious packages introduced by the PR. |
suspicious_count |
Number of suspicious packages introduced by the PR. |
diff_count |
Total added or changed packages considered by the action. |
Scan one file:
- uses: HacktronAI/dependency-scan@v1
with:
lockfile: services/api/pnpm-lock.yamlScan explicit files or patterns:
- uses: HacktronAI/dependency-scan@v1
with:
lockfiles: |
pnpm-lock.yaml
services/*/package-lock.json
requirements/*.txtCreate .hfwignore at the repository root to suppress known false positives:
some-package@1.2.3
@scope/package@4.5.6
Allowlisted packages are still parsed, but they do not fail the workflow.
Use:
permissions:
contents: read
pull-requests: writecontents: read is required to read manifests and compare against the base
branch. pull-requests: write is required only for the sticky PR comment. If
you remove comment posting in a forked workflow, contents: read is enough for
the scan itself.
Install tooling:
uv sync --locked --group devRun checks:
uv run --locked ruff format --check .
uv run --locked ruff check .
uv run --locked pytest
python -c "import yaml; yaml.safe_load(open('action.yml'))"
git diff --checkInstall pre-commit hooks:
uv run --locked pre-commit installRun hooks manually:
uv run --locked pre-commit run --all-filesMerges to main run the Release PR workflow. It uses release-please to
create or update a release pull request from Conventional Commit history. Use
fix:, feat:, and BREAKING CHANGE: when merging user-facing changes so the
next version is calculated correctly.
- Update the README if inputs, outputs, or behavior changed.
- Run the full local check suite.
- Merge the change to
main. - Review the release-please pull request that updates
CHANGELOG.mdand the release manifest. - Merge the release pull request to publish the semver tag and GitHub release.
The Release workflow runs after a GitHub release is published and moves the
major tag, such as v1, to the same commit as the semver tag. Use the
Release PR workflow dispatch button if the release PR needs to be recreated
or refreshed.
Marketplace users should pin to a major tag (@v1) or exact version
(@v1.0.0) depending on their update policy.