Back to skill

Security audit

Api Odontosoft

Security checks for vulnerabilities and agentic risk

Overview

This dental scheduling skill appears purpose-aligned, but it handles patient data and an API token with weak destination validation and limited privacy disclosure.

Review this before installing in a real clinic environment. Only configure the official HTTPS Odontosoft API host, use a least-privileged token, confirm user authorization before patient lookup or booking, and ensure logging and privacy controls cover patient identifiers and appointment details.

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
index.js:42
Finding

Unrestricted API Base URL Can Expose Bearer Credentials and Patient Data

Content
View full analysis

Vulnerability Details

File Location: index.js:42-77, with attacker-influenced request construction at index.js:4, index.js:12, index.js:20, and index.js:33-39
Vulnerability Type: Unvalidated network destination receiving sensitive credentials and data
Risk Level: High

Vulnerable Code

js
async function apiFetch(url, method, body, token) {
  const opciones = {
    method,
    headers: {
      'Authorization': `Bearer ${token}`,
      'Content-Type':  'application/json',
      'Accept':        'application/json',
    },
  };

  if (body && ['POST', 'PUT', 'PATCH'].includes(method)) {
    opciones.body = JSON.stringify(body);
  }

  let res;
  try {
    res = await fetch(url, opciones);
  } catch (err) {
    return { error: true, message: `Error de red: ${err.message}` };
  }

  const texto = await res.text();
  let datos;

  try {
    datos = JSON.parse(texto);
  } catch {
    return { error: true, status: res.status, message: texto.slice(0, 500) };
  }

  if (!res.ok) {
    return { error: true, status: res.status, ...datos };
  }

  return datos;
}

The sensitive appointment request is constructed as follows:

js
return apiFetch(
  `${config.baseUrl}/api/agendar_turno.php`,
  'POST',
  { paciente_id, doctor_id, fecha, hora, motivo },
  config.apiKey
);

Technical Analysis

All request destinations are derived from the unrestricted config.baseUrl value. The code does not parse or validate the configured URL before attaching config.apiKey as a bearer credential and transmitting the request.

The manifest declares only the network:api.odontosoft.com.py permission, but its baseUrl configuration is a free-form required string. Consequently, the implementation itself does not guarantee the README's claim that requests are limited to api.odontosoft.com.py.

The runtime may independently enforce the manifest ...[truncated 2008 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove baseUrl from user-controlled configuration if the Skill only supports the production Odontosoft service. Use a fixed trusted origin instead.
  2. If multiple deployments must be supported, parse the value with new URL(config.baseUrl) and enforce:
    • protocol === 'https:';
    • an exact allowlist of approved hostnames;
    • an approved port or the default HTTPS port;
    • no embedded username or password;
    • no unexpected path, query string, or fragment in the configured origin.
  3. Construct request URLs with the URL API rather than string concatenation.
  4. Maintain independent runtime egress controls that permit only the approved Odontosoft host.
  5. Avoid forwarding authorization headers across redirects, or reject redirects entirely for authenticated API calls.
  6. Validate that the final request origin is approved immediately before attaching the bearer token.
  7. Add tests confirming rejection of HTTP URLs, attacker-controlled hosts, internal IP addresses, malformed URLs, alternate ports, and credential-bearing URLs.
  8. Rotate the API token if there is evidence that the Skill has operated with an untrusted baseUrl.

T09 · Insecure Skill Coding Practices

Warning
Location
index.js:16
Finding

Patient Document Number Is Exposed in the Request URL

Content
View full analysis

Vulnerability Details

File Location: index.js:16-20
Vulnerability Type: Sensitive personal information transmitted in a URL query string
Risk Level: Medium

Vulnerable Code

js
async buscar_paciente({ documento }, { config }) {
  if (!documento) {
    return { error: true, message: 'Se requiere el campo documento' };
  }
  const qs = new URLSearchParams({ documento });
  return apiFetch(`${config.baseUrl}/api/buscar_paciente.php?${qs}`, 'GET', null, config.apiKey);
},

Technical Analysis

The patient document number is personally identifiable information. The Skill embeds this value directly in a GET request URL as the documento query parameter.

HTTPS protects the request from passive network interception when correctly configured, but it does not prevent the complete URL from being retained by systems that process the request. Query strings are commonly recorded in reverse-proxy logs, web-server access logs, application monitoring, tracing systems, security appliances, and error telemetry.

Patient lookup requires transmitting an identifier to the Odontosoft service, but placing that identifier in the URL is not the minimum-exposure approach. A JSON request body provides better separation from infrastructure that logs request targets by default.

Attack Path

  1. A user invokes buscar_paciente with a real document number.
  2. The Skill constructs a URL such as /api/buscar_paciente.php?documento=12345678.
  3. The request traverses the API gateway, reverse proxy, monitoring infrastructure, or application server.
  4. One or more systems record the complete request URL in logs or telemetry.
  5. An attacker or unauthorized operator with access to those records retrieves the patient's document number.

This attack does not require interception of encrypted network traffic; access to any infrastructure component that records request URLs may be sufficient.

Impact Assessment

...[truncated 448 chars]

Remediation
View remediation

Remediation Suggestions

  1. Change the patient-search endpoint to accept a POST request with the document number in a JSON body, for example:

    js
    return apiFetch(
      `${validatedBaseUrl}/api/buscar_paciente.php`,
      'POST',
      { documento },
      config.apiKey
    );
    
  2. Ensure the API, reverse proxy, observability platform, and error-reporting systems do not record sensitive request bodies.

  3. If the existing GET endpoint cannot be changed, configure every intermediary to redact the documento query parameter before logging.

  4. Apply access controls, encryption, retention limits, and audit logging to any records that may contain patient identifiers.

  5. Do not include full request URLs containing patient identifiers in network-error messages or diagnostic telemetry.

  6. Add tests verifying that patient document numbers do not appear in generated URLs, logs, or returned error messages.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (7)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 8)May include surrounding context.

md
Conecta con la API REST de Odontosoft usando las herramientas definidas en `manifest.json`.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README documents searching for patients by document number and scheduling dental appointments through a third-party API, but it does not warn that personally identifiable information and potentially health-related data will be transmitted off-platform. In a healthcare context, this omission increases privacy and compliance risk because operators may use the skill without understanding the sensitivity of the data flow or confirming they are authorized to send it.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill exposes network-capable operations against an external healthcare scheduling API but does not declare any explicit tool scope such as permissions or allowed-tools. This weakens policy enforcement and reviewability, increasing the risk that the skill can invoke broader or unintended network actions than users or operators expect, especially in a context involving patient and appointment data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill sends the documento parameter in a network request to buscar_paciente.php, which appears to be personally identifying patient data. The file contains no confirmation prompt, user-facing log/print, or explanatory comment/docstring disclosing that sensitive data will be transmitted.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The agendar_turno operation performs a POST request containing paciente_id, doctor_id, fecha, hora, and motivo, which can affect user records and includes sensitive medical/scheduling context. There is no confirmation prompt, user-visible notice, or inline documentation warning that this data is being sent to an external backend.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest explicitly enables patient lookup by document number and dental appointment booking, which involves personally identifiable information and health-related data, yet it provides no user-facing privacy warning, consent guidance, or data-handling constraints. In a healthcare context, this omission increases the risk of unauthorized disclosure, inappropriate collection, or misuse of sensitive patient information by downstream agents or users.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

Natural-language content throughout the file is entirely Spanish, including user-facing invocation examples, with no indication that the skill supports other languages or is intentionally limited to Spanish-speaking users. This can violate language/locale policy when a skill appears to force a specific language without opt-in or justification.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.