Back to skill

Security audit

Cliento Booker

Security checks for vulnerabilities and agentic risk

Overview

This booking skill is purpose-aligned, but it needs Review because its registration helper can fetch arbitrary URLs and print raw page contents while the workflow handles live bookings and personal data.

Review this skill carefully before installing. Use it only with Cliento booking URLs you trust, confirm every live booking step yourself, and avoid saving contact details in USER.md unless you intentionally want them reused. Be aware that registration currently fetches whatever URL is supplied and that booking confirmation sends your contact details, note, and possibly a PIN to Cliento APIs.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • 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
  • 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 (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/cliento.py:18
Finding
Unrestricted URL Fetching Enables SSRF and Local File Disclosure## Vulnerability Details **File Location**: `scripts/cliento.py:18-36` **Vulnerability Type**: Server-Side Request Forgery and unrestricted local resource access **Risk Level**: High ### Vulnerable Code ```python def request(url, payload=None, method="GET"): req = urllib.request.Request(url, method=method) if payload: req.add_header('Content-Type', 'application/json') data = json.dumps(payload).encode('utf-8') else: data = None try: with urllib.request.urlopen(req, data=data) as response: return response.read().decode('utf-8') except urllib.error.URLError as e: if hasattr(e, 'read'): return e.read().decode('utf-8') return str(e) def register(url): html = request(url) print(html) # Raw html output for the agent to parse ``` The associated workflow in `SKILL.md:22-24` passes a user-provided URL to this function: ```markdown When the user provides a Cliento URL to register: 1. Verify the URL is safe, then fetch the raw HTML by executing `python3 scripts/cliento.py register <URL>`. 2. Parse the embedded Next.js JSON (inside `<script id="__NEXT_DATA__" type="application/json">`) to extract the Company ID, available services, and barbers. ``` ### Technical Analysis The `register` command passes an externally supplied URL directly to `urllib.request.urlopen` without enforcing an allowed scheme, hostname, port, resolved address, or redirect destination. The documentation asks the agent to verify safety, but the executable security boundary does not perform that validation. Depending on the handlers enabled by Python's `urllib`, this can permit both HTTP requests to internal services and retrieval of non-HTTP resources such as `file:` URLs. Automatic HTTP redirects also create a validation-bypass risk if only the initial URL is inspected outside the script. The response body is returned ...[truncated 1134 chars]
Remediation
## Remediation Suggestions - Enforce an explicit allowlist of required HTTPS Cliento hostnames inside `cliento.py`. - Reject all schemes other than HTTPS, including `file`, `ftp`, and `data`. - Reject URLs containing user information, custom ports, or other unnecessary components. - Resolve the hostname before connecting and reject loopback, private, link-local, multicast, unspecified, and reserved IP ranges for both IPv4 and IPv6. - Disable automatic redirects or validate the scheme, hostname, port, and resolved destination at every redirect hop. - Consider DNS rebinding protections by connecting only to the validated address while preserving the expected TLS hostname. - Add connection/read timeouts and strict response-size limits. - Return a clear validation error rather than relying on agent-side inspection. - Add tests for local-file URLs, localhost, private addresses, IPv6 literals, encoded hostnames, alternate ports, and redirect-based bypasses.

T01 · Skill Instruction Hijacking

Error
Location
scripts/cliento.py:34
Finding
Untrusted Remote HTML Is Passed Directly to the Agent## Vulnerability Details **File Location**: `scripts/cliento.py:34-36` **Vulnerability Type**: Indirect prompt injection through untrusted web content **Risk Level**: High ### Vulnerable Code ```python def register(url): html = request(url) print(html) # Raw html output for the agent to parse ``` The workflow explicitly instructs the agent to parse the returned page in `SKILL.md:22-24`: ```markdown When the user provides a Cliento URL to register: 1. Verify the URL is safe, then fetch the raw HTML by executing `python3 scripts/cliento.py register <URL>`. 2. Parse the embedded Next.js JSON (inside `<script id="__NEXT_DATA__" type="application/json">`) to extract the Company ID, available services, and barbers. ``` ### Technical Analysis The script emits the complete contents of a remotely controlled HTML document into agent-visible tool output. It does not parse the required `__NEXT_DATA__` object within a non-agent component, constrain output to a defined schema, remove unrelated text, or identify the content as untrusted data. A malicious or compromised page can therefore include instruction-like content intended to influence the agent. Such text may direct the agent to ignore the booking workflow, disclose workspace data, invoke unrelated tools, or alter booking parameters. The risk is amplified by the unrestricted registration URL, which allows an attacker to select the content source. This is an indirect prompt-injection boundary failure: web content that should be treated exclusively as data is placed in the same operational context used by the agent to decide subsequent actions. ### Attack Path 1. An attacker hosts or controls a page containing valid-looking Cliento data and malicious natural-language instructions. 2. The attacker provides that page as a store registration URL. 3. The agent runs the `register` command. 4. The script downloads and prints the entire HTML document. ...[truncated 709 chars]
Remediation
## Remediation Suggestions - Do not return raw HTML for agent-side interpretation. - Parse the `script` element with ID `__NEXT_DATA__` inside `cliento.py` using a non-executing HTML parser. - Decode the embedded JSON and validate it against a strict schema containing only required company, service, and resource fields. - Return a bounded JSON object rather than arbitrary text or markup. - Reject missing, malformed, excessively large, or unexpectedly structured data. - Treat all names and descriptions obtained remotely as untrusted display data; never interpret them as operational instructions. - Combine this change with strict HTTPS hostname validation for registration URLs. - Ensure that tool-output handling explicitly distinguishes untrusted content from system, developer, user, and Skill instructions.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cliento.py:10
Finding
Booking PII and PINs Are Exposed Through Process Arguments## Vulnerability Details **File Location**: `scripts/cliento.py:10-15, 98-108` **Vulnerability Type**: Plaintext sensitive data exposure through command-line arguments **Risk Level**: Medium ### Vulnerable Code ```python def usage(): print("""Usage: cliento.py <command> [args] Commands: register <url> slots <company_id> <service_id> <from_date> <to_date> [resource_id] reserve <company_id> <slot_key> confirm <company_id> <cb_uuid> <first_name> <last_name> <email> <phone> <note> <booked_specific> [pin]""") sys.exit(1) ``` ```python if __name__ == "__main__": if len(sys.argv) < 2: usage() cmd = sys.argv[1] args = sys.argv[2:] try: if cmd == "register": register(*args) elif cmd == "slots": slots(*args) elif cmd == "reserve": reserve(*args) elif cmd == "confirm": confirm(*args) else: usage() except Exception as e: print(json.dumps({"error": str(e)})) ``` The documented invocation in `SKILL.md:54-60` requires the same sensitive values to be supplied on the command line: ```markdown 1. Execute the confirmation POST sequence to finalize the booking using the script: `python3 scripts/cliento.py confirm <company_id> <cb_uuid> "<first_name>" "<last_name>" "<email>" "<phone>" "<note>" "<booked_specific_true_false>"` 2. If the API returned that a Pin is required, the script output will notify you and you must ask the user for the pin, then append it to the args. ``` ### Technical Analysis First name, last name, email address, phone number, free-form booking note, and an optional PIN are passed through `sys.argv`. Command-line arguments are not an appropriate confidential-dat ...[truncated 1597 chars]
Remediation
## Remediation Suggestions - Remove sensitive booking fields from command-line arguments. - Accept confirmation data as structured JSON over standard input. - If standard input is unavailable, use a temporary file created with mode `0600`, verify ownership and permissions, and securely delete it immediately after use. - Keep only non-sensitive operation selectors in `sys.argv`. - Avoid printing submitted PII, PINs, or complete upstream responses that may repeat those values. - Add centralized redaction for errors, telemetry, and diagnostic output. - Configure the execution layer not to retain sensitive standard input or unredacted command transcripts. - Minimize collection and retention of optional notes. - Document which data is transmitted to Cliento and obtain user confirmation immediately before final submission.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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 (4)

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill performs networked booking operations and invokes a local Python script, but it does not declare any explicit tool scope or allowed-tools boundaries. That creates an over-privileged execution model where an agent may invoke shell/network capabilities more broadly than intended, increasing the blast radius if the skill is misused or prompt-injected through user-supplied booking URLs or stored data.

Vague Triggers

Medium
Confidence
90% confidence
Finding
The invocation text is broad enough to trigger on common booking-related requests, which can cause the skill to activate in situations the user did not clearly intend. Because this skill can read local files, access network resources, and ultimately execute live bookings, over-broad triggering raises the risk of unintended sensitive actions or premature collection/use of personal data.

External Transmission

Medium
Category
Data Exfiltration
Content
else:
        data = None
    try:
        with urllib.request.urlopen(req, data=data) as response:
            return response.read().decode('utf-8')
    except urllib.error.URLError as e:
        if hasattr(e, 'read'):
Confidence
92% confidence
Finding
The generic request() helper performs unrestricted outbound requests, and register() passes a user-supplied URL directly into it. This creates a server-side request forgery risk: an attacker could supply arbitrary internal or cloud-metadata URLs, causing the agent environment to fetch sensitive resources and print the raw response back to the caller.

Missing User Warnings

Medium
Confidence
88% confidence
Finding
The confirm flow transmits personally identifiable information including name, email, phone, note, and PIN to remote API endpoints, but the script provides no confirmation prompt, warning message, or explanatory docstring/comment for users invoking this action. The only comments are internal step labels, which do not disclose the privacy impact of sending customer data off-host.

Static analysis

No suspicious patterns detected.