Back to skill

Security audit

Citywalk Map

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it claims, but it should be reviewed because generated maps can execute untrusted route text as browser code and the screenshot workflow can expose all of /tmp.

Review before installing. Use only route titles, waypoint names, and descriptions you trust; avoid opening or sharing generated maps made from untrusted route data. Do not serve all of /tmp for screenshots; serve only a dedicated directory bound to localhost. Be aware that route coordinates and map views may be sent to OSRM, map tile providers, and optionally wttr.in/Nominatim, and install optional Python dependencies only in a controlled environment.

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 (2)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate.py:245
Finding

Stored Cross-Site Scripting in Generated Map HTML

Content
View full analysis
{w["num"]}{w["name"]}' ) ``` ```html
🚶 CITYWALK

{title}

{subtitle}

``` ```javascript waypoints.forEach(function(wp){ var last = waypoints[waypoints.length-1].num; var color = wp.num==='1'?'{accent}':(wp.num===last?'#10b981':'#ff9f43'); L.circleMarker([wp.lat,wp.lon], {radius:10, color:'white', weight:3, fillColor:color, fillOpacity:1}) .addTo(map).bindPopup('
'+wp.num+''+wp.name+'
'+(wp.desc||'')+'
'); var lbl = L.divIcon({html:'
'+wp.name+'
', className:'', iconSize:[0,0], iconAnchor:[0,0]}); L.marker([wp.lat,wp.lon], {icon:lbl}).addTo(map); }); ``` ```python html = HTML.format( lang='zh-CN' if args.lang == 'zh' else 'en', title=title, subtitle=subtitle, accent=ACCENT, accent_dark=darken(ACCENT), waypoints_json=json.dumps(waypoints), route_json=json.dumps(route_coords), center_lat=clat, center_lon=clon, dist=fmt_d(td), dur=total_dur_str, n=len(waypoints), walk=walk_str, route_html=route_html, tips_html=tips_html, stats_col=stats_col, stats_extra=stats_extra, tile_url=args.tile_url or 'https://tile.openstreetmap.org/{z}/{x ...[truncated 2743 chars]
Remediation
View remediation
... ``` Parse it using: ```javascript const waypoints = JSON.parse( document.getElementById('waypoints-data').textContent ); ``` When creating the JSON text, escape at least `<`, `>`, `&`, U+2028, and U+2029. 4. Add a restrictive Content Security Policy. Where practical, disallow inline scripts and event handlers: ```html ``` 5. Validate and normalize all route fields, but do not rely on input validation as a substitute for contextual encoding. 6. Add regression tests covering HTML tags, quotation marks, event handlers, ``, Unicode line separators, and malformed markup in titles, names, and descriptions. ]]>

T08 · Insecure Dependencies

Warning
Location
scripts/generate.py:267
Finding

Unverified Runtime Dependencies and Unpinned Python Packages

Content
View full analysis
``` A further CDN asset is used as an error tile: ```javascript var tileLayer = L.tileLayer(osmUrl, { maxZoom:19, attribution:"© OpenStreetMap contributors", subdomains:'abc', errorTileUrl:'https://unpkg.com/leaflet@1.9.4/dist/images/tile_404.png' }); ``` The renderer imports third-party Python packages: ```python import requests from PIL import Image, ImageDraw, ImageFont ``` The documentation instructs users to install them without version or hash constraints: ```bash pip install Pillow requests ``` ### Technical Analysis The generated map executes Leaflet JavaScript obtained at page-view time from `unpkg.com`. No `integrity` attribute is provided, so the browser cannot verify that the downloaded asset matches an audited cryptographic digest. The effective JavaScript payload can therefore differ from the code present during the Skill audit if the CDN, package publication, DNS path, or distribution account is compromised. The renderer also relies on `requests` and `Pillow`, but the project does not provide a version-locked dependency file. Installing unconstrained package versions makes builds non-reproducible and allows future package releases to be incorporated without review. No evidence was found that the currently named packages or CDN are malicious. The issue is the absence of integrity verification, dependency locking, and reproducible installation controls. ### Attack Path #### Browser dependency path 1. A victim g ...[truncated 1655 chars]
Remediation
View remediation
" crossorigin="anonymous"> ``` The integrity values must be obtained from a trusted release source and independently verified. 3. Add an explicit dependency file for the renderer with exact versions: ```text Pillow== requests== ``` 4. For stronger reproducibility, use a lock file or hashed requirements: ```bash pip install --require-hashes -r requirements.txt ``` 5. Review and update dependency versions through a controlled process, including vulnerability scanning and release-note review. 6. Correct the package metadata and documentation so that the generator's standard-library requirements are clearly separated from the renderer's third-party dependencies. 7. Consider enforcing a Content Security Policy that restricts executable scripts to approved local files or explicitly approved hosts. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (17)

Env Variable Harvesting

High
Category
Data Exfiltration
Confidence
60% confidence
Finding

Code enumerates, copies, or searches environment variables for secrets. Bulk environment access can collect credentials unrelated to the skill's stated purpose.

Content

Scanner excerpt · scripts/demo.py (reported line 53)May include surrounding context.

python
print(f"Color: {demo['color']}")
    print('='*50)
    
    env = os.environ.copy()
    env['COLOR'] = demo['color']
    
    result = subprocess.run(

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README advertises weather lookup, routing, geocoding, and fallback map loading via external services, but it does not clearly warn users that route coordinates, place names, and possibly location-related queries will be transmitted to third parties such as wttr.in, OSRM, Nominatim, and Amap. In a location-processing tool, this omission can mislead users about privacy exposure and data flows, especially when the tool may handle sensitive travel plans or personally identifying places.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The screenshot instructions tell users to start a local HTTP server rooted at /tmp and later kill it with a broad pkill pattern, but they do not warn that files in that directory may become accessible to other local users or reachable over the network depending on bind behavior and host configuration. The cleanup command can also terminate unrelated matching Python HTTP server processes, creating avoidable denial-of-service or workflow disruption risks.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The demo titles and route descriptions are largely written in Chinese, including for London and Paris routes, which imposes a specific language/locale in user-facing content. The file does not indicate that Chinese output is optional, configurable, or justified as a region-specific tool.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
80% confidence
Finding

The module docstring says this file runs example routes to demonstrate the generator, but the implementation does so by spawning a separate Python process. With no manifest available to justify broader execution capability, subprocess invocation is a stronger capability than a straightforward in-process demo runner and is not clearly warranted by the stated documentation.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/demo.py (reported line 56)May include surrounding context.

python
env = os.environ.copy()
    env['COLOR'] = demo['color']
    
    result = subprocess.run(
        ['python3', __file__.replace('/demo.py', '/generate.py'), demo['title'], demo['route']],
        env=env,
        capture_output=False,

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script sends user-supplied coordinates to multiple third-party services (OSRM by default, and wttr.in/Nominatim optionally) without an explicit privacy warning or consent flow. Location data can be sensitive, and silent transmission to external endpoints can expose travel plans, destinations, or personal whereabouts.

Content

No source excerpt is available for this finding.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate.py (reported line 72)May include surrounding context.

python
'--data-urlencode', 'geometries=geojson'
            ]
            try:
                result = subprocess.run(cmd, capture_output=True, text=True, timeout=12)
                data = json.loads(result.stdout)
                if data.get('code') == 'Ok':
                    route = data['routes'][0]

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate.py (reported line 113)May include surrounding context.

python
"""用 wttr.in 获取目的地当前天气描述"""
    url = f"https://wttr.in/{lat},{lon}?format=j1&lang={lang}"
    try:
        result = subprocess.run(
            ['curl', '-s', '--max-time', '8', url],
            capture_output=True, text=True, timeout=10
        )

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/generate.py (reported line 141)May include surrounding context.

python
"""用 Nominatim 验证坐标是否落在有效陆地区域"""
    url = f"https://nominatim.openstreetmap.org/reverse?lat={lat}&lon={lon}&format=json"
    try:
        result = subprocess.run(
            ['curl', '-s', '--max-time', '6', '-H', 'User-Agent: CitywalkMap/2.0',
             url],
            capture_output=True, text=True, timeout=8

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The CLI help text declares '--tile-url' and '--tile-attrib' as functional customization options, implying the generated map will use caller-supplied tile sources. However, the HTML/JavaScript later hardcodes OpenStreetMap and Gaode URLs at L377-L379 and never interpolates the formatted tile_url/tile_attrib values, so the documented behavior contradicts the actual implementation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring and CLI usage/help text are presented in Chinese, and later print messages are also Chinese-only. This creates a natural-language locale policy issue because the skill imposes a specific language on users without any opt-in or documented region-specific justification.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
78% confidence
Finding

The primary README content is presented in Chinese, with an English link offered secondarily, which can amount to forcing a specific language unless the user opts in. The policy allows language choice or opt-in, but here the main document starts in one language rather than neutrally offering a selection first.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
77% confidence
Finding

The document is primarily written in Chinese with partial English headings and examples, and it does not tell users how to select their preferred language version within this file. Under the stated policy, forcing a language or locale without opt-in can be a natural-language policy concern unless the choice is explicit or justified.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
80% confidence
Finding

The package metadata provides a Chinese default description and a separate English description, and the same pattern appears again in the clawhub metadata. This suggests the skill may present locale-specific content without stating how the user language is selected or offering explicit opt-in/choice, which can conflict with language/locale policy expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
79% confidence
Finding

The script writes generated HTML to a filesystem path, defaulting to /tmp/citywalk_map.html, but the help text only states the output path and does not explicitly warn that running the tool creates or overwrites a file. This is a file-writing operation with no confirmation prompt, and the user-facing description does not disclose overwrite behavior.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The function docstring at L053 says it '并发调用' (concurrently calls) OSRM, which describes a materially different execution model from the actual code. The implementation iterates over waypoint pairs in a normal for-loop and invokes subprocess.run synchronously for each segment, so the inline documentation actively misstates what the code does.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.