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 +28 -0
- package/dist/compliance-snapshot.d.ts +10 -0
- package/dist/compliance-snapshot.js +25 -3
- package/dist/index.js +22 -0
- package/dist/status-renderer.js +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
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:
|
|
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)(),
|
package/dist/status-renderer.js
CHANGED
|
@@ -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:
|
|
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 ---
|
package/openclaw.plugin.json
CHANGED
|
@@ -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.
|
|
5
|
+
"version": "0.12.2",
|
|
6
6
|
"skills": ["./skills"],
|
|
7
7
|
"contracts": {
|
|
8
8
|
"tools": [
|
package/package.json
CHANGED