Back to skill

Security audit

AI Agent Cost Optimization Cookbook: Cut Your Agent Bills by 60% Without Sacrificing Quality

Security checks for vulnerabilities and agentic risk

Overview

This is a non-executable cost-optimization guide with disclosed examples, though users should harden one batch-file example and avoid providing an unnecessary wallet address.

Before installing, understand that this is a guide, not an executable integration. Do not provide a wallet address unless the platform requires it for a clear reason, and if you copy the batch API sample into real code, replace the /tmp timestamp filename with secure temporary-file handling and cleanup.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:837
Finding

Predictable Temporary File Permits Symlink Attacks and Retains Sensitive Batch Data

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 837-845
Vulnerability Type: Unsafe temporary-file handling
Risk Level: Medium

Vulnerable Code

python
# Write JSONL file for batch submission
batch_file = f"/tmp/batch_{int(time.time())}.jsonl"
with open(batch_file, "w") as f:
    for task in self._queue:
        f.write(json.dumps(task) + "\n")

# Upload and create batch
with open(batch_file, "rb") as f:
    uploaded = self.client.files.create(file=f, purpose="batch")

Technical Analysis

The batch filename is derived from the current Unix timestamp and placed directly in the shared /tmp directory. This makes the path predictable and allows another local user to anticipate candidate filenames.

The first open() call uses ordinary write mode without exclusive creation, symlink rejection, or verification that the resulting file is a newly created regular file. On systems where symbolic links are followed, an attacker can pre-create the expected path as a symbolic link to another file. The process running this example would then truncate and overwrite the linked file with its own privileges.

The file contains serialized batch tasks, including the complete messages supplied to enqueue(). These messages may contain prompts, conversation content, customer information, or other application data. The example does not delete the temporary file after upload, so this information can remain on disk beyond the required lifetime. Its readability depends on the process umask and host configuration.

Attack Path

  1. An attacker with local access monitors the system clock and predicts a filename such as /tmp/batch_1760000000.jsonl.
  2. Before the victim calls submit(), the attacker creates that path as a symbolic link to a file writable by the victim process.
  3. The victim executes open(batch_file, "w"), which follows the symbolic link.
  4. The linked target is truncated and replac ...[truncated 1004 chars]
Remediation
View remediation

Remediation Suggestions

  • Use Python's tempfile module to generate an unpredictable, atomically created file.
  • Set restrictive permissions, ideally 0600, so only the creating account can read or write the file.
  • Delete the file in a finally block immediately after the upload attempt.
  • Avoid reopening a path where possible; retain and reuse the securely created file descriptor.
  • Ensure batch data is not written to disk at all when the client library supports in-memory streams.

Example hardening approach:

python
import json
import os
import tempfile

path = None
try:
    with tempfile.NamedTemporaryFile(
        mode="w",
        encoding="utf-8",
        suffix=".jsonl",
        prefix="agent_batch_",
        delete=False,
    ) as tmp:
        path = tmp.name
        os.chmod(path, 0o600)
        for task in self._queue:
            tmp.write(json.dumps(task) + "\n")

    with open(path, "rb") as batch_stream:
        uploaded = self.client.files.create(
            file=batch_stream,
            purpose="batch",
        )
finally:
    if path is not None:
        try:
            os.unlink(path)
        except FileNotFoundError:
            pass

T05 · Unauthorized Access and Privilege Escalation

Note
Location
SKILL.md:14
Finding

Unnecessary Wallet Address Environment Requirement Violates Least Privilege

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 14-20 and 29-30
Vulnerability Type: Unnecessary credential and environment access declaration
Risk Level: Low

Vulnerable Configuration

yaml
credentials: [WALLET_ADDRESS]
metadata:
  openclaw:
    requires:
      env:
        - WALLET_ADDRESS
    primaryEnv: WALLET_ADDRESS

The document separately describes the declared value as follows:

markdown
> **Referenced credentials** (you supply these in your own environment):
> - `WALLET_ADDRESS`: Blockchain wallet address for receiving payments (public address only — no private keys)

Technical Analysis

The Skill declares WALLET_ADDRESS as a required environment value and its primary environment setting. However, the audited document contains no example that reads or uses this variable. The guide's cost-optimization functionality therefore does not demonstrate a legitimate technical need for access to the identifier.

A public blockchain address is not equivalent to a private key and ordinarily cannot authorize blockchain transactions by itself. Nevertheless, it is a persistent financial identifier that can be correlated with public transaction history, balances, counterparties, and activity patterns. Requiring it when it is unused unnecessarily expands the data made available to the Skill context and violates least-privilege principles.

Attack Path

  1. A user or host loads the Skill and processes its declared environment requirements.
  2. The host requests or injects the user's WALLET_ADDRESS because the metadata marks it as required and primary.
  3. The identifier becomes available to the Skill execution context even though none of the documented cost-optimization examples require it.
  4. Any future code added to that context, or another component with access to the same environment, could read and correlate the address with public blockchain activity.

The audited ...[truncated 658 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove WALLET_ADDRESS from credentials, requires.env, and primaryEnv because the audited guide does not use it.
  • If a future optional payment feature legitimately needs the address, make the setting optional and document the exact operation that consumes it.
  • Do not classify public identifiers as credentials unless the platform requires that designation for a concrete feature.
  • Isolate any future payment integration from the guide's core cost-optimization functionality.
  • Never request wallet private keys, seed phrases, signing credentials, or unrestricted wallet-provider access.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep

Static analysis

No suspicious patterns detected.