Back to skill

Security audit

Reach

Security checks for vulnerabilities and agentic risk

Overview

The skill is a mostly coherent live-data query engine, but it adds high-impact account, messaging, self-update, session-mining, and mutable third-party runtime execution capabilities that need review before installation.

Install only if you are comfortable with a data-query skill that can create third-party API accounts, store keys in the agent profile, start third-party MCP subprocesses, use an authenticated LinkedIn browser session including messaging actions, scan recent session history for API discovery, and self-update from GitHub. Prefer disabling self-update/api-mining, pinning MCP dependencies, filtering subprocess environments, and removing LinkedIn messaging before routine use.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (2)

T08 · Insecure Dependencies

Error
Location
scripts/sources/_mcp_client.py:27
Finding

Mutable Third-Party MCP Packages Execute with the Agent's Full Environment

Content
View full analysis

Vulnerability Details

File Location: scripts/sources/_mcp_client.py:27-39
Vulnerability Type: Remote dependency execution with excessive environment exposure
Risk Level: High

Vulnerable Code

python
def start(self):
    import os
    env = os.environ.copy()
    if self._env:
        env.update(self._env)
    self._proc = subprocess.Popen(
        self._cmd,
        stdin=subprocess.PIPE,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        env=env,
        text=True,
        bufsize=1,
    )
    self._initialize()

The affected MCP commands are configured through mutable remote package references:

python
# scripts/sources/linkedin_mcp.py:31-39
def _get_client(auth: dict) -> _MCPClient:
    global _client
    if _client is None or _client._proc is None or _client._proc.poll() is not None:
        env = {}
        for key in ("LINKEDIN_USER_DATA_DIR", "LINKEDIN_CHROME_PATH"):
            val = auth.get(key) or auth.get(key.lower())
            if val:
                env[key] = val
        _client = _MCPClient(["uvx", "linkedin-scraper-mcp@latest"], env=env if env else None)
        _client.start()
    return _client
python
# scripts/sources/reddit_mcp_buddy.py:143-152
def _get_client(auth: dict) -> _MCPClient:
    global _client
    if _client is None or _client._proc is None or _client._proc.poll() is not None:
        env = {}
        for key in ("REDDIT_CLIENT_ID", "REDDIT_CLIENT_SECRET", "REDDIT_USERNAME", "REDDIT_PASSWORD"):
            val = auth.get(key) or auth.get(key.lower())
            if val:
                env[key] = val
        _client = _MCPClient(["npx", "-y", "reddit-mcp-buddy"], env=env if env else None)
        _client.start()
    return _client
python
# scripts/sources/yahoo_finance_mcp.py:27-38
def _get_client(auth: dict) -> _MCPClient:
    global _client
    if _cl
