Back to skill

Security audit

SmartBill Invoicing

Security checks for vulnerabilities and agentic risk

Overview

This SmartBill invoicing skill is coherent, but it needs review because a configurable API URL can redirect SmartBill credentials and invoice data to an unapproved server.

Review this before installing in a production SmartBill account. Use only the default SmartBill API host, do not set MAVERICK_SMARTBILL_API_BASE or --base-url from untrusted input, keep debug mode off around real customer data, and require a human confirmation before --allow-final invoice creation.

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

Error
Location
scripts/smartbill_cli.py:57
Finding
SmartBill credentials and invoice data can be transmitted to an arbitrary endpoint<![CDATA[ ## Vulnerability Details **File Location**: `scripts/smartbill_cli.py`, lines 57-59, 108-110, 125-127, and 153-158 **Vulnerability Type**: Unrestricted authentication endpoint configuration **Risk Level**: High ### Vulnerable Code ```python username = args.username or os.getenv("MAVERICK_SMARTBILL_USERNAME") token = args.token or os.getenv("MAVERICK_SMARTBILL_TOKEN") base_url = args.base_url if args.base_url is not None else os.getenv("MAVERICK_SMARTBILL_API_BASE", DEFAULT_BASE_URL) ``` ```python def __init__(self, config: ClientConfig): self.config = config auth_value = f"{config.username}:{config.token}".encode("utf-8") self._auth_header = f"Basic {base64.b64encode(auth_value).decode('ascii')}" ``` ```python url = f"{self.config.base_url}{path}" if query: compact_query = {k: v for k, v in query.items() if v is not None} ``` ```python request = Request(url=url, data=payload, headers=headers, method=method) try: with urlopen(request, timeout=self.config.timeout_seconds) as response: response_body = response.read() ``` ### Technical Analysis The API base URL can be supplied through either `--base-url` or the `MAVERICK_SMARTBILL_API_BASE` environment variable. The implementation only checks that the resulting value is non-empty. It does not validate: - That the URL uses HTTPS. - That the destination host is `ws.smartbill.ro`. - That redirects remain on the trusted host. - That a custom endpoint has been explicitly approved for testing. The client constructs an HTTP Basic Authorization header containing the SmartBill username and API token and attaches it to requests sent to the selected base URL. Creating an invoice also sends the complete invoice payload, which may contain customer names, addresses, email addresses, VAT identifiers, product information, and financial data. Base64 encoding at lines 109-110 is normal HTTP Basic authentication and is not encryption. If the configured destination is controlled by an att ...[truncated 1766 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Enforce HTTPS and an explicit hostname allowlist for normal operation: ```python from urllib.parse import urlparse parsed = urlparse(base_url) if parsed.scheme != "https": raise CliError("SmartBill API base URL must use HTTPS.") if parsed.hostname != "ws.smartbill.ro": raise CliError("SmartBill API host must be ws.smartbill.ro.") ``` 2. Prefer removing `--base-url` from production-facing commands. If custom endpoints are required for development, place them behind an explicit option such as `--allow-custom-endpoint`. 3. Never send production credentials to a custom endpoint. Require separately supplied test credentials when custom endpoint mode is enabled. 4. Prevent cross-origin credential forwarding during redirects. Disable automatic redirects or verify that every redirect target retains the approved HTTPS scheme and hostname before resending authentication. 5. Normalize and validate the URL, including scheme, hostname, port, username information, and malformed or ambiguous URL forms. 6. Document that environment variables controlling network destinations are security-sensitive and must not be populated from untrusted agent instructions, invoice files, or user-provided payload fields. 7. Add tests confirming rejection of: - Plaintext HTTP URLs. - Non-SmartBill hosts. - URLs containing embedded user information. - Redirects to unapproved hosts. - Hostname-confusion values such as `ws.smartbill.ro.attacker.example`. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/smartbill_cli.py:135
Finding
Debug mode exposes customer and invoice data through process logs<![CDATA[ ## Vulnerability Details **File Location**: `scripts/smartbill_cli.py`, lines 135-149, 162-184, and 195-209 **Vulnerability Type**: Sensitive information exposure through debug logging **Risk Level**: Medium ### Vulnerable Code ```python # --- request debug log --- if self.config.debug: log_entry: Dict[str, Any] = { "smartbill_request": { "attempt": attempt + 1, "method": method, "url": url, "headers": {k: v for k, v in headers.items() if k != "Authorization"}, } } if payload is not None: try: log_entry["smartbill_request"]["body"] = json.loads(payload.decode("utf-8")) except Exception: log_entry["smartbill_request"]["body"] = payload.decode("utf-8", errors="replace") print(json.dumps(log_entry, ensure_ascii=False), file=sys.stderr) ``` ```python # --- response debug log --- if self.config.debug: try: response_body_log: Any = json.loads(response_body.decode("utf-8")) except Exception: response_body_log = response_body.decode("utf-8", errors="replace") if not expect_binary else f"<binary {len(response_body)} bytes>" print(json.dumps({ "smartbill_response": { "status": response.status, "headers": response_headers, "body": response_body_log, } }, ensure_ascii=False), file=sys.stderr) ``` ```python # --- error response debug log --- if self.config.debug: try: error_body_log: Any = json.loads(error_body.decode("utf-8")) except Exception: error_body_log = error_body.decode("utf-8", errors="replace") print(json.dumps({ "smartbill_response": { "status": exc.code, "headers": error_headers, "body": error_body_log, } }, ensure_ascii=False), file=sys.stderr) ``` ### Technical Analysis When debug mode is enabled using `--debug` or `MAVERICK_SMARTBILL_DEBUG`, the implementation ...[truncated 2527 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Replace complete payload logging with a field allowlist containing only operational metadata, such as request method, approved hostname, endpoint path, attempt number, HTTP status, response size, and request correlation ID. 2. Redact sensitive request fields recursively. At minimum, remove or mask: - `client.name` - `client.email` - `client.address` - VAT codes - Contact information - Invoice prices and free-text fields 3. Filter response headers. Log only headers needed for operation, such as the documented rate-limit headers. Do not emit cookies, authorization-related headers, or arbitrary server-provided metadata. 4. Avoid logging complete response bodies. Extract and log only approved fields, such as success state, invoice series, invoice number, and a sanitized error code. 5. If full payload logging must remain available, require a separate explicit acknowledgement such as `--unsafe-debug-sensitive-data`, display a warning, and prevent use in normal production workflows. 6. Ensure generated logs have restrictive permissions, limited retention, and no exposure to unrelated tenants or users. 7. Add tests confirming that names, email addresses, addresses, VAT codes, and authentication values do not appear in debug output. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (7)

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

Critical
Category
Data Flow
Content
request = Request(url=url, data=payload, headers=headers, method=method)
            try:
                with urlopen(request, timeout=self.config.timeout_seconds) as response:
                    response_body = response.read()
                    response_headers = dict(response.headers.items())
Confidence
95% confidence
Finding
The CLI allows the API base URL to be overridden from arguments or environment and then sends Basic-authenticated requests to that URL. If an attacker can influence MAVERICK_SMARTBILL_API_BASE or --base-url, they can redirect requests and exfiltrate the SmartBill username/token to an arbitrary server, which is especially relevant in agent or automation contexts where environment variables may be prompt-influenced or inherited.

Credential Access

High
Category
Privilege Escalation
Content
Two controls are applied in combination:

    1. Must have a .pdf suffix — prevents overwriting files that can never
       legitimately be PDFs (/etc/passwd, ~/.ssh/authorized_keys, …).
    2. Must resolve within an OpenClaw-allowed media root or the current
       working directory — prevents a prompt-injected agent from writing to
       arbitrary locations (e.g. ~/.ssh/authorized_keys.pdf) even when the
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Two controls are applied in combination:

    1. Must have a .pdf suffix — prevents overwriting files that can never
       legitimately be PDFs (/etc/passwd, ~/.ssh/authorized_keys, …).
    2. Must resolve within an OpenClaw-allowed media root or the current
       working directory — prevents a prompt-injected agent from writing to
       arbitrary locations (e.g. ~/.ssh/authorized_keys.pdf) even when the
Confidence
90% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
Two controls are applied in combination:

    1. Must have a .pdf suffix — prevents overwriting files that can never
       legitimately be PDFs (/etc/passwd, ~/.ssh/authorized_keys, …).
    2. Must resolve within an OpenClaw-allowed media root or the current
       working directory — prevents a prompt-injected agent from writing to
       arbitrary locations (e.g. ~/.ssh/authorized_keys.pdf) even when the
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill describes capabilities that access environment secrets, read/write local files, and make network requests to a billing API, but it does not declare any explicit tool scope or permissions boundary. This creates a least-privilege gap: an agent or runtime may grant broader access than intended, increasing the chance of credential exposure, unintended filesystem writes, or unauthorized invoice/API actions.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
When debug mode is enabled, the code logs full request bodies to stderr, which can include invoice contents, customer details, addresses, tax identifiers, and other billing data. In agent environments, stderr is often centrally collected, persisted, or exposed to users/operators, turning debugging into a data leakage channel.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The debug response logging prints full API response and error bodies to stderr, which may contain invoice data, account information, or other sensitive business records returned by SmartBill. Because these logs may be stored outside the user's control, sensitive server responses can be unintentionally disclosed.

Static analysis

No suspicious patterns detected.