Skip to content

Commit f2616c3

Browse files
committed
add support for publishing vet to npm
1 parent 7d4569f commit f2616c3

File tree

7 files changed

+610
-0
lines changed

7 files changed

+610
-0
lines changed

.github/workflows/publish-npm.yml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
name: Publish NPM Package
2+
3+
on:
4+
push:
5+
tags:
6+
- "v[0-9]+.[0-9]+.[0-9]+"
7+
8+
jobs:
9+
publish-npm:
10+
runs-on: ubuntu-latest
11+
permissions:
12+
contents: read
13+
id-token: write
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: actions/setup-node@v4
19+
with:
20+
node-version: "18"
21+
registry-url: "https://registry.npmjs.org"
22+
23+
- name: Extract version from tag
24+
id: version
25+
run: |
26+
echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
27+
28+
- name: Wait for GitHub release
29+
run: |
30+
echo "Waiting for GitHub release v${{ steps.version.outputs.version }}..."
31+
i=1
32+
while [ $i -le 30 ]; do
33+
if curl -s -f "https://api.github.com/repos/safedep/vet/releases/tags/v${{ steps.version.outputs.version }}" > /dev/null; then
34+
echo "Release found!"
35+
break
36+
fi
37+
if [ $i -eq 30 ]; then
38+
echo "Release not found after 10 minutes"
39+
exit 1
40+
fi
41+
echo "Waiting... ($i/30)"
42+
sleep 20
43+
i=$((i + 1))
44+
done
45+
46+
- name: Prepare package
47+
run: |
48+
cd publish/npm
49+
npm version ${{ steps.version.outputs.version }} --no-git-tag-version
50+
51+
- name: Publish to npm
52+
run: |
53+
cd publish/npm
54+
npm publish --provenance
55+
env:
56+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
57+
58+
test-installation:
59+
needs: publish-npm
60+
runs-on: ${{ matrix.os }}
61+
strategy:
62+
matrix:
63+
os: [ubuntu-latest, macos-latest, windows-latest]
64+
node-version: ["16", "18", "20"]
65+
66+
steps:
67+
- uses: actions/setup-node@v4
68+
with:
69+
node-version: ${{ matrix.node-version }}
70+
71+
- name: Extract version from tag
72+
id: version
73+
run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
74+
75+
- name: Wait for npm package
76+
shell: bash
77+
run: |
78+
echo "Waiting for npm package..."
79+
i=1
80+
while [ $i -le 20 ]; do
81+
if npm view @safedep/vet@${{ steps.version.outputs.version }} > /dev/null 2>&1; then
82+
echo "Package available!"
83+
break
84+
fi
85+
if [ $i -eq 20 ]; then
86+
echo "Package not available after 10 minutes"
87+
exit 1
88+
fi
89+
echo "Waiting... ($i/20)"
90+
sleep 30
91+
i=$((i + 1))
92+
done
93+
94+
- name: Test installation
95+
run: |
96+
npm install -g @safedep/vet@${{ steps.version.outputs.version }}
97+
vet version
98+
vet --help || true

publish/npm/.npmignore

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Exclude the actual binary (but keep the wrapper script)
2+
bin/vet
3+
bin/vet.exe
4+
5+
# Exclude temp files and directories
6+
temp/
7+
.temp/
8+
9+
# Exclude development files
10+
node_modules/
11+
.git/
12+
.gitignore
13+
*.log
14+
.DS_Store
15+
16+
# Keep only the wrapper script in bin/
17+
!bin/vet.js

publish/npm/README.md

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# 🔍 vet
2+
3+
Enterprise-grade open source software supply chain security in one CLI.
4+
5+
This package delivers the `vet` binary via npm for teams that prefer Node.js tooling for install & upgrades.
6+
Full project: https://github.com/safedep/vet • Docs: https://docs.safedep.io/
7+
8+
## ✨ What It Does
9+
10+
- Detects vulnerabilities (context & usage aware)
11+
- Flags malicious / typosquatted packages (active + reputation)
12+
- Enforces “Policy as Code” (licenses, popularity, scorecards) with CEL filters
13+
- Works across ecosystems: npm, PyPI, Maven, Go, containers, SBOMs
14+
- Outputs actionable reports: JSON, SARIF, Markdown, CycloneDX SBOM
15+
16+
## 📦 Install
17+
18+
```bash
19+
npm install -g @safedep/vet
20+
```
21+
22+
Check:
23+
```bash
24+
vet version
25+
```
26+
27+
(Alternative installs: brew, direct binary, see upstream README.)
28+
29+
## ⚡ Quick Start
30+
31+
```bash
32+
# Scan current project (auto-detect lock/manifests)
33+
vet scan -D .
34+
35+
# Scan a specific manifest
36+
vet scan -M package-lock.json
37+
```
38+
39+
## 🛡 Basic Policies
40+
41+
```bash
42+
# Fail on critical vulns
43+
vet scan -D . --filter 'vulns.critical.exists(p, true)' --filter-fail
44+
45+
# License guard (example)
46+
vet scan -D . --filter 'licenses.contains_license("GPL-3.0")' --filter-fail
47+
48+
# Scorecard maintenance threshold
49+
vet scan -D . --filter 'scorecard.scores.Maintained < 5' --filter-fail
50+
```
51+
52+
## 🔬 Malware Detection
53+
54+
```bash
55+
# Setup (get API key)
56+
vet cloud quickstart
57+
58+
# Active malicious package analysis
59+
vet scan -D . --malware
60+
61+
# Known-malicious lookup only (no key)
62+
vet scan -D . --malware-query
63+
```
64+
65+
## 📊 Reports
66+
67+
```bash
68+
vet scan -D . \
69+
--report-json=report.json \
70+
--report-sarif=report.sarif \
71+
--report-markdown=report.md
72+
```
73+
74+
Generate SBOM:
75+
```bash
76+
vet scan -D . --report-cdx=sbom.json
77+
```
78+
79+
## 🧪 Re-query Saved Data
80+
81+
```bash
82+
vet scan -D . --json-dump-dir ./.vet-scan
83+
vet query --from ./.vet-scan --filter 'vulns.high.exists(p, true)'
84+
```
85+
86+
## 🤖 Integrations
87+
88+
GitHub Action: safedep/vet-action
89+
90+
GitLab Component: safedep/ci-components/vet
91+
92+
Container: ghcr.io/safedep/vet:latest
93+
94+
---
95+
For complete documentation, advanced usage, troubleshooting, and more information, please visit: github.com/safedep/vet

