openclaw-plugin-vt-sentinel 0.12.0 → 0.12.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/CHANGELOG.md CHANGED
@@ -2,6 +2,30 @@
2
2
 
3
3
  All notable changes to `openclaw-plugin-vt-sentinel`.
4
4
 
5
+ ## 0.12.1 — Security audit collector: dual-path wiring
6
+
7
+ Fix for the collector introduced in 0.12.0. The in-gateway registration via
8
+ `api.registerSecurityAuditCollector` was insufficient: `openclaw security audit
9
+ --deep` runs in a fresh Node process that doesn't share state with the gateway
10
+ and reads collectors from the plugin's **module-level** `securityAuditCollectors`
11
+ field instead. Verified empirically on the production Linux VM (OpenClaw
12
+ 2026.4.12): 0.12.0 shipped with a runtime-only collector that emitted zero
13
+ `vt-sentinel.*` findings under `openclaw security audit --deep --json`.
14
+
15
+ ### Fixed
16
+
17
+ - **`securityAuditCollectors` declared at module level.** The default export
18
+ is now a plugin-definition object: `{ id, name, register, securityAuditCollectors }`.
19
+ The CLI audit picks up the collector from the object's field; the gateway
20
+ still registers it at runtime via `api.registerSecurityAuditCollector` for
21
+ any in-process audit surface that uses `getActivePluginRegistry()`. Both
22
+ paths now light up.
23
+ - **`vtSentinelAuditCollector` is self-contained.** It rebuilds the
24
+ compliance snapshot exclusively from `ctx.config`, `ctx.stateDir`, and
25
+ `ctx.configPath` — no closure state, no shared-module variables. Usable
26
+ from a cold-loaded plugin metadata snapshot.
27
+ - Runtime behavior (scanning, hooks, policies) unchanged.
28
+
5
29
  ## 0.12.0 — Transparency surface + log hygiene
6
30
 
7
31
  **Headline:** what VT Sentinel does at runtime is now auditable from two
@@ -103,3 +103,16 @@ export declare function collectLogModes(stateDir: string): LogModesInfo;
103
103
  * first and pass the result as `input.logModes`.
104
104
  */
105
105
  export declare function buildComplianceSnapshot(input: ComplianceSnapshotInput): ComplianceSnapshot;
106
+ interface AuditCollectorCtx {
107
+ config: any;
108
+ sourceConfig: any;
109
+ env: NodeJS.ProcessEnv;
110
+ stateDir: string;
111
+ configPath: string;
112
+ }
113
+ /**
114
+ * Static security-audit collector. Pass this to OpenClaw via the plugin's
115
+ * module-level default export (`securityAuditCollectors: [vtSentinelAuditCollector]`).
116
+ */
117
+ export declare function vtSentinelAuditCollector(ctx: AuditCollectorCtx): AuditFinding[];
118
+ export {};
@@ -53,8 +53,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
53
53
  exports.computePaths = computePaths;
54
54
  exports.collectLogModes = collectLogModes;
55
55
  exports.buildComplianceSnapshot = buildComplianceSnapshot;
56
+ exports.vtSentinelAuditCollector = vtSentinelAuditCollector;
56
57
  const fs = __importStar(require("fs"));
57
58
  const path = __importStar(require("path"));
59
+ const config_manager_1 = require("./config-manager");
58
60
  // --- Core paths helper (pure) ---
59
61
  function computePaths(stateDir) {
60
62
  const auditDir = path.join(stateDir, 'vt-sentinel-audit');
@@ -301,3 +303,80 @@ function buildComplianceSnapshot(input) {
301
303
  risks,
302
304
  };
303
305
  }
