Back to skill

Security audit

17TRACK

Security checks for vulnerabilities and agentic risk

Overview

This package-tracking skill mostly matches its stated purpose, but its optional webhook path can accept and store untrusted data too broadly and its daily report deletes delivered-package records.

Use this skill only if you are comfortable sending tracking numbers, carrier data, optional parameters, language, token-authenticated requests, and possibly label-derived tags to 17TRACK. Prefer manual polling over webhooks. Do not expose the webhook server beyond localhost until signature rejection and request-size limits are fixed. Avoid sensitive labels, and be aware that running the daily report removes delivered packages from the local database.

Vulnerability Patterns
  • 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
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/track17.py:1237
Finding
Webhook Signature Validation Fails Open<![CDATA[ ## Vulnerability Details **File Location**: `scripts/track17.py:1237-1309` **Vulnerability Type**: Authentication bypass and untrusted webhook ingestion **Risk Level**: High ### Vulnerable Code ```python sig_valid: Optional[bool] = None if secret and sig_value: expected = compute_webhook_signature(raw_body, secret) sig_valid = expected.lower() == sig_value.lower() payload_sha = store_payload( conn, raw_body=raw_body, source=source, event_type=None, number=None, carrier=None, signature=sig_value, signature_valid=sig_valid, ) payload = parse_webhook_payload(raw_body) ``` The payload is subsequently applied regardless of the signature result: ```python # Ensure package exists pkg = conn.execute( "SELECT * FROM packages WHERE number=? AND carrier=? AND param=? ORDER BY id DESC LIMIT 1", (number_s, carrier_i, param), ).fetchone() if not pkg: pkg = upsert_package( conn, number=number_s, carrier=carrier_i, param=param, label=None, tag=tag, lang=os.environ.get("TRACK17_LANG") or "en", api_registered=True, ) # Use the data object as the response item; it includes track_info. changed, summary = apply_update_from_trackinfo( conn, package_row=pkg, response_item=data, raw_payload_sha=payload_sha, source=source, ) if secret and sig_value: validity = "valid" if sig_valid else "INVALID" summary = f"[{validity} signature via {sig_header_name}] " + summary elif secret and not sig_value: summary = "[no signature header] " + summary ``` ### Technical Analysis When `TRACK17_WEBHOOK_SECRET` is configured, the code calculates whether the supplied signature is valid, but does not use that result as an authorization decision. An invalid signature is only recorded in the database and displayed in a summary. A missing signature is similarly noted without causing rejection. As a result, signature validation operates ...[truncated 1804 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. When a webhook secret is configured, reject the payload before storage or parsing if the signature is missing or invalid. 2. Use constant-time comparison: ```python import hmac if secret: if not sig_value: raise Track17Error("Missing webhook signature") expected = compute_webhook_signature(raw_body, secret) if not hmac.compare_digest(expected.lower(), sig_value.lower()): raise Track17Error("Invalid webhook signature") ``` 3. Perform authentication before calling `store_payload`, `parse_webhook_payload`, `upsert_package`, or `apply_update_from_trackinfo`. 4. Return an HTTP 401 or 403 response for unauthenticated network webhook requests rather than acknowledging them with HTTP 200. 5. Consider requiring a webhook secret whenever the server binds to a non-loopback interface. 6. Add tests confirming that missing, malformed, and incorrect signatures cannot create or modify database records. 7. Store rejected-request metadata only if operationally necessary, and do not retain the full untrusted body by default. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/track17.py:1388
Finding
Unbounded Webhook Requests Can Exhaust Memory and Disk<![CDATA[ ## Vulnerability Details **File Location**: `scripts/track17.py:1388-1452` **Vulnerability Type**: Unbounded input handling and denial of service **Risk Level**: Medium ### Vulnerable Code ```python def do_POST(self) -> None: # noqa: N802 server: "WebhookServer" = self.server # type: ignore[assignment] try: length = int(self.headers.get("Content-Length") or "0") except ValueError: length = 0 raw_body = self.rfile.read(length) if length > 0 else self.rfile.read() # Basic response first to keep provider happy. self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(b"{\"ok\":true}\n") # Then spool payload to disk. server.spool(raw_body, dict(self.headers)) ``` The payload and all headers are written to disk without byte limits: ```python # Very small backpressure: if inbox is huge, drop oldest processed files. try: existing = sorted(self.inbox_dir.glob("*.json")) if len(existing) >= self.max_files: for old in existing[: max(0, len(existing) - self.max_files + 1)]: try: old.unlink() side = old.with_suffix(".headers.json") if side.exists(): side.unlink() except Exception: pass except Exception: pass fpath.write_bytes(raw_body) (self.inbox_dir / f"{fpath.stem}.headers.json").write_text(json.dumps(headers), "utf-8") ``` ### Technical Analysis The HTTP handler trusts `Content-Length` and reads the declared amount into memory without imposing a maximum request size. If the header is absent, zero, or malformed, it calls `self.rfile.read()` without a bound, potentially waiting for connection closure and tying up a request thread. The inbox limit controls only the number of JSON files, not their aggregate size. A single request can therefore consume substantial memory and disk. Repeated large requests can c ...[truncated 1631 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Define a strict maximum webhook size appropriate for 17TRACK payloads. 2. Require a valid numeric `Content-Length`; respond with HTTP 411 if it is absent and HTTP 400 if malformed. 3. Return HTTP 413 before reading the body when the declared length exceeds the limit: ```python MAX_WEBHOOK_BYTES = 1024 * 1024 length_header = self.headers.get("Content-Length") if length_header is None: self.send_error(411, "Content-Length required") return try: length = int(length_header) except ValueError: self.send_error(400, "Invalid Content-Length") return if length < 0 or length > MAX_WEBHOOK_BYTES: self.send_error(413, "Payload too large") return raw_body = self.rfile.read(length) if len(raw_body) != length: self.send_error(400, "Incomplete request body") return ``` 4. Configure socket read timeouts and concurrency limits to mitigate slow-request attacks. 5. Validate authentication and JSON structure before acknowledging and writing the request. 6. Enforce both per-file and aggregate inbox byte quotas. 7. Store only required headers, such as the signature header and content type; redact authorization, cookie, and proxy-authentication headers. 8. Use a TLS-enabled, rate-limited reverse proxy when exposing the server beyond localhost. 9. Retain the secure loopback default and warn or require explicit confirmation when binding to a non-loopback address. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/track17.py:743
Finding
Local Package Labels Are Unnecessarily Transmitted to 17TRACK<![CDATA[ ## Vulnerability Details **File Location**: `scripts/track17.py:743-760` **Vulnerability Type**: Excessive disclosure of user-supplied data **Risk Level**: Low ### Vulnerable Code ```python number = _normalise_number(args.number) carrier = int(args.carrier or 0) param = args.param or "" label = args.label tag = args.tag or (label[:32] if label else "") lang = args.lang or (os.environ.get("TRACK17_LANG") or "en") row = upsert_package( conn, number=number, carrier=carrier, param=param, label=label, tag=tag, lang=lang, api_registered=False, ) # Register with API payload_item: Dict[str, Any] = {"number": number} if carrier: payload_item["carrier"] = carrier if tag: payload_item["tag"] = tag if param: payload_item["param"] = param if lang: payload_item["lang"] = lang ``` The resulting payload is sent to the 17TRACK API: ```python resp = api_register([payload_item]) ``` ### Technical Analysis When the user provides a local descriptive label but does not explicitly provide `--tag`, the code automatically copies the first 32 characters of the label into the remote API tag. Package labels are useful for local presentation but are not necessary to identify or poll a shipment. This behavior sends more user information than the minimum required for the declared tracking function. The documentation explains that the API token and tracking number are sent to 17TRACK, but it does not clearly disclose that a friendly local label is transmitted by default. Labels such as product names, recipient references, or other personal descriptions may reveal purchase or behavioral information unrelated to the technical tracking request. ### Attack Path 1. A user adds a package with a descriptive label, for example through `add TRACKING_NUMBER --label "Personal medical supplies"`. 2. The user does not provide `--tag`. 3. The code automatically sets `tag` to the first 32 characters of the label. 4. The registration payl ...[truncated 854 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Keep `--label` local by default. 2. Send a remote tag only when the user explicitly supplies `--tag`: ```python label = args.label tag = args.tag or "" payload_item: Dict[str, Any] = {"number": number} if tag: payload_item["tag"] = tag ``` 3. Update `README.md` and `SKILL.md` to enumerate every field transmitted to 17TRACK, including tracking numbers, carrier codes, optional parameters, language, tags, and the API token header. 4. If automatic label-to-tag behavior must be retained for compatibility, make it opt-in and display a clear notice before transmission. 5. Avoid putting sensitive recipient, product, medical, financial, or address information in remote tags. 6. Consider a migration that preserves existing local labels while discontinuing their inclusion in future API registration requests. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (15)

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill description advertises API polling and webhook ingestion but does not clearly disclose that tracking numbers, shipment metadata, and related updates are sent to an external third-party service. Because shipment data can reveal sensitive personal or business logistics information, the missing privacy warning reduces informed consent and can lead to inappropriate exposure of user data.

Vague Triggers

Medium
Confidence
95% confidence
Finding
The trigger examples are broad enough to match generic phrases like 'where is my package?' or 'any updates on my orders?', which can cause the skill to activate in contexts the user did not clearly intend. In a package-tracking skill that can query external services and manipulate stored tracking data, over-broad routing increases the chance of unintended data disclosure or unintended actions.

Autonomous Decision Making

Medium
Category
Excessive Agency
Content
- **Add packages** — "track RR123456789CN, it's my new headphones"
- **Check status** — "where is my package?" / "any updates on my orders?"
- **Sync updates** — polls 17TRACK API for latest tracking events
- **Auto-cleanup** — delivered packages are automatically removed by daily reports
- **Webhook support** — optional real-time push updates from 17TRACK

## Features
Confidence
90% confidence
Finding
Automatic removal of delivered packages is an autonomous action that changes local state without an explicit per-item user decision. In the context of shipment records, this can destroy useful history and create operational or evidentiary issues if a delivery is disputed or if cleanup logic behaves incorrectly.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The README states that delivered packages are automatically removed by daily reports, but it does not clearly warn users that package history may be deleted without explicit review or consent. This creates a data-retention and integrity risk because users may lose records they expected to preserve for disputes, audits, or reimbursement purposes.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill declares broad operational capabilities in practice (environment access, filesystem reads/writes, network access, and shell execution) but does not explicitly constrain them with a tool scope. That increases the blast radius if the skill is invoked unexpectedly or modified later, because the agent may grant more authority than is necessary for parcel tracking tasks.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The invocation guidance includes very broad trigger phrases such as 'track this', 'where is my order', and 'any updates on my package', which can match common conversational requests without sufficient user intent verification. In a skill with network, shell, and local database capabilities, over-broad activation can cause unnecessary external API calls, local state changes, or processing of sensitive shipment data when the user did not clearly intend to use this specific integration.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print("❌ TRACK17_TOKEN not set")
        return False

    result = subprocess.run(
        [sys.executable, str(TRACK17_SCRIPT), "sync"],
        capture_output=True,
        text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def remove_package(pkg_id):
    """Remove a package via track17.py."""
    result = subprocess.run(
        [sys.executable, str(TRACK17_SCRIPT), "remove", str(pkg_id)],
        capture_output=True,
        text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script automatically removes delivered packages during report generation, creating an unexpected destructive side effect for what appears to be a read/report operation. In this skill context, package tracking data may be operationally important, so silent deletion can cause loss of audit/history, user confusion, and missed follow-up actions if delivery records are needed later.

External Transmission

Medium
Category
Data Exfiltration
Content
import urllib.request
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple

API_BASE = "https://api.17track.net/track/v2.2"
CARRIERS_URL = "https://res.17track.net/asset/carrier/info/apicarrier.all.json"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
import urllib.request
from typing import Any, Dict, Iterable, List, Optional, Sequence, Tuple

API_BASE = "https://api.17track.net/track/v2.2"
CARRIERS_URL = "https://res.17track.net/asset/carrier/info/apicarrier.all.json"
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
This code writes raw webhook bodies and request headers to files in the local inbox directory, which can include tracking data and metadata from external requests. Although the module docstring mentions webhook ingestion and storage directories, there is no nearby user-facing warning, confirmation, or explicit disclosure when enabling the webhook server that incoming request contents and headers will be persisted to disk.

Intent-Code Divergence

Low
Confidence
89% confidence
Finding
The docstring frames the script as producing stdout-only reporting with no effects beyond contacting 17TRACK, but main() performs auto-cleanup by calling track17.py remove for delivered packages. That behavior changes local tracking state, so the documentation understates the script's side effects.

Natural-Language Policy Violations

Low
Confidence
96% confidence
Finding
The status formatter hardcodes Italian-language labels such as `In Transito`, `Consegnato`, and `Non Trovato` regardless of user preference. This imposes a specific language/locale in natural-language output without offering choice or documenting a justified locale restriction.

Natural-Language Policy Violations

Low
Confidence
84% confidence
Finding
The natural-language documentation states the default translation language is "en", and the code uses English as the fallback language in multiple places. This imposes a specific locale by default rather than prompting for or offering a language choice at runtime.

Static analysis

No suspicious patterns detected.