Back to skill

Security audit

City of Montreal Open Data

Security checks for vulnerabilities and agentic risk

Overview

This skill is a straightforward Montreal open-data helper with limited, disclosed network and file behavior, though CSV exports should be treated carefully.

Install only if you are comfortable letting the agent run the bundled Python CLI to query donnees.montreal.ca. Treat exported CSV files as untrusted remote data, especially before opening them in spreadsheet software, and review commands if the skill activates from a broad Montreal-related prompt.

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/montreal_data.py:112
Finding

CSV Formula Injection in Exported Dataset Records

Content
View full analysis

Vulnerability Details

File Location: scripts/montreal_data.py, lines 112–116
Vulnerability Type: CSV formula injection and improper CSV encoding
Risk Level: Medium

python
if options.get("csv"):
    keys = list(records[0].keys())
    print(",".join(keys))
    for row in records:
        print(",".join([str(row.get(k, "")).replace(",", ";") for k in keys]))

Technical Analysis

The CSV export incorporates remotely retrieved CKAN field names and record values without neutralizing spreadsheet formula prefixes. Cells beginning with =, +, -, or @ may be interpreted as formulas when the resulting file is opened in formula-capable spreadsheet software.

Replacing commas with semicolons is not a safe CSV-encoding mechanism. It does not correctly handle quotation marks, carriage returns, line feeds, or other characters that can alter the output structure. Headers are also emitted without any encoding or formula neutralization.

Exploitation requires attacker-controlled content to enter a dataset returned by the remote CKAN service, followed by a user exporting and opening that data in spreadsheet software that evaluates formulas. The Python script itself does not execute the formulas.

Attack Path

  1. An attacker obtains the ability to add or modify a field name or record value in a dataset accessible through the Montreal CKAN service.
  2. The attacker supplies a value whose first significant character is a spreadsheet formula prefix, such as =, +, -, or @.
  3. A user runs a command such as python3 scripts/montreal_data.py fetch DATASET_ID --csv > output.csv.
  4. The Skill writes the attacker-controlled value into output.csv without formula neutralization.
  5. The user opens the exported file in spreadsheet software.
  6. If formula evaluation is enabled and supported by that software, the attacker-controlled cell is evaluated.

Impact Assessment

The vulnerability does no ...[truncated 648 chars]

Remediation
View remediation

Remediation Suggestions

  1. Replace manual string concatenation with Python's standard csv.writer, using an explicit newline and encoding configuration.
  2. Treat both headers and record values as untrusted data.
  3. Before writing each cell, detect whether its first non-whitespace character is =, +, -, or @. Neutralize such cells according to the intended consumer, commonly by prefixing a single quote.
  4. Preserve standards-compliant CSV quoting instead of replacing commas, so embedded commas, quotes, carriage returns, and line feeds cannot alter the record structure.
  5. Document that exported data originates from a remote source and should not be trusted when opened in formula-capable applications.
  6. Add tests covering formula prefixes, leading whitespace before formulas, embedded quotes, commas, and multiline values.

Example hardening approach:

python
import csv

def safe_csv_cell(value):
    text = str(value if value is not None else "")
    if text.lstrip().startswith(("=", "+", "-", "@")):
        return "'" + text
    return text

writer = csv.writer(sys.stdout, lineterminator="\n")
writer.writerow([safe_csv_cell(key) for key in keys])
for row in records:
    writer.writerow([safe_csv_cell(row.get(key, "")) for key in keys])
Vulnerability Patterns
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The trigger phrases are broad topical terms like 'montreal transit' and 'montreal crime' that can match ordinary user requests, causing the skill to activate when the user may only be asking a general question. Over-broad activation can route conversations into shell-enabled skill behavior unexpectedly, increasing the chance of unintended tool use or confusing, unnecessary data access.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This Python file contains multiple hard-coded French user-facing messages such as "Aucun dataset trouvé", "Résultats", and "Montréal" output labels. The policy requires flagging language or locale constraints when the skill forces a specific language without user opt-in, and this file provides no mechanism to choose another language or justification for the restriction.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The language note says search queries should use French terms, which imposes a specific language expectation. While the portal content may be primarily French, the file does not frame this as an opt-in preference or clearly justified locale-specific constraint for users.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

This markdown file uses bilingual dataset labels such as French names with English translations, but it does not instruct users to use a specific language or impose a locale restriction. Because the file is informational and not directive, this is only a weak potential policy concern rather than a clear violation.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.