306
+ // --- Static (module-level) security audit collector ---
307
+ //
308
+ // The OpenClaw `security audit --deep` CLI runs in a fresh process that
309
+ // does NOT share state with the gateway. It therefore reads
310
+ // `securityAuditCollectors` from the plugin's module-level definition, NOT
311
+ // from runtime `api.registerSecurityAuditCollector(...)` calls.
312
+ //
313
+ // This function is the canonical collector used by the CLI. It derives the
314
+ // same snapshot as the gateway-side collector, but exclusively from the
315
+ // audit context (ctx.config, ctx.stateDir, ctx.configPath) — no closure
316
+ // state, no cross-module shared variables. Everything is reconstructable
317
+ // from what the CLI hands us.
318
+ //
319
+ // Differences from the gateway-side invocation:
320
+ // - `watchDirs` only includes user-configured dirs (`cfg.watchDirs`); the
321
+ // auto-derived dirs (tmp, ~/Downloads, etc.) are a runtime concept.
322
+ // - `credentialMode` is inferred from (a) presence of `cfg.apiKey` and
323
+ // (b) presence of the persisted agent credentials file on disk.
324
+ const PLUGIN_ID = 'openclaw-plugin-vt-sentinel';
325
+ function extractPluginConfig(cfg) {
326
+ try {
327
+ return cfg?.plugins?.entries?.[PLUGIN_ID]?.config ?? null;
328
+ }
329
+ catch {
330
+ return null;
331
+ }
332
+ }
333
+ function readAgentPublicHandle(stateDir) {
334
+ try {
335
+ const raw = fs.readFileSync(path.join(stateDir, 'vt-sentinel-agent.json'), 'utf-8');
336
+ const parsed = JSON.parse(raw);
337
+ return typeof parsed?.publicHandle === 'string' ? parsed.publicHandle : undefined;
338
+ }
339
+ catch {
340
+ return undefined;
341
+ }
342
+ }
343
+ function credentialsFileExists(stateDir) {
344
+ try {
345
+ return fs.statSync(path.join(stateDir, 'vt-sentinel-agent.json')).isFile();
346
+ }
347
+ catch {
348
+ return false;
349
+ }
350
+ }
351
+ /**
352
+ * Static security-audit collector. Pass this to OpenClaw via the plugin's
353
+ * module-level default export (`securityAuditCollectors: [vtSentinelAuditCollector]`).
354
+ */
355
+ function vtSentinelAuditCollector(ctx) {
356
+ try {
357
+ const staticCfg = extractPluginConfig(ctx.config) ?? extractPluginConfig(ctx.sourceConfig);
358
+ const cm = new config_manager_1.ConfigManager(staticCfg);
359
+ const eff = cm.getEffective();
360
+ const hasUserKey = typeof staticCfg?.['apiKey'] === 'string' && staticCfg['apiKey'].trim().length > 0;
361
+ const hasCachedVtai = credentialsFileExists(ctx.stateDir);
362
+ const credentialMode = hasUserKey ? 'user_key' : (hasCachedVtai ? 'vtai' : 'none');
363
+ const watchDirs = Array.isArray(eff.watchDirs) ? [...eff.watchDirs] : [];
364
+ const snap = buildComplianceSnapshot({
365
+ config: eff,
366
+ stateDir: ctx.stateDir,
367
+ credentialMode,
368
+ watchDirs,
369
+ agentPublicHandle: readAgentPublicHandle(ctx.stateDir),
370
+ logModes: collectLogModes(ctx.stateDir),
371
+ });
372
+ return [...snap.baseline, ...snap.risks];
373
+ }
374
+ catch (err) {
375
+ return [{
376
+ checkId: 'vt-sentinel.audit-collector-error',
377
+ severity: 'warn',
378
+ title: 'VT Sentinel compliance snapshot failed to build',
379
+ detail: `Collector threw while assembling audit findings: ${err?.message || String(err)}`,
380
+ }];
381
+ }
382
+ }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { SensitiveFilePolicy } from './scanner';
2
2
  import { getCurrentVersion } from './version';
3
3
  import { generateUpdateCommands } from './update-commands';
