Back to skill

Security audit

Scout

Security checks for vulnerabilities and agentic risk

Overview

The skill is a real OSINT research package, but it asks for automatic background updates, broad person-data collection, and network behavior that should be reviewed before installation.

Review and disable the cron/background tasks unless you explicitly want unattended OSINT runs and self-updates. Run it with restricted network egress, avoid storing broad tokens in the profile .env, and treat outputs as sensitive personal data subject to legal and policy limits.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • 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
  • 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 (3)

T02 · Agent Memory Poisoning

Warning
Location
references/sources-refresh.md:60
Finding
Remote catalog content is persisted into future Agent instructions without strict sanitization## Vulnerability Details **File Location**: `references/sources-refresh.md:60-78`, `references/scout_mcp_discovery.md:106-108`, `SKILL.md:264-268` **Vulnerability Type**: Persistent instruction poisoning through untrusted catalog content **Risk Level**: Medium **Relevant code and instructions:** ```markdown ### Phase 3: Parse new entries For lists with changed hashes: 1. `grep` the relevant person-sections from the downloaded README 2. Extract entry names and URLs 3. Compare against existing `references/scout_person_sources.md` (use `grep -qi`) 4. Collect entries NOT already present → these are "new" 5. Classify each new entry: Tier 1 (free/no key), Tier 2 (freemium), Tier 3 (paid) ### Phase 4: Update scout_person_sources.md 1. Read existing `references/scout_person_sources.md` 2. For each new entry, determine which section it belongs to (1-9) 3. Insert into the appropriate table with: Tool name, Install/URL, Type/Scope, Price, Notes 4. Maintain table format consistency with existing entries 5. Update the "Updated:" date in the file header (line 3) 6. Omit paid-only tools (Tier 3) unless they fill a known gap — mark them as Tier 3 in notes ``` ```markdown Before starting a new research request, Scout checks if `scout_person_sources.md` is stale (> 7 days since last refresh). If stale, runs `scout.sources.refresh` silently. This ensures the source list is current. ``` ```markdown | `references/scout_person_sources.md` | At start of every person research run | ``` ### Technical Analysis The refresh workflow downloads third-party README files, extracts names, URLs, installation information, and notes, and then writes those values into `references/scout_person_sources.md`. This is a persistent Skill reference that the Agent is instructed to read at the start of person-research runs. The workflow does not define a strict parser, field-length constraints, Markdown escaping, an allowlist for acc ...[truncated 1904 chars]
Remediation
## Remediation Suggestions - Parse remote catalogs into a strict data schema rather than copying prose into Markdown. - Allow only expected fields such as a bounded tool name, HTTPS repository URL, enumerated tier, and short non-imperative description. - Reject shell syntax, multiline values, Markdown directives, embedded HTML, unexpected URL schemes, and instruction-like text. - Store imported entries in JSON or another data-only format and render values as escaped, quoted data. - Require explicit human approval before modifying any file that is loaded as Skill guidance. - Keep remote-source data separate from trusted Skill instructions and clearly tell the Agent never to treat imported fields as commands. - Record the source commit and a review decision for each accepted entry.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/research_person.py:578
Finding
Untrusted search-result URLs can trigger server-side requests to internal destinations## Vulnerability Details **File Location**: `scripts/research_person.py:578-586`, `scripts/research_person.py:4789-4819`, `scripts/research_person.py:4835-4837` **Vulnerability Type**: Server-Side Request Forgery **Risk Level**: Medium **Relevant code:** ```python def fetch_page_html(url, timeout=10, max_bytes=400_000): """Raw markup of a page, or "". For the few callers that need the LINKS. Text extraction throws hrefs away, and finding where a company lists its people means reading its own navigation. """ try: req = urllib.request.Request(url, headers={"User-Agent": _BROWSER_UA}) with urllib.request.urlopen(req, timeout=timeout) as resp: return resp.read(max_bytes).decode("utf-8", errors="ignore") except Exception: # noqa: BLE001 return "" ``` ```python for h in hits: u = (h.get("url") or "").strip() if not u or u.rstrip("/").lower() in seen_urls: continue seen_urls.add(u.rstrip("/").lower()) parsed = parse_profile_url(u) result["search_candidates"].append({ "candidate_id": f"SC{cid:03d}", "query": q, "url": u, "title": h.get("title", ""), "snippet": (h.get("content") or "")[:300], "platform": parsed["platform"] if parsed else "Website", "status": "unverified_candidate", "verified": False, }) ``` ```python for cand in result["search_candidates"][:_verify_cap]: title, body, _craw = fetch_page_text(cand["url"], with_raw=True) if not (title or body): continue ``` ### Technical Analysis Search results returned by SearXNG are treated as third-party input. Candidate URLs are copied directly into `result["search_candidates"]` and later passed to `fetch_page_text`, which calls `fetch_page_html` and ultimately `urllib.request.urlopen`. This path does not validate the URL scheme, resolve the hostname and r ...[truncated 1849 chars]
Remediation
## Remediation Suggestions - Permit only explicitly supported `http` and `https` URLs. - Resolve hostnames before connecting and reject loopback, private, link-local, multicast, reserved, unspecified, and non-global addresses. - Validate every resolved address, not only the first DNS result. - Disable automatic redirects or validate each redirect destination with the same policy. - Protect against DNS rebinding by connecting consistently to the validated address while preserving correct TLS hostname verification. - Apply one centralized safe-fetch function to page, title, archive, avatar, and company-page requests. - Consider an outbound proxy with network-layer denial rules for metadata, loopback, and private address ranges. - Limit response size, content type, request duration, and redirect count.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/update.sh:1
Finding
Scheduled updater destructively discards local files without an enforced confirmation gate## Vulnerability Details **File Location**: `scripts/update.sh:1-5`, with automatic scheduling documented at `SKILL.md:221-235` **Vulnerability Type**: Unconfirmed destructive filesystem operation **Risk Level**: Medium **Relevant code:** ```bash #!/bin/bash cd "$(dirname "$0")/.." git reset --hard HEAD 2>/dev/null git clean -fd 2>/dev/null git pull 2>/dev/null ``` **Relevant Skill instructions:** ```markdown ## Background tasks | Job | Schedule | Command | |---|---|---| | `scout:update` | `0 0 * * *` (midnight daily) | `scout.update` | ``` ```markdown `scout.update` pulls the latest package from the `source:` URL in this file's frontmatter. Runs silently — no output unless the version changed or an error occurred. ``` ### Technical Analysis The updater runs `git reset --hard HEAD`, which removes all tracked working-tree changes, followed by `git clean -fd`, which recursively deletes untracked files and directories. These operations occur before `git pull` and have no confirmation parameter, dry-run mode, dirty-tree refusal, or backup operation. The behavior also conflicts with the safer procedure documented in `references/self_update.md:39-55`, which recommends checking whether the repository is behind, stashing changes, pulling with rebase, and restoring the stash. The executable helper does not implement those safeguards. Because the Skill documents a daily background update and initialization that registers cron jobs, the destructive path is intended to be reachable automatically rather than only through an explicit interactive maintenance action. Redirecting errors to `/dev/null` also makes failures and cleanup details difficult to audit. ### Attack Path 1. The user or another legitimate local process creates or modifies files inside the Skill repository, such as local patches, configuration, generated artifacts, or untracked research material. 2. Initialization registers the d ...[truncated 929 chars]
Remediation
## Remediation Suggestions - Remove `git reset --hard` and `git clean -fd` from unattended update paths. - Check `git status --porcelain` and refuse automatic updates when the working tree is dirty. - Implement the documented fetch, behind-check, stash, pull/rebase, and stash-restore workflow. - Create a verified backup before any operation that can overwrite or delete files. - Require an explicit destructive flag and user confirmation before cleaning untracked content. - Provide a dry-run that lists every file that would be changed or deleted. - Preserve configuration, data, journals, and generated artifacts outside the repository. - Stop suppressing standard error; log the update result and cleanup decisions. - Verify the configured remote and expected branch before pulling.
Vulnerability Patterns
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • 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
Findings (130)

