Back to skill

Security audit

hermes agent skill

Security checks for vulnerabilities and agentic risk

Overview

The skill is not clearly malicious, but it deserves review because its shared agent router and optional databases can expose or retain sensitive task data without strong isolation or complete redaction.

Review before installing in any environment where multiple agents, sessions, or skills do not fully trust each other. Keep persistence disabled unless you need it, avoid sending secrets in task payloads, tags, metadata, or step results, and treat the ~/.hermes databases as sensitive local files if persistence is enabled.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (3)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
hermes.py:106
Finding

Missing Topic Authorization and Session Isolation Allows Cross-Session Message Disclosure and Spoofing

Content
View full analysis

Vulnerability Details

File Location: hermes.py:106-112, hermes.py:127-151, hermes_openclaw.py:100-119, hermes_openclaw.py:154-178, hermes_sessions_integration.py:43-89
Vulnerability Type: Missing authorization and tenant/session isolation in the multi-agent message bus
Risk Level: High

Vulnerable Code

hermes.py:106-112:

python
def subscribe(self, topic: str, agent_id: str, handler: Callable[[HermesMessage], None]):
    """Subscribe to a topic."""
    with self._global_lock:
        if topic not in self._synapses:
            self._synapses[topic] = HermesSynapse(topic)
        synapse = self._synapses[topic]
        synapse.add(agent_id, handler)
        self.stats["active_subscriptions"] += 1

hermes.py:127-151:

python
def publish(self, topic: str, from_agent: str, session_id: str,
            payload: Any, trace_id: str = "") -> int:
    """
    Publish a message and deliver it asynchronously.
    Returns the estimated number of deliveries.
    """
    with self._global_lock:
        self.stats["total_published"] += 1
        if topic not in self._synapses:
            return 0

        synapse = self._synapses[topic]

    msg = HermesMessage(
        topic=topic,
        from_agent=from_agent,
        session_id=session_id,
        payload=copy.deepcopy(payload),
        trace_id=trace_id,
        sent_at=time.time()
    )

    delivered = synapse.deliver(msg, self._executor)

    with self._global_lock:
        self.stats["total_delivered"] += delivered

    return delivered

hermes_openclaw.py:100-119:

