Back to skill

Security audit

调整 token 鉴权机制

Security checks for vulnerabilities and agentic risk

Overview

The skill matches its paid API purpose, but it handles reusable payment-service credentials and prompts with weak scoping and unsafe disclosure paths.

Review this skill carefully before installing. Use it only with a trusted Fast Claw service URL, avoid non-HTTPS remote endpoints, do not pass API keys on the command line, treat ~/.fast-claw/api-key.json as a secret, and confirm before any purchase, top-up, invoke, or report command because those actions can spend credits and transmit prompt content.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fast_claw_client.py:51
Finding
API Key File Is Stored Without Enforced Restrictive Permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fast_claw_client.py`, lines 51-68 **Vulnerability Type**: Insecure local credential storage **Risk Level**: High ### Vulnerable Code ```python def write_local_api_key(api_key: str) -> None: path = api_key_path() path.parent.mkdir(parents=True, exist_ok=True) path.write_text( json.dumps( { "api_key": api_key, "token": api_key, "service_url": service_url(), "saved_at": datetime.now(timezone.utc).isoformat(), }, indent=2, ) + "\n", encoding="utf-8", ) ``` ### Technical Analysis The client persists a reusable API key in plaintext but does not explicitly restrict the permissions of either the containing directory or the credential file. `Path.mkdir()` and `Path.write_text()` rely on the process's ambient `umask`. Under a permissive `umask`, the resulting file may be readable by other local users. If the destination file already exists with overly broad permissions, rewriting it does not correct those permissions. The implementation also does not verify file ownership or reject a symbolic-link destination before writing. Persisting a key is part of the Skill's declared functionality, but allowing ambient filesystem settings to determine access exceeds the minimum exposure required for that functionality. ### Attack Path 1. A user runs `purchase`, `wait`, or `set-api-key`. 2. The client saves the returned API key to `~/.fast-claw/api-key.json` or a path selected through `FAST_CLAW_API_KEY_PATH`. 3. The file is created or retained with permissions determined by the current `umask` or its previous mode. 4. Another local user or process with filesystem access reads the JSON file. 5. The attacker extracts the reusable `api_key` value. 6. The attacker uses the key to query the account or submit authenticated paid-service requests. A maliciously prepared dest ...[truncated 650 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Create the credential directory with mode `0700` and verify that it is owned by the current user. - Create the credential file with mode `0600`, using low-level flags such as `os.open(..., O_CREAT | O_EXCL | O_WRONLY | O_NOFOLLOW, 0o600)` where supported. - For updates, write to a protected temporary file in the same directory, flush and synchronize it, set mode `0600`, and atomically replace the destination with `os.replace()`. - Explicitly call `os.chmod(path, 0o600)` when safely updating an existing regular file. - Reject symbolic links and non-regular files, and validate ownership before reading, writing, or deleting the credential file. - Consider using an operating-system credential store instead of a plaintext JSON file. - Avoid storing the same secret twice under both `api_key` and `token`; retain legacy compatibility during reads without duplicating the value during writes. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fast_claw_client.py:212
Finding
Complete API Keys Can Be Exposed Through Output, Shell History, and Process Arguments<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fast_claw_client.py`, lines 82-85, 212-224, 268-276, and 383-395 **Vulnerability Type**: Sensitive credential exposure **Risk Level**: High ### Vulnerable Code ```python def mask_api_key(api_key: str) -> str: if len(api_key) <= 12: return api_key return f"{api_key[:6]}...{api_key[-4:]}" ``` ```python def run_checkout_wait(session: Dict[str, object], args: argparse.Namespace) -> None: session_id = str(session["session_id"]) checkout_url = str(session["checkout_url"]) print(f"Checkout session: {session_id}") print(f"Checkout URL: {checkout_url}") if args.open_browser: webbrowser.open(checkout_url) completed = wait_for_completion(session_id, args.wait_seconds, args.poll_interval) api_key = completed.get("api_key") or completed.get("token") if api_key: write_local_api_key(str(api_key)) print(f"Saved API key to {api_key_path()}") print_json(completed) ``` ```python def cmd_set_api_key(args: argparse.Namespace) -> None: if not args.api_key: raise SystemExit("Provide --api-key.") write_local_api_key(args.api_key) print(f"Saved API key to {api_key_path()}") ``` ```python set_api_key_parser = subparsers.add_parser( "set-api-key", aliases=["set-token"], help="Manually save an API key", ) set_api_key_parser.add_argument("--api-key", dest="api_key", required=False, help="API key to save") set_api_key_parser.add_argument("--token", required=False, help="Legacy alias for API key") set_api_key_parser.set_defaults( func=lambda args: cmd_set_api_key( argparse.Namespace(api_key=args.api_key or args.token) ) ) ``` ### Technical Analysis Three credential-disclosure channels are present: 1. `mask_api_key()` returns short API keys unchanged, causing `status` to display the entire credential when its length is 12 characters or fewer. 2. Checkout completion data is printed through `print_json(c ...[truncated 2040 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Recursively redact `api_key`, `token`, `authorization`, and similar secret fields before printing any server response. - Never display a complete key, regardless of its length. For short values, replace the entire value with a fixed marker such as `[REDACTED]`. - After processing checkout completion, print a deliberately constructed response containing only non-sensitive fields such as status, balance, and session ID. - Add a secure input mode that reads the key from a no-echo prompt using `getpass.getpass()`. - Optionally accept the key through standard input or a protected file descriptor for automation. - Deprecate command-line secret arguments and clearly warn users that compatibility forms may expose credentials. - Add automated tests that assert API keys and legacy token values never appear in standard output, standard error, or exception messages. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/fast_claw_client.py:100
Finding
API Keys and User Prompts Can Be Sent to an Arbitrary or Plaintext Service Endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/fast_claw_client.py`, lines 21-22 and 100-116 **Vulnerability Type**: Unvalidated credential destination and insecure transport **Risk Level**: High ### Vulnerable Code ```python def service_url() -> str: return os.getenv("FAST_CLAW_SERVICE_URL", DEFAULT_SERVICE_URL).rstrip("/") ``` ```python def request_json( method: str, path: str, payload: Optional[Dict[str, object]] = None, api_key: Optional[str] = None, ) -> Dict[str, object]: headers = {"Accept": "application/json"} data = None if payload is not None: data = json.dumps(payload).encode("utf-8") headers["Content-Type"] = "application/json" if api_key: headers["X-API-Key"] = api_key headers["Authorization"] = f"Bearer {api_key}" request = urllib.request.Request( f"{service_url()}{path}", data=data, headers=headers, method=method.upper(), ) ``` ### Technical Analysis `FAST_CLAW_SERVICE_URL` fully controls the destination of authenticated requests. The code does not validate the scheme, hostname, port, or relationship between the configured endpoint and the origin associated with the saved key. Consequently, a modified environment can redirect `status`, `topup`, `invoke`, `report`, and `wait-report` requests to an attacker-controlled server. Both `X-API-Key` and `Authorization` contain the same credential, although the API documentation identifies `X-API-Key` as the primary header and the bearer header only as a compatibility mechanism. Sending both unnecessarily increases secret exposure. The client also does not require HTTPS for non-loopback destinations. Although the default `http://localhost:8033` endpoint is appropriate for a local service, the same plaintext scheme can be selected for a remote host. In that situation, credentials, account data, and user prompts can be exposed to network interception or manipulation. The audit fo ...[truncated 1832 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require HTTPS for every non-loopback service URL. Permit plaintext HTTP only for validated loopback hosts such as `localhost`, `127.0.0.1`, and `::1`. - Parse the endpoint with `urllib.parse.urlsplit()` and reject unsupported schemes, embedded credentials, fragments, malformed hosts, and unexpected redirects. - Prefer an explicit allowlist of trusted service origins. If arbitrary origins are required, obtain explicit user confirmation before sending a saved credential to a new origin. - Bind each cached API key to its original normalized service origin. Refuse to send that key when the active endpoint differs unless the user explicitly migrates or reauthorizes it. - Send only the required `X-API-Key` header. Enable the legacy bearer header only through an explicit compatibility option, rather than transmitting both by default. - Disable or tightly validate cross-origin redirects for authenticated requests so credentials cannot be forwarded to another host. - Display the normalized destination before the first authenticated request and warn clearly when a non-default endpoint is selected. - Document that invocation and report prompts are transmitted to the configured service and should not contain secrets unless the destination is trusted. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
t(f"Saved API key to {api_key_path()}")