...[truncated 2727 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace os.environ.copy() with a minimal environment allowlist. Include only essential runtime variables such as a controlled PATH, locale settings, and the credentials required by the selected connector.
  2. Do not pass unrelated agent credentials to MCP subprocesses.
  3. Pin npm and Python packages to reviewed, immutable versions. Replace @latest and unversioned npx execution with exact versions.
  4. Pin Git dependencies to a reviewed commit hash rather than a mutable default branch.
  5. Resolve and install dependencies during an explicit installation step rather than silently fetching executable code during a normal query.
  6. Verify downloaded artifacts using hashes, lockfiles, or equivalent integrity metadata.
  7. Run MCP servers in a restricted subprocess or sandbox with limited filesystem and network access.
  8. Apply the same environment filtering to the duplicate MCP implementation in scripts/sources/reddit_mcp_buddy.py.

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
SKILL.md:147
Finding

Background API Discovery Instructions Scan Unrelated Session Transcripts

Content
View full analysis

Vulnerability Details

File Location: SKILL.md:147-165
Vulnerability Type: Excessive cross-session data access and retention
Risk Level: Medium

Vulnerable Instructions

markdown
## Background tasks

| Job | Mechanism | Schedule | Command |
|---|---|---|---|
| `reach:update` | cron | `0 0 * * *` | Self-update from GitHub source |
| `reach:api-mine` | cron | `0 4 * * *` | Scan sessions for sites with APIs → `references/discovered-apis.md` |

## Source Discovery (reach:api-mine)

The `reach:api-mine` cron job scans **all** session transcripts (not just research) for sites, services, databases, and archives that are used or needed by any skill. For each site found, it evaluates:

- [ ] **Does it have an API?** — Check for programmatic access (REST, GraphQL, etc.)
- [ ] **Can I access it?** — Free/freemium/paid? API key required? Account signup possible?
- [ ] **What data does it provide?** — What endpoints exist? What can you search/retrieve?
- [ ] **Does it belong as a preferred source?** — Is the API meaningfully better than the current access method (web scraping, SearXNG, browser) for the skill that found it?

A site is only cataloged if it has a **confirmed working API**, the data is useful to an active skill/workflow, and the API is better than the current approach. Deduplicate against both `sources.yml` and `references/discovered-apis.md`.

Catalog: `references/discovered-apis.md` — fully evaluated candidates ready for integration into `sources.yml`.

Technical Analysis

The Skill’s stated core purpose is to perform live factual queries against registered data sources. The background instructions expand that scope by directing a scheduled job to inspect all available session transcripts, explicitly including sessions unrelated to research or Reach.

No transcript-field allowlist, session ownership restriction, sensitivity filter, or explicit per-session consent requirement is specifie ...[truncated 1898 chars]

Remediation
View remediation

Remediation Suggestions

  1. Restrict API discovery to sessions explicitly associated with Reach or to sessions whose users have opted into discovery.
  2. Do not scan all session transcripts by default.
  3. Apply an allowlist of fields needed for API discovery instead of exposing complete transcript content to the workflow.
  4. Exclude private, confidential, authentication-related, and internal infrastructure content before extraction.
  5. Remove source-session identifiers and other conversation provenance from the persistent catalog unless strictly necessary.
  6. Store discovery results in access-controlled operational storage rather than in a broadly distributed Skill reference file.
  7. Add retention limits and deletion support for derived discovery records.
  8. Require explicit user or administrator authorization before enabling the scheduled mining task.
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (224)

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

Critical
Category
Data Flow
Confidence
90% confidence
Finding

Credentials or environment variables flow to a network sink. This is a high-confidence indicator of credential exfiltration.

Content

Scanner excerpt · scripts/katzilla.py (reported line 43)May include surrounding context.

python
data = json.dumps(body).encode("utf-8") if body else None
    req = urllib.request.Request(url, data=data, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        return {"error": f"HTTP {e.code}: {e.read().decode('utf-8')}"}

Context-Inappropriate Capability

Critical
Category
Not specified by scanner
Confidence
99% confidence
Finding

Authenticated LinkedIn inbox reading, conversation access, message search, and send_message are active-account actions unrelated to a read-only live fact-query engine. If invoked by an agent, they can expose private communications or perform unauthorized actions on the user's behalf, turning a data lookup tool into an account-acting capability.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Critical
Category
Not specified by scanner
Confidence
99% confidence
Finding

Exposing send_message gives the skill an active external side-effect on behalf of the authenticated user, which is unjustified for a fact-query tool. This can be abused to send spam, social-engineering messages, harassing content, or unauthorized outreach from the user's LinkedIn account, making the risk especially severe in an agentic environment.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
Confidence
95% confidence
Finding

A daily self-update mechanism allows the skill’s code or behavior to change after deployment, undermining review-time assurances and creating a supply-chain modification path. In a skill that already interfaces with many external sources and credentials, automatic updates can silently introduce new capabilities or malicious logic without operator re-approval.

Content

Scanner excerpt · CHANGELOG.md (reported line 91)May include surrounding context.

md
- Observation Journal emission for every query
- `queries.jsonl` append-only query log
- README.md and CHANGELOG.md per spec-ocas-skill-publishing.md
- `reach:update` daily self-update cron job

### Fixed
- Removed hardcoded `RT_KEY` value from SKILL.md and per-source documentation; all auth via `~/.hermes/.env`

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description presents a broad live data query engine that interfaces with numerous external sources for current factual information. The supplied code chunk, however, is only a thin helper script for parsing YAML text locally via PyYAML. It contains no network access, no API clients, no source routing, no query handling, and no domain-specific data retrieval logic. This is a materially different primary purpose, so the description does not accurately represent the behavior of this code chunk.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
87% confidence
Finding

The code does perform live external API queries, so it is related to the declared purpose at a high level. However, the declared description presents a broad, general-purpose world-data query engine with extensive source coverage and specific usage boundaries. The actual code is much narrower: it is a command-line wrapper around a single service, api.katzilla.dev, for listing agents and invoking actions. It does not itself implement routing across many named external providers, nor does it demonstrate the claimed registry breadth or enforce the declared limitations about research/synthesis. Therefore the description overstates and generalizes the behavior of the supplied code chunk enough to count as a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
91% confidence
Finding

The description claims a broad live-data engine for many categories of real-time factual queries across dozens of registered sources. The code instead is a single-purpose property lookup script with fixed commands for Redfin/Zillow-style APIs and one SF assessor dataset. While it does query live external APIs, that is only a narrow subset of the declared functionality. The primary purpose, scope of sources, and applicable triggers are materially different from the description.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a domain-specific skill: a real-time external world-data query engine with routing across ~55 registered factual data sources. The supplied code does not implement that behavior. Instead, it provides low-level transport/integration infrastructure for communicating with MCP servers over stdio JSON-RPC, including process startup, initialization, request sending, response parsing, and shutdown. While this could support tools that query live data, this specific code chunk itself does not perform source registry routing, external API selection, factual data retrieval across the described domains, or trigger-specific logic. This is a materially different primary purpose from the declared skill behavior, so it is a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents a broad real-time factual data query engine over a registry of many external sources, with explicit exclusions for entity investigations and non-research synthesis. The supplied code does something materially different: it is a dedicated LinkedIn connector that starts a linkedin-scraper MCP server and exposes LinkedIn profile/company/job lookup, employee search, message search, inbox/conversation retrieval, home feed access, and outbound message sending. This is not a generic world-data router and includes private/social-network investigation and messaging capabilities that are undeclared and inconsistent with the stated purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The code is not a general live world-data query router across many registered sources. It is a narrow source adapter for manuals content only. While product manuals are mentioned in the declared coverage, the implementation relies on autocomplete, search-query handoff, Wayback CDX archive enumeration, direct image fetching, and OCR extraction from manual page images. Those behaviors are materially different from the declared 'real-time external APIs for factual ground truth' framing and contradict the note 'Do not use for web research,' because the search action explicitly instructs the caller to run web_search. The random-page reading feature is also outside the stated purpose of factual external API querying. Therefore the description does not accurately represent this code chunk's actual behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The description presents a general-purpose live external API query engine covering dozens of source types and many factual domains. The code instead implements a specialized academic paper-search MCP connector with actions for searching scholarly repositories and downloading PDFs. While scholarly literature is one declared domain, the implemented behavior is only a small subset of the claimed system and its primary purpose is materially narrower than advertised. Additionally, the download capability is undeclared. Therefore the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code is not a generic live world-data query engine. It is a narrowly scoped Reddit integration that starts an MCP server and calls Reddit-specific tools (browse_subreddit, search_reddit, get_post_details, user_analysis, reddit_explain). This materially differs from the declared purpose of routing factual real-time queries across dozens of authoritative sources. The mismatch is especially strong because the code includes capabilities the description explicitly excludes, such as entity/user analysis and explanatory behavior. While both involve external data access, the actual resource accessed and the use cases supported are substantially different from the declared description and triggers.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The code does not implement a general-purpose live world-data query engine across a registry of many factual sources. Instead, it is a narrow connector for accessing academic papers through Sci-Hub mirrors, with CrossRef used only to resolve titles/keywords to DOIs. Its primary purpose is research-paper discovery and PDF retrieval, including downloading files locally and using DNS-over-HTTPS plus mirror rotation to work around blocked domains. Those are materially different capabilities and intent from the declared description, which emphasizes broad factual API queries and explicitly says not to use it for research.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The code clearly performs live external API queries, so it partially aligns with the 'real-time data' aspect of the description. However, its actual scope is much narrower: it is a dedicated weather source module with fixed actions for conditions, forecasts, alerts, METAR, global weather, and severe outlooks. There is no evidence of a multi-domain registry, broad source routing, or support for finance, courts, scholarly literature, news, property, satellite imagery, product manuals, or other declared domains. This is a material description-behavior mismatch in primary purpose and supported capabilities, not just an implementation detail.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description presents this skill as a general-purpose live external-data query engine covering many categories and sources. The supplied code chunk, however, only implements a wrapper around the WeLib API for searching books and academic papers and fetching item details. There is no evidence of source registry routing, support for dozens of data providers, or capabilities related to weather, government, finance, courts, property, satellite imagery, or product manuals. This is a materially different and much narrower primary purpose, so the description does not accurately represent the code.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents this skill as a general-purpose live external-data query engine covering many source categories and domains. The supplied code does not implement that broad behavior. Instead, it is a specialized adapter for a single Yahoo Finance MCP server, with actions limited to equity/market-related data. While finance is one of the domains mentioned in the description, the actual code's primary purpose is much narrower than declared, and it lacks the claimed multi-source routing behavior. This is a material description-to-behavior mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The declared description promises a real-time external API query engine with broad factual data access across many source categories. The actual code shown only runs python3 ~/.hermes/scripts/skill_update.py ocas-reach, which is a local updater wrapper. Its primary purpose is operational maintenance of a skill, not live data retrieval. This is a material description-behavior mismatch.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
Confidence
97% confidence
Finding

A scheduled self-update from GitHub gives the skill an autonomous code-modification path. If the upstream repository, release channel, or update transport is compromised, the agent can ingest and execute attacker-controlled changes without a human review step, turning this skill into a supply-chain entry point.

Content

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

md
| Job | Mechanism | Schedule | Command |
|---|---|---|---|
| `reach:update` | cron | `0 0 * * *` | Self-update from GitHub source |
| `reach:api-mine` | cron | `0 4 * * *` | Scan sessions for sites with APIs → `references/discovered-apis.md` |

## Source Discovery (reach:api-mine)

Self-Modification

High
Category
Rogue Agent
Confidence
96% confidence
Finding

The manifest states that reach.init registers a self-update task on first invocation, meaning simply using the skill can establish persistent code-changing behavior. That lowers the bar for unauthorized modification and increases blast radius because a one-time invocation can create an ongoing supply-chain risk.

Content

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

md
**Key principle:** Not a general search index. Only sites that specific skills already use or need, where an API would reduce friction vs. the current method.

`reach.init` registers `reach:update` on first invocation. No operational background tasks beyond self-update — Reach is purely reactive to user/agent queries.

**Session-retention limitation (Jun 18, 2026)** — The session database (via `session_search`) only retains recent sessions (typically 48-72h of FTS5-indexed content). Older research sessions — even those with significant API discoveries — become unsearchable once they age out. This means:
- The api-mine cron can only discover APIs from sessions that are still in the active session store

Self-Modification

High
Category
Rogue Agent
Confidence
95% confidence
Finding

The explicit Self-Update section confirms the skill includes code-changing behavior as a documented feature. Self-modification is dangerous in agent environments because it can bypass normal review, alter future behavior, and persist malicious or erroneous changes across runs.

Content

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

md
- A "0 new APIs" result from the cron is NORMAL and expected when sessions are current — it means the catalog is up-to-date, not that the cron is broken
- **Cron-skew (Jun 27, 2026)**: During periods when the agent runs primarily as cron jobs, there may be zero interactive sessions to mine. `[SILENT]` is correct — see `references/api-mine-cron-notes.md` § Cron-Skew.

## Self-Update

See `references/self-update-reach.md`.

Self-Modification

High
Category
Rogue Agent
Confidence
95% confidence
Finding

Referencing a dedicated self-update procedure indicates an intended mechanism for modifying the skill after deployment. In the context of a skill with shell, network, file-write, and broad invocation potential, this materially increases compromise impact and persistence potential.

Content

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

md
## Self-Update

See `references/self-update-reach.md`.

## Validation rules

Description-Behavior Mismatch

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The document authorizes account creation, credential capture, email verification, local secret storage, and post-registration validation for a skill whose declared purpose is only live factual API querying. That is a material scope expansion: it gives the skill persistent access acquisition and secret-handling capabilities that can be abused to create accounts and accumulate credentials across many third-party services without a narrowly scoped user approval step.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

Granting the skill authority to open browser sessions, submit registration forms, and read Gmail for verification codes gives it cross-system access well beyond data retrieval. This creates an unnecessary path to persistent third-party account creation and email-based confirmation flows, which could be leveraged to exfiltrate or misuse access if the skill is prompted adversarially or behaves incorrectly.

Content

No source excerpt is available for this finding.

YARA rule 'agent_skill_remote_bootstrap_execution': Remote script or code download followed by execution/bootstrap installation [agent_skills]

High
Category
YARA Match
Confidence
85% confidence
Finding

YARA rule matched a known malware signature (reverse shell, backdoor, ransomware, C2 framework, or info stealer).

Content

Scanner excerpt · references/plugin-eval-hermes-weather.md (reported line 39)May include surrounding context.

md
lready present)
- `numpy` ✓ (already present)
- `metrust` ✓ (on PyPI, v0.4.7)
- `rusbie` ✗ (GitHub-only, unpublished to PyPI)
- `rustweather` ✗ (GitHub-only)
- `wrf-rust` ✗ (GitHub-only)
- `cfrust` ✗ (GitHub-only)

### Rust binaries (need cargo to build)
- `radar-render` from `rustdar`
- `run_case` from `ecape-rs`
- `nexrad-render-cli` (bundled source)

## Install Result

**Failed.** `pip install git+https://github.com/FahrenheitResearch/hermes-weather-plugin.git` failed because `rustplots>=0.1.0` (a dependency of `rustweather`) is not published to PyPI. Even with Rust toolchain present, the build would fail.

## Tools Breakdown

### Data tools (would work if install succeeded)
| Tool | API | Reach equivalent |
|------|-----|-----------------|
| `wx_conditions` | NWS API | `noaa_nws` |
| `wx_forecast` | NWS API | `noaa_nws` |
| `wx_alerts` | NWS API | `noaa_nws` |
| `wx_metar` | METAR | Partial `noaa_nws` |
| `wx_brief` | Composite | Composite |
| `wx_global` | Open-Meteo |

Self-Modification

High
Category
Rogue Agent
Confidence
97% confidence
Finding

A self-modification capability is especially risky in a skill whose declared role is only to query external data sources. Allowing the skill to update itself creates a path for persistence, scope expansion, and supply-chain compromise, and the surrounding instructions indicate the modification can occur through automated remote retrieval rather than controlled installation.

Content

Scanner excerpt · references/self-update-reach.md (reported line 1)May include surrounding context.

md
# Reach — Self-Update Procedure

Standard GitHub tarball update via gh CLI. Runs silently.