securevibe 0.1.13 → 0.1.14

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 CHANGED
@@ -31,6 +31,7 @@ midnight). The `--staged` commit guard is never limited. See [`LICENSE`](LICENSE
31
31
  | `securevibe pr-comment [path]` | Post/update a PR findings summary (used by the GitHub Actions workflow) |
32
32
  | `securevibe deps [path]` | Audit dependencies for known CVEs against the local OSV database (SCA) |
33
33
  | `securevibe db <update\|status>` | Manage the local OSV database — `update` is networked |
34
+ | `securevibe update` | Update securevibe itself to the latest published version (also networked) |
34
35
  | `securevibe ready [path]` | Launch readiness scorecard: pass/fail gates + go/no-go verdict |
35
36
  | `securevibe ai-audit [path]` | Focus on the AI-agent attack surface (doc 04) |
36
37
  | `securevibe protect [path]` | Remediation-first view: the fix for every finding (doc 05) |
@@ -71,7 +72,8 @@ is sent to your provider. Needs the same key as `fix` and `explain`
71
72
  - a **GitHub Actions workflow** (`.github/workflows/securevibe.yml`) that scans every PR and uploads
72
73
  SARIF to GitHub code scanning, gates the build on deployment-readiness, and posts a PR comment
73
74
  summarizing findings in the *changed* files only (updated in place on new commits, not reposted —
74
- and the only other command besides `db update` that talks to the network, and only to post a
75
+ and one of the few other commands (besides `db update` and `update`) that talks to the network,
76
+ and only to post a
75
77
  findings summary already computed locally, never your source code);
76
78
  - gitignores `.env` / backups and drops a minimal `securevibe.config.json`.
77
79
 
@@ -142,9 +144,10 @@ against a local copy of the [OSV](https://osv.dev) advisory database, and report
142
144
  CVE still blocks deployment, and `securevibe fix` bumps the package to the lowest non vulnerable
143
145
  version while preserving your range prefix (`^`, `~`, `==`, ...).
144
146
  - **Offline first.** A bundled seed (`data/osv-seed.json`) ships with the CLI, so `deps` works with
145
- no network. `db update` is the only command that **downloads** anything (the public OSV npm and
146
- PyPI dumps, into `~/.securevibe/db`). `pr-comment` is the only command that **uploads** anything,
147
- and only a findings summary already computed locally — never your code or package list.
147
+ no network. `db update` and `update` are the only commands that **download** anything (OSV
148
+ advisory dumps, or the CLI's own next version, respectively). `pr-comment` is the only command
149
+ that **uploads** anything, and only a findings summary already computed locally — never your
150
+ code or package list.
148
151
  - **Honest framing.** A clean result reads "no known advisories as of `<db date>`", not "secure" —
149
152
  it means nothing matched the local database on that date, which is not a proof of safety.
150
153
 
package/dist/index.js CHANGED
@@ -353,6 +353,18 @@ program
353
353
  process.stderr.write(` unknown db action: ${action} (use: update | status)\n`);
354
354
  process.exit(64);
355
355
  });
356
+ program
357
+ .command("update")
358
+ .description("Update securevibe to the latest published version (npm global installs only)")
359
+ .option("--no-color", "disable coloured output")
360
+ .action(async (opts) => {
361
+ if (opts.color === false)
362
+ process.env.NO_COLOR = "1";
363
+ const { runUpdateCommand } = await import("./update-command.js");
364
+ const code = await runUpdateCommand();
365
+ if (code !== 0)
366
+ process.exit(code);
367
+ });
356
368
  program
357
369
  .command("config")
358
370
  .description("Manage local AI-fixer provider keys (Groq/Anthropic), stored in ~/.securevibe/config.json")
@@ -28,6 +28,16 @@ export function isNewer(current, latest) {
28
28
  }
29
29
  return false;
30
30
  }
