Back to skill

Security audit

turingnet-iran-connectivity-engineer

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly aligned with lawful connectivity troubleshooting, but its privacy and guard guarantees are weakened by concrete implementation issues users should review before installing.

Review before installing. The skill does not show exfiltration, destructive behavior, persistence, or hidden remote payloads, but it handles sensitive troubleshooting evidence and its privacy controls are weaker than advertised. Avoid --collect on shared machines, avoid --skip-guard, do not use the npx @latest install fallback, and only allowlist known public status-page hosts until these issues are fixed.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/guard.py:47
Finding
Attacker-Controlled Defense Marker Bypasses Guard Rules<![CDATA[ ## Vulnerability Details **File Location**: `scripts/guard.py`, lines 47–54 and 83 **Vulnerability Type**: Trust-boundary bypass through attacker-controlled exemption marker **Risk Level**: High ### Vulnerable Code ```python for gid, cat, rx, sev in PATTERNS: for m in re.finditer(rx, text, re.I): if defense_exempt and sev == "warn": continue if defense_exempt and sev == "block" and cat not in ("G09", "G05", "G07"): # inside a marked defense template, descriptive/defensive mentions # of bypass/tunnel/scan/harvest topics are the point of the file; # only explicit how-to-requests (G09) and attack instructions # (G05/G07) still block. continue ``` ```python result = check(text, defense_exempt=(DEFENSE_MARKER in text)) ``` ### Technical Analysis The guard treats the mere presence of the string `turingnet:defense` as proof that the input is a trusted defensive template. This marker is part of the input being inspected and can therefore be supplied by an attacker. When the marker is present, the guard suppresses all warning findings and suppresses blocking findings except categories `G09`, `G05`, and `G07`. Consequently, matches involving tunnel evasion, scanning, credential harvesting, interference, and several bypass-related patterns can be ignored. The exemption is not tied to a trusted file path, an immutable bundled template, a cryptographic digest, or another provenance control. This violates the principle that trust decisions must not be based on attacker-controlled content. ### Attack Path 1. An attacker creates a draft containing the text `<!-- turingnet:defense -->`. 2. The attacker adds prohibited material that matches an exempted blocking category, such as scanning, tunnel-evasion, or credential-harvesting language. 3. The draft is passed to `scripts/guard.py`, either directly or through `scripts/low_bandwidth_report.py`. 4. `DEFENSE_MARKER in tex ...[truncated 707 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove content-controlled exemptions. Do not grant trust based on a marker embedded in the inspected document. - Apply the same blocking rules to all untrusted drafts. - If trusted templates require special handling, establish provenance using: - A canonical path restricted to the bundled `templates/` directory. - A manifest of approved template hashes. - Secure path resolution that rejects symlink and traversal escapes. - Prefer context-aware defensive-language detection instead of broadly suppressing whole rule categories. - Add regression tests proving that an attacker-created document containing the marker cannot suppress scanning, credential-harvesting, tunnel-evasion, or interference findings. - Treat unknown or modified defense templates as ordinary untrusted input. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/rate_limiter.sh:48
Finding
Status-Page Client Allows Requests to Loopback and Internal Destinations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/rate_limiter.sh`, lines 48–57 and 68–82 **Vulnerability Type**: Insufficient destination validation enabling internal-network requests **Risk Level**: Medium ### Vulnerable Code ```bash allow) HOST="${2:-}" case "$HOST" in *.example|*.local|localhost) echo "refusing placeholder/internal host" >&2; exit 2 ;; esac [ -n "$HOST" ] || { echo "usage: rate_limiter.sh allow <status.host>" >&2; exit 2; } mkdir -p "$STATE_DIR" echo "$HOST" >> "$STATE_DIR/allowed_hosts.txt" sort -u "$STATE_DIR/allowed_hosts.txt" -o "$STATE_DIR/allowed_hosts.txt" echo "allowlisted status host: $HOST" exit 0 ``` ```bash case "$URL" in https://*) : ;; *) echo "refusing: only https:// URLs" >&2; exit 2 ;; esac HOST=$(printf '%s' "$URL" | sed -E 's#^https://([^/:]+).*#\1#') ALLOW="$STATE_DIR/allowed_hosts.txt" if [ ! -f "$ALLOW" ] || ! grep -qxF "$HOST" "$ALLOW"; then echo "refusing: host '$HOST' not allowlisted — run: rate_limiter.sh allow $HOST" >&2 exit 2 fi # one bounded attempt: HEAD first (cheapest), fallback to GET, 10s cap if curl -sS -I --max-time 10 "$URL" -o /dev/null 2>/dev/null; then write_ts "$(now)"; echo "HEAD $URL ok (budget now $((MAX - USED - 1))/$MAX)" elif curl -sS --max-time 10 "$URL" -o /dev/null 2>/dev/null; then ``` The project test explicitly demonstrates acceptance of a loopback address: ```bash bash "$HERE/rate_limiter.sh" allow "127.0.0.1" >/dev/null 2>&1 && note "allow subcommand registers host" || fail "allow failed" ``` ### Technical Analysis The script is described as a client for official public status pages, but the allowlist command accepts numeric loopback addresses and other private, link-local, reserved, or internal destinations. It rejects only `localhost`, `*.local`, and `*.example`. The allowlist stores a textual hostname without resolving and validating its addresses. The subsequent `curl` invocation also lacks destination-address ...[truncated 1483 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Reject loopback, private, link-local, multicast, unspecified, reserved, and special-purpose IPv4 and IPv6 ranges. - Resolve the hostname before each request and reject the request if any returned address is non-public. - Protect against DNS rebinding by ensuring the address actually used by `curl` is the previously validated public address, for example through carefully validated `--resolve` handling. - Maintain a curated allowlist of approved public status-page domains instead of permitting arbitrary user-provided hosts. - Normalize hostnames, reject user-info syntax and malformed authority components, and use a robust URL parser rather than `sed`. - Consider disabling proxy environment variables for this narrowly scoped client or documenting and validating proxy behavior. - Add tests confirming that loopback, RFC 1918, IPv6 loopback, link-local, and hostnames resolving to those ranges are rejected. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/turingnet_triage.sh:28
Finding
Predictable Temporary File Permits Local Symlink Attacks<![CDATA[ ## Vulnerability Details **File Location**: `scripts/turingnet_triage.sh`, lines 28–43 **Vulnerability Type**: Unsafe predictable temporary file creation **Risk Level**: Medium ### Vulnerable Code ```bash if [ "$MODE" = "--collect" ]; then OUT="evidence_$(date +%Y%m%d_%H%M%S).md" { echo "# TuringNet triage record (auto-redacted on write)" for q in "Safety: own/authorized? (yes/no)" "Scope (device/network/service/many)" \ "Last known working (time + tz)" "Observed time (time + tz)" \ "Intermittent or constant" "Access type (mobile/broadband/office/wifi)" \ "Redacted error text (no secrets, no numbers)" "Known-good comparison"; do printf '%s\n' "$q" read -r ANSW printf 'A: %s\n\n' "$ANSW" done } >> /tmp/turingnet_triage_raw.$$ 2>/dev/null || true # FAIL CLOSED: if redaction cannot run, never write unredacted answers. if python3 "$HERE/redact_pii.py" --input /tmp/turingnet_triage_raw.$$ --output "$OUT" --mode standard 2>/dev/null; then : else printf '# triage record withheld: redaction failed\n# (raw answers were NOT written; re-run and re-answer)\n' > "$OUT" fi rm -f /tmp/turingnet_triage_raw.$$ echo "wrote $OUT (run scripts/guard.py --input $OUT before sharing)" fi ``` ### Technical Analysis The script constructs its temporary filename from the process ID and places it in the shared `/tmp` directory. Process IDs are predictable, and the file is opened with append redirection without exclusive creation or a symlink check. A local attacker can pre-create the expected path as a symbolic link. The shell follows that link when opening the redirection target. The temporary file also lacks an explicit restrictive permission policy, so its protection depends on the invoking user's current `umask`. The script suppresses redirection errors with `|| true`, which can further obscure failures or manipulation. Cleanup is performed only at the end and is not protected ...[truncated 1207 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Set a restrictive permission mask before collecting data: ```bash umask 077 ``` - Create the temporary file securely with `mktemp`: ```bash RAW="$(mktemp "${TMPDIR:-/tmp}/turingnet_triage_raw.XXXXXX")" || exit 1 ``` - Quote every use of the generated path. - Install cleanup handlers immediately after creation: ```bash trap 'rm -f -- "$RAW"' EXIT INT TERM HUP ``` - Use ordinary overwrite redirection to the newly created file rather than append redirection to a predictable path. - Fail closed if temporary-file creation or writing fails; do not suppress the error with `|| true`. - Consider keeping sensitive intermediate content in a private runtime directory owned by the user. - Add a regression test that pre-creates a symlink at a candidate path and verifies that the script neither follows it nor writes raw answers outside the secure temporary file. ]]>

T08 · Insecure Dependencies

Warning
Location
README.md:33
Finding
Installation Instructions Execute an Unpinned Latest Package<![CDATA[ ## Vulnerability Details **File Location**: `README.md`, lines 33–35 **Vulnerability Type**: Mutable and unverified third-party installation dependency **Risk Level**: Medium ### Vulnerable Code ```bash openclaw skills install @orionshaowswmw/turingnet-iran-connectivity-engineer # or: npx --yes clawhub@latest install turingnet-iran-connectivity-engineer bash skills/turingnet-iran-connectivity-engineer/scripts/turingnet_triage.sh ``` ### Technical Analysis The documented alternative installation command uses `npx --yes` with the mutable `@latest` tag. This allows the package selected at installation time to differ from the package version reviewed during the audit. `npx` downloads and executes package code. The `--yes` option suppresses the interactive installation confirmation, while no exact version, integrity digest, lockfile, signature, or trusted artifact reference is supplied. This creates a supply-chain trust gap: the audited repository can be benign while a future, replaced, or compromised `clawhub@latest` package executes different code. ### Attack Path 1. An attacker compromises the publishing account, package registry, or release pipeline associated with the `clawhub` package. 2. The attacker publishes a malicious version and makes it the package resolved by `@latest`. 3. A user follows the README and runs `npx --yes clawhub@latest install ...`. 4. `npx` downloads and executes the malicious package without presenting an installation confirmation. 5. The package executes with the privileges of the user running the command and can tamper with the installation or local user data. ### Impact Assessment A compromised package can execute arbitrary code with the installing user's privileges. It may read or alter files accessible to that user, modify the installed skill, retrieve additional payloads, or establish persistence using permissions already held by the account. No malicious package is present in the audited project, and exploita ...[truncated 168 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Replace `@latest` with an exact, audited package version. - Publish and document a cryptographic integrity digest or signed release provenance for the installer. - Avoid `--yes` so users are informed before `npx` downloads and executes a package. - Prefer a reproducible installation process backed by a lockfile or immutable release artifact. - Document how users can verify the package publisher, version, checksum, and signature before execution. - Pin the skill version itself and verify its contents after installation. - Periodically review pinned dependencies and update them through an explicit audited release process rather than automatically tracking a mutable tag. ]]>
Vulnerability Patterns
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (4)

Description-Behavior Mismatch

Medium
Confidence
97% confidence
Finding
The tool exposes a --skip-guard flag even though the guard is intended to block unsafe or insufficiently redacted drafts before report generation. An operator can intentionally or accidentally bypass that safety control and produce HTML reports containing content the guard would have rejected, increasing the chance of leaking sensitive data or policy-violating material.

Intent-Code Divergence

Medium
Confidence
90% confidence
Finding
The documentation claims the guard always runs first and refuses blocked input, but the implementation includes a bypass flag. This mismatch can cause users and integrators to rely on a safety property that is not guaranteed, leading to unsafe handling of unreviewed or unredacted content in a privacy-sensitive reporting workflow.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
This is a real issue: the script stores operator answers in an unredacted temporary file under /tmp before redaction occurs, yet the comment and fallback message imply raw answers are never written if redaction fails. If the process crashes, is killed before cleanup, or /tmp is readable by another local user or backup/forensics tooling, sensitive troubleshooting notes may be exposed despite the privacy-focused purpose of the skill.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The script includes `args.input` and up to 20 `residual_suspects` snippets in its verdict output, and in non-JSON mode also writes residual snippets to stderr. Because this tool is explicitly designed to process sensitive evidence and PII, exposing file paths and fragments of unmatched suspicious content can leak identifiers, usernames, case names, or remaining PII into logs, terminals, CI artifacts, or support transcripts.

Static analysis

No suspicious patterns detected.