Back to skill

Security audit

domain-recon

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed passive reconnaissance helper that queries public OSINT services and does not show hidden persistence, credential use, destructive behavior, or purpose-incompatible actions.

Install only if you are comfortable sending the domains, URLs, IPs, and ASNs you query to public third-party OSINT services. Avoid sensitive incident-response targets on networks where plaintext HTTP exposure is unacceptable, scope profile or full-TLD runs narrowly, and prefer disabling Wayback or IP enrichment when you do not need those sources.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/recon.py:1174
Finding

Plaintext HTTP Exposes Reconnaissance Queries and Permits Response Tampering

Content
View full analysis
dict: addr = normalize_ip(ip) # Request the extended field set (geo + ISP + AS + proxy/hosting flags). fields = "status,message,country,countryCode,regionName,city,lat,lon,timezone,isp,org,as,reverse,mobile,proxy,hosting,query" url = "http://ip-api.com/json/{ip}?fields={f}".format(ip=urllib.parse.quote(addr, safe=""), f=fields) obj = http_get_json(url, timeout=timeout, retries=retries) return parse_ip(obj) ``` ```python def fetch_cdx(url: str, cdx_limit: int = 10, count: bool = False, timeout=20.0, retries=3) -> dict: q = urllib.parse.quote(url, safe="") base = "http://web.archive.org/cdx/search/cdx?url={q}&output=json".format(q=q) fl = "&fl=timestamp,original,statuscode,mimetype" ``` ```python def fetch_wayback(url: str, cdx=True, cdx_limit=10, count=False, timeout=15.0, retries=3) -> dict: if not url or not isinstance(url, str) or " " in url: raise InputError("invalid URL: {!r}".format(url)) q = urllib.parse.urlencode({"url": url}) api = "http://archive.org/wayback/available?{q}".format(q=q) result = parse_wayback(http_get_json(api, timeout=timeout, retries=retries), url) ``` ### Technical Analysis The IP geolocation and Wayback requests use unencrypted HTTP. HTTP provides neither confidentiality nor authenticated integrity. Consequently, any party able to observe or modify traffic between the host and these services—including a malicious access point, local-network adversary, compromised proxy, or upstream network intermediary—can inspect the lookup target or alter the returned data. The issue applies even if an HTTP service normally redirects to HTTPS because the init ...[truncated 2110 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/recon.py:264
Finding

Unbounded HTTP Response Reads Can Exhaust Process Memory