31
+ /**
32
+ * True when the running script is npm's npx cache copy rather than a
33
+ * persistent global install — npx always resolves to the latest published
34
+ * version on every invocation, so there is nothing for `securevibe update`
35
+ * to do in that case. The npx cache directory is named `_npx` on both Unix
36
+ * (~/.npm/_npx/...) and Windows (%LocalAppData%\npm-cache\_npx\...).
37
+ */
38
+ export function isRunningViaNpx(scriptPath = process.argv[1] ?? "") {
39
+ return scriptPath.split(/[\\/]/).includes("_npx");
40
+ }
31
41
  async function readCache(file) {
32
42
  try {
33
43
  const raw = JSON.parse(await fs.readFile(file, "utf8"));
@@ -48,7 +58,7 @@ async function writeCache(file, state) {
48
58
  /* best effort — a failed write must never break the command */
49
59
  }
50
60
  }
51
- async function fetchLatest(fetchImpl) {
61
+ export async function fetchLatest(fetchImpl) {
52
62
  const controller = new AbortController();
53
63
  const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
54
64
  try {
@@ -96,6 +106,6 @@ export async function checkForUpdate(currentVersion, opts = {}) {
96
106
  export function updateNotice(currentVersion, latestVersion) {
97
107
  return [
98
108
  ` A new version of securevibe is available: ${latestVersion} (you have ${currentVersion}).`,
99
- " Update: npm install -g securevibe",
109
+ " Update: securevibe update (or: npm install -g securevibe)",
100
110
  ].join("\n");
101
111
  }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * `securevibe update` — an explicit, user-triggered self-update for npm
3
+ * global installs. Reuses the same registry check as the passive daily
4
+ * notice (update-check.ts) but always fetches fresh: "check right now" is
5
+ * the whole point of running this command instead of waiting for tomorrow's
6
+ * cached check.
7
+ */
8
+ import { isNewer, isRunningViaNpx, fetchLatest as defaultFetchLatest } from "./update-check.js";
9
+ import { VERSION } from "./version.js";
10
+ async function defaultSpawnNpmInstall(write) {
11
+ const { spawn } = await import("node:child_process");
12
+ return new Promise((resolve) => {
13
+ const child = spawn("npm", ["install", "-g", "securevibe@latest"], {
14
+ stdio: ["ignore", "pipe", "pipe"],
15
+ shell: process.platform === "win32",
16
+ });
17
+ let output = "";
18
+ const onChunk = (chunk) => {
19
+ const text = chunk.toString("utf8");
20
+ output += text;
21
+ write(text);
22
+ };
23
+ child.stdout?.on("data", onChunk);
24
+ child.stderr?.on("data", onChunk);
25
+ child.on("error", (err) => resolve({ code: 1, output: output + String(err?.message ?? err) }));
26
+ child.on("close", (code) => resolve({ code, output }));
27
+ });
28
+ }
29
+ export function defaultUpdateCommandDeps() {
30
+ return {
31
+ currentVersion: VERSION,
32
+ platform: process.platform,
33
+ isNpx: () => isRunningViaNpx(),
34
+ fetchLatest: defaultFetchLatest,
35
+ fetchImpl: fetch,
36
+ spawnNpmInstall: defaultSpawnNpmInstall,
37
+ write: (text) => process.stderr.write(text),
38
+ };
39
+ }
40
+ /**
41
+ * True when npm's own output looks like a permission error and a sudo
42
+ * re-run would plausibly help. Never true on Windows, where elevation isn't
43
+ * a one-line fix.
44
+ */
45
+ export function shouldShowSudoHint(output, platform = process.platform) {
46
+ return platform !== "win32" && /eacces|permission denied/i.test(output);
47
+ }
48
+ /**
49
+ * Runs the `securevibe update` flow. Returns the process exit code (0
50
+ * success, 1 failure) rather than calling process.exit itself, so callers
51
+ * (and tests) control process lifecycle. Never throws.
52
+ */
53
+ export async function runUpdateCommand(deps = {}) {
54
+ const d = { ...defaultUpdateCommandDeps(), ...deps };
55
+ if (d.isNpx()) {
56
+ d.write(" You're running securevibe via npx, which always uses the latest published version — nothing to update.\n");
57
+ return 0;
58
+ }
59
+ const latest = await d.fetchLatest(d.fetchImpl);
60
+ if (latest === null) {
61
+ d.write(" Could not reach the npm registry to check for updates. Check your network and try again.\n");
62
+ return 1;
63
+ }
64
+ if (!isNewer(d.currentVersion, latest)) {
65
+ d.write(` Already on the latest version (${d.currentVersion}).\n`);
66
+ return 0;
67
+ }
68
+ d.write(` Updating securevibe ${d.currentVersion} -> ${latest}...\n`);
69
+ const result = await d.spawnNpmInstall(d.write);
70
+ if (result.code === 0) {
71
+ d.write(` Updated to ${latest}.\n`);
72
+ return 0;
73
+ }
74
+ d.write(" Update failed.\n");
75
+ if (shouldShowSudoHint(result.output, d.platform)) {
76
+ d.write(" Try: sudo npm install -g securevibe@latest\n");
77
+ }
78
+ return 1;
79
+ }
package/dist/version.js CHANGED
@@ -2,4 +2,4 @@
2
2
  // Single source of truth for the CLI's own version, so it can't drift from
3
3
  // what commander reports and what the update-check compares against.
4
4
  // Kept in sync with the "version" field in package.json by hand at release time.
5
- export const VERSION = "0.1.13";
5
+ export const VERSION = "0.1.14";
package/package.json CHANGED
@@ -1,71 +1,71 @@
1
- {
2
- "name": "securevibe",
3
- "version": "0.1.13",
4
- "description": "Autonomous AI security engineer for AI generated apps: scan, fix, verify, and gate every commit before you launch",
5
- "type": "module",
6
- "license": "SEE LICENSE IN LICENSE",
7
- "author": "Benedict Patrick <benedictroger2006@gmail.com>",
8
- "homepage": "https://github.com/Benedictpatrick/secure-vibe#readme",
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/Benedictpatrick/secure-vibe.git",
12
- "directory": "packages/cli"
13
- },
14
- "bugs": {
15
- "url": "https://github.com/Benedictpatrick/secure-vibe/issues"
16
- },
17
- "keywords": [
18
- "security",
19
- "scanner",
20
- "sast",
21
- "ai security",
22
- "prompt injection",
23
- "vulnerability",
24
- "secrets",
25
- "sca",
26
- "cve",
27
- "launch readiness",
28
- "vibe coding",
29
- "cli"
30
- ],
31
- "engines": {
32
- "node": ">=20"
33
- },
34
- "bin": {
35
- "securevibe": "./dist/index.js"
36
- },
37
- "files": [
38
- "dist",
39
- "data",
40
- "LICENSE"
41
- ],
42
- "scripts": {
43
- "build": "tsc -p tsconfig.json",
44
- "dev": "tsx src/index.ts",
45
- "start": "node dist/index.js",
46
- "test": "node run-tests.mjs"
47
- },
48
- "dependencies": {
49
- "commander": "^12.1.0",
50
- "diff": "^5.2.0",
51
- "ink": "^6.8.0",
52
- "ink-text-input": "^6.0.0",
53
- "picocolors": "^1.1.1",
54
- "react": "^19.2.7",
55
- "tree-sitter-wasms": "^0.1.12",
56
- "web-tree-sitter": "^0.25.10",
57
- "yauzl": "^3.2.0"
58
- },
59
- "optionalDependencies": {
60
- "@anthropic-ai/sdk": "^0.39.0"
61
- },
62
- "devDependencies": {
63
- "@types/diff": "^5.2.0",
64
- "@types/node": "^20.14.0",
65
- "@types/react": "^19.2.17",
66
- "@types/yauzl": "^2.10.3",
67
- "ink-testing-library": "^4.0.0",
68
- "tsx": "^4.19.0",
69
- "typescript": "^5.6.0"
70
- }
71
- }
1
+ {
2
+ "name": "securevibe",
3
+ "version": "0.1.14",
4
+ "description": "Autonomous AI security engineer for AI generated apps: scan, fix, verify, and gate every commit before you launch",
5
+ "type": "module",
6
+ "license": "SEE LICENSE IN LICENSE",
7
+ "author": "Benedict Patrick <benedictroger2006@gmail.com>",
8
+ "homepage": "https://github.com/Benedictpatrick/secure-vibe#readme",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/Benedictpatrick/secure-vibe.git",
12
+ "directory": "packages/cli"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/Benedictpatrick/secure-vibe/issues"
16
+ },
17
+ "keywords": [
18
+ "security",
19
+ "scanner",
20
+ "sast",
21
+ "ai security",
22
+ "prompt injection",
23
+ "vulnerability",
24
+ "secrets",
25
+ "sca",
26
+ "cve",
27
+ "launch readiness",
28
+ "vibe coding",
29
+ "cli"
30
+ ],
31
+ "engines": {
32
+ "node": ">=20"
33
+ },
34
+ "bin": {
35
+ "securevibe": "./dist/index.js"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "data",
40
+ "LICENSE"
41
+ ],
42
+ "scripts": {
43
+ "build": "tsc -p tsconfig.json",
44
+ "dev": "tsx src/index.ts",
45
+ "start": "node dist/index.js",
46
+ "test": "node run-tests.mjs"
47
+ },
48
+ "dependencies": {
49
+ "commander": "^12.1.0",
50
+ "diff": "^5.2.0",
51
+ "ink": "^6.8.0",
52
+ "ink-text-input": "^6.0.0",
53
+ "picocolors": "^1.1.1",
54
+ "react": "^19.2.7",
55
+ "tree-sitter-wasms": "^0.1.12",
56
+ "web-tree-sitter": "^0.25.10",
57
+ "yauzl": "^3.2.0"
58
+ },
59
+ "optionalDependencies": {
60
+ "@anthropic-ai/sdk": "^0.39.0"
61
+ },
62
+ "devDependencies": {
63
+ "@types/diff": "^5.2.0",
64
+ "@types/node": "^20.14.0",
65
+ "@types/react": "^19.2.17",
66
+ "@types/yauzl": "^2.10.3",
67
+ "ink-testing-library": "^4.0.0",
68
+ "tsx": "^4.19.0",
69
+ "typescript": "^5.6.0"
70
+ }
71
+ }