openclaw-plugin-vt-sentinel 0.12.1 → 0.12.2

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,34 @@
2
2
 
3
3
  All notable changes to `openclaw-plugin-vt-sentinel`.
4
4
 
5
+ ## 0.12.2 — Audit collector accuracy + legacy log sanitization
6
+
7
+ Small follow-up to 0.12.1. The static collector could only see
8
+ user-configured watch dirs (never the runtime auto-derived ones like
9
+ `/tmp`, `~/Downloads`, OpenClaw state subdirs), so its auto-scan finding
10
+ read "Watching 0 directories" on fresh installs — technically correct but
11
+ misleading to operators.
12
+
13
+ ### Fixed
14
+
15
+ - **Auto-scan finding text reflects the snapshot source.** The
16
+ `buildComplianceSnapshot` helper now accepts `source: 'runtime' | 'static'`.
17
+ Runtime (gateway) callers still report the live watcher list.
18
+ Static (CLI audit) callers now spell out that additional dirs are
19
+ auto-derived by the gateway and point to `vt_sentinel_status` for the
20
+ live list.
21
+ - **Legacy audit-log paths mentioned in `vt_sentinel_help` updated.** The
22
+ help text used to say `~/.openclaw/vt-sentinel-uploads.log` and
23
+ `vt-sentinel-detections.log`; since 0.12.0 they live in
24
+ `<stateDir>/vt-sentinel-audit/{uploads,detections}.log`.
25
+ - **Legacy log files tightened to `0o600` on plugin load.** Pre-0.12.0
26
+ installs left the old stateDir-root log files (`vt-sentinel-uploads.log`,
27
+ `vt-sentinel-detections.log`) at the process default (typically `0o664`).
28
+ On each gateway start the plugin now best-effort `chmod 0o600`s them and
29
+ logs the change. POSIX-only; no-op on Windows.
30
+ - **`package-lock.json` regenerated to the new version** — the shipped
31
+ tarball never included it, but a stale lock on disk caused confusion.
32
+
5
33
  ## 0.12.1 — Security audit collector: dual-path wiring
6
34
 
7
35
  Fix for the collector introduced in 0.12.0. The in-gateway registration via
@@ -39,6 +39,16 @@ export interface ComplianceSnapshotInput {
39
39
  agentPublicHandle?: string;
40
40
  /** Optional: pre-collected log permission info (see `collectLogModes`). */
41
41
  logModes?: LogModesInfo;
42
+ /**
43
+ * Where the snapshot is being built from. `runtime` means the gateway is
44
+ * running and `watchDirs` reflects the live watcher state (including
45
+ * auto-derived dirs like `/tmp`, `~/Downloads`, OpenClaw state subdirs).
46
+ * `static` means the snapshot is being built by a fresh CLI process
47
+ * (e.g. `openclaw security audit --deep`) that cannot see auto-derived
48
+ * dirs — only the user-configured `config.watchDirs` entries.
49
+ * Defaults to `static`.
50
+ */
51
+ source?: 'runtime' | 'static';
42
52
  }
