vigilnz 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +278 -0
- package/bin/vigilnz.js +89 -0
- package/checksums/checksums.json +10 -0
- package/install.js +86 -0
- package/lib/binaryPath.js +19 -0
- package/lib/checksums.js +129 -0
- package/lib/download.js +141 -0
- package/lib/platform.js +97 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
# vigilnz
|
|
2
|
+
|
|
3
|
+
**Vigilnz is a security scanner for your code and container images.** It runs
|
|
4
|
+
SAST, secret, SCA (dependency), IaC and SBOM scans against your local
|
|
5
|
+
working directory, prints a readable report, and can sync findings to your
|
|
6
|
+
Vigilnz dashboard.
|
|
7
|
+
|
|
8
|
+
This npm package is a **thin installer/wrapper** around the native Vigilnz
|
|
9
|
+
CLI binary — it downloads the right prebuilt binary for your platform,
|
|
10
|
+
verifies it against a checksum pinned inside this package, and runs it with
|
|
11
|
+
your arguments passed through unchanged.
|
|
12
|
+
|
|
13
|
+
- Scans run **entirely against your local filesystem**. Nothing is cloned or
|
|
14
|
+
downloaded, and no Git remote is required.
|
|
15
|
+
- **No source code is ever uploaded** — only findings metadata, and only
|
|
16
|
+
when you're signed in. Use `--no-upload` to keep a run fully local.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
**As a command-line tool (recommended):**
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g vigilnz
|
|
24
|
+
vigilnz scan .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Without installing:**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npx vigilnz scan .
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
vigilnz login # sign in via your browser
|
|
37
|
+
vigilnz scan # scan the current directory
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Commands
|
|
41
|
+
|
|
42
|
+
| Command | What it does |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `vigilnz scan [path]` | Scan a local directory (SAST, secret, SCA, IaC, SBOM) |
|
|
45
|
+
| `vigilnz scan-image <image>` | Scan a local Docker image for vulnerabilities |
|
|
46
|
+
| `vigilnz login` | Authenticate the CLI with your Vigilnz account |
|
|
47
|
+
| `vigilnz logout` | Remove stored credentials |
|
|
48
|
+
| `vigilnz whoami` | Show which account you're signed in as |
|
|
49
|
+
| `vigilnz config` | Show sign-in status and local configuration |
|
|
50
|
+
| `vigilnz version` | Print the CLI version |
|
|
51
|
+
|
|
52
|
+
Run `vigilnz <command> --help` for the full, authoritative flag list.
|
|
53
|
+
|
|
54
|
+
## `vigilnz scan` — scan a directory
|
|
55
|
+
|
|
56
|
+
The path is optional; with no path the current directory is scanned.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
vigilnz scan # scan the current directory
|
|
60
|
+
vigilnz scan . # same, explicit
|
|
61
|
+
vigilnz scan ./src/lib # scan a specific directory
|
|
62
|
+
vigilnz scan /abs/path # relative or absolute
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### What it scans
|
|
66
|
+
|
|
67
|
+
| Type | What it finds |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `sast` | Insecure code patterns |
|
|
70
|
+
| `secret` | Hardcoded secrets and keys (values are masked, never uploaded) |
|
|
71
|
+
| `sca` | Known vulnerabilities in your dependencies |
|
|
72
|
+
| `iac` | Infrastructure-as-code misconfigurations (including Dockerfiles) |
|
|
73
|
+
| `sbom` | Software bill of materials for your dependencies |
|
|
74
|
+
|
|
75
|
+
All five run by default. SCA and SBOM work by collecting only your
|
|
76
|
+
dependency manifest and lock files (`package.json`, `go.mod`, `pom.xml`,
|
|
77
|
+
`requirements.txt`, `Cargo.toml`, …) — never source code. Because they
|
|
78
|
+
require that upload, both are skipped under `--no-upload`; use
|
|
79
|
+
`--local-audit` for a fully offline dependency check.
|
|
80
|
+
|
|
81
|
+
### Options
|
|
82
|
+
|
|
83
|
+
| Flag | Description |
|
|
84
|
+
|---|---|
|
|
85
|
+
| `--scan-types <list>` | Which scans to run (default `sast,secret,sca,iac,sbom`) |
|
|
86
|
+
| `--fail-on <severity>` | Exit `1` if any finding is at or above `critical`\|`high`\|`medium`\|`low` |
|
|
87
|
+
| `--json` | Print the report as a single JSON document |
|
|
88
|
+
| `--no-upload` | Never send results anywhere; keep the scan entirely local |
|
|
89
|
+
| `--remote` | Require the upload to succeed — fail the scan if it can't be sent (CI) |
|
|
90
|
+
| `--include <paths>` | Comma-separated repo-relative paths to limit the scan to |
|
|
91
|
+
| `--no-gitignore` | Don't skip files matched by `.gitignore` |
|
|
92
|
+
| `--local-audit` | Also run the ecosystem's own audit tools alongside Vigilnz SCA |
|
|
93
|
+
| `--max-concurrency <n>` | Max parallel file-scanning workers (`0` = auto) |
|
|
94
|
+
|
|
95
|
+
`--local-audit` additionally runs `npm audit`, `pip-audit`, `govulncheck` or
|
|
96
|
+
`cargo-audit` where available. Its results are reported and counted
|
|
97
|
+
separately from Vigilnz SCA, so the two are never confused. Java/Maven/Gradle
|
|
98
|
+
projects have no comparable local tool and are skipped with a notice — scan
|
|
99
|
+
them with the default flow instead.
|
|
100
|
+
|
|
101
|
+
### Examples
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
vigilnz scan ./src --scan-types sast,secret # only these two scans
|
|
105
|
+
vigilnz scan --json > report.json # machine-readable output
|
|
106
|
+
vigilnz scan ./backend --fail-on high # fail on high+ findings
|
|
107
|
+
vigilnz scan . --local-audit # add local dependency audit
|
|
108
|
+
vigilnz scan . --no-upload # never leaves this machine
|
|
109
|
+
vigilnz scan . --remote --fail-on high # strict CI mode
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## `vigilnz scan-image` — scan a Docker image
|
|
113
|
+
|
|
114
|
+
Scans an image **already present in your local Docker daemon**. It never
|
|
115
|
+
pulls or downloads an image.
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
vigilnz scan-image nginx:latest
|
|
119
|
+
vigilnz scan-image myapp:v1 --json
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Reports four separate categories: vulnerabilities, misconfigurations,
|
|
123
|
+
secrets, and binary-hash vulnerabilities. Misconfiguration checks cover both
|
|
124
|
+
image config/history (root user, exposed ports, missing healthcheck, …) and
|
|
125
|
+
the image filesystem (SSH keys, credential files, `.env` files, world-writable
|
|
126
|
+
paths, SUID/SGID binaries, EOL base image, …).
|
|
127
|
+
|
|
128
|
+
Only small metadata leaves your machine — the detected package inventory,
|
|
129
|
+
binary hashes, the image's own OCI config/history, per-layer file path lists,
|
|
130
|
+
and already-masked findings. Never the image, its layers, or raw secret
|
|
131
|
+
values.
|
|
132
|
+
|
|
133
|
+
Dockerfile analysis is **not** part of this command — run
|
|
134
|
+
`vigilnz scan <path>` against a repo containing a Dockerfile for that.
|
|
135
|
+
|
|
136
|
+
| Flag | Description |
|
|
137
|
+
|---|---|
|
|
138
|
+
| `--json` | Print the report as a single JSON document |
|
|
139
|
+
|
|
140
|
+
## Authentication
|
|
141
|
+
|
|
142
|
+
A Vigilnz account is required to scan.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
vigilnz login # browser sign-in
|
|
146
|
+
vigilnz login --no-browser # print the URL instead of opening a browser
|
|
147
|
+
vigilnz login --force # switch accounts without logging out first
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The authorization URL is always printed first, so sign-in still works over
|
|
151
|
+
SSH, in a container, or on a headless host — copy it to a browser anywhere.
|
|
152
|
+
|
|
153
|
+
**For non-interactive environments**, supply an API key instead and no browser
|
|
154
|
+
is involved:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
vigilnz login --api-key <key>
|
|
158
|
+
VIGILNZ_API_KEY=<key> vigilnz login
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Create the key from your Vigilnz dashboard, and keep it in a secret store —
|
|
162
|
+
never in your repository. The resulting token is written to your user config
|
|
163
|
+
directory with owner-only permissions; the raw API key itself is never
|
|
164
|
+
written to disk.
|
|
165
|
+
|
|
166
|
+
`--api-key` is read by `vigilnz login`, which exchanges it for that stored
|
|
167
|
+
token — so sign in first, then scan:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
vigilnz login --api-key <key>
|
|
171
|
+
vigilnz scan . --fail-on high
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
vigilnz whoami # which account am I signed in as?
|
|
176
|
+
vigilnz config # sign-in status + config file path
|
|
177
|
+
vigilnz logout # remove stored credentials
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Exit codes
|
|
181
|
+
|
|
182
|
+
Stable across releases, so CI can rely on them:
|
|
183
|
+
|
|
184
|
+
| Code | Meaning |
|
|
185
|
+
|---|---|
|
|
186
|
+
| `0` | Scan completed, no policy violation |
|
|
187
|
+
| `1` | Findings violate the configured policy (`--fail-on`) |
|
|
188
|
+
| `2` | Scanner or runtime error |
|
|
189
|
+
| `3` | Authentication or configuration error |
|
|
190
|
+
|
|
191
|
+
This wrapper mirrors the binary's exit code and signal exactly, so these
|
|
192
|
+
work identically through `npx`.
|
|
193
|
+
|
|
194
|
+
## Global options
|
|
195
|
+
|
|
196
|
+
| Flag | Description |
|
|
197
|
+
|---|---|
|
|
198
|
+
| `--api-key <key>` | API key to sign in with (see [Authentication](#authentication)) |
|
|
199
|
+
| `--config <path>` | Path to the config file (default: your OS user config dir) |
|
|
200
|
+
| `-v`, `--verbose` | Show redacted diagnostic detail on errors (never secrets) |
|
|
201
|
+
|
|
202
|
+
## This package exposes no importable module
|
|
203
|
+
|
|
204
|
+
`vigilnz` is a **CLI-only** package. It has no `main` and no `exports` entry
|
|
205
|
+
point, so there is nothing to `require()` or `import`:
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
const vigilnz = require('vigilnz'); // ✗ throws MODULE_NOT_FOUND
|
|
209
|
+
import 'vigilnz'; // ✗ throws ERR_MODULE_NOT_FOUND
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Run it as a command instead (`npx vigilnz ...` or a global install). There is
|
|
213
|
+
deliberately no JavaScript API: the CLI is a Go binary
|
|
214
|
+
with its own authentication and configuration, and every distribution channel
|
|
215
|
+
wraps that one binary rather than reimplementing it. To trigger a scan from
|
|
216
|
+
Node, spawn the CLI as a child process and read its exit code — exit codes and
|
|
217
|
+
stdio pass through this wrapper unchanged.
|
|
218
|
+
|
|
219
|
+
## Supported platforms
|
|
220
|
+
|
|
221
|
+
| OS | Architecture |
|
|
222
|
+
|---|---|
|
|
223
|
+
| Windows | x64 (arm64 runs the x64 build via Windows' built-in emulation — no native windows-arm64 binary is published) |
|
|
224
|
+
| Linux | x64, arm64 |
|
|
225
|
+
| macOS | x64, arm64 |
|
|
226
|
+
|
|
227
|
+
This package deliberately does **not** declare npm's `os`/`cpu` fields. Those
|
|
228
|
+
are independent lists and cannot express "every combination except
|
|
229
|
+
windows+arm64", so declaring them would hard-fail installs on platforms that
|
|
230
|
+
actually work. Platform support is enforced at runtime instead, with an
|
|
231
|
+
actionable error.
|
|
232
|
+
|
|
233
|
+
## If the install-time download fails
|
|
234
|
+
|
|
235
|
+
The `postinstall` hook is **fail-soft**: if it cannot reach the download host
|
|
236
|
+
it prints a warning and lets your install succeed. Nothing is broken — the
|
|
237
|
+
binary is downloaded automatically the first time you run `vigilnz`. This
|
|
238
|
+
also covers installs that skip lifecycle scripts entirely
|
|
239
|
+
(`npm install --ignore-scripts`).
|
|
240
|
+
|
|
241
|
+
That means a failed download can never break your `npm install` because of a
|
|
242
|
+
proxy, an offline machine, or a CDN blip.
|
|
243
|
+
|
|
244
|
+
## Environment variables
|
|
245
|
+
|
|
246
|
+
**CLI**
|
|
247
|
+
|
|
248
|
+
- `VIGILNZ_API_KEY` — API key for `vigilnz login`, for non-interactive use. Equivalent to `--api-key`.
|
|
249
|
+
|
|
250
|
+
**This npm wrapper**
|
|
251
|
+
|
|
252
|
+
- `VIGILNZ_CLI_CDN_URL` — override the download host (default `https://releases.vigilnz.com/releases`), for private mirrors or air-gapped environments.
|
|
253
|
+
- `VIGILNZ_CLI_SKIP_DOWNLOAD=1` — skip the postinstall download entirely; you're responsible for placing the binary at `bin/vigilnz-bin/vigilnz(.exe)` yourself.
|
|
254
|
+
|
|
255
|
+
## How downloads are verified
|
|
256
|
+
|
|
257
|
+
The expected SHA256 digests live in `checksums/checksums.json`, **inside this
|
|
258
|
+
published package**, pinned when the version is released.
|
|
259
|
+
|
|
260
|
+
They are deliberately not fetched from the release host at install time:
|
|
261
|
+
verifying a download against a checksum from the same host only proves that
|
|
262
|
+
host agrees with itself. Pinning the digests here puts them behind npm
|
|
263
|
+
registry integrity instead.
|
|
264
|
+
|
|
265
|
+
Verification **fails closed** — if the pinned digest is missing, malformed, or
|
|
266
|
+
stamped for a different version, the download is refused rather than falling
|
|
267
|
+
back to trusting the host.
|
|
268
|
+
|
|
269
|
+
Every distribution channel (direct binary, this npm package, Homebrew,
|
|
270
|
+
Docker) runs the exact same CLI binary — only the reported distribution
|
|
271
|
+
differs, so the Vigilnz Platform can show which channel a scan came from
|
|
272
|
+
(e.g. "Vigilnz CLI · npm").
|
|
273
|
+
|
|
274
|
+
## License
|
|
275
|
+
|
|
276
|
+
The license for this wrapper is not finalized yet. The Vigilnz CLI binary it
|
|
277
|
+
downloads, and the Vigilnz platform it connects to, are governed by the
|
|
278
|
+
Vigilnz terms of service.
|
package/bin/vigilnz.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// File: vigilnz.js
|
|
3
|
+
// Purpose: npm distribution wrapper entry point. Locates the native
|
|
4
|
+
// Vigilnz CLI binary (downloaded by install.js) and execs it with
|
|
5
|
+
// every argument passed through unmodified — this file contains
|
|
6
|
+
// NO scan logic of its own. Every distribution channel (binary,
|
|
7
|
+
// npm, Homebrew, Docker, ...) wraps one real CLI implementation.
|
|
8
|
+
//
|
|
9
|
+
// Self-heals a missing binary (e.g. `npx vigilnz` on an npm
|
|
10
|
+
// version that skips lifecycle scripts, or an install that used
|
|
11
|
+
// --ignore-scripts) by running the same download step
|
|
12
|
+
// install.js's postinstall hook already ran, once, on demand.
|
|
13
|
+
// Author: Vigilnz
|
|
14
|
+
// Date: 2026-08-23
|
|
15
|
+
// Modified: 2026-08-27 — verify the self-healed download against the digest
|
|
16
|
+
// pinned in this tarball (lib/checksums.js), the same source
|
|
17
|
+
// install.js's postinstall path uses. This supersedes an earlier
|
|
18
|
+
// same-day fix that passed fallbackNames through to a CDN-fetched
|
|
19
|
+
// SHA256SUMS: that file is no longer fetched at runtime at all.
|
|
20
|
+
// Because postinstall is now fail-soft, this path is the real
|
|
21
|
+
// install for anyone behind a proxy at install time — it must
|
|
22
|
+
// stay working.
|
|
23
|
+
|
|
24
|
+
'use strict';
|
|
25
|
+
|
|
26
|
+
const fs = require('fs');
|
|
27
|
+
const { spawnSync } = require('child_process');
|
|
28
|
+
const { resolvePlatform, artifactUrl } = require('../lib/platform');
|
|
29
|
+
const { binaryDestPath } = require('../lib/binaryPath');
|
|
30
|
+
const { downloadVerified } = require('../lib/download');
|
|
31
|
+
const { pinnedChecksumFor } = require('../lib/checksums');
|
|
32
|
+
|
|
33
|
+
async function ensureBinary() {
|
|
34
|
+
const { platformDir, binName, remoteFileName } = resolvePlatform();
|
|
35
|
+
const destPath = binaryDestPath(binName);
|
|
36
|
+
if (fs.existsSync(destPath)) {
|
|
37
|
+
return destPath;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const pkg = require('../package.json');
|
|
41
|
+
process.stderr.write('[vigilnz] Binary not found — downloading now (this only happens once)...\n');
|
|
42
|
+
await downloadVerified({
|
|
43
|
+
binaryUrl: artifactUrl(pkg.version, platformDir, remoteFileName),
|
|
44
|
+
expectedSha256: pinnedChecksumFor(remoteFileName, pkg.version),
|
|
45
|
+
artifactName: remoteFileName,
|
|
46
|
+
destPath,
|
|
47
|
+
});
|
|
48
|
+
return destPath;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function main() {
|
|
52
|
+
let binaryPath;
|
|
53
|
+
try {
|
|
54
|
+
binaryPath = await ensureBinary();
|
|
55
|
+
} catch (err) {
|
|
56
|
+
process.stderr.write(`[vigilnz] Unable to locate or download the Vigilnz CLI binary: ${err.message}\n`);
|
|
57
|
+
process.exitCode = 1;
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Pass every argument through untouched, inherit stdio so interactive
|
|
62
|
+
// prompts (vigilnz login) and colored output work exactly as running
|
|
63
|
+
// the binary directly would. VIGILNZ_CLI_DISTRIBUTION overrides the
|
|
64
|
+
// binary's compiled-in default so scan uploads report this channel
|
|
65
|
+
// correctly — npm never builds its own binary, it downloads the same
|
|
66
|
+
// prebuilt artifact every channel uses, so the compile-time value alone
|
|
67
|
+
// would misreport here. A caller that already set the var (e.g. for
|
|
68
|
+
// testing) is left untouched.
|
|
69
|
+
const result = spawnSync(binaryPath, process.argv.slice(2), {
|
|
70
|
+
stdio: 'inherit',
|
|
71
|
+
env: { ...process.env, VIGILNZ_CLI_DISTRIBUTION: process.env.VIGILNZ_CLI_DISTRIBUTION || 'npm' },
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (result.error) {
|
|
75
|
+
process.stderr.write(`[vigilnz] Failed to run the Vigilnz CLI binary: ${result.error.message}\n`);
|
|
76
|
+
process.exitCode = 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
// Mirror the child's exit code/signal exactly, so CI pipelines relying on
|
|
80
|
+
// `vigilnz scan --fail-on ...`'s exit code see the same result they would
|
|
81
|
+
// running the binary directly.
|
|
82
|
+
if (result.signal) {
|
|
83
|
+
process.kill(process.pid, result.signal);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
process.exitCode = result.status ?? 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
main();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.0.1",
|
|
3
|
+
"artifacts": {
|
|
4
|
+
"vigilnz-windows-amd64.exe": "a6c4a4d1e4a0a70249241c3466b7c6931f4ef1bb34fedb2cf23c50017f89dc32",
|
|
5
|
+
"vigilnz-linux-amd64": "572bc1ad4b9ac20e1fbe32168174e36304488be990f0d9e6b8260e73e3ed4392",
|
|
6
|
+
"vigilnz-linux-arm64": "87bc197fa1e4734e3e41e7ac06b52cc2790c734568d02a17490df7ad419548e3",
|
|
7
|
+
"vigilnz-darwin-amd64": "42f0da8eb6a0333e9b09b906a5c593206f3f04de2dbdb979075174e582fdff53",
|
|
8
|
+
"vigilnz-darwin-arm64": "5bcef2b111d12d337852aeca4ad7fd13dec7b58c2668b6a2fd295130bc26bd1d"
|
|
9
|
+
}
|
|
10
|
+
}
|
package/install.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// File: install.js
|
|
3
|
+
// Purpose: npm postinstall hook — downloads the correct prebuilt Vigilnz
|
|
4
|
+
// CLI binary for the current OS/architecture from the public
|
|
5
|
+
// release CDN, verifies it against the SHA256 digest pinned in
|
|
6
|
+
// this tarball (checksums/checksums.json), and stores it inside
|
|
7
|
+
// this package (bin/vigilnz-bin/). This package never builds or
|
|
8
|
+
// reimplements the CLI itself — every distribution channel wraps
|
|
9
|
+
// one real implementation.
|
|
10
|
+
//
|
|
11
|
+
// FAIL-SOFT BY DESIGN: this hook never exits non-zero. npm treats
|
|
12
|
+
// a failing postinstall as a failed install, which would break the
|
|
13
|
+
// whole dependency install of any project that merely lists
|
|
14
|
+
// vigilnz as a devDependency — and npm does NOT roll the package
|
|
15
|
+
// back, so it leaves a half-installed tree behind. A blocked
|
|
16
|
+
// proxy or an offline CI runner must not do that. Instead we warn
|
|
17
|
+
// here and let bin/vigilnz.js download on first actual use — the
|
|
18
|
+
// same fail-soft posture other native-binary npm wrappers take.
|
|
19
|
+
//
|
|
20
|
+
// VIGILNZ_CLI_SKIP_DOWNLOAD=1 skips this entirely — for offline/
|
|
21
|
+
// air-gapped installs where the binary is placed by some other
|
|
22
|
+
// means (e.g. a corporate mirror step) before first run.
|
|
23
|
+
// Author: Vigilnz
|
|
24
|
+
// Date: 2026-08-23
|
|
25
|
+
// Modified: 2026-08-27 — dropped an unused `path` import; verify against
|
|
26
|
+
// the pinned digest instead of a CDN-fetched SHA256SUMS; made
|
|
27
|
+
// the hook fail-soft (warn + exit 0) so it cannot break a
|
|
28
|
+
// consumer's `npm install`.
|
|
29
|
+
|
|
30
|
+
'use strict';
|
|
31
|
+
|
|
32
|
+
const fs = require('fs');
|
|
33
|
+
const { resolvePlatform, artifactUrl } = require('./lib/platform');
|
|
34
|
+
const { downloadVerified } = require('./lib/download');
|
|
35
|
+
const { binaryDestPath } = require('./lib/binaryPath');
|
|
36
|
+
const { pinnedChecksumFor } = require('./lib/checksums');
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Explain a non-fatal failure without dumping a stack trace into a
|
|
40
|
+
* consumer's install log, and point at every real escape hatch.
|
|
41
|
+
*
|
|
42
|
+
* @param {Error} err
|
|
43
|
+
*/
|
|
44
|
+
function warnNonFatal(err) {
|
|
45
|
+
process.stderr.write(
|
|
46
|
+
`\n[vigilnz] WARNING: could not install the Vigilnz CLI binary now:\n` +
|
|
47
|
+
` ${err.message}\n` +
|
|
48
|
+
'\n[vigilnz] This is not fatal — your install continues, and the binary will be\n' +
|
|
49
|
+
'[vigilnz] downloaded automatically the first time you run `vigilnz`.\n' +
|
|
50
|
+
'[vigilnz] If that machine also cannot reach the download host, you can:\n' +
|
|
51
|
+
'[vigilnz] - Set VIGILNZ_CLI_CDN_URL to a private mirror\n' +
|
|
52
|
+
'[vigilnz] - Set VIGILNZ_CLI_SKIP_DOWNLOAD=1 and place the binary yourself\n\n'
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async function main() {
|
|
57
|
+
if (process.env.VIGILNZ_CLI_SKIP_DOWNLOAD === '1') {
|
|
58
|
+
console.log('[vigilnz] VIGILNZ_CLI_SKIP_DOWNLOAD=1 set — skipping binary download.');
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const pkg = require('./package.json');
|
|
63
|
+
const version = pkg.version;
|
|
64
|
+
const { platformDir, binName, remoteFileName } = resolvePlatform();
|
|
65
|
+
const destPath = binaryDestPath(binName);
|
|
66
|
+
|
|
67
|
+
if (fs.existsSync(destPath)) {
|
|
68
|
+
console.log(`[vigilnz] Binary already present at ${destPath} — skipping download.`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(`[vigilnz] Downloading Vigilnz CLI v${version} for ${platformDir}...`);
|
|
73
|
+
await downloadVerified({
|
|
74
|
+
binaryUrl: artifactUrl(version, platformDir, remoteFileName),
|
|
75
|
+
expectedSha256: pinnedChecksumFor(remoteFileName, version),
|
|
76
|
+
artifactName: remoteFileName,
|
|
77
|
+
destPath,
|
|
78
|
+
});
|
|
79
|
+
console.log(`[vigilnz] Installed to ${destPath}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
main().catch((err) => {
|
|
83
|
+
warnNonFatal(err);
|
|
84
|
+
// Deliberately exit 0 — see the FAIL-SOFT note in the file header.
|
|
85
|
+
process.exitCode = 0;
|
|
86
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// File: binaryPath.js
|
|
2
|
+
// Purpose: Single source of truth for where the downloaded Vigilnz CLI
|
|
3
|
+
// binary lives on disk, shared by install.js and bin/vigilnz.js.
|
|
4
|
+
// Author: Vigilnz
|
|
5
|
+
// Date: 2026-08-23
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} binName - "vigilnz" or "vigilnz.exe" (see lib/platform.js)
|
|
13
|
+
* @returns {string} absolute path inside this package where the binary is stored
|
|
14
|
+
*/
|
|
15
|
+
function binaryDestPath(binName) {
|
|
16
|
+
return path.join(__dirname, '..', 'bin', 'vigilnz-bin', binName);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = { binaryDestPath };
|
package/lib/checksums.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// File: checksums.js
|
|
2
|
+
// Purpose: Reads the SHA256 digests that are PINNED INSIDE this npm
|
|
3
|
+
// tarball (checksums/checksums.json), rather than fetching a
|
|
4
|
+
// SHA256SUMS file from the same CDN the binary itself comes from.
|
|
5
|
+
//
|
|
6
|
+
// Why this matters: verifying a download against a checksum
|
|
7
|
+
// fetched from that same host only proves "the host agrees with
|
|
8
|
+
// itself" — whoever can tamper with one can tamper with both.
|
|
9
|
+
// Pinning the digests in the published package puts them behind
|
|
10
|
+
// npm registry integrity instead, so the trust anchor is the
|
|
11
|
+
// registry rather than the release host. The Homebrew channel
|
|
12
|
+
// does the equivalent by baking digests into its formula.
|
|
13
|
+
//
|
|
14
|
+
// The digest file is generated at publish time and is deliberately
|
|
15
|
+
// NOT committed, so a stale digest set can never be published by
|
|
16
|
+
// accident; the release pipeline verifies it before publishing.
|
|
17
|
+
// Author: Vigilnz
|
|
18
|
+
// Date: 2026-08-27
|
|
19
|
+
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
|
|
25
|
+
/** Location of the generated, tarball-pinned digest set. */
|
|
26
|
+
const CHECKSUMS_PATH = path.join(__dirname, '..', 'checksums', 'checksums.json');
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Every platform the release pipeline actually publishes. windows-arm64 is
|
|
30
|
+
* absent on purpose (see lib/platform.js) — keep this in sync with what
|
|
31
|
+
* the pipeline builds.
|
|
32
|
+
*/
|
|
33
|
+
const PUBLISHED_PLATFORMS = [
|
|
34
|
+
'windows-amd64',
|
|
35
|
+
'linux-amd64',
|
|
36
|
+
'linux-arm64',
|
|
37
|
+
'darwin-amd64',
|
|
38
|
+
'darwin-arm64',
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const SHA256_HEX_LENGTH = 64;
|
|
42
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Load and structurally validate the pinned digest set.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} [filePath] - override, for tests only. Production callers
|
|
48
|
+
* must use the default: tests that wrote to the real path used to
|
|
49
|
+
* leave a bogus digest set behind, which `npm pack` then shipped.
|
|
50
|
+
* @returns {{ version: string, artifacts: Record<string, string> }}
|
|
51
|
+
* @throws {Error} if the file is missing, unparseable, or malformed
|
|
52
|
+
*/
|
|
53
|
+
function loadPinnedChecksums(filePath = CHECKSUMS_PATH) {
|
|
54
|
+
let raw;
|
|
55
|
+
try {
|
|
56
|
+
raw = fs.readFileSync(filePath, 'utf8');
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`No pinned checksum file at ${filePath} (${err.code}). This package was ` +
|
|
60
|
+
'published without its checksums being stamped, or you are running from ' +
|
|
61
|
+
'a source checkout where they have not been generated yet.'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let parsed;
|
|
66
|
+
try {
|
|
67
|
+
parsed = JSON.parse(raw);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
throw new Error(`Pinned checksum file ${filePath} is not valid JSON: ${err.message}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!parsed || typeof parsed.version !== 'string' || !parsed.artifacts) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Pinned checksum file ${filePath} is malformed: expected {version, artifacts}.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return parsed;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Look up the pinned digest for one release artifact, failing closed on any
|
|
82
|
+
* problem — a missing entry, a malformed digest, or a digest set stamped for
|
|
83
|
+
* a different CLI version. There is deliberately NO fallback to downloading
|
|
84
|
+
* SHA256SUMS from the CDN: that fallback would silently restore exactly the
|
|
85
|
+
* weaker trust model this module exists to remove.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} artifactName - remote artifact name, e.g. "vigilnz-linux-amd64"
|
|
88
|
+
* @param {string} expectedVersion - the version being installed (package.json's own)
|
|
89
|
+
* @param {string} [filePath] - override, for tests only (see loadPinnedChecksums)
|
|
90
|
+
* @returns {string} lowercase 64-char hex digest
|
|
91
|
+
* @throws {Error} if no trustworthy digest is available
|
|
92
|
+
*/
|
|
93
|
+
function pinnedChecksumFor(artifactName, expectedVersion, filePath = CHECKSUMS_PATH) {
|
|
94
|
+
const { version, artifacts } = loadPinnedChecksums(filePath);
|
|
95
|
+
|
|
96
|
+
// Guards against stamping drift: a checksums.json left over from an
|
|
97
|
+
// earlier version would otherwise verify this version's binary against
|
|
98
|
+
// the wrong digest and fail with a confusing "corrupted download".
|
|
99
|
+
if (version !== expectedVersion) {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Pinned checksums are for CLI v${version} but this package is v${expectedVersion}. ` +
|
|
102
|
+
'This package was built with a mismatched checksum set — please report it.'
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const digest = artifacts[artifactName];
|
|
107
|
+
if (typeof digest !== 'string') {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`Pinned checksums have no entry for ${artifactName} ` +
|
|
110
|
+
`(have: ${Object.keys(artifacts).sort().join(', ') || 'none'}).`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const normalized = digest.trim().toLowerCase();
|
|
115
|
+
if (!SHA256_PATTERN.test(normalized)) {
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Pinned digest for ${artifactName} is not a ${SHA256_HEX_LENGTH}-char hex sha256: "${digest}".`
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
return normalized;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
loadPinnedChecksums,
|
|
125
|
+
pinnedChecksumFor,
|
|
126
|
+
CHECKSUMS_PATH,
|
|
127
|
+
PUBLISHED_PLATFORMS,
|
|
128
|
+
SHA256_PATTERN,
|
|
129
|
+
};
|
package/lib/download.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// File: download.js
|
|
2
|
+
// Purpose: Download + SHA256-verify one Vigilnz CLI release artifact.
|
|
3
|
+
// Shared by install.js (postinstall) and bin/vigilnz.js's
|
|
4
|
+
// lazy-self-heal path (npx / --ignore-scripts installs that
|
|
5
|
+
// skipped postinstall). Uses only Node built-ins — no runtime
|
|
6
|
+
// dependency footprint for a package whose whole job is being a
|
|
7
|
+
// thin wrapper.
|
|
8
|
+
// Author: Vigilnz
|
|
9
|
+
// Date: 2026-08-23
|
|
10
|
+
// Modified: 2026-08-27 — downloadVerified now takes the expected digest
|
|
11
|
+
// from the caller (lib/checksums.js reads it from the pinned,
|
|
12
|
+
// in-tarball checksums.json) instead of fetching SHA256SUMS from
|
|
13
|
+
// the same host as the binary, which only proved that host agreed
|
|
14
|
+
// with itself. findChecksum stays exported: the release pipeline's
|
|
15
|
+
// stamping step still parses the sha256sum-format SHA256SUMS at
|
|
16
|
+
// publish time to build that pinned set.
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const http = require('http');
|
|
22
|
+
const https = require('https');
|
|
23
|
+
const crypto = require('crypto');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
const MAX_REDIRECTS = 5;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* GET a URL to a Buffer, following redirects (S3/CloudFront commonly
|
|
30
|
+
* 301/302 on a stale cache miss). Rejects on any non-2xx final status.
|
|
31
|
+
* Picks http vs https by URL scheme — the default public CDN is always
|
|
32
|
+
* https, but a private mirror (VIGILNZ_CLI_CDN_URL, or a test server)
|
|
33
|
+
* may reasonably be plain http on an internal network.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} url
|
|
36
|
+
* @param {number} [redirectsLeft]
|
|
37
|
+
* @returns {Promise<Buffer>}
|
|
38
|
+
*/
|
|
39
|
+
function fetchBuffer(url, redirectsLeft = MAX_REDIRECTS) {
|
|
40
|
+
const transport = url.startsWith('http://') ? http : https;
|
|
41
|
+
return new Promise((resolve, reject) => {
|
|
42
|
+
const req = transport.get(url, { headers: { 'User-Agent': 'vigilnz-npm-cli' } }, (res) => {
|
|
43
|
+
const { statusCode, headers } = res;
|
|
44
|
+
if (statusCode >= 300 && statusCode < 400 && headers.location && redirectsLeft > 0) {
|
|
45
|
+
res.resume();
|
|
46
|
+
fetchBuffer(headers.location, redirectsLeft - 1).then(resolve, reject);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (statusCode !== 200) {
|
|
50
|
+
res.resume();
|
|
51
|
+
reject(new Error(`GET ${url} returned HTTP ${statusCode}`));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const chunks = [];
|
|
55
|
+
res.on('data', (chunk) => chunks.push(chunk));
|
|
56
|
+
res.on('end', () => resolve(Buffer.concat(chunks)));
|
|
57
|
+
res.on('error', reject);
|
|
58
|
+
});
|
|
59
|
+
req.on('error', reject);
|
|
60
|
+
req.setTimeout(60_000, () => req.destroy(new Error(`GET ${url} timed out after 60s`)));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parse a `sha256sum`-format SHA256SUMS file (`<hex> <filename>` per line,
|
|
66
|
+
* matching the release pipeline's checksum generation) and return the hex
|
|
67
|
+
* digest for one file name.
|
|
68
|
+
*
|
|
69
|
+
* @param {string} sha256sumsText
|
|
70
|
+
* @param {string} fileName
|
|
71
|
+
* @returns {string | null}
|
|
72
|
+
*/
|
|
73
|
+
function findChecksum(sha256sumsText, fileName, fallbackNames = []) {
|
|
74
|
+
const entries = [];
|
|
75
|
+
|
|
76
|
+
for (const line of sha256sumsText.split('\n')) {
|
|
77
|
+
const trimmed = line.trim();
|
|
78
|
+
if (!trimmed) continue;
|
|
79
|
+
const match = trimmed.match(/^([a-f0-9]{64})\s+\*?(.+)$/i);
|
|
80
|
+
if (match) {
|
|
81
|
+
entries.push({
|
|
82
|
+
digest: match[1].toLowerCase(),
|
|
83
|
+
name: path.basename(match[2].trim()),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const wanted of [fileName, ...fallbackNames]) {
|
|
89
|
+
const hit = entries.find((entry) => entry.name === wanted);
|
|
90
|
+
if (hit) return hit.digest;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Deliberately NO "if there's only one entry, use it" fallback: a lone
|
|
94
|
+
// entry naming a different artifact would then be verified against,
|
|
95
|
+
// which is exactly the case test/download.test.js guards. Matching is by
|
|
96
|
+
// name only — an unmatched name returns null and the caller fails.
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Download one binary artifact, verify it against a digest the CALLER
|
|
102
|
+
* supplies (pinned in this tarball — see lib/checksums.js), and write it to
|
|
103
|
+
* destPath (0755 on non-Windows). Throws with a clear message on any
|
|
104
|
+
* network, HTTP, or checksum-mismatch failure — never writes a partial or
|
|
105
|
+
* unverified file to destPath.
|
|
106
|
+
*
|
|
107
|
+
* @param {{ binaryUrl: string, expectedSha256: string, artifactName: string, destPath: string }} opts
|
|
108
|
+
*/
|
|
109
|
+
async function downloadVerified({ binaryUrl, expectedSha256, artifactName, destPath }) {
|
|
110
|
+
const expected = String(expectedSha256 || '')
|
|
111
|
+
.trim()
|
|
112
|
+
.toLowerCase();
|
|
113
|
+
if (!/^[a-f0-9]{64}$/.test(expected)) {
|
|
114
|
+
// Fail before spending bandwidth: an absent/garbage expected digest
|
|
115
|
+
// must never degrade into "download it and hope".
|
|
116
|
+
throw new Error(
|
|
117
|
+
`Refusing to download ${artifactName} without a valid pinned sha256 digest ` +
|
|
118
|
+
`(got "${expectedSha256}").`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const binary = await fetchBuffer(binaryUrl);
|
|
123
|
+
|
|
124
|
+
const actual = crypto.createHash('sha256').update(binary).digest('hex');
|
|
125
|
+
if (actual !== expected) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`Checksum mismatch for ${artifactName}: expected ${expected}, got ${actual}. ` +
|
|
128
|
+
'The download may be corrupted or tampered with — not installing it.'
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
133
|
+
const tmpPath = `${destPath}.download-${process.pid}`;
|
|
134
|
+
fs.writeFileSync(tmpPath, binary, { mode: 0o755 });
|
|
135
|
+
fs.renameSync(tmpPath, destPath); // atomic on the same filesystem — no partially-written binary left behind
|
|
136
|
+
if (process.platform !== 'win32') {
|
|
137
|
+
fs.chmodSync(destPath, 0o755);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = { downloadVerified, fetchBuffer, findChecksum };
|
package/lib/platform.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// File: platform.js
|
|
2
|
+
// Purpose: OS/CPU-architecture detection and download-URL construction for
|
|
3
|
+
// the Vigilnz CLI's npm distribution wrapper. This is the ONLY
|
|
4
|
+
// place that knows the published release-artifact layout — a
|
|
5
|
+
// version bump or a new platform target only needs a change here,
|
|
6
|
+
// never in install.js/bin/vigilnz.js.
|
|
7
|
+
//
|
|
8
|
+
// This package never reimplements any scan logic — it only
|
|
9
|
+
// locates and executes the single native Vigilnz CLI binary.
|
|
10
|
+
// Author: Vigilnz
|
|
11
|
+
// Date: 2026-08-23
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Public host serving CLI release artifacts — deliberately SEPARATE from
|
|
17
|
+
* the API the CLI talks to at runtime. Never point this at an application
|
|
18
|
+
* backend.
|
|
19
|
+
*/
|
|
20
|
+
const DEFAULT_CDN_BASE_URL = 'https://releases.vigilnz.com/releases';
|
|
21
|
+
|
|
22
|
+
/** Override for private mirrors / air-gapped installs / CI testing. */
|
|
23
|
+
function cdnBaseUrl() {
|
|
24
|
+
return (process.env.VIGILNZ_CLI_CDN_URL || DEFAULT_CDN_BASE_URL).replace(/\/+$/, '');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Maps Node's process.platform to the release's OS name and the local
|
|
29
|
+
* binary filename — must stay in sync with the platforms the release
|
|
30
|
+
* pipeline publishes.
|
|
31
|
+
*/
|
|
32
|
+
const PLATFORM_MAP = {
|
|
33
|
+
win32: { os: 'windows', binName: 'vigilnz.exe' },
|
|
34
|
+
linux: { os: 'linux', binName: 'vigilnz' },
|
|
35
|
+
darwin: { os: 'darwin', binName: 'vigilnz' },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Maps Node's process.arch to the architecture names releases publish under. */
|
|
39
|
+
const ARCH_MAP = {
|
|
40
|
+
x64: 'amd64',
|
|
41
|
+
arm64: 'arm64',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the current process's platform/arch into this release's naming
|
|
46
|
+
* scheme, or throw a clear, actionable error for an unsupported combination
|
|
47
|
+
* — either one the release pipeline never produces (e.g. 32-bit Windows),
|
|
48
|
+
* or windows/arm64 specifically, which is deliberately excluded.
|
|
49
|
+
*
|
|
50
|
+
* @returns {{ platformDir: string, binName: string, remoteFileName: string }}
|
|
51
|
+
*/
|
|
52
|
+
function resolvePlatform() {
|
|
53
|
+
const platformEntry = PLATFORM_MAP[process.platform];
|
|
54
|
+
const goArch = ARCH_MAP[process.arch];
|
|
55
|
+
|
|
56
|
+
if (!platformEntry || !goArch) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Vigilnz CLI has no prebuilt binary for ${process.platform}/${process.arch}. ` +
|
|
59
|
+
'Supported: Windows/Linux/macOS on x64 or arm64 (Windows/arm64 excepted — see below). ' +
|
|
60
|
+
'Build the CLI from source for this platform, or set VIGILNZ_CLI_SKIP_DOWNLOAD=1 ' +
|
|
61
|
+
'and place a binary yourself.'
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (platformEntry.os === 'windows' && goArch === 'arm64') {
|
|
66
|
+
throw new Error(
|
|
67
|
+
'Vigilnz CLI has no prebuilt binary for windows/arm64. Windows 11 on ARM can ' +
|
|
68
|
+
'run the windows-amd64 build via its built-in x64 emulation instead: set ' +
|
|
69
|
+
'VIGILNZ_CLI_SKIP_DOWNLOAD=1 and place that binary yourself.'
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const platformDir = `${platformEntry.os}-${goArch}`;
|
|
74
|
+
const ext = platformEntry.os === 'windows' ? '.exe' : '';
|
|
75
|
+
return {
|
|
76
|
+
platformDir,
|
|
77
|
+
binName: platformEntry.binName,
|
|
78
|
+
// The name the file actually has on releases.vigilnz.com — distinct
|
|
79
|
+
// from binName, which is the LOCAL name this package stores/execs the
|
|
80
|
+
// binary as once downloaded (see lib/binaryPath.js).
|
|
81
|
+
remoteFileName: `vigilnz-${platformDir}${ext}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Build the download URL for one release artifact.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} version - CLI version (matches this package's own package.json version)
|
|
89
|
+
* @param {string} platformDir - e.g. "linux-amd64" (from resolvePlatform())
|
|
90
|
+
* @param {string} fileName - the remote binary name (resolvePlatform().remoteFileName) or "SHA256SUMS"
|
|
91
|
+
* @returns {string}
|
|
92
|
+
*/
|
|
93
|
+
function artifactUrl(version, platformDir, fileName) {
|
|
94
|
+
return `${cdnBaseUrl()}/v${version}/${platformDir}/${fileName}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = { resolvePlatform, artifactUrl, cdnBaseUrl, DEFAULT_CDN_BASE_URL };
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vigilnz",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Vigilnz security scanner CLI — thin installer/wrapper that downloads and runs the native Vigilnz CLI binary. No scan logic lives in this package.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"vigilnz": "bin/vigilnz.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/vigilnz.js",
|
|
10
|
+
"lib/",
|
|
11
|
+
"checksums/",
|
|
12
|
+
"install.js",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"postinstall": "node install.js",
|
|
17
|
+
"stamp-checksums": "node scripts/stamp-checksums.js",
|
|
18
|
+
"prepack": "node scripts/stamp-checksums.js --check",
|
|
19
|
+
"prepublishOnly": "node scripts/stamp-checksums.js --check",
|
|
20
|
+
"test": "node --test test/*.test.js"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=16"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"vigilnz",
|
|
27
|
+
"security",
|
|
28
|
+
"sast",
|
|
29
|
+
"sca",
|
|
30
|
+
"secret-scanning",
|
|
31
|
+
"iac",
|
|
32
|
+
"sbom",
|
|
33
|
+
"container-scanning"
|
|
34
|
+
],
|
|
35
|
+
"author": "Vigilnz",
|
|
36
|
+
"license": "UNKNOWN",
|
|
37
|
+
"homepage": "https://vigilnz.com"
|
|
38
|
+
}
|