Tainted flow: 'req' from os.environ.get (line 54, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
for attempt in range(max_retries + 1):
        req = urllib.request.Request(url, headers=h)
        try:
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                return resp.read()
        except urllib.error.HTTPError as e:
            if e.code == 429:
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
url = f"https://api.github.com/users/{handle}"
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "hermes-scout/1.0"})
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            if resp.status != 200:
                return {}
            d = json.loads(resp.read().decode("utf-8", errors="ignore"))
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"""
    try:
        req = urllib.request.Request(url, headers={"User-Agent": _BROWSER_UA})
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return resp.read(max_bytes).decode("utf-8", errors="ignore")
    except Exception:  # noqa: BLE001
        return ""
Confidence
90% confidence
Finding
This helper fetches arbitrary URLs supplied through contact records, search results, and discovered profile links, creating a server-side request forgery surface. An attacker who can seed a crafted URL into the data sources could cause requests to internal services, cloud metadata endpoints, or other restricted network locations from the agent host.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers={
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                          "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            raw = r.read(max_bytes).decode("utf-8", errors="ignore")
    except Exception:  # noqa: BLE001
        return out
Confidence
90% confidence
Finding
This function downloads and parses arbitrary personal-site URLs and follows externally supplied links, which exposes the agent to SSRF if untrusted URLs are present in contact data or discovered during research. Because it fetches full page content, successful exploitation could also reach internal-only HTTP services and leak response-derived metadata into results.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"https://www.gravatar.com/%s.json" % h,
            headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                                   "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            d = json.loads(r.read())
    except Exception:  # noqa: BLE001
        return None
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"https://www.gravatar.com/%s.json" % h,
            headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                                   "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            d = json.loads(r.read())
    except Exception:  # noqa: BLE001
        return None
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"https://www.gravatar.com/%s.json" % h,
            headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                                   "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            d = json.loads(r.read())
    except Exception:  # noqa: BLE001
        return None
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"https://www.gravatar.com/%s.json" % h,
            headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                                   "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            d = json.loads(r.read())
    except Exception:  # noqa: BLE001
        return None
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"https://www.gravatar.com/%s.json" % h,
            headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                                   "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            d = json.loads(r.read())
    except Exception:  # noqa: BLE001
        return None
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
"https://www.gravatar.com/%s.json" % h,
            headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                                   "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as r:
            d = json.loads(r.read())
    except Exception:  # noqa: BLE001
        return None
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
req = urllib.request.Request(url, headers={
            "User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
                          "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"})
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            raw = resp.read(max_bytes).decode("utf-8", errors="ignore")
    except Exception:  # noqa: BLE001
        return ""
Confidence
90% confidence
Finding
This title-fetch helper requests arbitrary URLs and is reachable from curated website inputs and discovered URLs, so it presents the same SSRF class of risk as the broader fetch helpers. Even though it only reads limited bytes, it still allows network interaction with attacker-chosen destinations from the agent environment.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
time.sleep(1.5 * (2 ** (attempt - 1)))
        try:
            req = urllib.request.Request(url, headers={"User-Agent": "HermesAgent/1.0"})
            with urllib.request.urlopen(req, timeout=timeout) as resp:
                data = json.loads(resp.read())
            return [
                {"url": i.get("url", ""), "title": i.get("title", ""),
Confidence
90% confidence
Finding
The SearXNG URL is configurable via environment variable and is used directly for outbound requests, so a hostile or misconfigured environment can redirect queries and all searched personal identifiers to an attacker-controlled endpoint. In this skill, those queries can contain names, employers, cities, phone-number forms, and other sensitive research inputs.

Tainted flow: 'data_req' from os.environ.get (line 1524, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
_CC_DATA + rec["filename"],
                headers={"User-Agent": "hermes-scout/1.0",
                         "Range": "bytes=%d-%d" % (offset, offset + length - 1)})
            with urllib.request.urlopen(data_req, timeout=timeout) as r:
                blob = r.read(4_000_000)
            import gzip as _gzip
            import io as _io
Confidence
90% confidence
Finding
Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Tainted flow: 'req' from os.environ.get (line 1501, credential/environment) → urllib.request.urlopen (network output)

Critical
Category
Data Flow
Content
def _github_json(path, timeout=10):
    try:
        req = urllib.request.Request(_GITHUB_API + path, headers=_github_headers())
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            if resp.status != 200:
                return None
            return json.loads(resp.read().decode("utf-8", errors="ignore"))
Confidence
91% confidence
Finding
This GitHub API helper sends an Authorization bearer token obtained from environment variables or a profile .env file to api.github.com for all matching requests. While intended, it means the skill accesses and transmits sensitive credentials without clear isolation or user approval, and any misuse of the request path or compromise of surrounding logic could expose a powerful API token.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared purpose is an OSINT research capability focused on researching people, companies, and organizations. The actual code does not perform any research, entity resolution, source retrieval, citation handling, or public-source querying. Instead, it is an infrastructure/helper module for formatting output and errors for 'Scout' scripts. While such a helper could support an OSINT tool indirectly, this chunk’s primary purpose is materially different from the declared skill behavior, so this is a clear description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description presents a general OSINT research skill for people, companies, and organizations, emphasizing provenance-backed briefs and entity resolution across public sources. The supplied code does not perform research, source gathering, citation retrieval, or broad entity-focused OSINT workflows. Instead, it reads precomputed local outputs (cross_links.csv and timing.json) and synthesizes them into structured findings. It also contains specialized heuristics and reporting for bundled donor/candidate patterns and donation timing near awards, which are materially narrower and more investigative/campaign-finance-oriented than the declared purpose. While there is some overlap with 'entity resolution' and provenance-backed evidence chains, the code's primary purpose is a downstream analysis/report-generation step with undeclared capabilities, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a broad OSINT research skill centered on people, companies, and organizations, including provenance-backed briefs and entity resolution across public sources. The supplied code instead implements a narrow technical utility: given a username, it generates domain candidates, checks whether they resolve in DNS, and whether they respond over HTTP. While this could be a supporting tactic within OSINT, it is not itself structured person/org/company research, does not gather or cite public-source evidence, does not perform entity resolution, and is not oriented around briefs or escalation workflows. Its primary purpose is materially different from the declared purpose, so this is a mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description suggests a broad OSINT research capability centered on people, companies, and organizations across public sources, with provenance-backed briefs and entity resolution workflows. The actual code only performs a specific search against NYC ACRIS property records, filtering by name/address and exporting structured filing data. While this can support a limited slice of OSINT on people or organizations, it does not implement the broader multi-source research, provenance-brief generation, or escalation behavior described. Its primary purpose is materially narrower and domain-specific (NYC real property records), so the description does not accurately represent the code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The declared description suggests a broad OSINT research capability covering people, companies, and organizations across public sources with provenance-backed briefing and entity-resolution functionality. The actual code does not perform broad research or multi-source aggregation; it only downloads three specific OFAC CSV files (SDN, addresses, aliases), joins and normalizes them, applies simple filters, and outputs a CSV. While OFAC data can support OSINT on people or organizations, this script’s primary purpose is sanctions-list ingestion/normalization, which is materially narrower and different from the declared skill. There is no evidence of general research workflows, citation/provenance handling, escalation to paid sources, or broad entity-resolution behavior.

Tp4

High
Category
MCP Tool Poisoning
Confidence
90% confidence
Finding
The declared description suggests a broad OSINT research skill for conducting provenance-backed investigations on people, companies, and organizations across public sources. The actual code does not implement a general research or entity-resolution workflow; instead, it performs a specific query against the USAspending.gov API to retrieve federal award/contract records filtered by recipient and/or agency for a fiscal year, then exports them to CSV. While this could support company or organization research in some cases, the primary purpose is much narrower and materially different from the declared general OSINT capability. Therefore, this is a description-behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description suggests a broad OSINT research skill centered on people, companies, and organizations, including entity resolution and provenance-backed briefs across public sources. The supplied code does not implement that kind of research workflow. Instead, it performs a specific archive-search function against the Internet Archive CDX endpoint for a supplied URL/host/domain and exports capture metadata. This is a materially different primary purpose: website archival lookup, not structured entity-focused OSINT research. While Wayback lookups can support OSINT, this code chunk alone is much narrower and not accurately represented by the declared description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding
The declared description presents the skill as a general structured OSINT research tool for people/companies/organizations that produces provenance-backed briefs and entity resolution. The supplied code is much narrower and more operational: it is specifically a 'research_person' entrypoint for contact enrichment. Its primary behavior is not just researching a person/org and summarizing sources, but actively discovering, testing, corroborating, and extracting specific personal contact fields. It runs username and email enumeration tools (Maigret, Holehe, user-scanner), mines websites for phone/email/city/profile links, queries GitHub and Gravatar, checks WHOIS/RDAP, searches archives, and even constructs employer-domain email permutations to probe mailbox existence. Those are materially stronger and more specialized capabilities than the declared high-level OSINT-brief description. The person/org OSINT theme is related, so this is not a totally unrelated skill, but the actual code performs undeclared contact-enrichment and account-enumeration functions that are significant enough to count as a description/behavior mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description suggests a general OSINT research skill for people/companies/orgs that produces provenance-backed briefs and entity resolution across public sources. The code does do OSINT-style research on people and organizations, but its actual scope is materially narrower and more operational: it is a contact-enrichment pipeline centered on identifying and filling missing personal contact fields (email, phone, city, website, LinkedIn) for an individual. It includes several specific capabilities not implied by the description, such as harvesting commit-author emails from GitHub, enumerating account registrations from an email, generating and probing corporate email permutations, extracting WHOIS registrant contact details, and deriving phone intelligence. Those are not merely implementation details of 'background research'; they are distinct investigative/enrichment capabilities with privacy sensitivity and a different primary use case than a provenance-backed OSINT brief. So while related to OSINT, the description does not accurately represent the full behavior of the code.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
This is a material description-behavior mismatch. The declared purpose is an operational OSINT research skill focused on researching people/orgs and synthesizing cited findings. The supplied code chunk instead evaluates the measured accuracy of other tools using a pre-labeled corpus, calculating precision/recall/F1/FPR by tool and site. While this may support an OSINT ecosystem indirectly, its primary purpose is reliability assessment, not subject-focused research. There is no code for querying public sources, resolving identities, compiling briefs, or escalating from free to paid sources. Therefore the description does not accurately represent what this code actually does.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description suggests an operational research skill focused on collecting and synthesizing information about people, companies, or organizations from public sources. In contrast, the code chunk only runs local regression tests against helper functions `name_tokens` and `token_overlap_ratio`. It validates tokenization and overlap behavior for names, including short surnames, particles like 'van'/'de', initials, suffixes, and empty inputs. While name matching could be a supporting component inside an entity-resolution system, this specific code does not perform OSINT, access external/public sources, produce cited briefs, or implement a research workflow. Its primary purpose is unrelated test coverage for normalization logic, so the description does not accurately represent the supplied code.

Static analysis

No suspicious patterns detected.