Back to skill

Security audit

A2a Manager

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches its agent-management purpose, but it can mutate Notion and local OpenClaw state and contains unsafe path handling that could delete or overwrite unintended local files.

Review before installing. Use this only in a workspace where creating, editing, and deleting OpenClaw agent state is intended, and avoid passing untrusted agent names, specialist IDs, or rollback IDs. Do not connect it to a sensitive Notion workspace unless you are comfortable with task data being sent to Notion. The package should add explicit confirmations and path validation before it is used for destructive actions.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/create_agent.py:17
Finding

Path Traversal Enables Recursive Deletion Outside the Intended Agent Workspace

Content
View full analysis
Path: """Lấy đường dẫn workspace của agent""" workspace_name = agent_name.lower().replace(" ", "-") return WORKSPACE_ROOT / f"workspace-{workspace_name}" ``` The generated path is later passed directly to recursive deletion: ```python def delete_agent(agent_name: str) -> Dict[str, Any]: """Xóa agent""" # 1. Xóa workspace workspace = get_agent_workspace(agent_name) if workspace.exists(): shutil.rmtree(workspace) ``` ### Technical Analysis The caller-controlled `agent_name` is transformed only by lowercasing it and replacing spaces with hyphens. Directory separators and `..` traversal components remain valid. The fixed `workspace-` prefix does not provide containment. For example, an input containing multiple traversal components can produce a path resembling: ```text ~/.openclaw/workspace/workspace-../../../target ``` After filesystem path resolution, this may refer to a directory outside the intended agent workspace. The application does not call `resolve()` and does not verify that the resolved target remains beneath `WORKSPACE_ROOT`. The resulting path is passed to `shutil.rmtree()`, which recursively deletes the target directory and its contents. ### Attack Path 1. An attacker or untrusted caller gains the ability to invoke the `delete` action with a chosen agent name. 2. The attacker supplies an agent name containing enough `../` components to escape the intended workspace. 3. `get_agent_workspace()` incorporates those components into the filesystem path. 4. If the resolved directory exists, `delete_agent()` passes it to `shutil.rmtree()`. 5. The selected directory and all accessible cont ...[truncated 765 chars]
Remediation
View remediation
None: if not AGENT_NAME_PATTERN.fullmatch(agent_name): raise ValueError("Invalid agent name") ``` 2. Resolve the generated path and enforce containment before creating or deleting it: ```python def get_agent_workspace(agent_name: str) -> Path: validate_agent_name(agent_name) root = WORKSPACE_ROOT.resolve() target = (root / f"workspace-{agent_name.lower()}").resolve() if target.parent != root: raise ValueError("Agent workspace escapes the workspace root") return target ``` 3. Before recursive deletion, require an application-owned marker file inside the target directory. 4. Reject names containing `/`, `\`, `.`, `..`, null bytes, or platform-specific path separators. 5. Refuse to delete `WORKSPACE_ROOT`, its ancestors, symbolic links, or paths not registered as agent workspaces. 6. Run the Skill under a minimally privileged account and maintain backups of persistent agent data. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/specialist_manager.py:94
Finding

Unsanitized Specialist IDs Permit Traversal-Based JSON File Access and Modification

Content
View full analysis
Dict[str, Any]: """ Dispose một Specialist - xóa sau khi task hoàn thành """ spec_file = SPECIALISTS_DIR / f"{specialist_id}.json" if not spec_file.exists(): return {"success": False, "error": "Specialist not found"} with open(spec_file) as f: specialist = json.load(f) # Mark as disposed specialist["status"] = "disposed" specialist["disposed_at"] = datetime.now().isoformat() specialist["dispose_reason"] = reason # Save disposal record with open(spec_file, "w") as f: json.dump(specialist, f, indent=2) ``` The read operation is also exposed through `get_specialist()`: ```python def get_specialist(specialist_id: str) -> Optional[Dict[str, Any]]: """Lấy thông tin Specialist""" spec_file = SPECIALISTS_DIR / f"{specialist_id}.json" if not spec_file.exists(): return None with open(spec_file) as f: return json.load(f) ``` Context updates also rewrite the selected path: ```python def add_context(specialist_id: str, message: str, sender: str = "system"): """Thêm context cho Specialist""" spec = get_specialist(specialist_id) if not spec: return None spec["context"].append({ "timestamp": datetime.now().isoformat(), "sender": sender, "message": message }) spec_file = SPECIALISTS_DIR / f"{specialist_id}.json" with open(spec_file, "w") as f: json.dump(spec, f, indent=2) ``` ### Technical Analysis The public `get`, `dispose`, and `add_context` operatio ...[truncated 1770 chars]
Remediation
View remediation
Path: if not SPECIALIST_ID_PATTERN.fullmatch(specialist_id): raise ValueError("Invalid specialist ID") root = SPECIALISTS_DIR.resolve() target = (root / f"{specialist_id}.json").resolve() if target.parent != root: raise ValueError("Specialist path escapes storage directory") return target ``` 3. Use the secure helper in `get_specialist()`, `dispose_specialist()`, and `add_context()`. 4. Validate the complete JSON schema before modifying a record. 5. Use atomic writes through a temporary file in the same directory followed by `os.replace()`. 6. Create stored files with restrictive permissions, such as mode `0600`, where supported. 7. Prevent symlink-based escape by rejecting symbolic-link targets or securely opening files relative to a trusted directory descriptor. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/a2a_map.py:247
Finding

Rollback Path Traversal Can Replace the Active A2A Map with an Unintended Markdown File

Content
View full analysis
bool: """Rollback về version cũ""" version_file = WORKSPACE_A2A_VERSION / f"{version_id}.md" if not version_file.exists(): return False # Read version content (skip header) with open(version_file) as f: content = f.read() # Find the separator if "---" in content: content = content.split("---", 1)[1] content = content.strip() # Restore to current map map_path = get_map_path() map_path.parent.mkdir(parents=True, exist_ok=True) with open(map_path, "w") as f: f.write(content) # Also sync to skill if SKILL_A2A_MAP.exists(): shutil.copy2(map_path, SKILL_A2A_MAP) return True ``` The command-line interface passes user input directly into this function: ```python elif action == "rollback" and len(sys.argv) >= 3: print(json.dumps({"success": a2a.rollback(sys.argv[2])}, indent=2)) ``` ### Technical Analysis `version_id` is expected to be a timestamp-based identifier, but the rollback implementation does not enforce that format. It directly constructs a path under `WORKSPACE_A2A_VERSION` using caller-controlled input. A traversal-bearing identifier can select another accessible Markdown file outside the version directory. The file is read and its content is written into the current A2A map. If the Skill reference map exists, the poisoned map is also copied to `references/A2A_MAP.md`. Because the A2A map represents persistent agent registry and communication state, replacing it with unintended or attacker-controlled Markdown can influence future agent-management behavior. ### Attack Path 1. An attacker or untrusted caller invokes the `rollback` command with a crafted version identifi ...[truncated 1128 chars]
Remediation
View remediation
bool: if not VERSION_ID_PATTERN.fullmatch(version_id): return False root = WORKSPACE_A2A_VERSION.resolve() version_file = (root / f"{version_id}.md").resolve() if version_file.parent != root or not version_file.is_file(): return False ``` 3. Maintain an explicit version manifest and permit rollback only to entries present in that manifest. 4. Validate the selected snapshot as a legitimate A2A map before replacing active state. 5. Write the replacement atomically and retain a verified backup of the current map. 6. Avoid automatically copying mutable workspace content into the packaged Skill reference directory. 7. Reject symbolic links and require version files to be regular files owned by the expected user. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (20)

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill advertises capabilities that can create agents, manage Discord channels/roles, update A2A mapping files, and manipulate Notion tasks, but it does not warn users that these operations can modify external services and project data. Without prominent warnings and consent/confirmation boundaries, users or orchestrators may invoke destructive or unintended actions without understanding the side effects.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The trigger phrases are broad enough to match ordinary coordination language like 'quản lý agent', 'điều phối', 'phân công', or 'map', which can cause the skill to activate outside its intended scope. In this skill, accidental activation is more dangerous because the documented actions include creating agents, modifying Discord resources, updating task systems, and orchestrating sub-agents that can affect external systems and project state.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The module docstring is written entirely in Vietnamese, and the rest of the user-facing comments/docstrings are also localized to Vietnamese without any indication that this skill is region-specific or that another language is available. This creates a language/locale policy concern because the skill appears to impose a specific language by default rather than offering user opt-in or documenting a justified locale restriction.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The rollback function restores old content by overwriting the current map file and optionally copying it into the skill reference file. Although this is a potentially destructive state-changing operation, the code provides no confirmation prompt and no user-facing disclosure at the point of execution beyond a terse docstring.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The help text and command dispatcher present 'sync' as a supported feature, and line L559 calls a2a.sync(), but the A2AMap class explicitly says sync was removed and provides no sync method. This is an active contradiction between the file's user-facing documentation/interface and the actual implemented code path, which would fail at runtime.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The module docstring describes the file as 'Create Agent - Tạo agent mới với đầy đủ cấu hình', which frames the script as creating new agents. However, the same file also exposes delete_agent and a CLI delete action that recursively removes the agent workspace and unregisters it from mappings/Discord. This is an active contradiction in stated intent versus implemented behavior, not just missing detail.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The generated SOUL.md sets the agent language to Vietnamese by default, and the create_agent defaults also enforce 'Vietnamese' when no language is specified. This is a natural-language locale constraint applied automatically rather than offered as an explicit user choice.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The defaults dictionary assigns 'Vietnamese' to the language field whenever the caller does not provide one. This forces a locale preference by default and does not present the user with a language choice or opt-in path.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The delete_agent function performs shutil.rmtree on a computed workspace path with no confirmation, safety guardrails, or validation that the target is an expected agent directory. If invoked with an unexpected or manipulated agent name, this can cause irreversible deletion of local data under the workspace tree and amplifies the risk of accidental or unauthorized destructive actions.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The module docstring says this script manages Discord categories, channels, and roles, which implies actions against Discord itself. However, the implementation only reads and writes local configuration state under ~/.openclaw/workspace, and the create_category docstring explicitly states that real Discord API or message tool integration is not used here, contradicting the advertised behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

This code persists Discord management state to a local JSON file, and multiple delete/rename/update functions rely on it to overwrite or remove stored configuration. While there are internal docstrings, there is no user-facing confirmation, warning, or visible disclosure that invoking these operations will modify local workspace data under ~/.openclaw/workspace.

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 multiple natural-language strings intended for users, including the module description, specialist descriptions, docstrings, and command help, all written in Vietnamese. Because the skill does not provide an opt-in language selection or explain that it is intentionally region-specific, it violates the language/locale policy criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The function accesses a Notion API key from a local file or the NOTION_API_KEY environment variable, which is a sensitive credential operation covered by the warning requirement for code files. The code includes no user-facing notice, confirmation, or explanatory comment indicating that credentials will be accessed for authentication.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

This code sends task titles, descriptions, tags, and other metadata to the external Notion API, which can affect user privacy and data handling. Although the module docstrings mention Notion, there is no visible confirmation prompt or user-facing warning near the network transmission itself to disclose that task data is being sent to a third-party service.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · scripts/task_board.py (reported line 50)May include surrounding context.

python
if not api_key:
        return {"error": "No Notion API key found"}
    
    url = f"https://api.notion.com/v1{endpoint}"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Notion-Version": "2025-09-03",

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

In mark_blocked, the inline comment says it will add a comment with the blocking reason, but the function only adds a tag and updates state before returning. The reason parameter is therefore ignored, which contradicts the documented intent and can mislead callers into believing explanatory context is stored in Notion.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

reject_task is documented as returning a task to IN_PROGRESS with feedback, and the inline comment says feedback is added as a comment. However, the feedback parameter is never used; the function only removes a tag and updates the task state, so the documented behavior contradicts the implementation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This markdown template contains user-facing natural language in multiple languages, such as Vietnamese on L024 and later workflow labels in Vietnamese on L093-L097. Because the file does not state that the skill or template is intentionally bilingual or region-specific, it may violate language/locale policy by implicitly forcing or assuming a locale without user opt-in.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
92% confidence
Finding

This file contains user-visible natural-language strings in Vietnamese, and similar localized strings appear throughout docstrings and CLI help. The skill does not indicate that it is region-specific or provide an opt-in choice of language, which can violate language/locale policy expectations.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The module docstring and much of the user-visible/descriptive text are written only in Vietnamese, which imposes a language choice without opt-in or an alternative. The policy requires avoiding forced language or locale constraints unless explicitly offered as a choice or justified as region-specific.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.