Back to skill

Security audit

Monzo

Security checks for vulnerabilities and agentic risk

Overview

This Monzo banking skill is coherent and disclosed, but it needs review because it handles bank tokens and money-related actions with risky secret-handling and endpoint controls.

Install only on a machine you control. Prefer a password manager or secret manager for MONZO_KEYRING_PASSWORD, avoid passing OAuth secrets on the command line, leave MONZO_API_BASE unset unless you have audited the endpoint, and require explicit user confirmation before pot deposits, withdrawals, receipt deletion, or webhook changes. If this has run with an untrusted environment or exposed command history, rotate or revoke the Monzo OAuth credentials.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/lib/monzo.sh:8
Finding

Unrestricted API Base Override Can Exfiltrate OAuth Secrets and Bearer Tokens

Content
View full analysis
&2 return 1 fi local response response=$(curl -s -X POST "$MONZO_API_BASE/oauth2/token" \ --data-urlencode "grant_type=refresh_token" \ --data-urlencode "client_id=$CLIENT_ID" \ --data-urlencode "client_secret=$CLIENT_SECRET" \ --data-urlencode "refresh_token=$REFRESH_TOKEN") ``` `scripts/lib/monzo.sh:168-189`: ```bash # Make authenticated API call # Usage: monzo_api_call GET /path [--data "key=value"] monzo_api_call() { local method="$1" local path="$2" shift 2 # Check token expiry before making call monzo_check_expiry local url="$MONZO_API_BASE$path" local response local http_code # Make the API call if [[ "$method" == "GET" ]]; then response=$(curl -s -w "\n%{http_code}" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ "$url" "$@") else response=$(curl -s -w "\n%{http_code}" -X "$method" \ -H "Authorization: Bearer $ACCESS_TOKEN" \ "$url" "$@") fi ``` `scripts/setup.sh:17-20`: ```bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CREDENTIALS_DIR="${OPENCLAW_CREDENTIALS_DIR:-${CLAWDBOT_CREDENTIALS_DIR:-${HOME}/.openclaw/credentials}}" CREDENTIALS_FILE="${CREDENTIALS_DIR}/monzo.json" MONZO_API_BASE="${MONZO_API_BASE:-https://api.monzo.com}" ``` `scripts/setup.sh:439-444`: ```bash TOKEN_RESPONSE=$(curl -s -X POST "$MONZO_API_BASE/oauth2/token" \ --data-urlencode "grant_typ ...[truncated 2648 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/setup.sh:211
Finding

Non-Interactive Setup Exposes OAuth Secrets Through Command-Line Arguments

Content
View full analysis
/cmd ...[truncated 1828 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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 (56)

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The skill stores highly sensitive material at rest, including the OAuth client secret, access token, and refresh token. Even though the README says the file is encrypted, co-locating long-lived banking secrets on disk materially increases the blast radius of host compromise, password disclosure, or implementation flaws in the encryption handling.

Content

Scanner excerpt · README.md (reported line 163)May include surrounding context.

md
The following are stored in the encrypted credentials file:
- OAuth Client ID and Client Secret
- Access Token and Refresh Token
- Default Account ID

### What This Skill Does NOT Protect Against

Tool Parameter Abuse

High
Category
Tool Misuse
Confidence
85% confidence
Finding

Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Content

Scanner excerpt · SECURITY.md (reported line 37)May include surrounding context.

md
shred -u ~/.openclaw/credentials/monzo.json

# Or if shred is not available:
rm -P ~/.openclaw/credentials/monzo.json  # macOS
srm ~/.openclaw/credentials/monzo.json    # If srm is installed

# Remove from OpenClaw config

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The code’s behavior is narrowly focused on sending a custom feed item to the Monzo app via the /feed API endpoint after loading credentials and resolving an account ID. That aligns with one part of the description ('send feed notifications'), but the declared purpose presents the skill as a broader banking tool that can also check balances, view transactions, and manage pots. None of those capabilities are evidenced in this code chunk. There is no sign of unrelated or dangerous undeclared behavior beyond notification creation, but the description does not accurately represent this specific chunk because it materially overstates the implemented functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

This code is focused on one subset of Monzo functionality: savings pot management. It parses commands for list, deposit, and withdraw, loads Monzo credentials, resolves an account ID, and calls Monzo API endpoints related to pots. There is no code here for retrieving general account balances, listing transactions, or creating feed notifications. Because the declared description presents a broader set of capabilities than the supplied code actually implements, the description does not accurately represent this specific code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents the skill as a general Monzo banking automation tool focused on balances, transactions, pots, and feed notifications. This code chunk instead has a narrower and different function: receipt management for transactions. Receipt creation/retrieval/deletion is a banking-related Monzo capability, but it is materially different from the listed capabilities and is undeclared. Because the actual behavior does not match the described functions and adds an unmentioned primary capability, this is a description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The description presents an operational banking skill for account access tasks like balances, transactions, pots, and feed notifications. This code chunk does not implement those user-facing banking actions. Instead, it performs authentication bootstrap and credential management: prompting for client ID/secret, generating an authorization URL, validating OAuth state, exchanging auth codes for tokens, saving encrypted credentials, and fetching an account ID. While this setup supports later Monzo access, it is a materially different capability from the declared primary purpose and includes sensitive credential-handling behavior not reflected in the description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The supplied script is narrowly focused on transactions. It loads Monzo credentials, retrieves transactions for an account, supports date filters, search, pagination, JSON/human output, single-transaction lookup, and PATCH-based metadata annotation. The declared description presents a broader banking skill including balance checks, pot management, and feed notifications, none of which appear in this code chunk. Additionally, the code can modify transaction metadata through annotation, which is more specific than merely 'view transactions' and not explicitly disclosed. Because the declared purpose materially overstates some capabilities while omitting a write capability present in the code, this chunk does not accurately match the description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description presents the skill as a general Monzo banking automation tool focused on account balance, transactions, pots, and feed notifications. However, this code chunk is narrowly focused on webhook administration via the Monzo API. Webhook management is a distinct capability not mentioned in the description, and the listed declared capabilities do not cover registering or deleting callback URLs. While webhook notifications are related to Monzo automation, this is still a materially different and undeclared function, so the description does not accurately represent this code chunk.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
84% confidence
Finding

The skill requires an encryption password environment variable for stored banking credentials, which places sensitive material in the process environment and within the skill's trust boundary. In agent environments, env vars may be exposed to subprocesses, logs, crash reports, or other tools unless carefully isolated.

Content

Scanner excerpt · SKILL.md (reported line 23)May include surrounding context.

Quick Start (TL;DR)

bash
# 1. Set the MONZO_KEYRING_PASSWORD env var (see "Setting the Password" below)

# 2. Create OAuth client at https://developers.monzo.com/
#    - Set Confidentiality: Confidential

Credential Access

High
Category
Privilege Escalation
Confidence
92% confidence
Finding

Option A explicitly suggests storing the keyring password in plaintext in configuration. Even with chmod 600, plaintext secrets in config files are vulnerable to accidental backup exposure, mis-scoped access, and source-control leaks.

Content

Scanner excerpt · SKILL.md (reported line 60)May include surrounding context.

md
"monzo": {
        enabled: true,
        env: {
          "MONZO_KEYRING_PASSWORD": "choose-a-secure-password-here"
        }
      }
    }

