Back to skill

Security audit

Nyc 311 Reporter

Security checks for vulnerabilities and agentic risk

Overview

This skill does what it says at a high level, but it handles personal data and can submit NYC 311 reports with unsafe defaults and weak guardrails.

Install only after reviewing and replacing all sample identity data, avoiding package-local storage of real personal information, running dry-run mode first, and confirming any live 311 submission manually. Treat /tmp screenshots as potentially sensitive and delete them after use.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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
Findings (4)

T09 · Insecure Skill Coding Practices

Warning
Location
assets/config.json:2
Finding

Plaintext Personal Information and Unsafe Identity Defaults

Content
View full analysis

Vulnerability Details

File Location: assets/config.json:2-7; additional executable defaults at scripts/browse_311.py:204-209 and examples at SKILL.md:35-43,56-65
Vulnerability Type: Plaintext sensitive data and unsafe hardcoded defaults
Risk Level: Medium

Vulnerable Code

json
{
  "name": "Ricardo Díaz",
  "phone": "+19299990000",
  "email": "ricardo@example.com",
  "home_address": "726 DeKalb Avenue",
  "work_address": "726 DeKalb Avenue",
  "apartment": "1E",
  "preferred_contact": "email",
  "notify_updates": true
}

The same identity is embedded as executable defaults:

python
parser.add_argument('--location', default='726 DeKalb Avenue, Brooklyn, NY')
parser.add_argument('--vehicle', default='Gray Nissan Micra')
parser.add_argument('--duration', default='10 hours')
parser.add_argument('--name', default='Ricardo Díaz')
parser.add_argument('--email', default='ricardo@example.com')
parser.add_argument('--phone', default='+19299990000')

Technical Analysis

The package stores reporter identity and address information in plaintext and duplicates that information in command-line defaults. Any person or process able to read the distributed project can recover these details.

The executable defaults create an additional integrity risk. The submit command does not require the operator to provide reporter details, so invoking it without overrides can populate a government-service form with the embedded identity. The profile in assets/config.json is not read by the script, meaning its inclusion does not currently provide a functional configuration benefit.

Attack Path

  1. An attacker obtains read access to the Skill package, repository, archive, build artifact, or backup.
  2. The attacker reads assets/config.json, SKILL.md, or the argument defaults in scripts/browse_311.py.
  3. The attacker recovers the embedded name, telephone number, email address ...[truncated 578 chars]
Remediation
View remediation

Remediation Suggestions

  • Remove personal or real-looking identity data from source code, configuration files, and documentation.
  • Replace documentation values with unmistakably synthetic placeholders.
  • Remove defaults for identity, contact, and location arguments; mark required values with required=True.
  • Reject missing or placeholder values before opening or submitting a form.
  • Store user profiles outside the distributed Skill package in a user-controlled file with restrictive filesystem permissions.
  • Do not commit user profile files; provide a sanitized template such as config.example.json instead.
  • Add secret and personal-data scanning to repository and release workflows.
  • Require an explicit review of the reporter identity and complaint details immediately before submission.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/browse_311.py:113
Finding

Sensitive Form Screenshots Written to Predictable Shared Temporary Files

Content
View full analysis

Vulnerability Details

File Location: scripts/browse_311.py:113-114,158-159,169-170
Vulnerability Type: Unsafe temporary-file handling and sensitive-data exposure
Risk Level: Medium

Vulnerable Code

python
# Take screenshot of current state
await page.screenshot(path='/tmp/311_step1.png')
result['screenshot_1'] = '/tmp/311_step1.png'
python
# Take screenshot of filled form
await page.screenshot(path='/tmp/311_filled.png')
result['screenshot_filled'] = '/tmp/311_filled.png'
python
await page.screenshot(path='/tmp/311_submitted.png')
result['screenshot_submitted'] = '/tmp/311_submitted.png'
result['status'] = 'submitted'

Technical Analysis

The script captures screenshots before, during, and after complaint submission using fixed filenames in the shared /tmp directory. The filled and submitted screenshots can contain names, addresses, phone numbers, email addresses, vehicle details, complaint descriptions, and confirmation identifiers.

Fixed paths cause different executions and users to overwrite the same files. The script neither creates a private temporary directory nor removes the screenshots after use. Depending on local permissions and system configuration, another local user or process may read the artifacts. Predictable paths also create opportunities for race conditions or symbolic-link attacks where the runtime environment permits them.

