Back to skill

Security audit

Ville de Québec Open Data

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Quebec City open-data helper, with a limited CSV-export safety caveat users should understand.

Install only if you are comfortable running a Bash-backed helper that contacts donneesquebec.ca and caches the public dataset catalog locally. Treat CSV exports as untrusted public data; import them as text or inspect them before opening in spreadsheet software.

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/quebec_data.py:198
Finding

CSV Formula Injection in Dataset Export

Content
View full analysis

Vulnerability Details

File Location: scripts/quebec_data.py, lines 198–203
Vulnerability Type: CSV formula injection caused by insufficient sanitization of untrusted remote data
Risk Level: Medium

Vulnerable Code

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

Technical Analysis

Dataset field names and values originate from the external CKAN service and are therefore untrusted. The CSV export logic removes commas and newline characters but does not neutralize cells beginning with spreadsheet formula indicators such as =, +, -, or @.

If an attacker can insert data into a listed dataset, or compromise its upstream source, a generated CSV can contain formula expressions. When a user opens that file in spreadsheet software, the application may interpret the values as formulas rather than plain text. The implementation also constructs CSV output manually instead of using a standards-compliant CSV writer, so quoting and escaping behavior is incomplete.

Attack Path

  1. An attacker gains the ability to add or modify a value in a CKAN dataset consumed by the skill.
  2. The attacker supplies a formula-like value, such as one beginning with =, +, -, or @.
  3. A user runs a command such as:
    bash
    python3 scripts/quebec_data.py fetch DATASET_ID --csv > output.csv
    
  4. The export routine writes the attacker-controlled value without formula neutralization.
  5. The user opens output.csv in spreadsheet software.
  6. If that software evaluates CSV cells as formulas, the injected expression executes within the spreadsheet application's security context.

Impact Assessment

Exploitation does not grant privileges inside the Python process itself. Impact occurs when the exporte ...[truncated 452 chars]

Remediation
View remediation

Remediation Suggestions

  1. Use Python's standard csv module rather than constructing rows with ",".join(...). This provides standards-compliant quoting and escaping.
  2. Treat both field names and field values as untrusted.
  3. Before writing each cell, detect formula-leading content after any relevant leading whitespace. Neutralize cells beginning with =, +, -, or @, for example by prefixing a single quote when compatibility with the target spreadsheet allows it.
  4. Apply the same protection consistently to the header row.
  5. Document that CSV output contains untrusted public dataset content and should be imported as text when possible.
  6. Add tests covering formula indicators, commas, quotes, carriage returns, line feeds, Unicode content, and leading whitespace.

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)
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
  • 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
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (1)

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This code presents multiple user-facing messages in French only, such as status, error, and result text, without offering a language option or documenting that the skill is intentionally limited to French users. That creates a natural-language policy concern under the locale rule because the skill imposes a specific language by default.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.