Back to skill

Security audit

calendarizador-urgencias-formula100k

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent urgency-calendar generator, but it needs review because it relies on an undeclared external bridge skill and can create Excel files from web results without neutralizing hidden spreadsheet formulas.

Install only if you are comfortable with Tavily/Composio web searches using the product brief and with the skill reading narrowly relevant local business context from cerebro/. Treat generated Excel files as untrusted until the spreadsheet-writing code escapes formula-like values, and review any referenced f100k-puente bridge skill before relying on this package.

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/build_xlsx.py:83
Finding

Untrusted Tavily Results Can Cause Spreadsheet Formula Injection

Content
View full analysis

Vulnerability Details

File Location: scripts/build_xlsx.py:83-91, 333-347
Vulnerability Type: Spreadsheet formula injection
Risk Level: Medium

Complete Code Snippet

python
def fill_row(ws, row: int, values: list, fill: PatternFill, height: int = None) -> None:
    for j, v in enumerate(values, start=1):
        cell = ws.cell(row=row, column=j, value=v)
        cell.alignment = WRAP
        cell.border = BORDER
        cell.fill = fill
    if height:
        ws.row_dimensions[row].height = height

The vulnerable helper is used to write Tavily source metadata directly into workbook cells:

python
fuentes = data.get("fuentes_tavily") or []
if fuentes:
    ws7 = wb.create_sheet("Fuentes Tavily")
    style_title(ws7, 1, 2, "FUENTES CONSULTADAS (TAVILY)")
    for j, h in enumerate(["Título", "URL"]):
        ws7.cell(row=2, column=j + 1, value=h)
    style_header(ws7, 2, 2)
    for i, f in enumerate(fuentes):
        fill = ALT_FILL if i % 2 == 0 else WHITE_FILL
        fill_row(ws7, 3 + i, [f.get("titulo", ""), f.get("url", "")], fill, 28)
    autosize(ws7, [50, 80])
    ws7.freeze_panes = "A3"

out_path.parent.mkdir(parents=True, exist_ok=True)
wb.save(out_path)

Technical Analysis

SKILL.md:215-248 directs the agent to perform Tavily web searches and retain consulted source titles and URLs. Those values originate from independently controlled web pages and therefore cross an external-content trust boundary.

build_xlsx.py reads these values from fuentes_tavily and passes them unchanged to openpyxl through Worksheet.cell(..., value=v). Values beginning with spreadsheet formula prefixes—most importantly =—are not neutralized or forced to the string data type. Consequently, a malicious page title such as a formula expression can be stored as an active formula rather than displayed as literal text.

The generic fill_row function also writes othe ...[truncated 1955 chars]

Remediation
View remediation

Remediation Suggestions

Treat every value obtained from Tavily or other external content as untrusted before writing it to a workbook.

  1. Introduce a centralized literal-cell sanitizer and apply it in fill_row:

    python
    FORMULA_PREFIXES = ("=", "+", "-", "@")
    
    def spreadsheet_literal(value):
        if isinstance(value, str) and value.startswith(FORMULA_PREFIXES):
            return "'" + value
        return value
    
    def fill_row(ws, row: int, values: list, fill: PatternFill, height: int = None) -> None:
        for j, value in enumerate(values, start=1):
            cell = ws.cell(
                row=row,
                column=j,
                value=spreadsheet_literal(value),
            )
            cell.alignment = WRAP
            cell.border = BORDER
            cell.fill = fill
        if height:
            ws.row_dimensions[row].height = height
    
  2. Apply the same protection to every direct cell assignment containing brief, generated, or Tavily-derived data—not only to the source-title column.

  3. Alternatively, explicitly force untrusted cells to the string data type after assignment, while verifying through tests that openpyxl serializes them as literal strings.

  4. Add regression tests covering values beginning with =, +, -, and @, including formulas that reference external resources.

  5. Keep legitimate hyperlinks separate from displayed text. Validate URL schemes against a narrow allowlist such as https before assigning hyperlink targets.

Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (11)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
1. `references/anatomia-urgencia.md` — Motor biológico, 5 gatillos de decisión, anatomía F100K, reglas de oro

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
1. `references/anatomia-urgencia.md` — Motor biológico, 5 gatillos de decisión, anatomía F100K, reglas de oro

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
1. `references/anatomia-urgencia.md` — Motor biológico, 5 gatillos de decisión, anatomía F100K, reglas de oro

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
2. `references/pool-semanal-templates.md` — Templates de razones semanales por TIPO de activo del producto

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
2. `references/pool-semanal-templates.md` — Templates de razones semanales por TIPO de activo del producto

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

La skill usa capacidades de lectura de archivos de forma explícita (references/..., cerebro/, detección de carpetas) pero no declara un alcance de herramientas/permisos. Esa omisión reduce la visibilidad y el control del entorno sobre qué recursos puede tocar la skill, lo que puede llevar a acceso a archivos no previsto o demasiado amplio si el runtime concede capacidades implícitas.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

El disparador cualquier variación que combine urgencia/escasez con planear cuándo activarla es demasiado amplio y puede hacer que la skill se active fuera de contexto. Eso incrementa el riesgo de ejecución no intencional con acceso web/archivos, o de que intercepte solicitudes que deberían ir a otra skill menos privilegiada o más específica.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

La descripción inicial menciona investigación web real y entrega de archivos, pero no advierte claramente que la skill hará llamadas a servicios externos ni que escribirá en disco. Esa falta de transparencia debilita el consentimiento informado del usuario y puede exponer datos del brief a terceros o generar archivos locales sin expectativa clara.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The file defines a default estacionality calendar specifically for LATAM/ES without requiring confirmation of the user's geography or locale. In a planning skill, this can cause the agent to generate culturally or commercially incorrect urgency calendars for users in other markets, leading to misleading recommendations and degraded output quality.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

Todo el contenido instruccional y las plantillas del skill están redactados en español y no incluyen ninguna indicación de que el idioma de salida deba adaptarse a la preferencia del usuario ni una justificación de que la skill sea exclusiva para un contexto hispanohablante. Según la política indicada, forzar un idioma sin opt-in del usuario constituye una violación de locale/language policy.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code file contains natural-language instructions and console-visible text exclusively in Spanish, beginning in the module docstring and continuing through argument help text and worksheet labels. Under the language/locale policy, forcing a specific language without user opt-in or a clearly documented regional justification is a policy concern.

Content

No source excerpt is available for this finding.