Back to skill

Security audit

Thrd Skill

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its stated THRD email-inbox purpose, but one polling script can send the THRD API key to an arbitrary user-supplied server.

Install only if you trust THRD and are comfortable giving the skill a THRD_API_KEY for email operations. Do not run poll_daemon.py with a custom --base-url unless you have reviewed and trust that endpoint; prefer the default api.thrd.email endpoint, use an isolated environment, and consider pinning dependencies before installation.

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/poll_daemon.py:27
Finding
API Key Disclosure Through Unrestricted Polling Base URL<![CDATA[ ## Vulnerability Details **File Location**: `scripts/poll_daemon.py`, lines 27–76 **Vulnerability Type**: Bearer credential exposure through a user-controlled network destination **Risk Level**: High ### Vulnerable Code ```python parser.add_argument("--base-url", default="https://api.thrd.email") parser.add_argument("--cursor-file", default=".thrd_cursor") parser.add_argument("--timeout-ms", type=int, default=25000) parser.add_argument("--limit", type=int, default=50) args = parser.parse_args() api_key = os.environ.get("THRD_API_KEY") if not api_key: print(json.dumps({"ok": False, "error": "THRD_API_KEY environment variable not set."})) return 1 base_url = args.base_url.rstrip("/") cursor_path = Path(args.cursor_file) cursor = load_cursor(cursor_path) headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", } print(f"Thrd poll daemon started (cursor={cursor}, file={cursor_path}).", file=sys.stderr) while True: try: resp = requests.get( f"{base_url}/v1/events", headers=headers, params={"cursor": cursor, "timeout": args.timeout_ms, "limit": args.limit}, timeout=(args.timeout_ms / 1000.0) + 10, ) resp.raise_for_status() data = resp.json() events = data.get("events", []) next_cursor = data.get("next_cursor", cursor) if events: summary = { "ok": True, "received": len(events), "cursor": next_cursor, "types": [ev.get("type") for ev in events], } print(json.dumps(summary)) ack = requests.post( f"{base_url}/v1/events/ack", headers=headers, json={"cursor": str(next_cursor)}, timeout=30, ) ``` ### Technical Analysis The `--base-url` command-line argument controls the origin of both polling and acknowledgment requests. ...[truncated 2004 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove `--base-url` from production use and hardcode the authenticated endpoint: ```python BASE_URL = "https://api.thrd.email" ``` 2. If endpoint configurability is required for testing, validate the parsed URL before constructing authentication headers: ```python from urllib.parse import urlparse parsed = urlparse(args.base_url) if ( parsed.scheme != "https" or parsed.hostname != "api.thrd.email" or parsed.port not in (None, 443) or parsed.username is not None or parsed.password is not None ): raise ValueError("Unapproved THRD API origin") ``` 3. Use separate test credentials for development endpoints rather than transmitting production credentials to configurable origins. 4. Construct or attach the Authorization header only after destination validation succeeds. 5. Consider using a configured `requests.Session` with a policy that prevents credentials from being attached to unapproved origins. 6. Add automated tests confirming rejection of HTTP, alternate domains, subdomain tricks, nonstandard ports, embedded credentials, and malformed URLs. 7. Rotate any API key that may already have been used with an untrusted `--base-url`. ]]>

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding
Unpinned and Unhashed Third-Party Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `requirements.txt`, line 1; installation command declared in `SKILL.md`, lines 15–20 **Vulnerability Type**: Non-reproducible dependency resolution and supply-chain exposure **Risk Level**: Medium ### Vulnerable Code `requirements.txt`: ```text requests>=2.31.0 ``` `SKILL.md`: ```yaml install: [ { "id": "pip", "kind": "exec", "command": "pip install -r requirements.txt", "label": "Install Python dependencies", }, ], ``` ### Technical Analysis The requirement specifies only a minimum version and has no upper bound, exact pin, lock file, or package hashes. Each installation can therefore resolve to a different future version of `requests` and its transitive dependencies. The installation command does not use `--require-hashes`, so package integrity is based only on the package-index and transport trust model. The reviewed source does not introduce dependency confusion or typosquatting directly, but its open-ended resolution creates avoidable supply-chain drift and prevents reproducible verification of the installed code. ### Attack Path 1. A user installs the Skill dependencies with: ```bash pip install -r requirements.txt ``` 2. Pip resolves the newest package versions satisfying `requests>=2.31.0`, along with compatible transitive dependencies. 3. A future compromised, malicious, or unexpectedly incompatible matching release is selected. 4. The package is installed into the Skill's Python environment. 5. Package installation behavior or imported runtime code executes with the permissions of the installing user or Skill process. 6. Because every project script imports `requests`, malicious runtime behavior could affect API credentials and network traffic. This finding represents supply-chain exposure rather than evidence that the currently published `requests` package is malicious. ### Impact Assessment A compromised resolved dependency would run with ...[truncated 512 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin an audited exact version instead of using an open-ended minimum: ```text requests==<audited-version> ``` 2. Lock all transitive dependencies so installations are reproducible. 3. Generate and verify cryptographic hashes, then install with: ```bash pip install --require-hashes -r requirements.txt ``` 4. Regenerate dependency pins through a controlled update process that includes vulnerability scanning, release review, and tests. 5. Use an isolated virtual environment with only the permissions required by the Skill. 6. Configure pip to use the intended trusted package index explicitly and prevent unexpected extra-index dependency resolution. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • 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 (32)

Tainted flow: 'headers' from os.environ.get (line 16, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
payload = {"plan": plan}
    
    try:
        response = requests.post(url, json=payload, headers=headers, timeout=30)
        response.raise_for_status()
        data = response.json()
        print(json.dumps(data, indent=2))
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.environ.get (line 45, credential/environment) → requests.get (network output)

Critical
Category
Data Flow
Content
while True:
        try:
            resp = requests.get(
                f"{base_url}/v1/events",
                headers=headers,
                params={"cursor": cursor, "timeout": args.timeout_ms, "limit": args.limit},
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'headers' from os.environ.get (line 45, credential/environment) → requests.post (network output)

Critical
Category
Data Flow
Content
}
                print(json.dumps(summary))

                ack = requests.post(
                    f"{base_url}/v1/events/ack",
                    headers=headers,
                    json={"cursor": str(next_cursor)},
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
This finding highlights that the skill description promises secure email-management features, while the referenced workflow prominently includes OpenAPI synchronization/caching and local persistence in a .cache directory that are not fully disclosed in the top-level declaration. In security-sensitive agent ecosystems, undocumented persistence and behavior drift can hide data retention, stale-trust assumptions, or unexpected network/file operations from users and policy engines.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This finding highlights that the skill description promises secure email-management features, while the referenced workflow prominently includes OpenAPI synchronization/caching and local persistence in a .cache directory that are not fully disclosed in the top-level declaration. In security-sensitive agent ecosystems, undocumented persistence and behavior drift can hide data retention, stale-trust assumptions, or unexpected network/file operations from users and policy engines.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
Many LLM runtimes do not reliably maintain background polling. Use wake webhooks when possible:
- Configure webhook: `PUT /v1/wake/webhook`
- Read status: `GET /v1/wake/webhook`
- Disable webhook: `DELETE /v1/wake/webhook`

THRD sends signed `inbox.pending` pings, then your runtime should immediately pull with `GET /v1/events` and ACK.
Confidence
80% confidence
Finding
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).

Lp3

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding
The skill declares executable install/runtime behaviors and clearly implies access to environment variables, filesystem state, and network resources, but it does not declare an explicit tool scope such as permissions or allowed-tools. In an agent setting, missing scope boundaries increases the chance the runtime grants broader capabilities than intended, making misuse of secrets, disk writes, or outbound requests harder to constrain and audit.

External Transmission

Medium
Category
Data Exfiltration
Content
payload = {"plan": plan}
    
    try:
        response = requests.post(url, json=payload, headers=headers, timeout=30)
        response.raise_for_status()
        data = response.json()
        print(json.dumps(data, indent=2))
Confidence
80% 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
payload = {"plan": plan}
    
    try:
        response = requests.post(url, json=payload, headers=headers, timeout=30)
        response.raise_for_status()
        data = response.json()
        print(json.dumps(data, indent=2))
Confidence
80% 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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
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 requests


SPEC_URL = "https://api.thrd.email/openapi.json"


def now_iso() -> str:
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.