Content
View full analysis
str: """GET a URL and return the decoded text body. Retries on 429 and 5xx with exponential backoff, honoring an integer Retry-After header when present. Raises a ReconError subclass on failure. """ hdrs = {"User-Agent": USER_AGENT, "Accept": "application/json"} if headers: hdrs.update(headers) req = urllib.request.Request(url, headers=hdrs, method="GET") attempt = 0 while True: attempt += 1 try: resp = _open(req, timeout) raw = resp.read() charset = "utf-8" # Best-effort charset sniff from Content-Type. ctype = "" try: ctype = resp.headers.get("Content-Type", "") or "" except Exception: ctype = "" m = re.search(r"charset=([\w-]+)", ctype) if m: charset = m.group(1) return raw.decode(charset, errors="replace") except urllib.error.HTTPError as e: status = e.code if status == 429 or 500 <= status < 600: if attempt > retries: if status == 429: raise RateLimitError( "rate limited by {u} after {n} attempts".format(u=url, n=attempt) ) raise HTTPStatusError(status, url) _sleep(_retry_delay(e, attempt, backoff, max_backoff)) continue body = "" try: body = e.read().decode("utf- ...[truncated 3119 chars]
Remediation
View remediation
maximum: raise ReconError("upstream response exceeded the allowed size") chunks.append(chunk) return b"".join(chunks) ``` 4. Apply the same limit to HTTP error bodies. Only retain a small diagnostic prefix rather than buffering an arbitrary error response. 5. Enforce limits before text decoding and JSON parsing. 6. Add tests for: - Oversized responses with a large `Content-Length`. - Oversized chunked responses without `Content-Length`. - Oversized HTTP error bodies. - Boundary-sized valid responses. 7. Where feasible, use streaming parsing or paginated APIs for potentially large Certificate Transparency datasets rather than constructing several full in-memory representations. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (19)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared description suggests a runtime passive OSINT capability for querying domains/IPs across several public services. The supplied code does not implement those lookups. Instead, it is explicitly a non-runtime maintenance tool that retrieves IANA TLD and RDAP bootstrap data, combines them with a local RDAP supplement table, classifies TLDs by RDAP support, and emits a Markdown coverage report. While related to the broader domain recon project and RDAP functionality, its primary purpose is documentation generation and RDAP coverage research, which is materially different from the declared five-API passive OSINT behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The implementation largely matches the declared passive OSINT purpose and remains keyless/public-API based, with no direct target interaction or shell execution. It does perform the declared subdomain, RDAP/WHOIS, DNS-over-HTTPS, IP geo/ISP, and ASN/prefix ownership lookups. However, the description says 'over five keyless public APIs' and lists only those five source categories, while the code includes an additional sixth OSINT capability: Archive.org Wayback availability and CDX capture history. The code header explicitly documents this extra source, and the CLI exposes a wayback command plus profile orchestration that invokes it. That is a real undeclared capability, though adjacent to the same recon domain. The orchestration/profile behavior is mostly a packaging layer over the same recon functions, but it is also an extra exposed capability beyond the concise five-API description. Therefore this is a description-behavior mismatch, primarily due to the undeclared Wayback/archive lookup functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The supplied code chunk does not implement passive OSINT or interact with any public APIs, domains, DNS, RDAP/WHOIS, IP geolocation, or ASN ownership services. Its primary purpose is unrelated test support: making a helper module importable and reading local fixture files. This is materially different from the declared skill purpose, so it should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 116)May include surrounding context.

md
`docs/tld-rdap-coverage.md` for the full per-TLD map (which of the 1,438

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
91% confidence
Finding

The skill advertises executable Python and explicitly documents network access, test-time package installation, and generation of local documentation files, yet it declares no explicit tool scope or permission boundary. In an agent environment, missing scope declarations can let the skill be invoked with broader-than-expected network and filesystem capabilities, increasing the chance of unintended outbound requests or local writes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document explicitly frames itself as a seed list for a full-TLD recon sweep and states that every target was DNS-verified via third-party services, but it does not warn users that using this list will trigger broad outbound lookups across more than a thousand external domains and public APIs. In an OSINT/recon skill context, that omission matters because it can cause unintended mass third-party network activity, operational noise, rate-limit issues, or policy violations by users who may assume the workflow is purely local or low-volume.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The manifest says the skill performs passive domain/infra OSINT over five specific public APIs: subdomains, RDAP/WHOIS, DNS-over-HTTPS, IP geo/ISP, and ASN/prefix ownership. This file additionally documents a Wayback/archive.org lookup feature as a 'bonus' capability, which expands behavior beyond the declared five-source scope rather than merely explaining one of them.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The profile orchestration is documented as chaining certs, dns, ip, asn, and wayback, which expands collection beyond the five APIs stated in the manifest description. Hidden or under-declared data collection paths are a supply-chain transparency issue because operators may approve the skill under one external-access profile while it actually performs broader third-party queries.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The changelog explicitly documents a sixth data source, Wayback/archive.org, even though the skill metadata describes only five keyless public APIs. This mismatch can mislead users and downstream policy/enforcement systems about what external services are contacted and what historical data is collected, undermining informed consent and review boundaries.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · docs/TODO.md (reported line 57)May include surrounding context.

md
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import recon  # noqa: E402  (local import after sys.path tweak)

TLD_LIST_URL = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
BOOTSTRAP_URL = recon._RDAP_BOOTSTRAP_URL
UA = "domain-recon-coverage/1.0 (+https://github.com/maggiedev-bot/domain-recon)"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/API_NOTES.md (reported line 31)May include surrounding context.

md
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import recon  # noqa: E402  (local import after sys.path tweak)

TLD_LIST_URL = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
BOOTSTRAP_URL = recon._RDAP_BOOTSTRAP_URL
UA = "domain-recon-coverage/1.0 (+https://github.com/maggiedev-bot/domain-recon)"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/gen_coverage.py (reported line 28)May include surrounding context.

python
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import recon  # noqa: E402  (local import after sys.path tweak)

TLD_LIST_URL = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
BOOTSTRAP_URL = recon._RDAP_BOOTSTRAP_URL
UA = "domain-recon-coverage/1.0 (+https://github.com/maggiedev-bot/domain-recon)"

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/recon.py (reported line 711)May include surrounding context.

python
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import recon  # noqa: E402  (local import after sys.path tweak)

TLD_LIST_URL = "https://data.iana.org/TLD/tlds-alpha-by-domain.txt"
BOOTSTRAP_URL = recon._RDAP_BOOTSTRAP_URL
UA = "domain-recon-coverage/1.0 (+https://github.com/maggiedev-bot/domain-recon)"

Dynamic import via __import__()

Medium
Category
Dangerous Code Execution
Confidence
75% confidence
Finding

Dynamic import() can load arbitrary modules at runtime, bypassing static analysis and potentially importing malicious code.

Content

Scanner excerpt · scripts/gen_coverage.py (reported line 153)May include surrounding context.

python
version, tlds = load_tlds()
    boot = recon.parse_rdap_bootstrap(json.loads(_get(BOOTSTRAP_URL)))
    supp = recon._RDAP_SUPPLEMENT
    generated = args.date or __import__("datetime").datetime.utcnow().strftime("%Y-%m-%d")

    text, (nb, ns, nn) = build(version, tlds, boot, supp, generated)
    out = os.path.abspath(args.out)

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/API_NOTES.md (reported line 12)May include surrounding context.

md
"""GET certSpotter issuances and return the decoded JSON array."""
    q = urllib.parse.quote(domain, safe="")
    url = (
        "https://api.certspotter.com/v1/issuances?domain={q}"
        "&include_subdomains=true&expand=dns_names&expand=issuer".format(q=q)
    )
    text = http_get(url, timeout=timeout, retries=retries)

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/recon.py (reported line 568)May include surrounding context.

python
"""GET certSpotter issuances and return the decoded JSON array."""
    q = urllib.parse.quote(domain, safe="")
    url = (
        "https://api.certspotter.com/v1/issuances?domain={q}"
        "&include_subdomains=true&expand=dns_names&expand=issuer".format(q=q)
    )
    text = http_get(url, timeout=timeout, retries=retries)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill sends user-supplied domains, IPs, and URLs to third-party services, and some requests use plain HTTP rather than HTTPS. This exposes reconnaissance targets and query contents to network observers or intermediaries, creating confidentiality and integrity risks for users who may assume the tool is purely local or securely transports requests.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest description limits the skill to five public APIs for subdomains, RDAP/WHOIS, DNS-over-HTTPS, IP geo/ISP, and ASN/prefix ownership. The code and module docstring add a distinct Wayback capability via archive.org, which extends the behavior beyond the described five-source scope.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest describes passive domain/infra OSINT over five specific keyless public APIs: subdomains, RDAP/WHOIS, DNS-over-HTTPS, IP geo/ISP, and ASN/prefix ownership. This test file invokes recon.fetch_wayback("example.com"), indicating the skill also implements archive/Wayback lookup behavior, which is outside the enumerated five-API scope in the description.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.