T09 · Insecure Skill Coding Practices
- Location
scripts/twtapi.py:15- Finding
Custom gateway permits plaintext transmission of the TwtAPI skill key
- Content
View full analysis
Vulnerability Details
File Location:
scripts/twtapi.py:15-40andscripts/twtapi.py:69-80
Vulnerability Type: Unvalidated URL scheme resulting in potential plaintext credential transmission
Risk Level: MediumVulnerable Code
python BASE_URL = os.environ.get("TWTAPI_SKILL_BASE_URL") or DEFAULT_BASE_URL TIMEOUT = 30 USER_AGENT = "curl/8.7.1" def _call(tool: str, params: dict | None = None) -> dict: if not SKILL_KEY: print("Error: TWTAPI_SKILL_KEY is not set. Get it from your TwtAPI dashboard.", file=sys.stderr) sys.exit(1) url = f"{BASE_URL.rstrip('/')}/api/v1/agent/invoke" payload = json.dumps( { "tool": tool, "params": params or {}, } ).encode("utf-8") req = urllib.request.Request( url, data=payload, method="POST", headers={ "X-Skill-Key": SKILL_KEY, "Accept": "application/json", "Content-Type": "application/json", "User-Agent": USER_AGENT, }, )The same unsafe destination construction is used by the tool-discovery endpoint:
python def _call_tools() -> dict: if not SKILL_KEY: print("Error: TWTAPI_SKILL_KEY is not set. Get it from your TwtAPI dashboard.", file=sys.stderr) sys.exit(1) url = f"{BASE_URL.rstrip('/')}/api/v1/agent/tools" req = urllib.request.Request( url, headers={ "X-Skill-Key": SKILL_KEY, "Accept": "application/json", "User-Agent": USER_AGENT, }, )Technical Analysis
TWTAPI_SKILL_BASE_URLis accepted directly from the process environment and used as the destination for authenticated requests. The code does not parse the URL or require thehttpsscheme before placingTWTAPI_SKILL_KEYin theX-Skill-Keyheader.The default endpoint, `http ...[truncated 2187 chars]
- Remediation
View remediation
Remediation Suggestions
- Parse
TWTAPI_SKILL_BASE_URLwithurllib.parse.urlparsebefore constructing a request. - Require the
httpsscheme for all remote endpoints. - Reject URLs with missing hostnames, embedded user information, fragments, or unsupported schemes.
- If plaintext HTTP is required for local development, permit it only for explicit loopback destinations such as
localhost,127.0.0.1, or::1, and require a separate opt-in environment variable. - Perform validation before adding
X-Skill-Keyto any request. - Clearly warn users that any custom gateway receives the dedicated skill key and request parameters.
- Recommend immediate key rotation if the credential has previously been sent to an HTTP or untrusted endpoint.
Example hardening logic:
python import ipaddress import urllib.parse def validate_base_url(value: str) -> str: parsed = urllib.parse.urlparse(value) if not parsed.hostname or parsed.username or parsed.password: raise ValueError("The TwtAPI base URL is invalid.") is_loopback = parsed.hostname == "localhost" if not is_loopback: try: is_loopback = ipaddress.ip_address(parsed.hostname).is_loopback except ValueError: is_loopback = False if parsed.scheme != "https" and not ( parsed.scheme == "http" and is_loopback and os.environ.get("TWTAPI_ALLOW_INSECURE_LOCALHOST") == "1" ): raise ValueError("The TwtAPI base URL must use HTTPS.") return value.rstrip("/") BASE_URL = validate_base_url( os.environ.get("TWTAPI_SKILL_BASE_URL") or DEFAULT_BASE_URL )- Parse
