openclaw-plugin-vt-sentinel 0.12.1 → 0.12.3

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,14 +2,74 @@
2
2
 
3
3
  All notable changes to `openclaw-plugin-vt-sentinel`.
4
4
 
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/).
7
+
8
+ ## 0.12.3 — Dependency hygiene + OSS polish
9
+
10
+ No runtime behavior changes. Pure housekeeping release.
11
+
12
+ ### Security
13
+
14
+ - Bumped `axios` past GHSA-3p68-rc4w-qgx5 and GHSA-fvcv-3m26-pcqx (critical).
15
+ - Bumped `follow-redirects` past GHSA-r4q5-vmmm-2653 (moderate, header leak
16
+ on cross-domain redirects).
17
+ - Bumped `picomatch` past GHSA-3v7f-55p6-f55p and GHSA-c2c7-rcm5-vvqj
18
+ (high, method injection + ReDoS).
19
+ - `npm audit --package-lock-only` now reports `found 0 vulnerabilities`.
20
+
21
+ ### Added
22
+
23
+ - `LICENSE` (MIT).
24
+ - `SECURITY.md` with private vulnerability reporting process.
25
+ - `CONTRIBUTING.md` with build/test/release workflow.
26
+ - `CODE_OF_CONDUCT.md` (Contributor Covenant 2.1).
27
+ - `engines.node` declaration (`">=18"`) in `package.json`.
28
+
29
+ ### Changed
30
+
31
+ - `package.json`: corrected `homepage` to point at the repository (was
32
+ pointing at the VTAI backend service), added `author` and refreshed
33
+ `openclaw.build.openclawVersion`.
34
+ - `README.md`: fixed a stale reference to the ClawHub host.
35
+ - Changelog now follows Keep a Changelog headings.
36
+
37
+ ## 0.12.2 — Audit collector accuracy + legacy log sanitization
38
+
39
+ Small follow-up to 0.12.1. The static collector could only see
40
+ user-configured watch dirs (never the runtime auto-derived ones like
41
+ `/tmp`, `~/Downloads`, OpenClaw state subdirs), so its auto-scan finding
42
+ read "Watching 0 directories" on fresh installs — technically correct but
43
+ misleading to operators.
44
+
45
+ ### Fixed
46
+
47
+ - **Auto-scan finding text reflects the snapshot source.** The
48
+ `buildComplianceSnapshot` helper now accepts `source: 'runtime' | 'static'`.
49
+ Runtime (gateway) callers still report the live watcher list.
50
+ Static (CLI audit) callers now spell out that additional dirs are
51
+ auto-derived by the gateway and point to `vt_sentinel_status` for the
52
+ live list.
53
+ - **Legacy audit-log paths mentioned in `vt_sentinel_help` updated.** The
54
+ help text used to say `~/.openclaw/vt-sentinel-uploads.log` and
55
+ `vt-sentinel-detections.log`; since 0.12.0 they live in
56
+ `<stateDir>/vt-sentinel-audit/{uploads,detections}.log`.
57
+ - **Legacy log files tightened to `0o600` on plugin load.** Pre-0.12.0
58
+ installs left the old stateDir-root log files (`vt-sentinel-uploads.log`,
59
+ `vt-sentinel-detections.log`) at the process default (typically `0o664`).
60
+ On each gateway start the plugin now best-effort `chmod 0o600`s them and
61
+ logs the change. POSIX-only; no-op on Windows.
62
+ - **`package-lock.json` regenerated to the new version** — the shipped
63
+ tarball never included it, but a stale lock on disk caused confusion.
64
+
5
65
  ## 0.12.1 — Security audit collector: dual-path wiring
6
66
 
7
67
  Fix for the collector introduced in 0.12.0. The in-gateway registration via
8
68
  `api.registerSecurityAuditCollector` was insufficient: `openclaw security audit
9
69
  --deep` runs in a fresh Node process that doesn't share state with the gateway