Credential Access

High
Category
Privilege Escalation
Confidence
87% confidence
Finding

Storing the keyring password in shell startup files leaves a persistent plaintext secret on disk and may expose it to other local tools, backups, or accidental disclosure. While common, this is a weaker secret-handling practice for a banking integration.

Content

Scanner excerpt · SKILL.md (reported line 77)May include surrounding context.

Add to your shell profile (~/.bashrc, ~/.zshrc, etc.):

bash
export MONZO_KEYRING_PASSWORD="choose-a-secure-password-here"

Then restart your shell and OpenClaw.

Credential Access

High
Category
Privilege Escalation
Confidence
86% confidence
Finding

A dedicated secrets file under /etc with chmod 600 is better than shell profiles, but it still stores the decryption password in plaintext at rest. For a banking skill, that expands the consequences of local file compromise or administrative misconfiguration.

Content

Scanner excerpt · SKILL.md (reported line 87)May include surrounding context.

Create a secrets file (e.g. /etc/openclaw/monzo.env):

text
MONZO_KEYRING_PASSWORD=choose-a-secure-password-here

Set permissions: chmod 600 /etc/openclaw/monzo.env

Credential Access

High
Category
Privilege Escalation
Confidence
91% confidence
Finding

The setup flow collects client ID, client secret, authorization code, and exchanges them for access tokens, creating a high-value credential handling path. In a banking skill, any mishandling of this interactive flow can lead to account compromise or long-lived unauthorized access via refresh tokens.

Content

Scanner excerpt · SKILL.md (reported line 121)May include surrounding context.

md
1. Ask for your Client ID and Client Secret
2. Give you an authorization URL to open in your browser
3. Ask you to paste the redirect URL back
4. Exchange the code for access tokens
5. Save encrypted credentials

**Alternative: Non-interactive mode** (useful for automation or agents):

Credential Access

High
Category
Privilege Escalation
Confidence
89% confidence
Finding

Auto-refreshing access tokens and encrypted-at-rest credential storage indicate persistent sensitive credential handling. This is expected for the integration but materially increases impact if the local credential store or decryption key is exposed, because access can continue beyond a single session.

Content

Scanner excerpt · SKILL.md (reported line 366)May include surrounding context.

md
- Credentials are **encrypted at rest** (AES-256-CBC)
- Encryption key is your `MONZO_KEYRING_PASSWORD`
- Access tokens auto-refresh (no manual intervention needed)
- File permissions are set to 600 (owner only)
- All API calls use HTTPS
- No sensitive data is logged

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · README.md (reported line 189)May include surrounding context.

md
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/lib/monzo.sh (reported line 29)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/lib/monzo.sh (reported line 30)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/lib/monzo.sh (reported line 48)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/lib/monzo.sh (reported line 49)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/lib/monzo.sh (reported line 347)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/lib/monzo.sh (reported line 348)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup.sh (reported line 79)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup.sh (reported line 80)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup.sh (reported line 100)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/setup.sh (reported line 241)May include surrounding context.

sh
monzo_encrypt_credentials() {
  local json="$1"

  if [[ -z "${MONZO_KEYRING_PASSWORD:-}" ]]; then
    echo "Error: MONZO_KEYRING_PASSWORD not set" >&2
    return 1
  fi

Static analysis

No suspicious patterns detected.