Back to skill

Security audit

Earl Display Control

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent local TV dashboard controller, but it exposes and renders sensitive household state in ways users should review before installing.

Install only if you are comfortable with a local household dashboard storing reminders, room notes, patterns, and optional precise coordinates in earl_mind.json. Before use, bind the server to 127.0.0.1, avoid exposing port 8000 to other devices, use approximate coordinates or disable weather if privacy matters, and do not let untrusted messages or web content write into the state file until the dashboard escapes or sanitizes rendered fields. Back up earl_mind.json before using clear/reset helper methods, and verify the target process before running any force-kill command.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (2)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:40
Finding

Unauthenticated Exposure of Household State on All Network Interfaces

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
VisuoSpatialSketchpad/sketchpad.html:803
Finding

Stored DOM Cross-Site Scripting Through Unescaped Dashboard State

Content
View full analysis
{ const dots = Array.from({length: 5}, (_, i) => `
` ).join(''); return `
${p.pattern}
${dots}
${p.observations} obs
`; }).join('') : ''; ``` Additional untrusted fields are interpolated into the main HTML template: ```javascript app.innerHTML = `
${hasPhoto ? `Earl` : `
${gnomeEmoji}
` }

${mind.identity.name}

${mind.identity.role} · ${mind.spatial_awareness.house_name}
``` Household items are also rendered directly: ```javascript ${(mind.house_stuff?.items || []).map(item => `
${item.icon || '📌'}
${item.title}
${item.detail}
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (26)

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The description partially matches the content-update portion of the code: it can update Earl's mood, house stuff, hot takes, sketchpad, and room/spatial state by modifying a local JSON file. However, several prominent declared capabilities are not implemented at all in this code chunk, especially the operational TV/dashboard management actions (wake display, restart server, launch kiosk browser) and weather updates. Conversely, the code includes additional capabilities not mentioned in the description, such as setting Earl's photo and recording long-term patterns. Because the declared purpose emphasizes broader TV dashboard management beyond what this code actually does, this is a material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a broad TV dashboard management skill with multiple operational actions and general content-updating capabilities. The supplied code chunk instead performs one narrow file-editing task: it reorders a single hard-coded 'hot take' entry in earl_mind.json. While this is loosely related to 'hot takes,' the actual behavior is much narrower and materially different from the declared primary purpose. It neither manages the display/server/browser nor generally updates dashboard state; it only reprioritizes one specific item in stored data.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The description presents a broader TV dashboard management skill, including operational actions on the display/server/browser and general updates to several dashboard sections. This code chunk performs only narrow local data mutation in earl_mind.json. It does support part of the declared domain by updating house stuff and a hot take-like section, but it omits the major advertised behaviors and instead makes specific hard-coded edits plus an extra reset of long_term_patterns. Therefore the supplied code does not accurately represent the declared description.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
60% confidence
Finding

Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Content

Scanner excerpt · README.md (reported line 83)May include surrounding context.

Setup

  1. Clone this repo
  2. Copy the template state file to create your live state:
    bash
    cp VisuoSpatialSketchpad/earl_mind.template.json VisuoSpatialSketchpad/earl_mind.json
    

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
96% confidence
Finding

The skill documents capabilities to read and write persistent dashboard data and to fetch network data, but it does not declare any explicit tool scope or permissions boundary. This increases the chance that an agent executes file or network actions without least-privilege controls or clear operator review, especially because the skill encourages direct JSON mutation and external weather fetching.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill instructs use of forceful process-termination commands such as kill -9 and Stop-Process -Force without requiring validation of the target process or warning about collateral termination. In an agentic setting, this can disrupt unrelated services, cause data loss, or be misapplied if port ownership or process selection is wrong.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill explicitly instructs direct mutation and manual editing of a persisted JSON state file without emphasizing that these changes are durable and can alter live dashboard behavior. This creates risk of accidental corruption, unauthorized content changes, or integrity issues if an agent writes malformed or unintended data to the file.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The clear_house_stuff method deletes all house-stuff items and immediately persists the change via save(), but there is no confirmation prompt or user-facing disclosure at the point of deletion. Although the docstring names the action, the operation is irreversible within this API and lacks any stronger warning about bulk data removal.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The clear_sketchpad method removes all sketchpad canvas entries and immediately writes the updated state to disk. While the docstring says 'Wipe the sketchpad clean,' there is no confirmation step, user-visible log, or stronger warning that this is a destructive persisted action.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script silently removes a hard-coded 'hot take' entry from Earl's persisted state, which does not match the declared display-control/dashboard-management purpose of the skill. In a skill expected to update or display information, undisclosed deletion of content is risky because it can tamper with user data, hide information, or serve as covert behavior inconsistent with the manifest.

Content

No source excerpt is available for this finding.

Tainted flow: 'data' from pathlib.Path.read_text (line 6, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · VisuoSpatialSketchpad/reorder_take.py (reported line 15)May include surrounding context.

python
break
if match is not None:
    takes.insert(0, takes.pop(match))
    path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding='utf-8')
    print(f"Moved '{TARGET}' to top")
else:
    print(f"Take '{TARGET}' not found")

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The page builds a third-party weather request using precise latitude, longitude, and timezone from local state and sends it to api.open-meteo.com without any user-facing disclosure or consent flow. In a home-dashboard context, that leaks household location metadata to an external service and can expose sensitive occupancy or residence information.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This code transmits location-derived query parameters to an external domain over the network. Although the destination is a legitimate weather API and HTTPS is used, the security concern is the unnecessary exposure of household coordinates and related metadata to a third party from a domestic display-control skill.