4
+ import { vtSentinelAuditCollector } from './compliance-snapshot';
4
5
  interface VTSentinelConfig {
5
6
  apiKey?: string;
6
7
  watchDirs?: string[];
@@ -77,10 +78,34 @@ declare function buildEnhancedBio(eff: {
77
78
  configPreset?: string;
78
79
  autoScan?: boolean;
79
80
  }): string;
80
- export default function vtSentinelPlugin(api: PluginApi): void;
81
+ declare function vtSentinelPlugin(api: PluginApi): void;
81
82
  export declare const _generateUpdateCommands: typeof generateUpdateCommands;
82
83
  export declare const _fetchLatestVersion: typeof fetchLatestVersion;
83
84
  export declare const _getCurrentVersion: typeof getCurrentVersion;
84
85
  export declare const _generateAgentName: typeof generateAgentName;
85
86
  export declare const _buildEnhancedBio: typeof buildEnhancedBio;
86
- export {};
87
+ /**
88
+ * Module-level plugin definition.
89
+ *
90
+ * `register(api)` runs inside the gateway — it's where the plugin wires up
91
+ * tools, hooks, the service, the runtime security-audit collector, and
92
+ * everything else that needs access to the live gateway API.
93
+ *
94
+ * `securityAuditCollectors` is read by `openclaw security audit --deep` in
95
+ * a FRESH Node process that does NOT share state with the gateway. That
96
+ * collector (`vtSentinelAuditCollector`) is self-contained: it rebuilds the
97
+ * compliance snapshot from ctx alone. Keeping it at module level is how
98
+ * plugin-declared findings land in `security audit` output.
99
+ *
100
+ * The dual-path wiring (runtime via `api.registerSecurityAuditCollector`
101
+ * inside `register`, static via `securityAuditCollectors` here) lets both
102
+ * the in-gateway audit flow and the out-of-process CLI flow surface the
103
+ * same data.
104
+ */
105
+ declare const _default: {
106
+ id: string;
107
+ name: string;
108
+ register: typeof vtSentinelPlugin;
109
+ securityAuditCollectors: (typeof vtSentinelAuditCollector)[];
110
+ };
111
+ export default _default;
package/dist/index.js CHANGED
@@ -39,7 +39,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports._buildEnhancedBio = exports._generateAgentName = exports._getCurrentVersion = exports._fetchLatestVersion = exports._generateUpdateCommands = void 0;
40
40
  exports.isNewerVersion = isNewerVersion;
41
41
  exports.isSelfPath = isSelfPath;
42
- exports.default = vtSentinelPlugin;
43
42
  const axios_1 = __importDefault(require("axios"));
44
43
  const chokidar = __importStar(require("chokidar"));
45
44
  const fs = __importStar(require("fs"));
@@ -1612,3 +1611,27 @@ exports._fetchLatestVersion = fetchLatestVersion;
1612
1611
  exports._getCurrentVersion = version_1.getCurrentVersion;
1613
1612
  exports._generateAgentName = generateAgentName;
1614
1613
  exports._buildEnhancedBio = buildEnhancedBio;
1614
+ /**
1615
+ * Module-level plugin definition.
1616
+ *
1617
+ * `register(api)` runs inside the gateway — it's where the plugin wires up
1618
+ * tools, hooks, the service, the runtime security-audit collector, and
1619
+ * everything else that needs access to the live gateway API.
1620
+ *
1621
+ * `securityAuditCollectors` is read by `openclaw security audit --deep` in
1622
+ * a FRESH Node process that does NOT share state with the gateway. That
1623
+ * collector (`vtSentinelAuditCollector`) is self-contained: it rebuilds the
1624
+ * compliance snapshot from ctx alone. Keeping it at module level is how
1625
+ * plugin-declared findings land in `security audit` output.
1626
+ *
1627
+ * The dual-path wiring (runtime via `api.registerSecurityAuditCollector`
1628
+ * inside `register`, static via `securityAuditCollectors` here) lets both
1629
+ * the in-gateway audit flow and the out-of-process CLI flow surface the
1630
+ * same data.
1631
+ */
1632
+ exports.default = {
1633
+ id: 'openclaw-plugin-vt-sentinel',
1634
+ name: 'VT Sentinel',
1635
+ register: vtSentinelPlugin,
1636
+ securityAuditCollectors: [compliance_snapshot_1.vtSentinelAuditCollector],
1637
+ };
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-plugin-vt-sentinel",
3
3
  "name": "VT Sentinel",
4
4
  "description": "VirusTotal Sentinel for OpenClaw — malware detection, active protection, and AI-powered code analysis.",
5
- "version": "0.12.0",
5
+ "version": "0.12.1",
6
6
  "skills": ["./skills"],
7
7
  "contracts": {
8
8
  "tools": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-plugin-vt-sentinel",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "displayName": "VT Sentinel",
5
5
  "description": "VirusTotal Sentinel for OpenClaw - Malware detection and AI-powered code analysis",
6
6
  "main": "dist/index.js",