T09 · Insecure Skill Coding Practices
- Location
- references/gmail_link_archiver.py:162
- Finding
- Attacker-Controlled Email Links Can Access Internal Network Resources<![CDATA[ ## Vulnerability Details **File Location**: `references/gmail_link_archiver.py:162-191`, `references/gmail_link_archiver.py:247-249`, and `references/gmail_link_archiver.py:365-380` **Vulnerability Type**: Server-Side Request Forgery through unrestricted browser navigation **Risk Level**: High ### Vulnerable Code ```python def extract_links_from_email(msg) -> list: """Extract HTTP/HTTPS links from email body (HTML and plain text parts).""" links = set() url_pattern = re.compile(r'https?://[^\s<>"\')\]]+(?<![.,;:])') if msg.is_multipart(): for part in msg.walk(): ctype = part.get_content_type() try: payload = part.get_payload(decode=True) if payload is None: continue text = payload.decode("utf-8", errors="replace") except Exception: continue if ctype in ("text/plain", "text/html"): found = url_pattern.findall(text) links.update(found) else: try: payload = msg.get_payload(decode=True) if payload: text = payload.decode("utf-8", errors="replace") links.update(url_pattern.findall(text)) except Exception: pass # Filter out common tracking / unsubscribe links filtered = [ l for l in links if not any(skip in l.lower() for skip in [ "unsubscribe", "tracking", "click.email", "list-manage", "mailchimp", "googleadservices", ]) ] return sorted(filtered) ``` ```python page = context.new_page() page.goto(url, wait_until="networkidle", timeout=timeout) # Wait a bit for JS-rendered content page.wait_for_timeout(2000) return page.content() ``` ```python for em in emails: for link in em["links"]: if link not in link_subjects: link_subjects[link] = em["subject"] all_links.append(link) print(f"\n ...[truncated 3041 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Parse every URL with a standards-compliant URL parser and permit only explicitly supported schemes, normally HTTPS. 2. Resolve the hostname before navigation and reject every resolved IPv4 or IPv6 address that is loopback, private, link-local, multicast, reserved, unspecified, or otherwise non-global. 3. Explicitly block known metadata destinations, including `169.254.169.254`, relevant IPv6 link-local addresses, and provider-specific metadata hostnames. 4. Restrict destination ports to a narrow allowlist, such as 443 and, only if necessary, 80. 5. Validate every redirect destination using the same policy. Do not rely solely on checking the initial URL. 6. Defend against DNS rebinding by binding validation to the actual connection destination or by routing traffic through a hardened outbound proxy that enforces destination policy. 7. Consider an explicit domain allowlist for expected newsletter and archive sources. 8. Disable JavaScript unless it is essential. If JavaScript is required, intercept all browser requests and reject requests to disallowed destinations. 9. Run Chromium in an isolated network namespace or container without access to the host, private networks, or cloud metadata services. 10. Add automated tests covering direct private addresses, IPv6 addresses, encoded IP representations, redirects, mixed DNS answers, and DNS rebinding scenarios. ]]>
