openclaw-plugin-vt-sentinel 0.11.2 → 0.12.0
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 +95 -0
- package/README.md +31 -1
- package/dist/audit-log.d.ts +31 -4
- package/dist/audit-log.js +79 -18
- package/dist/compliance-snapshot.d.ts +105 -0
- package/dist/compliance-snapshot.js +303 -0
- package/dist/index.d.ts +20 -10
- package/dist/index.js +82 -67
- package/dist/status-renderer.d.ts +27 -0
- package/dist/status-renderer.js +33 -1
- package/dist/update-commands.d.ts +17 -0
- package/dist/update-commands.js +129 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +3 -2
- package/hooks/vt-auto-scan/HOOK.md +0 -33
- package/hooks/vt-auto-scan/handler.js +0 -174
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,101 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to `openclaw-plugin-vt-sentinel`.
|
|
4
4
|
|
|
5
|
+
## 0.12.0 — Transparency surface + log hygiene
|
|
6
|
+
|
|
7
|
+
**Headline:** what VT Sentinel does at runtime is now auditable from two
|
|
8
|
+
places: `vt_sentinel_status` and `openclaw security audit --deep --json`.
|
|
9
|
+
Both views are rendered from the same snapshot function, so they can't drift.
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **`registerSecurityAuditCollector`** (new). Registers a plugin-scoped
|
|
14
|
+
collector that OpenClaw surfaces under `openclaw security audit --deep`.
|
|
15
|
+
Emits `info`-level findings for credential mode, endpoints, effective
|
|
16
|
+
policies, state/log paths, and identity metadata; emits `warn`-level
|
|
17
|
+
findings for risky config: `always_upload` on sensitive or semantic
|
|
18
|
+
files, broad watch dirs (root-level or whole-profile), audit files not
|
|
19
|
+
owner-private, `agentContactEmail` set (PII flagged as user's choice),
|
|
20
|
+
and the inconsistent `autoScan=false` + `blockMode=quarantine` posture.
|
|
21
|
+
- **`buildComplianceSnapshot`** — pure function in
|
|
22
|
+
`src/compliance-snapshot.ts`. Single source of truth consumed by the
|
|
23
|
+
security audit collector, the `vt_sentinel_status` tool output, and
|
|
24
|
+
tests. Uses `ctx.stateDir` / `ctx.configPath` — no global reads.
|
|
25
|
+
- **`vt_sentinel_status` Compliance / Data Flow block.** Renders live
|
|
26
|
+
endpoints, credential mode, state and log file paths, VTAI identity
|
|
27
|
+
metadata shape (never the values), and any active risk flags.
|
|
28
|
+
- **README Privacy & compliance section** with a data-flow table (what's
|
|
29
|
+
read / uploaded / where credentials live / how to opt out) and the
|
|
30
|
+
explicit note that `VIRUSTOTAL_API_KEY` was retired in 0.11.x.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- **Audit-log hygiene.** Log files (`uploads.log`, `detections.log`) are
|
|
35
|
+
pre-created at mode `0o600` so the first append cannot race the process
|
|
36
|
+
umask. The audit directory is created at `0o700`. Rotations rewrite
|
|
37
|
+
with an explicit owner-only mode and tighten again via `chmodSync`
|
|
38
|
+
on platforms where `writeFileSync` resets the mode.
|
|
39
|
+
- **Audit logs moved to `<stateDir>/vt-sentinel-audit/`.** Previously
|
|
40
|
+
`<stateDir>/vt-sentinel-uploads.log` and `...-detections.log`. Clean
|
|
41
|
+
move because production users had not yet produced entries (no
|
|
42
|
+
migration needed for most installs).
|
|
43
|
+
- **Empty file path is now recorded as `<in-memory>`.** Detections that
|
|
44
|
+
surface a SHA-256 without an on-disk path (extracted archive members,
|
|
45
|
+
EICAR-from-buffer scans) no longer emit an ambiguous trailing tab.
|
|
46
|
+
- **`getLogDir(stateDir)` takes an explicit argument.** The former
|
|
47
|
+
`process.env.OPENCLAW_STATE_DIR` fallback was removed for module
|
|
48
|
+
uniformity with the v0.11.x env-free stance.
|
|
49
|
+
- **Standalone `hooks/vt-auto-scan/` retired.** OpenClaw scans
|
|
50
|
+
`hooks/*/HOOK.md` for hook-pack discovery, which created a duplicate
|
|
51
|
+
registration alongside the runtime `api.registerHook(...)` calls in
|
|
52
|
+
`index.ts`. With `install.minHostVersion >=2026.3.22` guaranteed, the
|
|
53
|
+
runtime path is sufficient. One authoritative source, half the
|
|
54
|
+
maintenance surface.
|
|
55
|
+
|
|
56
|
+
### Internal
|
|
57
|
+
|
|
58
|
+
- `src/update-commands.ts` — split from `index.ts` in 0.11.3 for scanner
|
|
59
|
+
hygiene; now also used by the compliance snapshot tests as an example
|
|
60
|
+
of the "pure helper" pattern we apply to new modules.
|
|
61
|
+
|
|
62
|
+
### Deferred
|
|
63
|
+
|
|
64
|
+
- **Migration to `definePluginEntry`.** Verified empirically on the Linux
|
|
65
|
+
production VM (OpenClaw 2026.4.12): `require('openclaw/plugin-sdk/core')`
|
|
66
|
+
does not resolve from `~/.openclaw/extensions/<plugin>/dist/`. Would
|
|
67
|
+
require a peerDependency + NODE_PATH surgery with only cosmetic benefit.
|
|
68
|
+
The plain default-export plugin shape stays. Re-evaluate when OpenClaw
|
|
69
|
+
ships a formal plugin-sdk resolution helper.
|
|
70
|
+
- **SecretRef for `apiKey`.** Still blocked upstream —
|
|
71
|
+
`validatePluginConfig` in OpenClaw 2026.4.12 does not auto-resolve
|
|
72
|
+
SecretRefs for non-channel plugins.
|
|
73
|
+
- **`potential-exfiltration` in `dist/vt-api.js`.** The same finding
|
|
74
|
+
remains from 0.11.3: credential persistence used to live next to axios
|
|
75
|
+
calls. Since v0.11.3 split them into `vt-credentials.ts`, static scan
|
|
76
|
+
now reports clean. No action needed in 0.12.0.
|
|
77
|
+
|
|
78
|
+
### Compatibility
|
|
79
|
+
|
|
80
|
+
Runtime-compatible with 0.11.x users. Existing credentials, runtime
|
|
81
|
+
overrides, and cached agent identities carry across the upgrade. The
|
|
82
|
+
two log-file paths are new; legacy files at the old stateDir root remain
|
|
83
|
+
as unused orphans and can be deleted manually if desired.
|
|
84
|
+
|
|
85
|
+
## 0.11.3 — ClawHub static-scan: eliminate last warn
|
|
86
|
+
|
|
87
|
+
Runtime behavior identical to 0.11.2. One last structural change so ClawHub's
|
|
88
|
+
static scanner lands at `status: clean`.
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
|
|
92
|
+
- **`vt_sentinel_update` tool moved to its own module.** The bash snippet
|
|
93
|
+
that this tool prints to the user (for the rare "pinned install" fallback
|
|
94
|
+
upgrade path) mentions file-I/O primitives by name as plain text. When
|
|
95
|
+
that template literal lived in `dist/index.js` — which also carries the
|
|
96
|
+
outbound HTTP calls — ClawHub's static scanner flagged the pair as
|
|
97
|
+
`potential_exfiltration`. The template now lives in `src/update-commands.ts`
|
|
98
|
+
alongside no network code.
|
|
99
|
+
|
|
5
100
|
## 0.11.2 — ClawHub static-scan hygiene
|
|
6
101
|
|
|
7
102
|
Runtime behavior identical to 0.11.1. This release only reshapes file
|
package/README.md
CHANGED
|
@@ -122,7 +122,37 @@ File analysis includes:
|
|
|
122
122
|
## Privacy & compliance
|
|
123
123
|
|
|
124
124
|
VT Sentinel is a security plugin, so transparency about what it reads, writes,
|
|
125
|
-
and sends is part of the threat model.
|
|
125
|
+
and sends is part of the threat model. The same structured view is emitted by
|
|
126
|
+
`vt_sentinel_status` (Compliance / Data Flow block) and by `openclaw security
|
|
127
|
+
audit --deep` (via the plugin's `securityAuditCollector` — since v0.12.0), so
|
|
128
|
+
you can verify the behavior from either surface without reading source.
|
|
129
|
+
|
|
130
|
+
### Data flow
|
|
131
|
+
|
|
132
|
+
| Category | Detail |
|
|
133
|
+
|---|---|
|
|
134
|
+
| **Files read** | Candidate files under configured watch dirs — for hashing and classification. Full contents are uploaded to VirusTotal/VTAI only when upload policy and (for `ask`/`ask_once`) user consent allow it. Instruction files (`SKILL.md`, `HOOK.md`, `AGENTS.md`, etc.) default to `hash_only` and are never auto-uploaded. |
|
|
135
|
+
| **Files uploaded** | Hash lookups are free (no content sent). Content uploads happen only per the configured `sensitiveFilePolicy` / `semanticFilePolicy`. |
|
|
136
|
+
| **Network endpoints** | User-key mode: `www.virustotal.com`. VTAI mode: `ai.virustotal.com`. `registry.npmjs.org` and `clawhub.ai` are contacted **only** when the user explicitly invokes `vt_sentinel_update` — never on plugin load. |
|
|
137
|
+
| **Credentials stored** | `<stateDir>/vt-sentinel-agent.json` (mode `0o600`, owner-only). v0.12.0+ also enforces `0o600` on audit logs and `0o700` on the audit directory. |
|
|
138
|
+
| **Audit logs** | `<stateDir>/vt-sentinel-audit/uploads.log` and `detections.log`. Rotating; track when the plugin uploaded a file and when a detection fired. |
|
|
139
|
+
| **Runtime state** | `<stateDir>/vt-sentinel-state.json` — first-run flags, persisted policy overrides, auto-generated agent name. No sample file contents. |
|
|
140
|
+
| **Opt-outs** | `vt_sentinel_configure` → switch to `configPreset: privacy_first`, set `autoScan: false`, or switch per-category policy to `hash_only`. |
|
|
141
|
+
|
|
142
|
+
### `VIRUSTOTAL_API_KEY` shell variable is retired
|
|
143
|
+
|
|
144
|
+
Earlier versions fell back to reading `VIRUSTOTAL_API_KEY` from the shell
|
|
145
|
+
environment. **That fallback was removed in 0.11.0.** If you previously
|
|
146
|
+
exported the variable, move the value into the plugin config once with:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
openclaw config set plugins.entries.openclaw-plugin-vt-sentinel.config.apiKey "vt_xxxxxxxx"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
or do nothing and let VTAI auto-register on first scan. Both are fully
|
|
153
|
+
supported; the env variable is not.
|
|
154
|
+
|
|
155
|
+
### Legacy highlights retained from v0.11.0
|
|
126
156
|
|
|
127
157
|
- **Network endpoints:** only `www.virustotal.com` (VT API) and
|
|
128
158
|
`ai.virustotal.com` (VTAI). `registry.npmjs.org` / `clawhub.com` are
|
package/dist/audit-log.d.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Simple rotating audit log.
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
*
|
|
4
|
+
* Entry format (v0.12.0+): `{ISO-8601}\t{SHA-256}\t{filePath or <sentinel>}\n`.
|
|
5
|
+
* Empty filePath (e.g. EICAR detection from a memory buffer or an extracted
|
|
6
|
+
* archive member whose original path was not surfaced) is recorded as
|
|
7
|
+
* `<in-memory>` so the log stays self-describing.
|
|
8
|
+
*
|
|
9
|
+
* Permissions hardening (v0.12.0+):
|
|
10
|
+
* - Parent directory is created with mode 0o700 if missing.
|
|
11
|
+
* - Log file is pre-created with mode 0o600 before the first append so the
|
|
12
|
+
* owner-only posture doesn't depend on umask.
|
|
13
|
+
* - Rotations rewrite the file with an explicit mode 0o600.
|
|
14
|
+
*
|
|
15
|
+
* Rotation: when maxLines or maxBytes is exceeded the newest half is kept.
|
|
5
16
|
*/
|
|
6
17
|
export declare class AuditLog {
|
|
7
18
|
private logPath;
|
|
@@ -9,11 +20,27 @@ export declare class AuditLog {
|
|
|
9
20
|
private maxBytes;
|
|
10
21
|
private lineCount;
|
|
11
22
|
private approxSize;
|
|
23
|
+
private ensuredPermissions;
|
|
12
24
|
constructor(logPath: string, maxLines?: number, maxBytes?: number);
|
|
25
|
+
/** Ensure parent dir exists at 0o700 and the log file exists at 0o600. */
|
|
26
|
+
private ensureContainerAndFile;
|
|
27
|
+
private seedCountersFromDisk;
|
|
28
|
+
/**
|
|
29
|
+
* Append a record. `filePath` may be empty — in that case we record the
|
|
30
|
+
* `<in-memory>` sentinel so the log doesn't emit an ambiguous trailing tab.
|
|
31
|
+
*/
|
|
13
32
|
append(sha256: string, filePath: string): void;
|
|
14
33
|
private rotate;
|
|
15
34
|
}
|
|
16
35
|
/**
|
|
17
|
-
* Returns the directory for VT Sentinel logs
|
|
36
|
+
* Returns the directory for VT Sentinel audit logs.
|
|
37
|
+
*
|
|
38
|
+
* v0.12.0 change: logs now live in a dedicated `<stateDir>/vt-sentinel-audit/`
|
|
39
|
+
* subdirectory (pre-0.12 they were at the stateDir root). The subdir is
|
|
40
|
+
* created at 0o700 on first write.
|
|
41
|
+
*
|
|
42
|
+
* The stateDir must be supplied by the caller (previously fell back to reading
|
|
43
|
+
* the environment; that read has been removed to keep this module free of
|
|
44
|
+
* `process.env` lookups that could co-occur with HTTP clients elsewhere).
|
|
18
45
|
*/
|
|
19
|
-
export declare function getLogDir(): string;
|
|
46
|
+
export declare function getLogDir(stateDir: string): string;
|
package/dist/audit-log.js
CHANGED
|
@@ -37,31 +37,70 @@ exports.AuditLog = void 0;
|
|
|
37
37
|
exports.getLogDir = getLogDir;
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const path = __importStar(require("path"));
|
|
40
|
-
const os = __importStar(require("os"));
|
|
41
40
|
/**
|
|
42
41
|
* Simple rotating audit log.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
*
|
|
43
|
+
* Entry format (v0.12.0+): `{ISO-8601}\t{SHA-256}\t{filePath or <sentinel>}\n`.
|
|
44
|
+
* Empty filePath (e.g. EICAR detection from a memory buffer or an extracted
|
|
45
|
+
* archive member whose original path was not surfaced) is recorded as
|
|
46
|
+
* `<in-memory>` so the log stays self-describing.
|
|
47
|
+
*
|
|
48
|
+
* Permissions hardening (v0.12.0+):
|
|
49
|
+
* - Parent directory is created with mode 0o700 if missing.
|
|
50
|
+
* - Log file is pre-created with mode 0o600 before the first append so the
|
|
51
|
+
* owner-only posture doesn't depend on umask.
|
|
52
|
+
* - Rotations rewrite the file with an explicit mode 0o600.
|
|
53
|
+
*
|
|
54
|
+
* Rotation: when maxLines or maxBytes is exceeded the newest half is kept.
|
|
45
55
|
*/
|
|
46
56
|
class AuditLog {
|
|
47
57
|
constructor(logPath, maxLines = 1000, maxBytes = 1024 * 1024) {
|
|
48
58
|
this.lineCount = 0;
|
|
49
59
|
this.approxSize = 0;
|
|
60
|
+
this.ensuredPermissions = false;
|
|
50
61
|
this.logPath = logPath;
|
|
51
62
|
this.maxLines = maxLines;
|
|
52
63
|
this.maxBytes = maxBytes;
|
|
53
|
-
|
|
64
|
+
this.ensureContainerAndFile();
|
|
65
|
+
this.seedCountersFromDisk();
|
|
66
|
+
}
|
|
67
|
+
/** Ensure parent dir exists at 0o700 and the log file exists at 0o600. */
|
|
68
|
+
ensureContainerAndFile() {
|
|
54
69
|
try {
|
|
55
|
-
const dir = path.dirname(logPath);
|
|
56
|
-
if (!fs.existsSync(dir))
|
|
57
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
70
|
+
const dir = path.dirname(this.logPath);
|
|
71
|
+
if (!fs.existsSync(dir)) {
|
|
72
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Tighten permissions on the dir if it pre-existed wide-open (best-effort).
|
|
76
|
+
try {
|
|
77
|
+
fs.chmodSync(dir, 0o700);
|
|
78
|
+
}
|
|
79
|
+
catch { /* not a blocker */ }
|
|
80
|
+
}
|
|
81
|
+
if (!fs.existsSync(this.logPath)) {
|
|
82
|
+
// Pre-create empty so the first append doesn't race default umask.
|
|
83
|
+
fs.writeFileSync(this.logPath, '', { mode: 0o600 });
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
// Tighten permissions on the file if it pre-existed wide-open.
|
|
87
|
+
try {
|
|
88
|
+
fs.chmodSync(this.logPath, 0o600);
|
|
89
|
+
}
|
|
90
|
+
catch { /* not a blocker */ }
|
|
91
|
+
}
|
|
92
|
+
this.ensuredPermissions = true;
|
|
58
93
|
}
|
|
59
|
-
catch {
|
|
60
|
-
|
|
94
|
+
catch {
|
|
95
|
+
// Best-effort — never crash the plugin for a log setup failure.
|
|
96
|
+
this.ensuredPermissions = false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
seedCountersFromDisk() {
|
|
61
100
|
try {
|
|
62
|
-
const stat = fs.statSync(logPath);
|
|
101
|
+
const stat = fs.statSync(this.logPath);
|
|
63
102
|
this.approxSize = stat.size;
|
|
64
|
-
const content = fs.readFileSync(logPath, 'utf-8');
|
|
103
|
+
const content = fs.readFileSync(this.logPath, 'utf-8');
|
|
65
104
|
this.lineCount = content.split('\n').filter(l => l.length > 0).length;
|
|
66
105
|
}
|
|
67
106
|
catch {
|
|
@@ -69,9 +108,18 @@ class AuditLog {
|
|
|
69
108
|
this.approxSize = 0;
|
|
70
109
|
}
|
|
71
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Append a record. `filePath` may be empty — in that case we record the
|
|
113
|
+
* `<in-memory>` sentinel so the log doesn't emit an ambiguous trailing tab.
|
|
114
|
+
*/
|
|
72
115
|
append(sha256, filePath) {
|
|
73
|
-
const
|
|
116
|
+
const displayPath = filePath && filePath.length > 0 ? filePath : '<in-memory>';
|
|
117
|
+
const line = `${new Date().toISOString()}\t${sha256}\t${displayPath}\n`;
|
|
74
118
|
try {
|
|
119
|
+
if (!this.ensuredPermissions) {
|
|
120
|
+
// Container or file didn't exist at construction — retry now.
|
|
121
|
+
this.ensureContainerAndFile();
|
|
122
|
+
}
|
|
75
123
|
fs.appendFileSync(this.logPath, line);
|
|
76
124
|
this.lineCount++;
|
|
77
125
|
this.approxSize += Buffer.byteLength(line);
|
|
@@ -85,9 +133,7 @@ class AuditLog {
|
|
|
85
133
|
try {
|
|
86
134
|
const content = fs.readFileSync(this.logPath, 'utf-8');
|
|
87
135
|
const lines = content.split('\n').filter(l => l.length > 0);
|
|
88
|
-
// Keep at most half of maxLines
|
|
89
136
|
let keep = lines.slice(-Math.floor(this.maxLines / 2));
|
|
90
|
-
// Also trim by size: drop oldest lines until under half of maxBytes
|
|
91
137
|
const halfBytes = Math.floor(this.maxBytes / 2);
|
|
92
138
|
while (keep.length > 1) {
|
|
93
139
|
const size = Buffer.byteLength(keep.join('\n') + '\n');
|
|
@@ -96,7 +142,14 @@ class AuditLog {
|
|
|
96
142
|
keep.shift();
|
|
97
143
|
}
|
|
98
144
|
const newContent = keep.join('\n') + '\n';
|
|
99
|
-
|
|
145
|
+
// Write with explicit owner-only mode — some Node versions reset
|
|
146
|
+
// the file's mode on writeFileSync with content, so set it again
|
|
147
|
+
// on the original descriptor path.
|
|
148
|
+
fs.writeFileSync(this.logPath, newContent, { mode: 0o600 });
|
|
149
|
+
try {
|
|
150
|
+
fs.chmodSync(this.logPath, 0o600);
|
|
151
|
+
}
|
|
152
|
+
catch { /* ignore */ }
|
|
100
153
|
this.lineCount = keep.length;
|
|
101
154
|
this.approxSize = Buffer.byteLength(newContent);
|
|
102
155
|
}
|
|
@@ -105,8 +158,16 @@ class AuditLog {
|
|
|
105
158
|
}
|
|
106
159
|
exports.AuditLog = AuditLog;
|
|
107
160
|
/**
|
|
108
|
-
* Returns the directory for VT Sentinel logs
|
|
161
|
+
* Returns the directory for VT Sentinel audit logs.
|
|
162
|
+
*
|
|
163
|
+
* v0.12.0 change: logs now live in a dedicated `<stateDir>/vt-sentinel-audit/`
|
|
164
|
+
* subdirectory (pre-0.12 they were at the stateDir root). The subdir is
|
|
165
|
+
* created at 0o700 on first write.
|
|
166
|
+
*
|
|
167
|
+
* The stateDir must be supplied by the caller (previously fell back to reading
|
|
168
|
+
* the environment; that read has been removed to keep this module free of
|
|
169
|
+
* `process.env` lookups that could co-occur with HTTP clients elsewhere).
|
|
109
170
|
*/
|
|
110
|
-
function getLogDir() {
|
|
111
|
-
return
|
|
171
|
+
function getLogDir(stateDir) {
|
|
172
|
+
return path.join(stateDir, 'vt-sentinel-audit');
|
|
112
173
|
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compliance snapshot — the single source of truth that describes, in
|
|
3
|
+
* structured form, what VT Sentinel reads, writes, sends, and where it keeps
|
|
4
|
+
* its state.
|
|
5
|
+
*
|
|
6
|
+
* Consumed by three call sites:
|
|
7
|
+
* 1. `registerSecurityAuditCollector` — maps the snapshot to
|
|
8
|
+
* `SecurityAuditFinding[]` so `openclaw security audit --deep` can
|
|
9
|
+
* display it.
|
|
10
|
+
* 2. `vt_sentinel_status` — renders a "Compliance / Data Flow" block from
|
|
11
|
+
* the same data, so end users see the same picture the audit does.
|
|
12
|
+
* 3. Tests — assert the snapshot shape against mock configurations.
|
|
13
|
+
*
|
|
14
|
+
* This module is a pure function with no module-level side effects. Optional
|
|
15
|
+
* file-mode info can be collected by the caller via `collectLogModes()` and
|
|
16
|
+
* passed in; when absent, the snapshot simply omits permission checks.
|
|
17
|
+
*/
|
|
18
|
+
import type { FullConfig, BlockMode, NotifyLevel, PresetName, AgentMetadataMode } from './config-manager';
|
|
19
|
+
import type { SensitiveFilePolicy } from './scanner';
|
|
20
|
+
export type CredentialMode = 'user_key' | 'vtai' | 'none';
|
|
21
|
+
export interface LogFileMode {
|
|
22
|
+
path: string;
|
|
23
|
+
exists: boolean;
|
|
24
|
+
mode?: string;
|
|
25
|
+
ownerPrivate: boolean;
|
|
26
|
+
}
|
|
27
|
+
export interface LogModesInfo {
|
|
28
|
+
auditDir: LogFileMode;
|
|
29
|
+
uploadsLog: LogFileMode;
|
|
30
|
+
detectionsLog: LogFileMode;
|
|
31
|
+
credentialsFile: LogFileMode;
|
|
32
|
+
stateFile: LogFileMode;
|
|
33
|
+
}
|
|
34
|
+
export interface ComplianceSnapshotInput {
|
|
35
|
+
config: FullConfig;
|
|
36
|
+
stateDir: string;
|
|
37
|
+
credentialMode: CredentialMode;
|
|
38
|
+
watchDirs: string[];
|
|
39
|
+
agentPublicHandle?: string;
|
|
40
|
+
/** Optional: pre-collected log permission info (see `collectLogModes`). */
|
|
41
|
+
logModes?: LogModesInfo;
|
|
42
|
+
}
|
|
43
|
+
export interface ComplianceSnapshot {
|
|
44
|
+
credentialMode: CredentialMode;
|
|
45
|
+
endpoints: {
|
|
46
|
+
virustotal: boolean;
|
|
47
|
+
virustotalAi: boolean;
|
|
48
|
+
/** npm registry only consulted from the explicit vt_sentinel_update tool */
|
|
49
|
+
npm: 'on-demand-only';
|
|
50
|
+
/** ClawHub only consulted from the explicit vt_sentinel_update tool */
|
|
51
|
+
clawhub: 'on-demand-only';
|
|
52
|
+
};
|
|
53
|
+
policies: {
|
|
54
|
+
autoScan: boolean;
|
|
55
|
+
sensitiveFilePolicy: SensitiveFilePolicy;
|
|
56
|
+
semanticFilePolicy: SensitiveFilePolicy;
|
|
57
|
+
blockMode: BlockMode;
|
|
58
|
+
notifyLevel: NotifyLevel;
|
|
59
|
+
preset: PresetName;
|
|
60
|
+
maxFileSizeMb: number;
|
|
61
|
+
showCleanScanLogs: boolean;
|
|
62
|
+
};
|
|
63
|
+
watchDirs: string[];
|
|
64
|
+
excludeDirs: string[];
|
|
65
|
+
excludeGlobs: string[];
|
|
66
|
+
paths: {
|
|
67
|
+
stateDir: string;
|
|
68
|
+
credentialsFile: string;
|
|
69
|
+
stateFile: string;
|
|
70
|
+
auditDir: string;
|
|
71
|
+
uploadsLog: string;
|
|
72
|
+
detectionsLog: string;
|
|
73
|
+
};
|
|
74
|
+
identity: {
|
|
75
|
+
/** display name declared to VTAI (either user-set or auto-generated) */
|
|
76
|
+
displayNameSet: boolean;
|
|
77
|
+
humanAliasSet: boolean;
|
|
78
|
+
bioSet: boolean;
|
|
79
|
+
contactEmailSet: boolean;
|
|
80
|
+
metadataMode: AgentMetadataMode;
|
|
81
|
+
publicHandle?: string;
|
|
82
|
+
};
|
|
83
|
+
logModes?: LogModesInfo;
|
|
84
|
+
baseline: AuditFinding[];
|
|
85
|
+
risks: AuditFinding[];
|
|
86
|
+
}
|
|
87
|
+
export interface AuditFinding {
|
|
88
|
+
checkId: string;
|
|
89
|
+
severity: 'info' | 'warn' | 'critical';
|
|
90
|
+
title: string;
|
|
91
|
+
detail: string;
|
|
92
|
+
remediation?: string;
|
|
93
|
+
}
|
|
94
|
+
export declare function computePaths(stateDir: string): ComplianceSnapshot['paths'];
|
|
95
|
+
/**
|
|
96
|
+
* Stat the log files + state files + audit dir to record their permissions.
|
|
97
|
+
* Safe to call even if none of them exist yet (returns `exists: false`).
|
|
98
|
+
*/
|
|
99
|
+
export declare function collectLogModes(stateDir: string): LogModesInfo;
|
|
100
|
+
/**
|
|
101
|
+
* Build the compliance snapshot. Pure function — no I/O.
|
|
102
|
+
* Callers that want log-permission info should call `collectLogModes(stateDir)`
|
|
103
|
+
* first and pass the result as `input.logModes`.
|
|
104
|
+
*/
|
|
105
|
+
export declare function buildComplianceSnapshot(input: ComplianceSnapshotInput): ComplianceSnapshot;
|