alvin-bot 4.4.2 → 4.4.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to Alvin Bot are documented here.
4
4
 
5
+ ## [4.4.3] — 2026-04-09
6
+
7
+ ### 🔐 Security
8
+ - **Sudo password storage** — Fixed a vulnerability where the sudo password was passed to `/usr/bin/security` as a command-line argument, making it briefly visible in `ps aux` output during keychain writes. Password is now piped via stdin using the documented `-w` prompt mode (must be the last option, and the password is supplied twice for the interactive prompt + confirmation). Byte-exact round-trip verified for arbitrary special characters.
9
+
10
+ ### 🛠 Providers
11
+ - **Gemini auto-registration narrowed** — The Google Gemini chat provider is no longer registered automatically just because `GOOGLE_API_KEY` is set. It is now registered only when `google` is referenced as the primary provider or in the fallback chain. The environment variable is still used for other Google-powered features (e.g. `/imagine` image generation) without forcing Gemini onto the chat provider list.
12
+
13
+ ### 🧰 Tooling
14
+ - `package-lock.json` now tracks `package.json` version correctly.
15
+
5
16
  ## [2.2.0] — 2026-02-24
6
17
 
7
18
  ### 🔐 Security
@@ -185,12 +185,13 @@ export function createRegistry(config) {
185
185
  model: "claude-opus-4-6",
186
186
  };
187
187
  }
188
- // Auto-register Google Gemini (single model) if key is available
189
- if (config.apiKeys?.google) {
188
+ // Register Google Gemini only if explicitly referenced as primary/fallback
189
+ // (GOOGLE_API_KEY is also used for image generation — doesn't mean Gemini should be a chat provider)
190
+ if (config.primary === "google" || config.fallbacks?.includes("google")) {
190
191
  providers["google"] = {
191
192
  ...PROVIDER_PRESETS["gemini-2.5-flash"],
192
193
  name: "Google Gemini",
193
- apiKey: config.apiKeys.google,
194
+ apiKey: config.apiKeys?.google,
194
195
  };
195
196
  }
196
197
  // Always try to detect local Ollama
@@ -11,7 +11,7 @@
11
11
  * - OS dialog handling (Accessibility, Full Disk Access, etc.)
12
12
  * - Status check (is sudo configured? does it work?)
13
13
  */
14
- import { execSync, spawn } from "child_process";
14
+ import { execSync, spawn, spawnSync } from "child_process";
15
15
  import os from "os";
16
16
  import fs from "fs";
17
17
  import crypto from "crypto";
@@ -34,7 +34,33 @@ export function storePassword(password) {
34
34
  execSync(`security delete-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" 2>/dev/null`, { stdio: "pipe" });
35
35
  }
36
36
  catch { /* didn't exist */ }
37
- execSync(`security add-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w "${password.replace(/"/g, '\\"')}" -U`, { stdio: "pipe", timeout: 5000 });
37
+ // Pipe password via stdin NEVER pass it as an argv element,
38
+ // or it leaks into `ps aux` during the lifetime of the child process.
39
+ //
40
+ // From `man security`:
41
+ // "Use of the -p or -w options is insecure. Specify -w as the last
42
+ // option to be prompted."
43
+ //
44
+ // Requirements for this to work:
45
+ // 1. `-w` must be the LAST argument (otherwise security consumes
46
+ // the next argv element as the password).
47
+ // 2. The password must be written to stdin TWICE: once for the
48
+ // prompt, once for the "retype password" confirmation.
49
+ const result = spawnSync("security", [
50
+ "add-generic-password",
51
+ "-a", KEYCHAIN_ACCOUNT,
52
+ "-s", KEYCHAIN_SERVICE,
53
+ "-U",
54
+ "-w", // MUST be last — triggers interactive prompt reading from stdin
55
+ ], {
56
+ input: password + "\n" + password + "\n", // prompt + confirm
57
+ stdio: ["pipe", "pipe", "pipe"],
58
+ timeout: 5000,
59
+ });
60
+ if (result.status !== 0) {
61
+ const stderr = result.stderr?.toString().trim() || `exit ${result.status}`;
62
+ throw new Error(stderr);
63
+ }
38
64
  return { ok: true, method: "macOS Keychain" };
39
65
  }
40
66
  else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alvin-bot",
3
- "version": "4.4.2",
3
+ "version": "4.4.3",
4
4
  "description": "Alvin Bot — Your personal AI agent on Telegram, WhatsApp, Discord, Signal, and Web.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",