Attack Path

  1. A victim runs the Skill to fill or submit a complaint.
  2. The script writes form screenshots to known paths such as /tmp/311_filled.png.
  3. The files remain after the browser closes because no cleanup is performed.
  4. A local attacker or other process monitors or reads the predictable paths.
  5. The attacker extracts personal, complaint, or confirmation information visible in the screenshots.

A secondary path is possible on systems with inadequate temporary-directory protections: an attacker ...[truncated 462 chars]

Remediation
View remediation

Remediation Suggestions

  • Do not capture filled or submitted forms by default.
  • If screenshots are necessary, redact or mask identity, address, contact, and confirmation fields first.
  • Create a private temporary directory with tempfile.TemporaryDirectory() and restrictive permissions.
  • Generate unpredictable filenames instead of using global fixed paths.
  • Delete screenshots in a finally block unless the user explicitly requests retention.
  • If retained, save them to a user-approved private directory and report the retention policy.
  • Verify that paths are regular files owned by the current user and avoid following attacker-controlled symbolic links.
  • Add automated tests confirming that sensitive temporary artifacts are removed after success and failure.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/browse_311.py:99
Finding

Unvalidated Form Navigation and Generic Submission Can Send Data to an Unintended Destination

Content
View full analysis

Vulnerability Details

File Location: scripts/browse_311.py:99-108,119-132,135-176
Vulnerability Type: Missing destination validation and unsafe form automation
Risk Level: Medium

Vulnerable Code

python
# Look for and click on Illegal Parking or Report Problems
links = await page.query_selector_all('a')
report_link = None

for link in links:
    text = await link.inner_text()
    if text and ('illegal parking' in text.lower() or 'report problems' in text.lower()):
        report_link = link
        break

if report_link:
    await report_link.click()
    await page.wait_for_load_state('networkidle')
    await page.wait_for_timeout(2000)
python
# Look for fire hydrant specific option
page_text = await page.content()

if 'fire hydrant' in page_text.lower():
    # Try to find and click fire hydrant option
    hydrant_links = await page.query_selector_all('a, button')
    for elem in hydrant_links:
        text = await elem.inner_text()
        if text and 'fire hydrant' in text.lower():
            await elem.click()
            await page.wait_for_load_state('networkidle')
            break
python
if any(x in name.lower() for x in ['location', 'address', 'street']):
    await field.fill(location)
    result['fields_filled'].append('location')
elif any(x in name.lower() for x in ['vehicle', 'car', 'description']):
    await field.fill(f"{vehicle} blocking fire hydrant")
    result['fields_filled'].append('vehicle_description')
elif any(x in name.lower() for x in ['duration', 'time', 'how long']):
    await field.fill(duration)
    result['fields_filled'].append('duration')
elif any(x in name.lower() for x in ['name', 'reporter']):
    await field.fill(reporter_name)
    result['fields_filled'].append('reporter_name')
elif any(x in name.lower() for x in ['email']):
    await field.fill(reporter_email)
    result['fields_filled'].appe
...[truncated 2562 chars]
Remediation
View remediation

Remediation Suggestions

  • Maintain an explicit allowlist of approved HTTPS hosts, paths, and form-action destinations.
  • Validate page.url after every navigation and immediately before entering sensitive data.
  • Reject redirects or links to non-approved origins.
  • Use stable, form-specific selectors and expected field identifiers rather than broad substring matching.
  • Validate the complaint category, page title, expected form schema, and form action before filling fields.
  • Verify that every required value maps to exactly one expected field; abort on missing or ambiguous matches.
  • Present the final origin, complaint category, and populated values to the user for explicit confirmation immediately before submission.
  • Determine success from a specific confirmation element or request identifier rather than generic page text.
  • Log safe metadata about navigation decisions without logging personal form values.

T08 · Insecure Dependencies

Note
Location
requirements.txt:1
Finding

Unpinned and Unused Third-Party Dependencies Increase Supply-Chain Exposure

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-3
Vulnerability Type: Non-reproducible dependency resolution and unnecessary dependency surface
Risk Level: Low

Vulnerable Code

text
requests>=2.28.0
beautifulsoup4>=4.11.0
playwright>=1.40.0

The documented installation command resolves these open-ended constraints:

bash
pip install -r requirements.txt
playwright install chromium

Technical Analysis

All dependencies use lower-bound-only version constraints. A future installation can therefore resolve package releases that were not present or reviewed when the Skill was audited. This prevents reproducible builds and increases exposure to compromised, incompatible, or unexpectedly changed releases.

The only Python script imports Playwright but does not import requests or beautifulsoup4. Those unused packages and their transitive dependencies unnecessarily expand the installation and supply-chain attack surface.

