T05 · Unauthorized Access and Privilege Escalation
- Location
SKILL.md:62- Finding
Persistent Agent Memory Access for API Credentials
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill performs the advertised map generation, but it mandates sending precise location artifacts externally and handles API keys in ways users should review before installing.
Install only if users are comfortable sending addresses, precise coordinates, shapefiles, imagery, and previews to Baidu, Mapbox, ESRI, and a Feishu destination. Before use, require explicit approval before any message/file send, confirm the recipient, use scoped environment variables or a secret manager for API keys, avoid command-line secrets, and install dependencies in a pinned virtual environment.
SKILL.md:62Persistent Agent Memory Access for API Credentials
scripts/generate_isochrone.py:536API Credentials Exposed Through Process Command-Line Arguments
SKILL.md:29Mandatory External Transmission of Sensitive Location Artifacts
SKILL.md:103Unpinned System-Wide Third-Party Dependency Installation
The skill mandates automatic transmission of a preview image, ZIP archive, and user-derived location details to an external channel without any per-run confirmation. This creates a direct exfiltration path for sensitive geolocation data and generated files, especially if the invoking user did not explicitly request outbound sharing or if the destination channel is wrong.
The skill clearly performs outbound network operations to Baidu, Mapbox, and likely tile servers, but it declares no explicit tool scope or permissions boundary. That increases the chance the agent can invoke broader network access than intended, weakening least-privilege controls and making misuse or accidental data disclosure harder to prevent.
The skill instructs the agent to package and transmit all generated files and location-derived metadata automatically, including shapefiles, a TIFF basemap, and a preview image. Because these artifacts encode sensitive user-request data and are sent without explicit consent checks, the behavior materially increases privacy and data-leakage risk.
The skill instructs the agent to retrieve API keys from MEMORY.md without any warning, scoping, or approval flow for credential access. Unconstrained secret retrieval increases the risk of exposing stored credentials to the model context, logs, downstream tools, or unintended outputs.
The script sends user-supplied addresses and derived coordinates to Baidu and Mapbox without any explicit consent, warning, or privacy notice. Because addresses and precise travel-origin points are sensitive location data, undisclosed third-party transmission can violate user expectations, internal policy, or privacy requirements.
This code transmits the user-provided address and API key to Baidu's geocoding service. In the context of an agent skill handling potentially sensitive addresses, external transmission is security-relevant because it shares location data with a third party and may create privacy/compliance exposure if undisclosed.
def geocode_baidu(address, ak):
"""调用百度地理编码API,返回第一个结果的 BD-09 坐标 (lng, lat)"""
url = "https://api.map.baidu.com/geocoding/v3/"
params = {"address": address, "ak": ak, "output": "json"}
resp = requests.get(url, params=params, timeout=10)
data = resp.json()
This request sends precise WGS84 coordinates and the Mapbox access token to Mapbox's isochrone API. The transmission is expected for functionality, but it still represents sensitive location sharing that should be explicitly disclosed and governed.
"""调用 Mapbox Isochrone API,返回 GeoJSON FeatureCollection"""
profiles = {"driving": "mapbox/driving", "walking": "mapbox/walking", "cycling": "mapbox/cycling"}
profile = profiles.get(mode, "mapbox/driving")
url = f"https://api.mapbox.com/isochrone/v1/{profile}/{wgs_lng},{wgs_lat}"
params = {
"access_token": mapbox_ak,
"contours_minutes": str(minutes),
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Mapbox Static Images API — 底图 + 等时圈 GeoJSON 叠加
# style: mapbox/streets-v12(街道图)
base_url = "https://api.mapbox.com/styles/v1/mapbox/streets-v12/static"
# 等时圈 GeoJSON 直接作为 overlay 传入(Mapbox 会在服务端渲染)
overlay_part = f"geojson({json.dumps({'type': 'FeatureCollection', 'features': []})})"
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Mapbox Static Images API — 底图 + 等时圈 GeoJSON 叠加
# style: mapbox/streets-v12(街道图)
base_url = "https://api.mapbox.com/styles/v1/mapbox/streets-v12/static"
# 等时圈 GeoJSON 直接作为 overlay 传入(Mapbox 会在服务端渲染)
overlay_part = f"geojson({json.dumps({'type': 'FeatureCollection', 'features': []})})"
The skill manifest describes Baidu geocoding, coordinate conversion, and Mapbox isochrone generation, but the code also downloads ESRI imagery tiles and writes an additional GeoTIFF basemap. This hidden data flow and artifact creation increases privacy, compliance, and operational risk because users may not expect another third-party service to receive location-derived requests or an extra geospatial file to be stored.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
row_tiles = []
for tx in range(tx_min, tx_max + 1):
url = f"https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{ty}/{tx}"
r = requests.get(url, timeout=request_timeout)
if r.status_code == 200 and len(r.content) > 1000:
img = Image.open(io.BytesIO(r.content)).convert("RGB")
else:
The script saves point and isochrone shapefiles plus preview imagery containing location-derived data to disk without an explicit warning. On shared systems or agent environments, this can expose sensitive geospatial artifacts to other users, later tasks, or backup processes.
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
plt.close()
img = Image.open(png_tmp).convert('RGB')
img.save(output_path, 'JPEG', quality=82, optimize=True)
__import__('os').remove(png_tmp)
size = __import__('os').path.getsize(output_path) // 1024
print(f" ✓ 已保存预览图(WGS84底图,JPG压缩{size}KB): {output_path}")
return
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
img = Image.open(png_tmp).convert('RGB')
img.save(output_path, 'JPEG', quality=82, optimize=True)
__import__('os').remove(png_tmp)
size = __import__('os').path.getsize(output_path) // 1024
print(f" ✓ 已保存预览图(WGS84底图,JPG压缩{size}KB): {output_path}")
return
except Exception as e:
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
plt.close()
img = Image.open(png_tmp).convert("RGB")
img.save(output_path, "JPEG", quality=75, optimize=True)
__import__("os").remove(png_tmp)
size_kb = __import__("os").path.getsize(output_path) // 1024
print(f" ✓ 已保存预览图(简洁WGS84网格背景,{size_kb}KB): {output_path}")
Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.
img = Image.open(png_tmp).convert("RGB")
img.save(output_path, "JPEG", quality=75, optimize=True)
__import__("os").remove(png_tmp)
size_kb = __import__("os").path.getsize(output_path) // 1024
print(f" ✓ 已保存预览图(简洁WGS84网格背景,{size_kb}KB): {output_path}")
"""
The dependency section states the system must install a Chinese font and names a specific CJK font for matplotlib use. This enforces a locale-specific rendering setup in natural language without indicating user opt-in or explaining why no language-neutral or user-selectable alternative is available.
The top-level docstring explicitly claims 预览图.png as an output artifact. Later code saves the final preview as JPEG, so the inline documentation contradicts actual behavior rather than merely omitting detail.
The module docstring states the output includes 预览图.png, but both preview-generation branches save a temporary PNG and then convert it to a final JPEG at the requested output path. The main routine also names the final file isochrone_preview.jpg, so the observed behavior does not match the documented output format.