10
70
  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
71
+ field instead. Verified on an integration environment (OpenClaw 2026.4.12):
72
+ 0.12.0 shipped with a runtime-only collector that emitted zero
13
73
  `vt-sentinel.*` findings under `openclaw security audit --deep --json`.
14
74
 
15
75
  ### Fixed
@@ -77,16 +137,16 @@ Both views are rendered from the same snapshot function, so they can't drift.
77
137
  runtime path is sufficient. One authoritative source, half the
78
138
  maintenance surface.
79
139
 
80
- ### Internal
140
+ ### Implementation notes
81
141
 
82
142
  - `src/update-commands.ts` — split from `index.ts` in 0.11.3 for scanner
83
143
  hygiene; now also used by the compliance snapshot tests as an example
84
- of the "pure helper" pattern we apply to new modules.
144
+ of the pure-helper pattern applied across the codebase.
85
145
 
86
146
  ### Deferred
87
147
 
88
- - **Migration to `definePluginEntry`.** Verified empirically on the Linux
89
- production VM (OpenClaw 2026.4.12): `require('openclaw/plugin-sdk/core')`
148
+ - **Migration to `definePluginEntry`.** Verified empirically on OpenClaw
149
+ 2026.4.12 integration: `require('openclaw/plugin-sdk/core')`
90
150
  does not resolve from `~/.openclaw/extensions/<plugin>/dist/`. Would
91
151
  require a peerDependency + NODE_PATH surgery with only cosmetic benefit.
92
152
  The plain default-export plugin shape stays. Re-evaluate when OpenClaw
@@ -279,7 +339,7 @@ scan, which requires no key. Both paths are fully supported.
279
339
 
280
340
  ## Earlier history
281
341
 
282
- See git log and `memory/v27-bugfixes.md` for details on 0.5.0 through 0.10.0.
342
+ See git log for details on 0.5.0 through 0.10.0.
283
343
  Highlights:
284
344
 
285
345
  - **0.10.0** — SEMANTIC_RISK files (SKILL.md, HOOK.md, AGENTS.md) route