def cmd_set_token(args: argparse.Namespace) -> None:
    write_local_api_key(args.token)
    print(f"Saved API key to {api_key_path()}")


def cmd_clear_token(_: argparse.Namespace) -> None:
    clear_local_api_key()
    print(f"Cleared API key file at {api_key_path()}")


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Fast Claw demo service client")
    subparsers = parser.add_subparsers(dest="command", required=True)

    status_parser = subparsers.add_parser("status", help="Inspect the saved API key and remote balance")
    status_parser.set_defaults(func=cmd_status)

    purchase_parser = subparsers.add_parser("purchase", help="Start a purchase for a new account API key")
    purchase_parser.add_argument("--account-name", required=True, help="Account name to create")
    purchase_parser.add_argument("--credits", type=int, default=10, help="Credits to buy")
    purchase_parser.add_argument("-
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Lp3

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding
The skill documents capabilities that involve reading environment variables, writing a local API key file, and making network requests, but it declares no explicit tool scope or permissions boundary. In an agent setting, this weakens least-privilege guarantees and can allow broader-than-expected access when handling credentials and external service calls.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The skill instructs saving a persistent API key to a local file but does not clearly warn that the key is a secret or describe risks such as local compromise, accidental disclosure, backups, or permissive file permissions. Because the same key is then reused for authenticated service calls and account top-ups, exposure could let another party spend credits or access account-backed operations.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The manifest explicitly states that the skill saves a local API key and calls a paid service, yet it provides no visible warning about persistent credential storage, billing implications, or the need for user consent. In this context, the absence of an explicit warning is security-relevant because the skill handles sensitive credentials and can trigger financial actions, making accidental or opaque use more dangerous.

Vague Triggers

Medium
Confidence
84% confidence
Finding
The default prompt broadly instructs use of the paid API skill to obtain or reuse a locally stored API key and call the microservice, but it does not define clear trigger conditions, user-consent requirements, or operational boundaries. This ambiguity can cause the agent to invoke a paid service or use persisted credentials in situations the user did not explicitly authorize, increasing the risk of unintended spending and credential misuse.

Natural-Language Policy Violations

Medium
Confidence
91% confidence
Finding
The entire skill reference is written in Chinese and does not indicate that other languages are available or that Chinese is a justified locale requirement. Under the policy, forcing a specific language without user opt-in is a natural-language policy violation.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The client persistently stores the API key in a local JSON file under the user's home directory without setting restrictive file permissions or warning the user that credentials are being cached on disk. On multi-user systems, shared environments, backups, or when the path is redirected via environment variables, this can expose a reusable bearer credential to unauthorized parties.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The skill tells the user to open a browser-based purchase or top-up checkout flow, including optional external success-url redirection, without a prominent warning that this leaves the local agent context and may disclose information to an external payment flow. This creates phishing, redirection, and privacy risks, especially if service URL or success URL values are changed from trusted defaults.

Static analysis

No suspicious patterns detected.