Back to skill

Security audit

agentrelay

Security checks for vulnerabilities and agentic risk

Overview

AgentRelay is a legitimate shared-file relay, but its current implementation can let relay inputs reach outside the intended storage area and stores verification secrets in plaintext.

Review before installing. Use only with trusted agents and non-sensitive payloads unless it is patched to validate event IDs and pointers, enforce storage-directory containment, use private file permissions, avoid logging or returning secrets, and replace the weak six-character secret with a cryptographic token. Avoid enabling automatic handling from untrusted message text.

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
__init__.py:107
Finding

Path Traversal Enables Filesystem Access Outside Relay Storage

Content
View full analysis
Path: """获取事件文件路径""" return STORAGE_PATH / f"{event_id}.json" ``` ```python def resolve_alias(ptr: str, storage_root: Path, alias: str = "s") -> Path: """解析别名路径到完整路径""" if ptr.startswith(f"{alias}/"): return storage_root / ptr[len(alias)+1:] return Path(ptr) ``` ```python file_name = f"{event_id}.json" file_path = STORAGE_PATH / file_name with open(file_path, 'w', encoding='utf-8') as f: json.dump(file_content, f, ensure_ascii=False, indent=2) ``` ```python event_id = parsed["event_id"] ptr = parsed["ptr"] file_path = resolve_alias(ptr, STORAGE_PATH, STORAGE_ALIAS) if not file_path.exists(): raise FileNotFoundError(f"File not found: {file_path}") with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) content = data.get("payload", {}).get("content", {}) secret = data.get("meta", {}).get("secret", "") meta = data.get("meta", {}) sender = meta.get("sender") receiver = meta.get("receiver") if not sender or not receiver: raise ValueError( f"Event {event_id} is missing explicit sender/receiver metadata" ) if meta.get("burn_on_read") or content.get("burn_on_read"): file_path.unlink(missing_ok=True) ``` ```python target_event_id = next_event_id if next_event_id else event_id file_path = STORAGE_PATH / f"{target_event_id}.json" if not file_path.exists(): raise FileNotFoundError(f"File not found: {file_path}") with open(file_path, 'r', encoding='utf-8') as f: data = json.load(f) if "payload" not in data: data["payload"] = {} if "content" not in data["payload"]: data["payload"]["content"] = {} data["payload"] ...[truncated 3444 chars]
Remediation
View remediation
str: if not EVENT_ID_PATTERN.fullmatch(event_id): raise ValueError("Invalid event ID") return event_id ``` 2. Reject absolute pointers, empty path segments, parent-directory components, and platform-specific path prefixes. 3. Canonicalize and enforce containment before every read, write, or deletion: ```python def safe_storage_path(relative_name: str) -> Path: root = STORAGE_PATH.resolve() candidate = (root / relative_name).resolve() try: candidate.relative_to(root) except ValueError: raise ValueError("Pointer escapes relay storage") return candidate ``` 4. Apply the same validation consistently to send, receive, CMP, verification, update, and cleanup operations. 5. Require the pointer filename to match the validated message event ID rather than trusting them independently. 6. Reject symbolic links or open files using no-follow semantics where supported. Recheck containment after resolving links. 7. Separate read, update, and delete capabilities. Burn-on-read deletion should only be allowed for files that were created and registered by AgentRelay. 8. Add regression tests for absolute paths, `../` traversal, nested traversal, symbolic links, mismatched pointer/event IDs, and platform-specific separators. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
__init__.py:84
Finding

Completion Verification Uses a Predictable Non-Cryptographic Secret

Content
View full analysis
str: """生成随机 Secret Code""" return ''.join(random.choices(string.ascii_letters + string.digits, k=length)) ``` ```python if secret is None: secret = generate_secret(6) ``` ```python received_secret = parsed["data"] event_record = get_registry_event(event_id) data = None try: data = read_event_file(event_id) expected_secret = data.get("meta", {}).get("secret", "") except FileNotFoundError: expected_secret = event_record.get("secret", "") if not expected_secret: raise FileNotFoundError( f"No event metadata available for {event_id}; cannot verify CMP" ) verified = bool(expected_secret) and received_secret == expected_secret ``` ### Technical Analysis The six-character verification code is generated with Python's `random` module. This module is based on a deterministic pseudorandom number generator and is explicitly unsuitable for authentication secrets. Although six alphanumeric characters provide a nominal search space of 62 to the sixth power, the effective assurance may be lower if an attacker can infer generator state from other outputs generated by the same process. The code is then checked with a normal string equality operation and treated as evidence that the intended receiver read and completed the event. The mechanism only proves knowledge of the token. It does not authenticate the original sender or intended receiver, bind the payload and pointer to the confirmation, protect message integrity, or prevent replay of a previously observed CMP message. ### Attack Path 1. A relay event is created with a six-character secret generated by `random.choices()`. 2. An attacker observes related pseudorandom outputs, obtains local access to store ...[truncated 1117 chars]
Remediation
View remediation
str: return secrets.token_urlsafe(32) ``` 2. Compare secret values using `secrets.compare_digest()` to avoid ordinary equality checks for authentication material. 3. Store only a keyed hash or verifier for the token where feasible, rather than retaining the plaintext token in multiple files. 4. Bind confirmations to the complete event context. A keyed MAC should cover at least: - Event ID - Sender and receiver identities - Pointer or canonical file identity - Payload digest - Creation time - Expiration time - Unique nonce 5. Add expiration and one-time-use enforcement to prevent replay. A successfully consumed confirmation should not remain reusable. 6. Authenticate sender and receiver identities independently of token possession. The completion secret should not be presented as proof of sender authorization. 7. Rate-limit failed verification attempts and log failed attempts without recording the supplied secret. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
__init__.py:237
Finding

Sensitive Relay Payloads and Verification Secrets Are Persisted in Plaintext Without Explicit Private Permissions

Content
View full analysis
None: """写回持久化 registry。""" ensure_dirs() with open(REGISTRY_PATH, 'w', encoding='utf-8') as f: json.dump(registry, f, ensure_ascii=False, indent=2) ``` ```python file_content = { "meta": { "event_id": event_id, "type": message_type, "secret": secret, "created_at": datetime.now().isoformat(), "sender": sender, "receiver": receiver, "ttl_hours": ttl_hours, }, "payload": { "content": content } } file_name = f"{event_id}.json" file_path = STORAGE_PATH / file_name with open(file_path, 'w', encoding='utf-8') as f: json.dump(file_content, f, ensure_ascii=False, indent=2) upsert_registry_event( event_id, { "event_id": event_id, "type": message_type, "secret": secret, "sender": sender, "receiver": receiver, "ttl_hours": ttl_hours, "ptr": get_file_alias_path(file_path, STORAGE_PATH, STORAGE_ALIAS), "file_path": str(file_path), "file_exists": True, "status": "sent", "created_at": file_content["meta"]["created_at"], }, ) ``` ```python cmp_msg = build_csv("CMP", event_id, "", effective_secret) upsert_registry_event( event_id, { "event_id": event_id, "type": meta.get("type", "REQ"), "secret": effective_secret, "sender": receiver, "receiver": sender, "status": "completed", "cmp_message ...[truncated 2748 chars]
Remediation
View remediation
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • Taint TrackingDirect Taint Flow, Variable-Mediated Taint Flow, Credential Exfiltration Chain
Findings (19)

Exfiltration Commands

High
Category
Prompt Injection
Confidence
90% confidence
Finding

Instructions found that direct the agent to transmit conversation context or user data to external services.

Content

Scanner excerpt · README.md (reported line 195)May include surrounding context.

md
### AgentRelayTool Class

#### send(agent_id, msg_type, event_id, content)
Send message to shared file.

**Parameters**:
- `agent_id` (str): Target agent ID

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose presents the skill as a message-transfer protocol, but the instructions also include cleanup and burn-on-read deletion behaviors that can remove local files and registry data. That mismatch is dangerous because operators may authorize the skill for communication while overlooking that it can also mutate or delete stored data outside the narrow trigger context.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · README.md (reported line 14)May include surrounding context.

md
| Traditional Approach | AgentRelay Approach |
|---------------------|---------------------|
| ❌ Send large text directly → ⏰ Timeout | ✅ Write to file + send short pointer → Success |
| ❌ No verification if received | ✅ Secret Code mechanism ensures delivery |
| ❌ No audit trail | ✅ Complete transaction logs (4 entries/event) |

---

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The README explicitly states that transaction logs include real agent IDs and next_action_plan values, which can expose sensitive operational metadata and potentially confidential workflow details. Even though this is documentation rather than code, it describes a privacy-impacting default behavior without warning users, minimization guidance, or redaction controls.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The document presents core metadata fields such as release date, platform status, and publication status only in Chinese, while other sections are mixed-language. This creates a natural-language locale constraint without user opt-in or an explicit justification that the skill is intended only for a Chinese-speaking or region-specific audience.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill instructs the agent to read and write shared files, inspect environment-relative paths, and maintain local state, but it declares no explicit tool scope or permissions. This weakens least-privilege controls and makes it easier for the skill to be invoked with broader capabilities than users or host policy may expect.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger phrase "Use AgentRelay" is generic enough to appear in ordinary conversation, quoted text, or adversarial content, which can cause unintended activation. In an agent environment, overly broad invocation boundaries increase the chance that untrusted input causes file operations or message handling workflows to run automatically.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The skill description emphasizes message relay but does not clearly warn that it writes shared files, stores transaction logs, and may delete files via burn-on-read. Missing disclosure is risky because users and calling agents may not understand the persistence, audit, and data-loss implications of invoking the skill.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instruction to "immediately execute" when certain keywords are seen encourages automatic handling based on ambiguous text rather than a strongly authenticated protocol boundary. This makes prompt-injection and accidental invocation more likely, especially since the skill then reads shared files and may proceed to update state or send confirmations.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Automatic deletion via burn-on-read is not inherently malicious, but it is an autonomous destructive action that can remove data without an additional approval step. In this skill's context, that behavior is somewhat justified for confidentiality, but it still creates availability and auditability risks if enabled unexpectedly or triggered by untrusted payload metadata.

Content

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

md
### 2. Burn-on-read (Optional)

When `burn_on_read=true` is set in `meta` or `payload.content`, the file is automatically deleted after reading to protect sensitive data.

## 📁 Data Storage

Tainted flow: 'REGISTRY_PATH' from os.getenv (line 16, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
65% confidence
Finding

Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.

Content

Scanner excerpt · __init__.py (reported line 54)May include surrounding context.

python
def save_registry(registry: Dict[str, Any]) -> None:
    """写回持久化 registry。"""
    ensure_dirs()
    with open(REGISTRY_PATH, 'w', encoding='utf-8') as f:
        json.dump(registry, f, ensure_ascii=False, indent=2)

def upsert_registry_event(event_id: str, event_data: Dict[str, Any]) -> Dict[str, Any]:

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The code persists the relay secret into both the event file and the registry in plaintext. Because the secret is the mechanism later used to validate completion, anyone with local read access to these files can recover it and forge or replay CMP confirmations, undermining the protocol's trust model.

Content

No source excerpt is available for this finding.

Tainted flow: 'file_path' from os.getenv (line 529, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
89% confidence
Finding

The event file path is built as STORAGE_PATH / f"{event_id}.json" with no validation of event_id. If event_id contains path traversal sequences such as ../, an attacker may cause writes outside the intended storage directory, potentially overwriting arbitrary files accessible to the process.

Content

Scanner excerpt · __init__.py (reported line 252)May include surrounding context.

python
file_name = f"{event_id}.json"
    file_path = STORAGE_PATH / file_name
    
    with open(file_path, 'w', encoding='utf-8') as f:
        json.dump(file_content, f, ensure_ascii=False, indent=2)

    upsert_registry_event(

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The receive operation loads secrets and payloads from disk and returns them wholesale to callers via content, secret, and full_data. In an agent-to-agent relay skill handling potentially sensitive payloads, broad exposure of raw stored data increases the chance of accidental disclosure to logs, downstream tools, or untrusted prompts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The verification function returns both expected_secret and received_secret to the caller. This leaks the canonical secret even when verification fails, allowing an attacker who can invoke verification or inspect its output to learn the secret and then submit a valid forged CMP.

Content

No source excerpt is available for this finding.

Tainted flow: 'file_path' from os.getenv (line 529, credential/environment) → open (file write)

Medium
Category
Data Flow
Confidence
92% confidence
Finding

The update path uses next_event_id or event_id directly in STORAGE_PATH / f"{target_event_id}.json" and then overwrites that file. Without sanitizing the identifier, a crafted value can escape the storage directory and modify arbitrary files writable by the process.

Content

Scanner excerpt · __init__.py (reported line 615)May include surrounding context.

python
data["payload"]["content"].update(updates)
    
    # 写回文件
    with open(file_path, 'w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

    upsert_registry_event(

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The trigger patterns include broad natural-language phrases like "Please use AgentRelay skill to handle" and "Use AgentRelay", which can be embedded in ordinary user or agent messages and unintentionally activate the skill. In a communication/relay skill, accidental activation is more dangerous because it may cause cross-agent message handling, file-pointer processing, or secret-code workflows to start in contexts where they were not intended.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This function rewrites existing event JSON files with updated payload content, which is a file-modifying operation. The code includes internal comments and logging, but no user-facing warning that invoking the tool will alter persisted relay data.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
93% confidence
Finding

The top-level docstring is written entirely in Chinese and states a mandatory usage requirement, which imposes a specific language context without any visible opt-in or alternative. Under the policy criteria, forcing a language or locale without user choice is a natural-language policy concern.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.