Back to plugin

Security audit

sil

Security checks for vulnerabilities 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.

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
The plugin persists access and refresh tokens on disk in tokens.json, creating a standing credential exposure risk if the local account, plugin data directory, backups, or host environment are compromised. Although the manifest claims owner-only permissions and limited refresh behavior, disk persistence of reusable session material still expands the blast radius beyond an in-memory 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
The plugin persists access and refresh tokens on disk in tokens.json, creating a standing credential exposure risk if the local account, plugin data directory, backups, or host environment are compromised. Although the manifest claims owner-only permissions and limited refresh behavior, disk persistence of reusable session material still expands the blast radius beyond an in-memory session.

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
This script intentionally modifies the host OpenClaw configuration outside the normal plugin write boundary, creating persistent trust state for the plugin by adding it to an allowlist. Although operator-invoked and not an automatic lifecycle hook, it still establishes durable session/config persistence on the host, which is security-sensitive because a package-distributed script is altering future authorization behavior.

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
The script intentionally creates a persistent routing/binding from a message channel to a newly created agent in host configuration, which can alter which agent receives future user messages beyond the immediate session. Even though this is documented and partially fail-open, persistent channel rebinding is a form of session persistence that can surprise operators, cause message interception by the wrong agent, or be abused if an attacker can influence the input spec or execution context.

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, {