Content

Scanner excerpt · VisuoSpatialSketchpad/sketchpad.html (reported line 960)May include surrounding context.

html
const unit = loc.temperature_unit || 'fahrenheit';
  const wind = loc.wind_speed_unit || 'mph';
  const tz = loc.timezone || 'America/New_York';
  return `https://api.open-meteo.com/v1/forecast?latitude=${loc.latitude}&longitude=${loc.longitude}&current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m&daily=temperature_2m_max,temperature_2m_min,precipitation_probability_max,weather_code&temperature_unit=${unit}&windspeed_unit=${wind}&timezone=${tz}&forecast_days=4`;
}

const WMO_CODES = {

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

This code overwrites the JSON file on disk via path.write_text(...), which is a safety-relevant data modification. The file contains no confirmation prompt, logging/print statement, docstring, or comment disclosing that it will persistently change earl_mind.json.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

This script writes the modified mind object back to earl_mind.json, replacing prior contents for several sections including house_stuff and long_term_patterns. There is no confirmation prompt, logging, comment, or docstring warning the user that running the script will alter persisted data.

Content

No source excerpt is available for this finding.

Tainted flow: 'mind' from pathlib.Path.read_text (line 3, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · VisuoSpatialSketchpad/update_mind.py (reported line 25)May include surrounding context.

python
mind["long_term_patterns"] = []
mind["meta"]["last_updated"] = datetime.datetime.utcnow().isoformat()
mind["meta"]["update_count"] = mind["meta"].get("update_count", 0) + 1
mind_path.write_text(json.dumps(mind, indent=2, ensure_ascii=False), encoding='utf-8')

Tainted flow: 'mind' from pathlib.Path.read_text (line 4, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · VisuoSpatialSketchpad/remove_noise_take.py (reported line 6)May include surrounding context.

python
path = Path('earl_mind.json')
mind = json.loads(path.read_text(encoding='utf-8'))
mind['identity']['current_vibe'] = 'Text me on Telegram to add stuff.'
path.write_text(json.dumps(mind, indent=2, ensure_ascii=False), encoding='utf-8')

Tainted flow: 'mind' from pathlib.Path.read_text (line 4, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · VisuoSpatialSketchpad/update_house_stuff.py (reported line 14)May include surrounding context.

python
path = Path('earl_mind.json')
mind = json.loads(path.read_text(encoding='utf-8'))
mind['identity']['current_vibe'] = 'Text me on Telegram to add stuff.'
path.write_text(json.dumps(mind, indent=2, ensure_ascii=False), encoding='utf-8')

Tainted flow: 'mind' from pathlib.Path.read_text (line 4, file read) → pathlib.Path.write_text (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · VisuoSpatialSketchpad/update_vibe.py (reported line 6)May include surrounding context.

python
path = Path('earl_mind.json')
mind = json.loads(path.read_text(encoding='utf-8'))
mind['identity']['current_vibe'] = 'Text me on Telegram to add stuff.'
path.write_text(json.dumps(mind, indent=2, ensure_ascii=False), encoding='utf-8')

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script constructs a weather API request using latitude, longitude, and timezone read from a local mind file, which discloses precise location data to a third-party service. In this skill context, the data appears functionally necessary for weather retrieval, but there is no minimization, consent, or indication that household location is being transmitted externally, creating a privacy leak of sensitive home-location metadata.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
88% confidence
Finding

This code makes an outbound HTTPS request to api.open-meteo.com and includes precise latitude and longitude in the query string. The transmission is not inherently malicious, but in a home-dashboard skill the context increases sensitivity because it can expose the physical location of a residence to an external provider and to logs, caches, or monitoring systems that capture URLs.

Content

Scanner excerpt · VisuoSpatialSketchpad/update_weather_ping.py (reported line 20)May include surrounding context.

python
_house_name = _mind_data.get("spatial_awareness", {}).get("house_name", "the house")

URL = (
    f"https://api.open-meteo.com/v1/forecast?latitude={_lat}&longitude={_lon}"
    f"&current=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m"
    f"&temperature_unit={_temp_unit}&windspeed_unit={_wind_unit}&timezone={_tz}"
)

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
85% confidence
Finding

This markdown file tells users to populate latitude and longitude for their house, which is sensitive location data. Although the README later mentions that the live state file contains real household state, the setup instructions themselves do not warn users that entering exact coordinates exposes private location information.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The configuration sets "timezone" to "America/New_York", "temperature_unit" to "fahrenheit", and "wind_speed_unit" to "mph". This imposes a specific locale and unit convention in natural-language/config values without any indication of user opt-in or that the skill is intentionally region-specific.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
97% confidence
Finding

The fallback message tells users "I auto-wake by restarting the server + reloading this page," implying this page performs server restart behavior. In this file, the implemented behavior is limited to fetching local JSON and weather data on intervals; there is no code that restarts any server or triggers a wake action.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The weather configuration defaults to fahrenheit, mph, and America/New_York, which bakes in a specific regional locale when user preferences are absent. This can violate language/locale policy expectations because the file does not offer user choice or clearly justify the U.S.-centric defaults.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.