Back to skill

Security audit

Bring

Security checks for vulnerabilities and agentic risk

Overview

The skill does what it claims for Bring! shopping lists, but it handles reusable account credentials and session tokens in ways users should review carefully before installing.

Install only if you are comfortable with a local CLI storing your Bring! password and tokens in plaintext files and using an unpinned global npm package. Prefer a version that prompts securely for credentials, stores secrets in an OS keychain or protected files, pins dependencies with a lockfile, and provides a clear logout/secret-removal command.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/bring-cli.js:26
Finding
Plaintext Credential and Session Token Storage Without Enforced Access Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/bring-cli.js:26-38, 121-123` **Vulnerability Type**: Plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```javascript function saveConfig(config) { fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2)); } // Token caching to avoid re-login on every command function loadTokenCache() { if (fs.existsSync(TOKEN_FILE)) { try { const cache = JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf8')); // Tokens valid for ~30 days; treat as expired after 7 days for safety if (cache.savedAt && (Date.now() - cache.savedAt) < 7 * 24 * 60 * 60 * 1000) { return cache; } } catch (_) { /* stale cache, ignore */ } } return null; } function saveTokenCache(uuid, bearerToken, refreshToken) { fs.writeFileSync(TOKEN_FILE, JSON.stringify({ uuid, bearerToken, refreshToken, savedAt: Date.now() }, null, 2)); } ``` The password is assigned to the persisted configuration before authentication is tested: ```javascript config.email = args[0]; config.password = args[1]; saveConfig(config); ``` ### Technical Analysis The Skill writes the user's Bring! email address, password, bearer token, and refresh token to plaintext JSON files under `~/.openclaw/bring/`. The file writes do not specify an owner-only mode such as `0600`, and directory creation does not explicitly specify mode `0700`. Effective access therefore depends on the process umask and any pre-existing filesystem permissions. The password is persisted before the login attempt succeeds. Consequently, even a mistyped or invalid password remains on disk. Storing the reusable account password is also unnecessary once a suitable session or refresh-token mechanism is available. Bearer and refresh tokens are authentication secrets. Anyone able to read them may be able to impersonate the user until the tokens expire or are revoked. ### Attack Path 1. The user configures the Skill ...[truncated 1133 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not persist the account password in `config.json`. - Store credentials in an operating-system credential manager or another dedicated secret store. - Prefer a revocable, narrowly scoped session or refresh token over retaining the reusable account password. - Create `~/.openclaw/bring/` with mode `0700`. - Create secret-bearing files with mode `0600`, and verify or repair permissions when existing files are loaded. - Write files atomically using a securely created temporary file followed by a rename. - Persist configuration only after authentication succeeds. - Remove expired token files rather than merely ignoring their contents. - Provide a logout or credential-removal command that deletes cached secrets. - Document the locally persisted data, retention period, and revocation procedure. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:21
Finding
Password Exposure Through Command-Line Arguments<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:21-25`; `scripts/bring-cli.js:120-123` **Vulnerability Type**: Sensitive information exposed through process arguments **Risk Level**: Medium ### Vulnerable Code The documented setup procedure requires placing the password directly in the command: ```bash NODE_PATH=$(npm root -g) node ~/.openclaw/workspace/skills/bring/scripts/bring-cli.js configure <email> <password> ``` The executable reads the password from the process argument vector: ```javascript if (args.length < 2) { console.error('Usage: configure <email> <password>'); process.exit(1); } config.email = args[0]; config.password = args[1]; saveConfig(config); ``` ### Technical Analysis Command-line arguments are not an appropriate transport for authentication secrets. Depending on the host environment, arguments may be exposed through process-inspection facilities, shell history, terminal recording, telemetry, audit systems, Agent tool-call logs, debugging output, or command execution records. Quoting the password only affects shell parsing; it does not prevent the password from appearing in the child process argument vector or retained command history. ### Attack Path 1. The user or Agent follows the documented configuration command and supplies the plaintext password as an argument. 2. The command is recorded in shell history, Agent execution logs, terminal telemetry, or an operating-system audit facility, or it is observed through process inspection while running. 3. A local attacker, administrator, compromised logging service, or process with access to those records retrieves the password. 4. The attacker uses the recovered password to authenticate to the Bring! account. 5. If the credential was reused, the attacker may attempt credential reuse against other services. ### Impact Assessment The immediate impact is disclosure of the user's reusable Bring! account password. This can permit unauthorized access to account-auth ...[truncated 356 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the password argument from the documented and implemented interface. - Read the password through hidden interactive input with terminal echo disabled. - For noninteractive operation, accept the secret through a protected file descriptor or integrate with an operating-system credential store. - Avoid ordinary environment variables for long-lived secrets because they may also be exposed by process and diagnostic facilities. - Ensure Agent and command-execution logs redact authentication values. - Warn users to remove any existing commands containing passwords from shell history and retained execution logs. - Rotate credentials that may already have been exposed through the documented workflow. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:9
Finding
Unpinned Globally Installed Authentication Dependency<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:9-19`; `scripts/bring-cli.js:7` **Vulnerability Type**: Mutable and unpinned third-party dependency **Risk Level**: Medium ### Vulnerable Code The Skill instructs users to install the latest available package globally and resolve it through the mutable global module directory: ```markdown ## Invocation The CLI lives at `scripts/bring-cli.js`. Always prefix with `NODE_PATH=$(npm root -g)`: ```bash NODE_PATH=$(npm root -g) node ~/.openclaw/workspace/skills/bring/scripts/bring-cli.js <command> [args] ``` ## Prerequisites ```bash npm install -g bring-shopping ``` ``` The executable then imports whichever global package version resolves under that name: ```javascript const BringApi = require('bring-shopping'); ``` ### Technical Analysis The installation command does not pin an audited package version and the project contains no local lockfile or integrity record for the dependency. A later execution can therefore load code that differs from the version originally reviewed. Using `NODE_PATH=$(npm root -g)` broadens module resolution to a mutable global installation area. A compromised future release, unauthorized modification of the global package tree, or package-account takeover could cause attacker-controlled JavaScript to execute with the same permissions as the Agent. The dependency receives the user's email and password and handles bearer and refresh tokens. Its position in the authentication path makes dependency integrity particularly important. The audited project does not contain the package implementation, so its actual network endpoints and internal handling of credentials cannot be independently verified from these files. ### Attack Path 1. A user follows the prerequisite and installs `bring-shopping` without specifying a version. 2. The npm package, publisher account, distribution channel, or global installation is compromised or modified. 3. The Skill sets `NODE_PATH` to the ...[truncated 960 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Pin the dependency to an exact reviewed version rather than installing the latest release. - Use a project-local `package.json` and lockfile with integrity hashes. - Install dependencies through a reproducible deployment process instead of resolving from a mutable global module directory. - Review package ownership, release history, source repository, and published artifact before upgrades. - Require explicit security review when changing the locked dependency version. - Use `npm ci` with a committed lockfile in controlled deployments. - Disable package lifecycle scripts where operationally feasible. - Run the Skill with a restricted operating-system account and limit filesystem and network access to reduce the impact of dependency compromise. - Verify that the dependency communicates only with expected Bring! service endpoints before entrusting it with credentials. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (6)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The core declared behavior is present: the code can view lists/items and add or remove shopping list items. However, it also includes several undeclared capabilities that go beyond simple shopping list management: local credential storage, token caching, catalog/translation retrieval, default-list configuration, and list language detection/caching. These are materially broader than the declared description, even though they support the Bring! integration. Therefore this is a description-behavior mismatch due to undeclared capabilities and local access to sensitive credentials.

Ae1

High
Category
analysis-evasion
Content
The CLI lives at `scripts/bring-cli.js`. Always prefix with `NODE_PATH=$(npm root -g)`:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The setup flow instructs users to pass their email and password directly on the command line while also noting that configuration and tokens are stored locally. Command-line credentials can be exposed through shell history, process listings, logs, or agent telemetry, and local secret storage without an explicit warning increases the chance of accidental credential disclosure on shared or monitored systems.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
Bearer and refresh tokens are cached to disk in a JSON file without user disclosure or access-control hardening. Anyone who can read that file may be able to impersonate the user for the token lifetime, and refresh tokens can extend that access beyond the current session.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The CLI stores the user's Bring account email and password in plaintext in a local JSON config file under the user's home directory. If the workstation, backups, logs, or dotfiles are exposed to another local user, malware, or accidental syncing, the credentials can be recovered and reused to access the user's account and shared shopping-list data.

Natural-Language Policy Violations

Low
Confidence
78% confidence
Finding
Language-sensitive behavior initializes `bestMatch` with `en-US`, and several commands also default locale arguments to `en-US`. This can impose a specific language/locale choice when the user has not explicitly opted in, which may conflict with organizational language/locale policy.

Static analysis

No suspicious patterns detected.