python
def subscribe_task_type(self, task_type: str, agent_id: str,
                        handler: Callable[[WorkflowTask, HermesMessage], None]):
    """Agent subscribes to a task type."""
    topic = f"workflow:task:{task_type}"

    def wrapped(msg: HermesMessage):
        task_data = msg.payload["task
...[truncated 4201 chars]
Remediation
View remediation

Remediation Suggestions

  1. Bind every registered agent to a trusted, immutable identity and session established by the host runtime rather than accepting caller-supplied identity strings.
  2. Store the authorized session and permitted topic patterns with each subscription.
  3. Enforce authorization during both subscription and publication.
  4. Filter message delivery by session_id unless an explicitly authorized cross-session channel is used.
  5. Generate unguessable per-task result and progress channels instead of relying only on predictable task-type topics.
  6. Track task assignment and verify that only an assigned agent may publish progress or completion for that task.
  7. Reject completion messages whose task type, task identifier, session, or publisher does not match scheduler state.
  8. Do not remove a task from _running_tasks until publisher authorization and result validity have been verified.
  9. Separate routers by tenant or session where possible, reducing the impact of an access-control failure.
  10. Add negative tests proving that an agent cannot subscribe to, read, or publish messages for another session.

T09 · Insecure Skill Coding Practices

Warning
Location
hermes_skill_evolution.py:145
Finding

Skill Evolution Persists Unfiltered Task Inputs, Outputs, Errors, and Step Results

Content
View full analysis

Vulnerability Details

File Location: hermes_skill_evolution.py:145-165, hermes_skill_evolution.py:420-476
Vulnerability Type: Plaintext persistence of arbitrary sensitive execution data
Risk Level: Medium

Vulnerable Code

hermes_skill_evolution.py:145-165:

python
def record_execution(self, execution: TaskExecution) -> str:
    """Record a task execution, subject to the persistence setting."""
    if not hermes_config.is_persistence_enabled():
        return execution.execution_id
    if not self._ensure_db():
        return execution.execution_id
    with self._lock:
        cursor = self._conn.cursor()
        cursor.execute("""
            INSERT INTO executions (execution_id, task_type, execution_json, created_at, success)
            VALUES (?, ?, ?, ?, ?)
        """, (
            execution.execution_id,
            execution.task_type,
            json.dumps(asdict(execution)),
            execution.created_at,
            1 if execution.success else 0
        ))
        self._conn.commit()

    self._try_evolve_skill(execution.task_type)
    return execution.execution_id

hermes_skill_evolution.py:420-476:

python
def start_task(self, task_type: str, input_params: Dict[str, Any]) -> str:
    """Start a new task."""
    import uuid
    execution_id = str(uuid.uuid4())[:8]
    self._current_execution = TaskExecution(
        execution_id=execution_id,
        task_type=task_type,
        steps=[],
        input_params=input_params,
        output_result=None,
        success=False,
        total_time=0,
        created_at=time.time()
    )
    self._current_steps = []
    return execution_id

def step(self, step_name: str, func: Callable, *args, **kwargs) -> Any:
    """Execute and automatically record one step."""
    start = time.time()
    step_record = StepExecution(
        step_name=step_name,
        started_
...[truncated 3761 chars]
Remediation
View remediation

Remediation Suggestions

  1. Apply centralized recursive redaction before any execution object is serialized.
  2. Filter strings nested in dictionaries, lists, tuples, dataclasses, exception objects, and return values.
  3. Store only the minimum fields needed for evolution, such as step names, success flags, and timing statistics.
  4. Exclude raw input parameters, outputs, and error messages by default.
  5. Introduce explicit field-level opt-in for callers that require example data to be retained.
  6. Replace raw example inputs in skill cards with sanitized schemas or synthetic examples.
  7. Add configurable retention periods and APIs to delete execution history.
  8. Create database files with restrictive owner-only permissions and document their contents.
  9. Consider encryption at rest where sensitive task history must be retained.
  10. Add tests using nested tokens, signed URLs, passwords, private keys, and secrets in exception messages to verify that none reach SQLite.
  11. Update the privacy documentation to distinguish insight filtering from skill-execution persistence behavior.

T09 · Insecure Skill Coding Practices

Warning
Location
hermes_agent_insight.py:197
Finding

Memory Metadata and Tags Bypass Sensitive-Information Filtering

Content
View full analysis

Vulnerability Details

File Location: hermes_agent_insight.py:197-227
Vulnerability Type: Incomplete sensitive-data filtering before persistent storage
Risk Level: Medium

Vulnerable Code

python
def add_memory(self, content: str, category: str, tags: List[str],
               metadata: Dict[str, Any] = None) -> str:
    """Add a memory entry, subject to the persistence setting."""
    if not hermes_config.is_persistence_enabled():
        return ""
    if not self._ensure_db():
        return ""
    entry_id = self._hash_id(f"{content}{time.time()}")
    entry = MemoryEntry(
        entry_id=entry_id,
        content=_filter_sensitive(content),
        category=category,
        tags=tags,
        created_at=time.time(),
        metadata=metadata or {}
    )
    with self._lock:
        cursor = self._conn.cursor()
        cursor.execute("""
            INSERT INTO memory_fts
            (entry_id, content, category, tags, created_at, metadata)
            VALUES (?, ?, ?, ?, ?, ?)
        """, (
            entry.entry_id,
            entry.content,
            entry.category,
            json.dumps(entry.tags),
            entry.created_at,
            json.dumps(entry.metadata)
        ))
        self._conn.commit()
    return entry_id

Technical Analysis

add_memory() applies _filter_sensitive() only to the content argument. The caller-controlled tags, category, and arbitrary nested metadata object are serialized without filtering or schema validation.

Metadata commonly carries source URLs, session identifiers, email addresses, authorization headers, request details, document identifiers, or provenance information. A caller may therefore place sensitive information into metadata or tags and bypass the privacy control even while HERMES_SENSITIVE_FILTER_ENABLED remains enabled.

The issue also affects nested metadata because no rec ...[truncated 1735 chars]

Remediation
View remediation

Remediation Suggestions

  1. Recursively filter every string value in metadata, tags, and category before serialization.
  2. Enforce an allowlisted metadata schema rather than accepting unrestricted dictionaries.
  3. Reject or redact fields commonly associated with secrets, including authorization, token, password, cookie, credential, and private-key fields.
  4. Apply filtering at the final persistence boundary so all current and future entry points receive the same protection.
  5. Avoid indexing sensitive metadata in FTS unless explicitly required.
  6. Add size and nesting-depth limits to metadata to prevent storage abuse.
  7. Provide a strict mode that rejects a record when suspected sensitive content is detected rather than silently retaining it.
  8. Add automated tests for secrets in tags, categories, nested dictionaries, arrays, URLs, and mixed structured data.
  9. Clarify in documentation which fields are retained and how redaction operates.
Vulnerability Patterns
  • Rogue AgentSelf-Modification, Session Persistence
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The skill appears to involve conditional SQLite persistence and local database storage while lacking clear permission declarations and while overstating other capabilities. In a memory-oriented agent skill, undeclared storage is especially sensitive because the data being processed may include personal preferences, conversation history, or other confidential context.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill appears to involve conditional SQLite persistence and local database storage while lacking clear permission declarations and while overstating other capabilities. In a memory-oriented agent skill, undeclared storage is especially sensitive because the data being processed may include personal preferences, conversation history, or other confidential context.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The skill appears to involve conditional SQLite persistence and local database storage while lacking clear permission declarations and while overstating other capabilities. In a memory-oriented agent skill, undeclared storage is especially sensitive because the data being processed may include personal preferences, conversation history, or other confidential context.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill appears to involve conditional SQLite persistence and local database storage while lacking clear permission declarations and while overstating other capabilities. In a memory-oriented agent skill, undeclared storage is especially sensitive because the data being processed may include personal preferences, conversation history, or other confidential context.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill appears to involve conditional SQLite persistence and local database storage while lacking clear permission declarations and while overstating other capabilities. In a memory-oriented agent skill, undeclared storage is especially sensitive because the data being processed may include personal preferences, conversation history, or other confidential context.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The skill appears to involve conditional SQLite persistence and local database storage while lacking clear permission declarations and while overstating other capabilities. In a memory-oriented agent skill, undeclared storage is especially sensitive because the data being processed may include personal preferences, conversation history, or other confidential context.

Content

No source excerpt is available for this finding.

Self-Modification

High
Category
Rogue Agent
Confidence
85% confidence
Finding

Skill modifies its own code, configuration, or behavior at runtime. Self-modification enables an agent to escalate privileges, disable safety constraints, or install persistent backdoors.

Content

Scanner excerpt · hermes_skill_evolution.py (reported line 380)May include surrounding context.

python
with self._lock:
            cursor = self._conn.cursor()
            cursor.execute("""
                UPDATE skill_cards
                SET invocation_count = invocation_count + 1
                WHERE skill_id = ?
            """, (skill_id,))

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding

The skill documents environment-variable based controls and local database paths, but it does not declare any tool scope or permissions. That creates an authorization/transparency gap: operators may install a skill that can read configuration from the environment and influence persistence behavior without an explicit capability declaration, increasing the risk of unexpected access to local state or secrets-adjacent configuration.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
87% confidence
Finding

The README content, headings, descriptions, and usage guidance are consistently presented in Chinese, which can impose a language requirement on users without explicit opt-in. The policy allows locale constraints only when they are clearly documented and justified, or when users are offered a choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

This Python file contains the module docstring and operational privacy notice entirely in Chinese, and later user-facing runtime messages are also emitted only in Chinese. The policy requires flagging language/locale constraints when the skill forces a specific language without user opt-in, and this file provides no alternative language path or documented regional justification.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module claims sensitive content is automatically filtered before storage, but only content and reference_context are sanitized. In add_memory, the metadata field is persisted to disk via json.dumps(entry.metadata) without passing through _filter_sensitive, so secrets, emails, tokens, or other sensitive data included in metadata can be stored despite the privacy guarantee.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This code file contains natural-language instructions and documentation exclusively in Chinese, including the module docstring and inline comments. Under the stated policy, forcing a specific language without offering user choice or documenting a justified locale constraint is a policy violation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python file contains its primary docstrings, comments, and runtime user-facing messages in Chinese only, which imposes a specific language on users and maintainers. The policy allows locale constraints only when they are optional or clearly justified, but this file does not provide an opt-in or explain a region-specific requirement.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Natural-language strings, comments, and user-facing output in this file consistently force Chinese as the interaction language. The file does not provide an opt-in language choice or state that the skill is intentionally limited to a Chinese-speaking or region-specific environment.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The demo callback prints full message payloads directly to stdout, which can expose sensitive task data, results, or system events during testing or when the demo code is reused in less controlled environments. In this skill’s context, messages may contain cross-agent workflow content, so indiscriminate payload logging increases confidentiality risk even though it appears intended for demonstration rather than abuse.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The manifest description claims broader Hermes Agent capabilities including '多智能体调度' and '主动记忆洞察', while the implemented code in this file is narrowly focused on GEPA-based execution logging, SQLite persistence, and workflow extraction. This is a semantic mismatch between the advertised behavior and the actual operations present here.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This Python file contains user-facing natural-language descriptions and runtime output in Chinese only, including the module docstring and printed status messages. The file does not offer any user opt-in or locale selection, which conflicts with the policy requirement to avoid forcing a specific language without user choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The top-level natural-language description is written entirely in Chinese, which can impose a language choice on users without indicating any option to use another language. The policy specifically calls for flagging language or locale constraints when no user choice or justification is provided.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
95% confidence
Finding

This Python file contains user-facing natural-language documentation entirely in Chinese and does not indicate that other languages are supported or selectable. Under the policy rule for language or locale constraints, this is a locale-specific presentation without user opt-in or justification.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
86% confidence
Finding

The documentation implies an operational reuse mechanism where learned skills are directly invoked on future tasks. In practice, the code stores a SkillCard and exposes getters like get_recommended_workflow/get_skill_for_task, but no code path actually auto-runs the learned workflow.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.