Back to skill

Security audit

TinkClaw

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated financial-analysis purpose, but its helper can send API credentials and user prompts to an arbitrary environment-configured URL.

Review before installing. Use this only in an environment where TINKCLAW_API_URL cannot be tampered with, avoid setting it unless you fully trust the destination, and do not include secrets, account details, or confidential trading strategies in ask prompts. Prefer a version that pins or allowlists https://tinkclaw.com and only sends Authorization headers to endpoints that need them.

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 (1)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/tinkclaw.py:42
Finding

Unvalidated API Base URL Can Expose Bearer Credentials and User Prompts

Content
View full analysis

Vulnerability Details

File Location: scripts/tinkclaw.py:20, scripts/tinkclaw.py:42-59, and scripts/tinkclaw.py:112-120
Vulnerability Type: Unvalidated destination for sensitive network requests
Risk Level: High

Vulnerable Code

python
API_BASE = os.getenv("TINKCLAW_API_URL", "https://tinkclaw.com")
python
def _request(path: str, method: str = "GET", body: dict | None = None, auth_key: str = "") -> dict:
    """Make an authenticated request to TinkClaw API."""
    url = f"{API_BASE}{path}"
    headers = {"Content-Type": "application/json"}
    key = auth_key or API_KEY
    if key:
        headers["Authorization"] = f"Bearer {key}"

    data = json.dumps(body).encode() if body else None
    req = urllib.request.Request(url, data=data, headers=headers, method=method)

    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode())
python
def cmd_ask(question: str):
    """Ask the Brain API a natural language question."""
    if not API_KEY:
        print("ERROR: Brain API requires an API key. Get one at https://tinkclaw.com/docs")
        sys.exit(1)

    data = _request("/v1/chat/completions", method="POST", body={
        "model": "tinkclaw-1",
        "messages": [{"role": "user", "content": question}],
        "stream": False,
    })

Technical Analysis

The destination of every API request is derived from the unrestricted TINKCLAW_API_URL environment variable. The code does not validate the URL scheme, hostname, port, or resolved destination before sending the request.

_request automatically adds TINKCLAW_API_KEY as a bearer credential whenever that key is present. Consequently, changing TINKCLAW_API_URL redirects authenticated requests to an arbitrary destination. A plain HTTP URL is also accepted, allowing credentials and request bodies to travel without transport enc ...[truncated 2465 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin the production API origin. Use a constant trusted origin such as https://tinkclaw.com rather than accepting an unrestricted environment-provided URL.

  2. Strictly validate any required development override. Parse the URL and require:

    • The https scheme.
    • A hostname from an explicit allowlist.
    • No embedded username or password.
    • An approved port, normally 443.
    • No fragments or unexpected base paths.
  3. Make non-production overrides explicit. Gate custom endpoints behind a separate development-only option that is disabled by default, and never transmit production credentials when an override is active.

  4. Apply endpoint-specific authentication. Add the bearer header only for endpoints that require authentication. Public Signal Market endpoints should be called without TINKCLAW_API_KEY.

  5. Separate credential scopes. Ensure SmartChart and Signal Market requests use only their respective keys. Avoid fallback logic that silently applies the primary key to every request.

  6. Protect free-form user content. Clearly notify users that ask transmits their question to an external service, and advise them not to include secrets, account credentials, personal information, or confidential trading data.

  7. Prevent plaintext transmission. Reject all non-HTTPS destinations before constructing or sending a request.

  8. Fail closed. If origin validation fails, terminate without sending the authorization header or request body, and return an error that does not reveal credential values.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Tainted flow: 'req' from os.getenv (line 52, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Confidence
92% confidence
Finding

The request destination is derived from the TINKCLAW_API_URL environment variable and the code automatically attaches the bearer token from environment variables to that destination. If an attacker can influence the environment or skill configuration, they can redirect requests to an arbitrary host and exfiltrate API credentials or send sensitive query content to an untrusted endpoint. In a skill that brokers financial queries and remote LLM prompts, this makes the issue more dangerous because both secrets and user-supplied market questions may be exposed off-platform.

Content

Scanner excerpt · scripts/tinkclaw.py (reported line 55)May include surrounding context.

python
req = urllib.request.Request(url, data=data, headers=headers, method=method)

    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read().decode())
    except urllib.error.HTTPError as e:
        error_body = e.read().decode() if e.fp else ""

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill requires environment access for API keys and makes outbound network requests through its helper script, but the manifest does not declare an explicit tool scope such as permissions or allowed-tools. This weakens least-privilege controls because an agent may invoke the skill with broader capabilities than users expect, increasing the risk of secret exposure or unintended external requests.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The ask command forwards arbitrary natural-language input directly to a remote API without any user-facing notice that their prompt will leave the local environment. Users may include proprietary trading strategies, account details, or other sensitive information in market-analysis questions, creating an avoidable privacy and data-handling risk. Given this skill's financial context, prompts are more likely than usual to contain commercially sensitive information.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.