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 +24 -0
- package/dist/compliance-snapshot.d.ts +13 -0
- package/dist/compliance-snapshot.js +79 -0
- package/dist/index.d.ts +27 -2
- package/dist/index.js +24 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
+
};
|
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.1",
|
|
6
6
|
"skills": ["./skills"],
|
|
7
7
|
"contracts": {
|
|
8
8
|
"tools": [
|
package/package.json
CHANGED