Back to skill

Security audit

Jentic WhatsApp QR

Security checks for vulnerabilities and agentic risk

Overview

The skill does the advertised WhatsApp QR-linking job, but it also uses a stored Mattermost bearer token and direct API calls to post the QR in threads, which deserves manual review before installation.

Install only if you are comfortable letting this skill initiate a WhatsApp device-linking session after confirmation and, in Mattermost threads, read a local OpenClaw config token to upload and post the QR. Prefer a brokered or scoped messaging tool over raw bearer-token curl commands, and rotate or narrowly scope the Mattermost token if this workflow is used.

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

Warning
Location
SKILL.md:47
Finding
Mattermost bearer token exposed through command-line arguments<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 47–68 **Vulnerability Type**: Insecure credential handling and unnecessary direct access to privileged configuration **Risk Level**: Medium ### Vulnerable Code ```bash SHIRKA_TOKEN=$(python3 -c "import json; print(json.load(open('/root/.openclaw/openclaw.json'))['env']['vars']['JENTIC_MM_SHIRKA_TOKEN'])") # 1. Upload the file FILE_ID=$(curl -s -X POST "https://mattermost.claw.jentic.ai/api/v4/files" \ -H "Authorization: Bearer $SHIRKA_TOKEN" \ -F "channel_id=CHANNEL_ID" \ -F "files=@/tmp/whatsapp_qr.png;filename=whatsapp_qr.png" \ | python3 -c "import json,sys; r=json.load(sys.stdin); print(r['file_infos'][0]['id'])") # 2. Post into the thread curl -s -X POST "https://mattermost.claw.jentic.ai/api/v4/posts" \ -H "Authorization: Bearer $SHIRKA_TOKEN" \ -H "Content-Type: application/json" \ -d "{ \"channel_id\": \"CHANNEL_ID\", \"root_id\": \"TOPIC_ID\", \"message\": \"Scan this now — you have ~60 seconds. WhatsApp → Settings → Linked Devices → Link a Device 👇\", \"file_ids\": [\"$FILE_ID\"] }" ``` ### Technical Analysis The instructions require the Agent to read `JENTIC_MM_SHIRKA_TOKEN` directly from the privileged OpenClaw configuration file at `/root/.openclaw/openclaw.json`. The token is stored in a shell variable and expanded into the `curl` `Authorization` header argument. Because the expanded header is passed as a command-line argument, the bearer token may temporarily be visible through process inspection facilities, debugging tools, shell tracing, execution telemetry, or command logging. Any process or monitoring component with sufficient access to inspect the `curl` command line could recover the credential. The reviewed instructions transmit the token only to the declared Mattermost domain, so there is no evidence of intentional credential exfiltration. Nevertheless, extracting a long-lived credential into the Agent's shell context is broader ...[truncated 1465 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Prefer a credential-brokered Mattermost messaging tool that supports file uploads to threads and does not expose the underlying token to the Agent. 2. If direct API access is unavoidable, provide a dedicated helper that: - Reads the token internally. - Restricts requests to an allowlisted HTTPS origin. - Never places the token in process arguments, standard output, standard error, or logs. - Accepts only validated channel, thread, message, and local file parameters. 3. Assign the token only the minimum Mattermost permissions required to upload the QR image and create a post in authorized channels. 4. Use a short-lived or workload-bound credential rather than a reusable long-lived bearer token. 5. Ensure shell tracing and verbose HTTP diagnostics are disabled around credential-bearing operations. 6. Rotate the existing token if command histories, process telemetry, or execution logs may already contain it. 7. Restrict permissions on `/root/.openclaw/openclaw.json` and avoid instructing general-purpose Agent workflows to parse privileged configuration directly. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/qr_decode.py:58
Finding
Unbounded QR dimensions allow local memory-exhaustion denial of service<![CDATA[ ## Vulnerability Details **File Location**: `scripts/qr_decode.py`, lines 58–87 **Vulnerability Type**: Unbounded resource allocation **Risk Level**: Low ### Vulnerable Code ```python # Build binary matrix (0=white, 1=black) matrix = [] for line in lines: top_row = [] bot_row = [] for ch in line: top_row.append(1 if ch in UPPER else 0) bot_row.append(1 if ch in LOWER else 0) matrix.append(top_row) matrix.append(bot_row) cols = max(len(r) for r in matrix) rows = len(matrix) # Block char encoding always produces even row counts (2 per char line). # If the QR has an odd number of modules (e.g. 59), the last char line # encodes a phantom bottom row. Drop it: if rows > cols, trim to cols. if rows > cols: matrix = matrix[:cols] rows = cols total_w = (cols + 2 * quiet) * scale total_h = (rows + 2 * quiet) * scale pixels = [[(255, 255, 255)] * total_w for _ in range(total_h)] for r, row in enumerate(matrix): for c, val in enumerate(row): color = (0, 0, 0) if val else (255, 255, 255) for pr in range(scale): for pc in range(scale): y = (r + quiet) * scale + pr x = (c + quiet) * scale + pc pixels[y][x] = color ``` The relevant command-line values are accepted without validation at `scripts/qr_decode.py`, lines 113–114: ```python parser.add_argument('--scale', type=int, default=10, help='Pixel scale per module (default: 10)') parser.add_argument('--quiet', type=int, default=6, help='Quiet zone modules (default: 6)') ``` ### Technical Analysis The decoder does not impose limits on: - The number of input lines. - The width of each input line. - The `--scale` value. - The `--quiet` value. - The resulting image width, height, or total pixel count. The code first creates a Python integer matrix for the decoded modules an ...[truncated 2134 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Validate `scale` and `quiet` before processing: - Require both values to be non-negative where appropriate. - Require `scale` to be at least 1. - Apply conservative upper bounds suitable for QR rendering. 2. Limit the maximum number of input rows and columns to valid WhatsApp QR dimensions plus a small compatibility margin. 3. Reject inconsistent line widths rather than silently constructing irregular matrices. 4. Calculate the resulting width, height, and total pixel count before allocating memory, and reject images exceeding a fixed safe limit. 5. Cap input-file size before reading the entire file into memory. 6. Avoid constructing multiple full-image representations. Stream PNG scanlines into the compressor or use a maintained image library with explicit dimension checks. 7. Catch `MemoryError`, `OverflowError`, and invalid-dimension conditions and return a controlled error without continuing allocation. 8. Apply equivalent argument validation in `generate_qr.py` before it forwards `--scale` and `--quiet` to the decoder. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (10)

Tp4

High
Category
MCP Tool Poisoning
Confidence
89% confidence
Finding
The description understates the operational behavior: the skill does more than passive QR generation by initiating a WhatsApp linking flow and directing the agent to perform time-sensitive actions immediately. This mismatch can mislead reviewers and users about what the skill actually does, reducing informed consent and increasing the chance of unintended account-linking operations.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The description understates the operational behavior: the skill does more than passive QR generation by initiating a WhatsApp linking flow and directing the agent to perform time-sensitive actions immediately. This mismatch can mislead reviewers and users about what the skill actually does, reducing informed consent and increasing the chance of unintended account-linking operations.

External Script Fetching

High
Category
Supply Chain
Content
SHIRKA_TOKEN=$(python3 -c "import json; print(json.load(open('/root/.openclaw/openclaw.json'))['env']['vars']['JENTIC_MM_SHIRKA_TOKEN'])")

# 1. Upload the file
FILE_ID=$(curl -s -X POST "https://mattermost.claw.jentic.ai/api/v4/files" \
  -H "Authorization: Bearer $SHIRKA_TOKEN" \
  -F "channel_id=CHANNEL_ID" \
  -F "files=@/tmp/whatsapp_qr.png;filename=whatsapp_qr.png" \
Confidence
90% confidence
Finding
Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
master_fd, slave_fd = pty.openpty()
    os.set_inheritable(slave_fd, True)
    env = os.environ.copy()
    env['COLUMNS'] = '120'
    env['TERM'] = 'xterm-256color'
Confidence
60% confidence
Finding
Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill invokes shell commands, writes files under /tmp, and reads environment-backed configuration, but it declares no explicit tool scope or permissions boundary. That increases the chance the skill is executed with broader-than-necessary capabilities and makes review, enforcement, and least-privilege controls harder.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The activation text is broad enough to match generic WhatsApp help requests, not just explicit requests to generate or scan a linking QR. In context, this is more dangerous because the skill initiates an account-linking workflow tied to a short-lived credential artifact, so accidental invocation could start a sensitive linking session the user did not clearly request.

Context-Inappropriate Capability

Medium
Confidence
98% confidence
Finding
The skill instructs the agent to read a bearer token from a local OpenClaw config file and use it for API calls. Accessing local credentials for a secondary delivery path expands the trust boundary significantly; if the skill is misused, modified, or triggered inappropriately, it can abuse stored credentials to act on Mattermost beyond the core QR-generation purpose.

External Transmission

Medium
Category
Data Exfiltration
Content
| python3 -c "import json,sys; r=json.load(sys.stdin); print(r['file_infos'][0]['id'])")

# 2. Post into the thread
curl -s -X POST "https://mattermost.claw.jentic.ai/api/v4/posts" \
  -H "Authorization: Bearer $SHIRKA_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
f.write('\n'.join(lines))
        tmpfile = f.name
    try:
        r = subprocess.run(
            ['python3', decoder, tmpfile, output_path,
             '--scale', str(scale), '--quiet', str(quiet)],
            capture_output=True, text=True
Confidence
70% confidence
Finding
The script invokes `python3` and a sibling script using a relative interpreter/binary lookup and accepts a user-controlled `output_path`. While arguments are passed safely without a shell, this still creates execution-trust and file-write risk: a hijacked PATH could run an unexpected interpreter, and the helper script can write to arbitrary filesystem locations supplied by the caller.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
env['COLUMNS'] = '120'
    env['TERM'] = 'xterm-256color'

    proc = subprocess.Popen(
        ['openclaw', 'channels', 'login', '--channel', 'whatsapp'],
        stdin=slave_fd, stdout=slave_fd, stderr=slave_fd,
        close_fds=True, env=env
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Static analysis

No suspicious patterns detected.