Back to plugin

Security audit

sil

Security checks across malware telemetry and agentic risk

Overview

The skill is a disclosed shopping integration that stores local credentials and shopper preferences, with sensitive but purpose-aligned behavior.

Install this only if you are comfortable with sil storing a local session token and shopper preferences, and with the optional helper scripts changing OpenClaw configuration or creating a persistent shopper agent. Review the helper command before running it, especially on shared machines or hosts with strict plugin/tool allowlists.

SkillSpector

By NVIDIA
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The tool description claims it 'never updates anything itself,' but the implementation does perform filesystem mutations by creating the data directory and tightening permissions with chmod. This is a security-relevant integrity issue because agents or users relying on the description may invoke the tool under a false assumption of read-only behavior, which can lead to unintended side effects in sensitive environments.

Vague Triggers

Medium
Confidence
91% confidence
Finding
The trigger condition is very broad: the skill loads when a user asks to 'create or set up their sil shopper,' which can overlap with ordinary shopping-assistance requests and cause the creation workflow to activate unexpectedly. In this skill, activation leads into a persistence-oriented onboarding and eventually local file/config modification, so accidental invocation increases the chance of unintended stateful actions or coercive setup flows.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The description is broad enough to trigger on routine shopping conversations because it says to load when resolving a domain's method or managing what the shopper knows, without strong narrowing conditions. Overbroad invocation can cause the skill to activate outside its intended scope, pulling in persistent profile/store operations and web-research guidance in contexts where they were not explicitly requested, which increases the risk of unintended data access or workflow interference.

Session Persistence

Medium
Category
Rogue Agent
Content
},
  "security": {
    "shipsRuntimeCode": true,
    "packagingNote": "Compiled JavaScript ships under ./dist. register() stays synchronous and opens nothing — it only registers tools. ALL I/O happens inside a tool's execute(): sil_register calls sil-web over the network, writes tokens.json + config.json to the plugin data directory (owner-only, 0600), and arms a bounded background poll timer that stops on the first terminal claim outcome or after the session deadline. sil_whoami reads the stored session token and calls sil-api for the user's identity, refreshing transparently via sil-web on a 401 (at most one refresh + one retry per call, no timer); a confirmed-dead session clears tokens.json. sil_search and sil_product_get read the stored session token and call sil-api's catalog endpoints (search / lookup) with it — read-only on catalog data, no timer, but with the SAME bounded 401 recovery as sil_whoami: on a 401 they refresh ONCE via sil-web (rotating tokens.json) and retry the read ONCE; a second 401 or a dead (invalid_grant) refresh token clears tokens.json and is reported as a terminal re-register hint. A non-401 error writes no token. The five profile verbs make NO network call and read no token; they write the one sil shopper's SINGLETON behaviour artefacts under a fixed $SIL_DATA_DIR/shopper/ (owner-only, files 0600, dirs 0700) as frontmatter-as-truth markdown — there is NO profile.json manifest; each file's own frontmatter IS the source of truth, discovered by a filesystem scan. sil_profile_materialize is SETUP-ONLY: it writes shopper/user_spec.md (the shared person spec; its frontmatter carries the shopper name), minting no domain. sil_learn is the single target+change verb over shopper/domains/<slug>/{method.md, prds/<product>-<intent>.md, assets/}: create mints a whole method or PRD, fail-closed if one already exists; write replaces an existing doc's whole body with a caller-authored reconciled version, fail-closed if the target is absent — there is no
...[truncated 26 chars]
Confidence
82% confidence
Finding
read the stored session

Session Persistence

Medium
Category
Rogue Agent
Content
},
  "security": {
    "shipsRuntimeCode": true,
    "packagingNote": "Compiled JavaScript ships under ./dist. register() stays synchronous and opens nothing — it only registers tools. ALL I/O happens inside a tool's execute(): sil_register calls sil-web over the network, writes tokens.json + config.json to the plugin data directory (owner-only, 0600), and arms a bounded background poll timer that stops on the first terminal claim outcome or after the session deadline. sil_whoami reads the stored session token and calls sil-api for the user's identity, refreshing transparently via sil-web on a 401 (at most one refresh + one retry per call, no timer); a confirmed-dead session clears tokens.json. sil_search and sil_product_get read the stored session token and call sil-api's catalog endpoints (search / lookup) with it — read-only on catalog data, no timer, but with the SAME bounded 401 recovery as sil_whoami: on a 401 they refresh ONCE via sil-web (rotating tokens.json) and retry the read ONCE; a second 401 or a dead (invalid_grant) refresh token clears tokens.json and is reported as a terminal re-register hint. A non-401 error writes no token. The five profile verbs make NO network call and read no token; they write the one sil shopper's SINGLETON behaviour artefacts under a fixed $SIL_DATA_DIR/shopper/ (owner-only, files 0600, dirs 0700) as frontmatter-as-truth markdown — there is NO profile.json manifest; each file's own frontmatter IS the source of truth, discovered by a filesystem scan. sil_profile_materialize is SETUP-ONLY: it writes shopper/user_spec.md (the shared person spec; its frontmatter carries the shopper name), minting no domain. sil_learn is the single target+change verb over shopper/domains/<slug>/{method.md, prds/<product>-<intent>.md, assets/}: create mints a whole method or PRD, fail-closed if one already exists; write replaces an existing doc's whole body with a caller-authored reconciled version, fail-closed if the target is absent — there is no
...[truncated 26 chars]
Confidence
82% confidence
Finding
write the one sil shopper's SINGLETON behaviour artefacts under a fixed $SIL_DATA_DIR/shopper/ (owner-only, files 0600, dirs 0700) as frontmatter-as-truth markdown — there is NO profile.json manifest;

Session Persistence

Medium
Category
Rogue Agent
Content
* runs nothing automatically; an operator explicitly running this is the
 * opposite of an install hook and honours it). This is a standalone script in
 * the same class as `scripts/release.mjs` — it is NOT the plugin process, so
 * the plugin's `~/.openclaw`-write boundary does not apply to it.
 *
 * What it does: resolve the host `openclaw.json`, read sil's facts from the
 * shipped manifest (single source of truth), call the pure `mergeSilAllowlist`
Confidence
96% confidence
Finding
write boundary does not apply to it. * * What it does: resolve the host `openclaw.json`, read sil's facts from the * shipped manifest (single source of truth), call the pure `mergeSilAllowlist` *

Session Persistence

Medium
Category
Rogue Agent
Content
/** True iff the `openclaw agents bindings --json` read-back (an ARRAY of
 * `{agentId, match:{channel}}`) actually shows the route `<channel> → <agentId>`.
 * This is the honesty rail: a bind is only "verified" once the read-back proves the
 * write stuck — issuing the write is not the same as the next message reaching it. */
function bindingPresent(stdout, agentId, channel) {
  let v;
  try {
Confidence
78% confidence
Finding
write stuck — issuing the write is not the same as the next message reaching it. */ function bindingPresent(stdout, agentId, channel) { let v; try { v = JSON.parse(stdout); } catch { ret

VirusTotal

60/60 vendors flagged this plugin as clean.

View on VirusTotal

Static analysis

Detected: suspicious.dangerous_exec

Shell command execution detected (child_process).

Critical
Code
suspicious.dangerous_exec
Location
scripts/create-shopper.mjs:233
Evidence
const stdout = execFileSync("openclaw", args, {