publish/npm/bin/vet.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require("fs");
4+
const path = require("path");
5+
const { spawn } = require("child_process");
6+
const { ORG_NAME, PACKAGE_NAME, BINARY_NAME } = require("../config");
7+
8+
const BINARY_NAME_WITH_EXT =
9+
process.platform === "win32" ? `${BINARY_NAME}.exe` : BINARY_NAME;
10+
const BINARY_PATH = path.join(__dirname, BINARY_NAME_WITH_EXT);
11+
12+
function main() {
13+
// Check if binary exists
14+
if (!fs.existsSync(BINARY_PATH)) {
15+
console.error(`❌ ${BINARY_NAME_WITH_EXT} binary not found`);
16+
console.error(
17+
`Try reinstalling: npm install -g ${ORG_NAME}/${PACKAGE_NAME}`,
18+
);
19+
process.exit(1);
20+
}
21+
22+
// Verify binary is executable
23+
try {
24+
fs.accessSync(BINARY_PATH, fs.constants.F_OK | fs.constants.X_OK);
25+
} catch (error) {
26+
console.error(`❌ ${BINARY_NAME_WITH_EXT} is not executable`);
27+
console.error(
28+
`Try reinstalling: npm install -g ${ORG_NAME}/${PACKAGE_NAME}`,
29+
);
30+
process.exit(1);
31+
}
32+
33+
// Pass all arguments to the binary
34+
const args = process.argv.slice(2);
35+
36+
// Spawn the binary with inherited stdio for proper terminal interaction
37+
const child = spawn(BINARY_PATH, args, {
38+
stdio: "inherit",
39+
windowsHide: false,
40+
});
41+
42+
// Handle process termination
43+
child.on("error", (error) => {
44+
console.error(
45+
`❌ Failed to execute ${BINARY_NAME_WITH_EXT}: ${error.message}`,
46+
);
47+
console.error(
48+
`Try reinstalling: npm install -g ${ORG_NAME}/${PACKAGE_NAME}`,
49+
);
50+
process.exit(1);
51+
});
52+
53+
// Exit with the same code as the child process
54+
child.on("exit", (code, signal) => {
55+
if (signal) {
56+
process.kill(process.pid, signal);
57+
} else {
58+
process.exit(code || 0);
59+
}
60+
});
61+
62+
// Handle termination signals
63+
process.on("SIGTERM", () => {
64+
child.kill("SIGTERM");
65+
});
66+
67+
process.on("SIGINT", () => {
68+
child.kill("SIGINT");
69+
});
70+
}
71+
72+
if (require.main === module) {
73+
main();
74+
}
75+
76+
module.exports = { main };

publish/npm/config.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Configuration for npm binary wrapper
2+
3+
const ORG_NAME = "@safedep";
4+
const PACKAGE_NAME = "vet";
5+
const BINARY_NAME = "vet";
6+
7+
// GitHub repository information for releases
8+
const REPO_OWNER = "safedep";
9+
const REPO_NAME = "vet";
10+
11+
// GitHub releases base URL (constructed from repo info)
12+
const GITHUB_RELEASES_BASE = `https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download`;
13+
14+
// Platform-specific binary filename patterns (GoReleaser format)
15+
const BINARY_PATTERNS = {
16+
"darwin-x64": `${BINARY_NAME}_Darwin_x86_64.tar.gz`,
17+
"darwin-arm64": `${BINARY_NAME}_Darwin_arm64.tar.gz`,
18+
"linux-x64": `${BINARY_NAME}_Linux_x86_64.tar.gz`,
19+
"win32-x64": `${BINARY_NAME}_Windows_x86_64.zip`,
20+
};
21+
22+
module.exports = {
23+
ORG_NAME,
24+
PACKAGE_NAME,
25+
BINARY_NAME,
26+
REPO_OWNER,
27+
REPO_NAME,
28+
GITHUB_RELEASES_BASE,
29+
BINARY_PATTERNS,
30+
};

0 commit comments

Comments
 (0)