Back to skill

Security audit

Mercado Público ChileCompra

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for ChileCompra work, but it needs review because optional API caching can save the API ticket to disk in plaintext.

Install only if you are comfortable granting this skill access to ChileCompra workflows and an API ticket. Keep caching disabled unless the cache code is fixed to redact the ticket, use a private cache directory, and rotate any ticket that may have been cached in a shared or backed-up location. Require explicit user approval before any offer, quotation, purchase-order, cancellation, complaint, or other state-changing portal action.

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

Warning
Location
scripts/mercado_publico_api.py:84
Finding

API Ticket Persisted in Plaintext Cache Metadata

Content
View full analysis

Vulnerability Details

File Location: scripts/mercado_publico_api.py, lines 84–88 and 118–126
Vulnerability Type: Plaintext persistence of sensitive authentication material
Risk Level: Medium

Vulnerable Code

python
def build_url(path: str, ticket: str, params: dict[str, Any]) -> str:
    clean = {k: str(v) for k, v in params.items() if v is not None and str(v) != ""}
    clean["ticket"] = ticket
    query = urllib.parse.urlencode(clean)
    return f"{API_BASE}{path}?{query}"
python
def _write_cache(cache_path: Path, url: str, payload: Any) -> None:
    cache_path.parent.mkdir(parents=True, exist_ok=True)
    data = {
        "url": _normalize_url_for_cache(url),
        "fetched_at": int(time.time()),
        "payload": payload,
    }
    tmp_path = cache_path.with_suffix(".tmp")
    tmp_path.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
    tmp_path.replace(cache_path)

The vulnerable cache write is reached at lines 155–156:

python
payload = fetch_with_retry(url, timeout, max_retries, backoff_seconds)
_write_cache(cache_path, url, payload)

Technical Analysis

The script correctly obtains MERCADO_PUBLICO_API_TICKET from an environment variable and sends it to the declared official Mercado Público HTTPS API. That network transmission is necessary for the Skill’s read-only API functionality and does not, by itself, indicate unauthorized exfiltration.

However, build_url() places the ticket in the URL query string. When the optional cache is enabled through --cache-ttl, _write_cache() stores the complete normalized URL in the cache document. URL normalization sorts and re-encodes the query parameters but does not remove or redact the ticket parameter. Consequently, the API ticket is written to disk in recoverable plaintext.

The cache destination is also configurable through --cache-dir. The implementation creates directories and files without explicitly enforcing owner-only permiss ...[truncated 2194 chars]

Remediation
View remediation

Remediation Suggestions

  1. Never store the ticket in cache metadata. Remove the ticket parameter before serializing the URL:
python
def _redact_url(url: str) -> str:
    parsed = urllib.parse.urlparse(url)
    pairs = urllib.parse.parse_qsl(parsed.query, keep_blank_values=True)
    safe_pairs = [
        (key, "[REDACTED]" if key.lower() == "ticket" else value)
        for key, value in pairs
    ]
    return urllib.parse.urlunparse(
        (
            parsed.scheme,
            parsed.netloc,
            parsed.path,
            "",
            urllib.parse.urlencode(safe_pairs),
            "",
        )
    )