43
53
  export interface ComplianceSnapshot {
44
54
  credentialMode: CredentialMode;
@@ -133,6 +133,7 @@ function isBroadWatchDir(dir) {
133
133
  */
134
134
  function buildComplianceSnapshot(input) {
135
135
  const { config, stateDir, credentialMode, watchDirs, agentPublicHandle, logModes } = input;
136
+ const source = input.source ?? 'static';
136
137
  const paths = computePaths(stateDir);
137
138
  const identity = {
138
139
  displayNameSet: !!config.agentDisplayName,
@@ -163,13 +164,34 @@ function buildComplianceSnapshot(input) {
163
164
  (credentialMode === 'vtai' ? 'ai.virustotal.com (scan requests)\n' : '') +
164
165
  'registry.npmjs.org + clawhub.ai — only when the user explicitly invokes vt_sentinel_update.',
165
166
  });
167
+ // Auto-scan finding. Detail text branches on (a) whether autoScan is on
168
+ // and (b) whether we're building the snapshot at runtime (live watcher
169
+ // list available) or statically (CLI audit process — only config.watchDirs
170
+ // is visible; auto-derived dirs like /tmp/Downloads/Desktop/OpenClaw state
171
+ // subdirs are computed by the gateway at runtime and cannot be recovered
172
+ // here).
173
+ const autoScanDetail = (() => {
174
+ if (!config.autoScan) {
175
+ return `Active protection hooks remain registered (block mode: ${config.blockMode}) but no background watcher is running.`;
176
+ }
177
+ const suffix = ` block mode: ${config.blockMode}. notify level: ${config.notifyLevel}.`;
178
+ if (source === 'runtime') {
179
+ return `Watching ${watchDirs.length} director${watchDirs.length === 1 ? 'y' : 'ies'}.${suffix}`;
180
+ }
181
+ // Static/CLI snapshot.
182
+ if (watchDirs.length > 0) {
183
+ return `User-configured watch dirs (${watchDirs.length}): ${watchDirs.join(', ')}. ` +
184
+ `Additional dirs auto-derived at gateway runtime (OS temp, ~/Downloads, ~/Desktop, OpenClaw state subdirs, workspace). ` +
185
+ `Run vt_sentinel_status inside the gateway for the live list.${suffix}`;
186
+ }
187
+ return `No user-configured watch dirs. At runtime the gateway auto-derives monitors from OS temp, ~/Downloads, ~/Desktop, and the OpenClaw state subdirs (skills, extensions, hooks, workspace). ` +
188
+ `Run vt_sentinel_status inside the gateway for the live list.${suffix}`;
189
+ })();
166
190
  baseline.push({
167
191
  checkId: 'vt-sentinel.auto-scan',
168
192
  severity: 'info',
169
193
  title: `Auto-scan: ${config.autoScan ? 'enabled' : 'disabled'}`,
170
- detail: config.autoScan
171
- ? `Watching ${watchDirs.length} director${watchDirs.length === 1 ? 'y' : 'ies'}. block mode: ${config.blockMode}. notify level: ${config.notifyLevel}.`
172
- : `Active protection hooks remain registered (block mode: ${config.blockMode}) but no background watcher is running.`,
194
+ detail: autoScanDetail,
173
195
  });
174
196
  baseline.push({
175
197
  checkId: 'vt-sentinel.upload-policies',
package/dist/index.js CHANGED
@@ -359,6 +359,26 @@ function vtSentinelPlugin(api) {
359
359
  const logDir = (0, audit_log_1.getLogDir)(resolvedStateDir);
360
360
  const uploadLog = new audit_log_1.AuditLog(path.join(logDir, 'uploads.log'));
361
361
  const detectionLog = new audit_log_1.AuditLog(path.join(logDir, 'detections.log'));
362
+ // v0.12.2: pre-0.12.0 installs left `vt-sentinel-uploads.log` and
363
+ // `vt-sentinel-detections.log` at the stateDir root with the process
364
+ // umask (typically 0o664). Upgrading doesn't rewrite them. Best-effort
365
+ // tighten-to-0o600 on load so operators who upgraded in place don't
366
+ // keep world-readable audit history. POSIX-only — no-op on Windows.
367
+ if (process.platform !== 'win32') {
368
+ for (const legacyName of ['vt-sentinel-uploads.log', 'vt-sentinel-detections.log']) {
369
+ const legacyPath = path.join(resolvedStateDir, legacyName);
370
+ try {
371
+ if (fs.existsSync(legacyPath)) {
372
+ const mode = fs.statSync(legacyPath).mode & 0o777;
373
+ if (mode !== 0o600) {
374
+ fs.chmodSync(legacyPath, 0o600);
375
+ api.logger.info(`[VT-Sentinel] Tightened permissions on legacy audit log ${legacyPath} (0o${mode.toString(8)} → 0o600)`);
376
+ }
377
+ }
378
+ }
379
+ catch { /* best-effort */ }
380
+ }
381
+ }
362
382
  /** Log a scan result to the appropriate audit log(s). */
363
383
  const auditResult = (result) => {
364
384
  if (!result.sha256)
@@ -764,6 +784,7 @@ function vtSentinelPlugin(api) {
764
784
  watchDirs: [...watchRoots],
765
785
  agentPublicHandle: (0, vt_api_1.loadAgentCredentials)()?.publicHandle,
766
786
  logModes,
787
+ source: 'runtime',
767
788
  });
768
789
  return [...snap.baseline, ...snap.risks].map(f => ({
769
790
  checkId: f.checkId,
@@ -958,6 +979,7 @@ function vtSentinelPlugin(api) {
958
979
  watchDirs: [...watchRoots],
959
980
  agentPublicHandle: (0, vt_api_1.loadAgentCredentials)()?.publicHandle,
960
981
  logModes: (0, compliance_snapshot_1.collectLogModes)(resolvedStateDir),
982
+ source: 'runtime',
961
983
  });
962
984
  return textResponse((0, status_renderer_1.renderStatus)({
963
985
  version: (0, version_1.getCurrentVersion)(),
@@ -224,7 +224,7 @@ function renderHelp() {
224
224
  lines.push(' - Files read by the agent (read tool) are hash-checked only, never auto-uploaded.');
225
225
  lines.push(' - Session memory files are NEVER uploaded (privacy protection).');
226
226
  lines.push(' - autoScan=false disables hook scanning (active blocking remains on).');
227
- lines.push(' - Audit logs: ~/.openclaw/vt-sentinel-uploads.log + vt-sentinel-detections.log');
227
+ lines.push(' - Audit logs: <stateDir>/vt-sentinel-audit/uploads.log + detections.log (rotating; dir 0o700, files 0o600).');
228
228
  return lines.join('\n');
229
229
  }
230
230
  // --- Config Change Result ---
@@ -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.1",
5
+ "version": "0.12.2",
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.1",
3
+ "version": "0.12.2",
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",