The audit did not identify a confirmed malicious or typosquatted package in the dependency list. The issue is the unsafe resolution policy and unnecessary package inclusion rather than proof that a listed release is malicious.

Attack Path

  1. A user follows the installation instructions and executes pip install -r requirements.txt.
  2. The package resolver selects the newest versions satisfying the open-ended lower bounds.
  3. A selected future release or transitive dependency has been compromised, contains a vulnerability, or introduces unsafe behavior.
  4. Installation hooks or imported runtime code execute in the user's environment with the privileges of the installing or running account.
  5. The affected dependency can access resources available to that account, subject to operating-system controls.

Impact Assessment

The current files do not establish that dependency compromise has occurred. If a resolved dependency were compromised, impac ...[truncated 245 chars]

Remediation
View remediation

Remediation Suggestions

  • Remove requests and beautifulsoup4 unless code that requires them is added.
  • Pin Playwright and every direct dependency to exact, reviewed versions.
  • Generate a lock file containing resolved transitive versions.
  • Use hashes with pip --require-hashes or an equivalent verified installation workflow.
  • Review release notes and security advisories before dependency upgrades.
  • Automate vulnerability and license scanning in continuous integration.
  • Pin and review the Playwright browser distribution where supported so browser installations are reproducible.
  • Perform upgrades through controlled pull requests rather than resolving unrestricted versions during deployment.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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 (8)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The description claims broad end-to-end NYC 311 service request filing, including scraping categories, finding forms, filling them with user data, and submitting reports generally. The code only partially matches that purpose. It does scrape candidate complaint links, but the actionable automation is specialized to a fire hydrant / illegal parking complaint path via find_fire_hydrant_form and fill_fire_hydrant_complaint. It does not implement general filing across complaint types; instead it heuristically searches links and form fields and may fail outside this narrow scenario. Also, submission is not the default behavior: the main submit flow is a dry run unless --submit is passed. Finally, it stores screenshots in /tmp, an undeclared side effect/resource access. So while related to NYC 311 browser automation, the declared description materially overstates the breadth and end-to-end generality of the implemented behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The skill instructs users to store sensitive personal information such as name, phone number, email, and home/work addresses in assets/config.json without any warning about persistence, access controls, or data handling. In an automation skill that also takes screenshots and submits browser forms, this increases the risk of unintended disclosure, local data exposure, or reuse of personal data beyond the user's expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script saves screenshots of the live complaint workflow to predictable files under /tmp, and those screenshots can contain personal information such as address, name, email, phone number, and complaint details. On shared systems or misconfigured environments, other local users or processes may read these files, causing unintended disclosure of sensitive data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

When --submit is used, the script clicks the submit button after filling location, vehicle details, name, email, and phone fields, causing those details to be transmitted to an external government website. Although the code supports a dry run, there is no confirmation prompt or explicit disclosure immediately before the live submission occurs.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency is specified with a lower bound only, which allows future installations to resolve to different versions over time. This weakens reproducibility and can unintentionally introduce vulnerable or breaking upstream releases into a browser-automation skill that handles user-submitted data and external web interactions.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
requests>=2.28.0
beautifulsoup4>=4.11.0
playwright>=1.40.0

Unverifiable Dependency: requests has 16 known advisory(ies) (CVE-2014-1830 (Exposure of Sensitive Information to an Unauthorized Actor in Requests); CVE-2024-47081 (Requests vulnerable to .netrc credentials leak via malicious URLs); CVE-2024-35195 (Requests `Session` object does not verify requests after making first request wi) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
93% confidence
Finding

The manifest does not pin requests, and that package has multiple known advisories across versions, so it is impossible to verify from this file whether installations will avoid affected releases. In this skill, requests may interact with external resources and potentially sensitive user data, making version ambiguity more concerning than in a purely local tool.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

Using an unpinned beautifulsoup4 version makes builds non-deterministic and increases supply-chain risk because later package releases may change behavior or introduce security issues. Although this package is not inherently dangerous here, dependency drift is still a real hardening weakness.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
requests>=2.28.0
beautifulsoup4>=4.11.0
playwright>=1.40.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

An unpinned Playwright dependency can cause the installed browser automation stack to vary between environments, which is risky for a skill that automates form submission on a live government portal. Unexpected upstream changes could affect security posture, browser behavior, or compatibility in ways that are hard to audit.

Content

Scanner excerpt · requirements.txt (reported line 3)May include surrounding context.

text
requests>=2.28.0
beautifulsoup4>=4.11.0
playwright>=1.40.0

Static analysis

No suspicious patterns detected.