Store _redact_url(url) rather than _normalize_url_for_cache(url).

  1. Exclude the credential from cache-key material. Construct cache keys from the endpoint and non-secret filters only. This avoids unnecessarily processing authentication material as persistent-cache identity.

  2. Separate request authentication from cache identity. Prefer passing a structured endpoint and parameter dictionary into the cache layer, with the ticket added only at the network-request boundary.

  3. Apply restrictive filesystem permissions. Create the cache directory with mode 0700 and cache files with mode 0600, while accounting for cross-platform behavior and existing directories.

  4. Use a safe default cache location. Prefer a user-private cache directory rather than a project-relative directory that may be shared, archived, or accidentally committed.

  5. Warn about custom cache locations. Document that --cache-dir must not point to shared, synchronized, publicly served, or version-controlled directories.

  6. Remove existing exposed cache entries. Delete cache files created by affected versions and rotate or revoke any API ticket that may have been stored in a location accessible to untrusted parties.

  7. Add regression tests. Verify that neither cache file contents nor cache filenames contain the literal ticket and th ...[truncated 61 chars]

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (15)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill claims broad authenticated portal capabilities, including login/session handling, OTP-related flows, and portal actions, but the described implementation appears limited to reference-reading plus a read-only public API helper. This mismatch is dangerous because operators or higher-level agents may trust the skill to safely handle sensitive authenticated workflows it cannot actually enforce, leading to unsafe manual workarounds, accidental credential exposure, or incorrect state-changing guidance.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill advertises access to environment variables, file operations, and networked API use, but does not declare explicit tool scope such as permissions or allowed-tools. In an agent setting, missing scope boundaries can allow the runtime or downstream orchestration to grant broader capabilities than intended, increasing the blast radius if the skill is misused or prompt-injected.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The entire skill document is written as operational guidance in Spanish and does not indicate that users may choose another language or that the skill is intentionally limited to Spanish-speaking contexts. Under the stated policy, forcing a specific language without user opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The file’s natural-language instructions, guardrails, and operational guidance are all presented in Spanish, and there is no indication that the user can opt into another language or that the skill is intentionally restricted to a Spanish-speaking context. Under the policy, forcing a specific language without opt-in is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file's natural-language instructions are entirely in Spanish, including operational guidance and safety-related steps. Under the language/locale policy, forcing a specific language without user opt-in or a documented justification can be a policy violation.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/api-publica.md (reported line 45)May include surrounding context.

md
from pathlib import Path
from typing import Any

API_BASE = "https://api.mercadopublico.cl/servicios/v1"
DEFAULT_TIMEOUT = 15
DEFAULT_MAX_RETRIES = 2
DEFAULT_BACKOFF_SECONDS = 1.0

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/api-publica.md (reported line 48)May include surrounding context.

md
from pathlib import Path
from typing import Any

API_BASE = "https://api.mercadopublico.cl/servicios/v1"
DEFAULT_TIMEOUT = 15
DEFAULT_MAX_RETRIES = 2
DEFAULT_BACKOFF_SECONDS = 1.0

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/api-publica.md (reported line 52)May include surrounding context.

md
from pathlib import Path
from typing import Any

API_BASE = "https://api.mercadopublico.cl/servicios/v1"
DEFAULT_TIMEOUT = 15
DEFAULT_MAX_RETRIES = 2
DEFAULT_BACKOFF_SECONDS = 1.0

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/api-publica.md (reported line 64)May include surrounding context.

md
from pathlib import Path
from typing import Any

API_BASE = "https://api.mercadopublico.cl/servicios/v1"
DEFAULT_TIMEOUT = 15
DEFAULT_MAX_RETRIES = 2
DEFAULT_BACKOFF_SECONDS = 1.0

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/mercado_publico_api.py (reported line 21)May include surrounding context.

python
from pathlib import Path
from typing import Any

API_BASE = "https://api.mercadopublico.cl/servicios/v1"
DEFAULT_TIMEOUT = 15
DEFAULT_MAX_RETRIES = 2
DEFAULT_BACKOFF_SECONDS = 1.0

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The helper writes full API responses to local disk when caching is enabled, and the payload may contain procurement, supplier, buyer, or order data that can be sensitive in the operator's environment. Because the cache is silent and there are no permission hardening, redaction, or user-facing warnings, another local user, backup system, or diagnostic bundle could access data that the operator did not realize was being persisted.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file includes imperative operational instructions in Spanish such as 'siempre, para decidir estado/transición e intervención humana' and 'Leer ... para ejecutar flujo operativo mínimo' without indicating that language selection is optional. This can violate a language/locale policy when users or operators are not given a choice of language for core instructions.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
82% confidence
Finding

Todo el contenido operativo de la skill está redactado en español y no indica que el usuario pueda elegir otro idioma ni que exista una limitación de locale documentada. Según la política indicada, forzar un idioma sin opt-in puede constituir una violación de política lingüística.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The file’s instructions and labels are entirely in Spanish, but the document does not state that the skill is Spanish-only, region-specific, or that users may choose their preferred language. Under the policy, forcing a specific language without user opt-in can be a natural-language policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

This file contains user-facing natural-language instructions entirely in Spanish, and there is no indication that the skill is region-specific or that users can opt into this locale. Under the policy rule for language/locale, forcing a specific language without user choice can be a natural-language policy violation.

Content

No source excerpt is available for this finding.