T09 · Insecure Skill Coding Practices
- Location
scripts/collect_stats.py:252- Finding
Webhook URLs Are Exposed in Generated Data and Reports
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a coherent local NxVET reporting tool, but it handles API credentials and clinic data with under-scoped local storage and can expose full webhook URLs in generated artifacts.
Review before installing. Use this only in a dedicated project folder, keep .env/state/output out of git and cloud sync, prefer setting NXVET_API_KEY via the process environment, delete or disable the cache for sensitive clinics, and redact webhook URLs before sharing reports. The skill does not appear malicious, but its local data handling should be tightened.
scripts/collect_stats.py:252Webhook URLs Are Exposed in Generated Data and Reports
scripts/collect_stats.py:37Sensitive Runtime State Is Anchored to the Caller’s Working Directory
scripts/collect_stats.py:110Cached Clinic API Responses Are Created Without Explicit Restrictive Permissions
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.
req = urllib.request.Request(url, headers={
"Authorization": f"Bearer {key}", "Accept": "application/json"})
try:
with urllib.request.urlopen(req, timeout=30) as resp:
body = resp.read().decode("utf-8")
obj = json.loads(body) if body else None
if use_cache:
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).
Override with the `CACHE_TTL_S` env var (e.g. `CACHE_TTL_S=0` to disable, `3600` for an hour).
The cache is keyed by full URL, written atomically (`.tmp` + `os.replace`), and safe to delete at
any time (`rm -rf state/http_cache/`) — the next run just repopulates it. Because this tool is
read-only and recomputes the whole report each run, a stale cache entry can at most show numbers
up to `CACHE_TTL_S` old; it can never corrupt anything.
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
project/
.env # NXVET_API_KEY (git-ignored)
config.json # orgId, orgName, timezone
scripts/ # collect_stats.py, write_report.py
state/http_cache/ # ETag cache (git-ignored, safe to delete)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
project/
.env # NXVET_API_KEY (git-ignored)
config.json # orgId, orgName, timezone
scripts/ # collect_stats.py, write_report.py
state/http_cache/ # ETag cache (git-ignored, safe to delete)
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
key = os.environ.get("NXVET_API_KEY")
if key:
return key.strip()
env_path = os.path.join(PROJECT, ".env")
fallback = None
if os.path.exists(env_path):
with open(env_path) as f:
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
key = os.environ.get("NXVET_API_KEY")
if key:
return key.strip()
env_path = os.path.join(PROJECT, ".env")
fallback = None
if os.path.exists(env_path):
with open(env_path) as f:
The skill requires environment access, file writes, and outbound network access but does not explicitly constrain tool scope with permissions or allowed-tools. In an agent setting, that broad implicit capability increases the risk of unintended secret access, filesystem modification, or arbitrary network requests beyond the read-only NxVET use case described in the text.
Earlier lines explicitly state NxVET does not provide ETag support and that collect_stats.py therefore uses a time-based TTL cache instead. The later statement that the 'ETag cache keeps repeat cost near zero' directly contradicts the described implementation and could mislead readers about how caching works.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# NxVET API — what the tool needs
Full docs: **https://api.nx.vet/** — machine-readable references to fetch and read:
- `https://api.nx.vet/llms-full.txt` — complete API guide written for AI agents.
- `https://api.nx.vet/openapi/nxvet-api.yaml` — OpenAPI spec.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# NxVET API — what the tool needs
Full docs: **https://api.nx.vet/** — machine-readable references to fetch and read:
- `https://api.nx.vet/llms-full.txt` — complete API guide written for AI agents.
- `https://api.nx.vet/openapi/nxvet-api.yaml` — OpenAPI spec.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# NxVET API — what the tool needs
Full docs: **https://api.nx.vet/** — machine-readable references to fetch and read:
- `https://api.nx.vet/llms-full.txt` — complete API guide written for AI agents.
- `https://api.nx.vet/openapi/nxvet-api.yaml` — OpenAPI spec.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# NxVET API — what the tool needs
Full docs: **https://api.nx.vet/** — machine-readable references to fetch and read:
- `https://api.nx.vet/llms-full.txt` — complete API guide written for AI agents.
- `https://api.nx.vet/openapi/nxvet-api.yaml` — OpenAPI spec.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# NxVET API — what the tool needs
Full docs: **https://api.nx.vet/** — machine-readable references to fetch and read:
- `https://api.nx.vet/llms-full.txt` — complete API guide written for AI agents.
- `https://api.nx.vet/openapi/nxvet-api.yaml` — OpenAPI spec.
The top-level documentation says nothing is sent anywhere and that output is only a single JSON object on stdout, but the implementation also writes fetched API response bodies to local cache files. This mismatch can mislead operators about data residency and retention, increasing the chance that sensitive clinic data is handled in ways they did not expect or approve.
The script claims to be read-only/local and implies minimal data handling, but it persists full API responses to disk under state/http_cache. Those cached files can contain clinic activity data, device metadata, and webhook details, creating a local data-at-rest exposure if filesystem permissions are weak, backups are broad, or shared workstations are used.
The instruction says to ask for the clinic timezone but to default to America/New_York if not provided. This imposes a specific locale choice by default, which can conflict with organizational language/locale policies unless the user explicitly opts in or the regional constraint is clearly justified.
The file instructs users to cache the organizationId in config.json, which is a form of local persistence of account metadata. The documentation does not explicitly warn users that this identifier will be stored on disk, which is relevant user disclosure for a markdown skill description.
This markdown file explains that the tool stores an HTTP cache under state/http_cache/ and later notes that reports are written to output/, but it does not include a user-facing warning that API response data will be persisted locally. Because the documented behavior affects local data storage, a brief disclosure would improve user awareness and safety.
The section heading says 'No state needed beyond the cache', which implies the cache is the only persisted state. However, the text immediately acknowledges that the tool writes reports to output/, which is additional on-disk state. This is a documentation-level contradiction about persistence behavior.
The module docstring describes reading JSON from stdin or a file and writing the report to stdout or an output file, but the overall skill manifest emphasizes 'read-only and local; nothing is sent or changed.' In this file, providing --out causes the skill to create directories and write a markdown file, which is a state-changing local filesystem operation rather than purely read-only reporting.