@@ -0,0 +1,53 @@
1
+ # Contributor Code of Conduct
2
+
3
+ This project follows the
4
+ [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
5
+
6
+ ## Our Pledge
7
+
8
+ We as members, contributors, and maintainers pledge to make participation in
9
+ this project a harassment-free experience for everyone, regardless of age,
10
+ body size, visible or invisible disability, ethnicity, sex characteristics,
11
+ gender identity and expression, level of experience, education, socio-economic
12
+ status, nationality, personal appearance, race, religion, or sexual identity
13
+ and orientation. We pledge to act and interact in ways that contribute to an
14
+ open, welcoming, diverse, inclusive, and healthy community.
15
+
16
+ ## Our Standards
17
+
18
+ Examples of behavior that contributes to a positive environment include:
19
+
20
+ - Demonstrating empathy and kindness toward other people.
21
+ - Being respectful of differing opinions, viewpoints, and experiences.
22
+ - Giving and gracefully accepting constructive feedback.
23
+ - Accepting responsibility, apologizing to those affected by our mistakes,
24
+ and learning from the experience.
25
+ - Focusing on what is best for the overall community.
26
+
27
+ Examples of unacceptable behavior:
28
+
29
+ - The use of sexualized language or imagery, and sexual attention or advances
30
+ of any kind.
31
+ - Trolling, insulting or derogatory comments, and personal or political
32
+ attacks.
33
+ - Public or private harassment.
34
+ - Publishing others' private information without their explicit permission.
35
+ - Other conduct which could reasonably be considered inappropriate in a
36
+ professional setting.
37
+
38
+ ## Enforcement Responsibilities
39
+
40
+ Project maintainers are responsible for clarifying and enforcing our
41
+ standards of acceptable behavior and will take appropriate and fair
42
+ corrective action in response to any behavior that they deem inappropriate,
43
+ threatening, offensive, or harmful.
44
+
45
+ ## Enforcement
46
+
47
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
48
+ reported to the project maintainer via the contact method on
49
+ `https://github.com/king-tero`. All complaints will be reviewed and
50
+ investigated promptly and fairly.
51
+
52
+ Community Impact Guidelines:
53
+ <https://www.contributor-covenant.org/version/2/1/code_of_conduct/#enforcement-guidelines>
@@ -0,0 +1,63 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in VT Sentinel. This guide covers the basics for
4
+ reporting issues, submitting patches, and building the plugin locally.
5
+
6
+ ## Reporting issues
7
+
8
+ - Non-security bugs and feature requests: open a GitHub issue.
9
+ - Security vulnerabilities: see [`SECURITY.md`](./SECURITY.md).
10
+
11
+ When filing a bug, please include:
12
+
13
+ - VT Sentinel version (`openclaw plugins inspect openclaw-plugin-vt-sentinel`).
14
+ - OpenClaw version (`openclaw --version`).
15
+ - OS and Node.js version.
16
+ - The output of `openclaw security audit --deep --json` filtered to
17
+ `vt-sentinel.*` findings, if relevant.
18
+
19
+ ## Building locally
20
+
21
+ Requirements: Node.js >= 18, npm, TypeScript.
22
+
23
+ ```bash
24
+ git clone https://github.com/king-tero/VT-sentinel.git
25
+ cd VT-sentinel
26
+ npm install
27
+ npm run build
28
+ npm test
29
+ npm run scan
30
+ ```
31
+
32
+ `npm run scan` runs a local re-implementation of OpenClaw's install-security
33
+ scanner against the compiled `dist/`. A clean baseline is `0 critical, 0 warn`.
34
+ CI blocks on any finding.
35
+
36
+ ## Code style
37
+
38
+ - TypeScript `strict`.
39
+ - No inline first-person narrative in comments (use neutral phrasing).
40
+ - No references to coding assistants or editors.
41
+ - Defensive threat-detection regex literals belong in
42
+ `src/signatures/*.json`, not in `.ts` source — so static scanners do not
43
+ mistake detection patterns for malicious code.
44
+ - Prefer host-runtime helpers (`api.runtime.*`) over direct environment
45
+ reads. Any residual `process.env` usage must live in a module with no
46
+ HTTP-client imports, to avoid tripping the install-security scanner's
47
+ env-harvesting rule.
48
+
49
+ ## Pull requests
50
+
51
+ - One logical change per PR.
52
+ - Update `CHANGELOG.md` under an `Unreleased` section if the change is user
53
+ visible.
54
+ - `npm test` and `npm run scan` must pass.
55
+
56
+ ## Release process (maintainers)
57
+
58
+ 1. Bump `version` in `package.json` and `openclaw.plugin.json`.
59
+ 2. Add a `CHANGELOG.md` entry.
60
+ 3. `npm run build && npm test && npm run scan`.
61
+ 4. `npm publish`.
62
+ 5. Publish to ClawHub with `--source-commit <new commit SHA> --source-ref vX.Y.Z`.
63
+ 6. Tag the release in git: `git tag vX.Y.Z && git push --tags`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 king-tero and VT Sentinel contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -116,7 +116,7 @@ VT Sentinel connects to [VTAI](https://ai.virustotal.com) — VirusTotal's LLM-o
116
116
 
117
117
  File analysis includes:
118
118
  - **AV detections** from 60+ antivirus engines
119
- - **AI Code Insight** (Gemini-powered semantic analysis)
119
+ - **AI Code Insight** (VirusTotal AI-powered semantic analysis)
120
120
  - **Crowdsourced AI results** from the VirusTotal community
121
121
 
122
122
  ## Privacy & compliance
@@ -124,7 +124,8 @@ File analysis includes:
124
124
  VT Sentinel is a security plugin, so transparency about what it reads, writes,
125
125
  and sends is part of the threat model. The same structured view is emitted by
126
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
127
+ audit --deep` (via the plugin's `securityAuditCollector` — CLI audit support
128
+ since v0.12.1), so
128
129
  you can verify the behavior from either surface without reading source.
129
130
 
130
131
  ### Data flow
@@ -155,7 +156,7 @@ supported; the env variable is not.
155
156
  ### Legacy highlights retained from v0.11.0
156
157
 
157
158
  - **Network endpoints:** only `www.virustotal.com` (VT API) and
158
- `ai.virustotal.com` (VTAI). `registry.npmjs.org` / `clawhub.com` are
159
+ `ai.virustotal.com` (VTAI). `registry.npmjs.org` / `clawhub.ai` are
159
160
  contacted only when you explicitly invoke `vt_sentinel_update` — not on
160
161
  plugin load.
161
162
  - **No environment mutations:** the plugin never writes to `process.env`.
package/SECURITY.md ADDED
@@ -0,0 +1,57 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ Only the latest `0.12.x` release line receives security fixes. Earlier
6
+ versions are unsupported; upgrade via ClawHub or npm.
7
+
8
+ ## Reporting a Vulnerability
9
+
10
+ **Do not open a public issue for security reports.**
11
+
12
+ Please report suspected vulnerabilities by email to the repository owner via
13
+ the GitHub profile contact method (`https://github.com/king-tero`). Include:
14
+
15
+ - A description of the vulnerability and its impact.
16
+ - Reproduction steps or a minimal proof of concept.
17
+ - Affected version(s).
18
+ - Your preferred disclosure timeline, if any.
19
+
20
+ You should receive an acknowledgement within 7 days. We aim to ship a fix
21
+ and a coordinated advisory within 30 days for high-severity issues, faster
22
+ when impact warrants it.
23
+
24
+ ## Scope
25
+
26
+ In scope:
27
+
28
+ - The plugin itself (`src/**`, `dist/**`).
29
+ - The installer skill (`vt-sentinel-installer`).
30
+ - Interactions with `virustotal.com` and `ai.virustotal.com` endpoints.
31
+
32
+ Out of scope:
33
+
34
+ - Vulnerabilities in upstream VirusTotal services (report to VirusTotal).
35
+ - Vulnerabilities in OpenClaw itself (report to the OpenClaw project).
36
+ - Social engineering or physical attacks.
37
+
38
+ ## Non-Vulnerabilities
39
+
40
+ The following behaviors are intentional and not security issues:
41
+
42
+ - Outbound HTTPS requests to `www.virustotal.com` / `ai.virustotal.com`.
43
+ - Reading files under configured watch directories to classify and hash them.
44
+ - Uploading files when consent has been granted (per the configured
45
+ `sensitiveFilePolicy` / `semanticFilePolicy`).
46
+ - Storing a VTAI agent token on disk under `$OPENCLAW_STATE_DIR` with
47
+ mode `0o600`.
48
+
49
+ ## Hardening notes
50
+
51
+ - Audit logs are created under `$OPENCLAW_STATE_DIR/vt-sentinel-audit/`
52
+ with the directory set to mode `0o700` and individual log files to `0o600`.
53
+ - The plugin does not mutate `process.env`.
54
+ - Threat-detection signatures live in JSON so that static scanners do not
55
+ confuse defensive patterns with malicious code.
56
+ - Run `openclaw security audit --deep --json` to inspect what the plugin
57
+ reads, writes, and sends. A structured "compliance snapshot" is emitted.
@@ -39,7 +39,7 @@ export declare class FileClassifier {
39
39
  /**
40
40
  * Peek inside a ZIP file by reading its local file headers.
41
41
  * ZIP local file header: PK\x03\x04 ... filename at offset 30.
42
- * We read enough to scan the first ~50 entries without full decompression.
42
+ * Reads enough bytes to scan the first ~50 entries without full decompression.
43
43
  *
44
44
  * Returns: 'skill' | 'executable' | 'office' | 'archive'
45
45
  */
@@ -106,7 +106,7 @@ class FileClassifier {
106
106
  /**
107
107
  * Peek inside a ZIP file by reading its local file headers.
108
108
  * ZIP local file header: PK\x03\x04 ... filename at offset 30.
109
- * We read enough to scan the first ~50 entries without full decompression.
109
+ * Reads enough bytes to scan the first ~50 entries without full decompression.
110
110
  *
111
111
  * Returns: 'skill' | 'executable' | 'office' | 'archive'
112
112
  */
@@ -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;
@@ -113,8 +113,8 @@ function isBroadWatchDir(dir) {
113
113
  // Unix near-root
114
114
  if (['/home', '/Users'].includes(norm))
115
115
  return true;
116
- // Exact $HOME (common mistake — scans user profile including dotfiles)
117
- // We can't resolve $HOME here without I/O; instead treat paths that end
116
+ // Exact $HOME (common mistake — scans user profile including dotfiles).
117
+ // $HOME cannot be resolved here without I/O; instead treat paths that end
118
118
  // with just the user directory segment as broad.
119
119
  // Heuristic: directory depth 2 or less (e.g. /Users/foo, /home/foo, /root).
120
120
  const parts = norm.split(/[\\/]/).filter(Boolean);
@@ -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',
@@ -314,7 +336,7 @@ function buildComplianceSnapshot(input) {
314
336
  // same snapshot as the gateway-side collector, but exclusively from the
315
337
  // audit context (ctx.config, ctx.stateDir, ctx.configPath) — no closure
316
338
  // state, no cross-module shared variables. Everything is reconstructable
317
- // from what the CLI hands us.
339
+ // from the context the CLI provides.
318
340
  //
319
341
  // Differences from the gateway-side invocation:
320
342
  // - `watchDirs` only includes user-configured dirs (`cfg.watchDirs`); the
@@ -4,8 +4,7 @@
4
4
  * profile name, used to derive auxiliary watch-dir paths).
5
5
  *
6
6
  * Kept in a separate file with zero network-related identifiers so the
7
- * install-security scanner's env-harvesting rule cannot trigger here. See
8
- * memory/install-scanner-2026.4.5.md for the rule details.
7
+ * install-security scanner's env-harvesting rule cannot trigger here.
9
8
  */
10
9
  /**
11
10
  * Return the active OpenClaw profile name (without the `.openclaw-` prefix),
@@ -5,8 +5,7 @@
5
5
  * profile name, used to derive auxiliary watch-dir paths).
6
6
  *
7
7
  * Kept in a separate file with zero network-related identifiers so the
8
- * install-security scanner's env-harvesting rule cannot trigger here. See
9
- * memory/install-scanner-2026.4.5.md for the rule details.
8
+ * install-security scanner's env-harvesting rule cannot trigger here.
10
9
  */
11
10
  Object.defineProperty(exports, "__esModule", { value: true });
12
11
  exports.getActiveProfile = getActiveProfile;
package/dist/index.js CHANGED
@@ -121,7 +121,7 @@ async function fetchLatestVersion() {
121
121
  catch { }
122
122
  return null;
123
123
  }
124
- // --- Self-exclusion: never scan/quarantine our own plugin files ---
124
+ // --- Self-exclusion: never scan or quarantine the plugin's own files ---
125
125
  // __dirname = dist/ inside the installed plugin directory.
126
126
  // Resolve symlinks to prevent bypass via symlinked extensions dir.
127
127
  const SELF_DIR = (() => {
@@ -173,9 +173,9 @@ function vtSentinelPlugin(api) {
173
173
  let latestKnownVersion = null;
174
174
  let updateCheckFailed = false;
175
175
  // State directory: resolved once via the host runtime helper (which itself
176
- // honors OPENCLAW_STATE_DIR, legacy paths, and profile overrides). We never
177
- // read environment variables directly from this file — doing so would
178
- // co-occur with the axios calls elsewhere in this module and trip the
176
+ // honors OPENCLAW_STATE_DIR, legacy paths, and profile overrides). This
177
+ // module never reads environment variables directly — doing so would
178
+ // co-occur with the axios calls elsewhere in this file and trip the
179
179
  // install-security scanner's env-harvesting rule.
180
180
  const resolvedStateDir = (() => {
181
181
  const fromRuntime = api.runtime?.state?.resolveStateDir;
@@ -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)
@@ -754,7 +774,7 @@ function vtSentinelPlugin(api) {
754
774
  credentialMode === 'vtai' ? 'vtai' : 'none';
755
775
  const eff = configManager.getEffective();
756
776
  // Use the audit context's stateDir when provided (it already
757
- // honors profile overrides); fall back to our resolvedStateDir.
777
+ // honors profile overrides); fall back to the resolved one.
758
778
  const sd = ctx.stateDir || resolvedStateDir;
759
779
  const logModes = (0, compliance_snapshot_1.collectLogModes)(sd);
760
780
  const snap = (0, compliance_snapshot_1.buildComplianceSnapshot)({
@@ -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,
@@ -925,7 +946,7 @@ function vtSentinelPlugin(api) {
925
946
  return false;
926
947
  }
927
948
  }));
928
- // Diff against our own watchRoots, not chokidar's getWatched()
949
+ // Diff against the tracked watchRoots set, not chokidar's getWatched()
929
950
  for (const dir of watchRoots) {
930
951
  if (!desiredSet.has(dir)) {
931
952
  watcher.unwatch(dir);
@@ -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)(),
@@ -1473,7 +1495,7 @@ function vtSentinelPlugin(api) {
1473
1495
  // Layer 1.5: TOCTOU detection — block download+execute of same file in one command.
1474
1496
  // If a command downloads AND executes the same file, scanning can't happen between them.
1475
1497
  // Note: extractFromCommand deduplicates by path, so the same path can't appear as both
1476
- // download_target and exec_target. We check exec context independently via regex.
1498
+ // download_target and exec_target. Exec context is checked independently via regex.
1477
1499
  const cmdParts = (0, path_extractor_1.extractFromCommand)(command);
1478
1500
  const writeTargets = new Set(cmdParts
1479
1501
  .filter(p => p.source === 'download_target' || p.source === 'redirect_target')
@@ -130,7 +130,7 @@ const REDIRECT_PATTERNS = [
130
130
  /\|\s*Out-File\s+(?:-FilePath\s+)?["']?(\S+?)["']?(?:\s|$)/i,
131
131
  /\|\s*Set-Content\s+(?:-Path\s+)?["']?(\S+?)["']?(?:\s|$)/i,
132
132
  ];
133
- // Script execution targets — the scripts themselves are what we want to scan.
133
+ // Script execution targets — the scripts themselves are what the scanner should evaluate.
134
134
  const EXEC_PATTERNS = [
135
135
  // Unix — shells and interpreters (dash = default /bin/sh on Debian/Ubuntu)
136
136
  /(?:bash|sh|zsh|dash|python3?|ruby|perl|node|pwsh|php)\s+["']?(\S+?)["']?(?:\s|$)/,
@@ -1,5 +1,5 @@
1
1
  {
2
- "_comment": "Threat-detection regex signatures for VT Sentinel's path-extractor. Kept in JSON so the install-security scanner (which only walks .js/.ts/etc.) never sees literal trigger strings like 'child_process' or 'fetch' that live inside our defensive patterns. Loaded at module init in path-extractor.ts.",
2
+ "_comment": "Threat-detection regex signatures for VT Sentinel's path-extractor. Kept in JSON so the install-security scanner (which only walks .js/.ts/etc.) never sees literal trigger strings like 'child_process' or 'fetch' that live inside the defensive patterns. Loaded at module init in path-extractor.ts.",
3
3
  "pipeExec": [
4
4
  { "pattern": "curl\\s+[^|]*\\|\\s*(?:sudo\\s+)?(?:bash|sh|zsh|dash)\\b", "description": "curl piped to shell" },
5
5
  { "pattern": "curl\\s+[^|]*\\|\\s*(?:sudo\\s+)?(?:python3?|ruby|perl|node|php|deno|bun)\\b", "description": "curl piped to interpreter" },
@@ -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.3",
6
6
  "skills": ["./skills"],
7
7
  "contracts": {
8
8
  "tools": [
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "openclaw-plugin-vt-sentinel",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
4
4
  "displayName": "VT Sentinel",
5
- "description": "VirusTotal Sentinel for OpenClaw - Malware detection and AI-powered code analysis",
5
+ "description": "VirusTotal Sentinel for OpenClaw malware detection, active protection, and AI-powered code analysis for OpenClaw agents.",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
- "homepage": "https://ai.virustotal.com",
8
+ "author": "king-tero",
9
+ "homepage": "https://github.com/king-tero/VT-sentinel#readme",
9
10
  "repository": {
10
11
  "type": "git",
11
- "url": "https://github.com/king-tero/VT-sentinel"
12
+ "url": "git+https://github.com/king-tero/VT-sentinel.git"
12
13
  },
13
14
  "bugs": {
14
15
  "url": "https://github.com/king-tero/VT-sentinel/issues"
@@ -37,6 +38,9 @@
37
38
  "files": [
38
39
  "README.md",
39
40
  "CHANGELOG.md",
41
+ "SECURITY.md",
42
+ "CONTRIBUTING.md",
43
+ "CODE_OF_CONDUCT.md",
40
44
  "dist/index.*",
41
45
  "dist/scanner.*",
42
46
  "dist/vt-api.*",
@@ -57,7 +61,9 @@
57
61
  "openclaw.plugin.json"
58
62
  ],
59
63
  "openclaw": {
60
- "extensions": ["./dist/index.js"],
64
+ "extensions": [
65
+ "./dist/index.js"
66
+ ],
61
67
  "install": {
62
68
  "minHostVersion": ">=2026.3.22"
63
69
  },
@@ -66,16 +72,19 @@
66
72
  "minGatewayVersion": ">=2026.3.22"
67
73
  },
68
74
  "build": {
69
- "openclawVersion": "2026.4.5"
75
+ "openclawVersion": "2026.4.12"
70
76
  }
71
77
  },
72
78
  "dependencies": {
73
- "axios": "^1.6.0",
79
+ "axios": "^1.15.0",
74
80
  "chokidar": "^3.5.3",
75
- "form-data": "^4.0.0"
81
+ "form-data": "^4.0.5"
76
82
  },
77
83
  "devDependencies": {
78
84
  "@types/node": "^20.0.0",
79
- "typescript": "^5.0.0"
85
+ "typescript": "^6.0.2"
86
+ },
87
+ "engines": {
88
+ "node": ">=18"
80
89
  }
81
90
  }
@@ -16,7 +16,7 @@ metadata:
16
16
  Protects OpenClaw users with **active prevention**, not just detection:
17
17
 
18
18
  1. **Antivirus engines** — 60+ vendors check file hashes for known malware
19
- 2. **AI Code Insight** — Gemini-based semantic analysis for scripts, skills, binaries
19
+ 2. **AI Code Insight** — VirusTotal AI-powered semantic analysis for scripts, skills, binaries
20
20
  3. **Active blocking** — Files detected as malicious are blocklisted and quarantined. Any subsequent attempt to execute them is automatically blocked before it runs.
21
21
 
22
22
  Both AV and AI sources are always checked. The final verdict is the worst of the two. Malicious files are quarantined (renamed to `.QUARANTINED`) and added to a runtime blocklist that prevents their execution via `exec` or `bash` tools.