Back to skill

Security audit

FIS Architecture

Security checks for vulnerabilities and agentic risk

Overview

This looks like a legitimate multi-agent coordination skill, but it needs review because it persists ticket state, posts task content to Discord, and has unsafe ticket-file handling.

Install only if you are comfortable with task details being shared through Discord and with the skill keeping local ticket files across sessions. Restrict Discord bot permissions and channel access, avoid posting sensitive content to public threads or #daily-chat, keep FIS_HUB_PATH confined to a dedicated directory, and review generated sessions_* commands before executing them.

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 (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/fis_lifecycle_pro.py:279
Finding
Path Traversal in Ticket File Operations## Vulnerability Details **File Location**: `scripts/fis_lifecycle_pro.py:279-306` **Vulnerability Type**: Unsanitized path construction using attacker-controlled ticket identifiers **Risk Level**: High ### Vulnerable Code ```python def archive_ticket(self, ticket_id): """Archive a completed ticket""" # Check completed first src = os.path.join(self.completed_dir, f"{ticket_id}.json") if not os.path.exists(src): # Check active src = os.path.join(self.active_dir, f"{ticket_id}.json") if not os.path.exists(src): print(f"✗ Ticket not found: {ticket_id}") return False with open(src, "r") as f: ticket = json.load(f) ticket["status"] = "archived" ticket["archived_at"] = datetime.now().isoformat() ticket["updated_at"] = datetime.now().isoformat() with open(src, "w") as f: json.dump(ticket, f, indent=2) # Move to archive dst = os.path.join(self.archive_dir, f"{ticket_id}.json") os.rename(src, dst) print(f"⚪ Archived: {ticket_id}") return True ``` The same unsafe path-construction pattern also appears in the following operations: - `update_status`: `scripts/fis_lifecycle_pro.py:163` - `complete_ticket`: `scripts/fis_lifecycle_pro.py:202,226` - `archive_ticket`: `scripts/fis_lifecycle_pro.py:282-302` - `get_ticket`: `scripts/fis_lifecycle_pro.py:349` ### Technical Analysis The `ticket_id` value originates from CLI arguments and is inserted directly into filesystem paths: ```python os.path.join(self.active_dir, f"{ticket_id}.json") ``` No validation prevents the identifier from containing absolute paths, `..` traversal components, path separators, or symlink-based escapes. `os.path.join` does not enforce containment within the intended ticket directory. As a result, a value such as `../../target` can resolve to `target.json` outside the active, completed, or archived ticket directory. The affected methods perform sensitive operations ...[truncated 2189 chars]
Remediation
## Remediation Suggestions 1. Enforce a strict ticket identifier format before any filesystem operation. For example: ```python import re TICKET_ID_PATTERN = re.compile(r"^TASK_[A-Za-z0-9_]+$") def validate_ticket_id(ticket_id: str) -> str: if not TICKET_ID_PATTERN.fullmatch(ticket_id): raise ValueError("Invalid ticket identifier") return ticket_id ``` 2. Centralize path construction and verify containment after canonicalization: ```python from pathlib import Path def safe_ticket_path(directory: str, ticket_id: str) -> Path: validate_ticket_id(ticket_id) base = Path(directory).resolve() candidate = (base / f"{ticket_id}.json").resolve() if candidate.parent != base: raise ValueError("Ticket path escapes the ticket directory") return candidate ``` 3. Use the centralized helper in `update_status`, `complete_ticket`, `archive_ticket`, and `get_ticket` rather than constructing paths independently. 4. Reject identifiers containing `/`, `\`, `..`, null bytes, or absolute-path syntax, even if additional validation is introduced elsewhere. 5. Account for symlink attacks. Where practical, ensure ticket directories and files are not symlinks and use secure descriptor-based file operations with no-follow behavior. 6. Run the Skill under a dedicated, minimally privileged account that cannot modify unrelated OpenClaw configuration or user files. 7. Add negative tests covering traversal identifiers, absolute paths, mixed separators, symlink escapes, and malformed ticket IDs.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/fis_worker_toolkit.py:20
Finding
Unescaped User Input in Generated Agent Tool Calls## Vulnerability Details **File Location**: `scripts/fis_worker_toolkit.py:20-49` **Vulnerability Type**: Injection into executable-looking Agent tool-call templates **Risk Level**: Medium ### Vulnerable Code ```python def spawn_for_subtask(self, parent_ticket_id, subtask_description): """Spawn sub-agent for complex subtask - runs in background""" print("=" * 60) print("🔄 Spawning SubAgent for Complex Subtask") print("=" * 60) sub_agent = self._select_sub_agent(subtask_description) sub_ticket_id, sub_ticket = self.fis.create_ticket( agent=sub_agent, task=f"[Subtask of {parent_ticket_id}] {subtask_description}", role="subagent", ) print(f"\n✅ Sub-ticket created: {sub_ticket_id}") print(f"SubAgent: {sub_agent}") print(f"Parent: {parent_ticket_id}") spawn_cmd = f"""sessions_spawn( agentId="{sub_agent}", task="{subtask_description}", mode="run", label="{sub_ticket_id}" )""" print(f"\n📋 Execute this to spawn sub-agent:") print(spawn_cmd) ``` A related unsafe template is generated in `scripts/fis_worker_toolkit.py:94-96`: ```python print( f'sessions_send(\n sessionKey="main",\n message="{report[:100]}..."\n)' ) ``` Task text is also embedded into message and command-oriented templates in `scripts/fis_coordinator.py:75-105` and `scripts/fis_coordinator.py:155-177`. ### Technical Analysis The toolkit interpolates attacker-influenced subtask, summary, and report content directly into strings that resemble executable Agent tool calls. The inserted values are placed inside quoted arguments without JSON encoding, escaping, or structural validation. For example, a `subtask_description` containing quotation marks, newlines, closing syntax, or additional instructions can terminate the intended `task` value and alter the generated template. The application itself only prints the resulting string; it does not directly execute `sessions_spawn` or `sessions_ ...[truncated 2129 chars]
Remediation
## Remediation Suggestions 1. Do not generate executable tool calls through string interpolation. Invoke typed Agent APIs directly with separately supplied arguments whenever the runtime permits it. 2. If textual serialization is unavoidable, encode every value using a suitable serializer rather than manually adding quotation marks: ```python import json spawn_cmd = ( "sessions_spawn(\n" f" agentId={json.dumps(sub_agent)},\n" f" task={json.dumps(subtask_description)},\n" ' mode="run",\n' f" label={json.dumps(sub_ticket_id)}\n" ")" ) ``` 3. Prefer a strict JSON data envelope that cannot be confused with instructions: ```python payload = { "agentId": sub_agent, "task": subtask_description, "mode": "run", "label": sub_ticket_id, } print(json.dumps(payload, ensure_ascii=False)) ``` 4. Clearly label task descriptions, summaries, and reports as untrusted data. Receiving Agents should be instructed not to interpret content inside those fields as tool commands or higher-priority instructions. 5. Require explicit operator confirmation showing the parsed destination Agent, mode, label, and complete task before dispatch. 6. Validate permitted Agent IDs and enforce length limits for task, summary, deliverable, and report fields. 7. Apply the same structured serialization to `sessions_send` templates and to the related coordinator templates. 8. Add tests using quotation marks, backslashes, multiline input, closing parentheses, code fences, and embedded tool-call text to verify that user data cannot alter the generated call structure.
Vulnerability Patterns
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Rogue AgentSelf-Modification, Session Persistence
Findings (9)

Lp3

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding
The skill documents shell execution, filesystem access, and environment-dependent operations, but it does not declare any tool scope such as permissions or allowed-tools. That creates a trust gap where an operator or platform may expose broader capabilities than intended, increasing the chance of unsafe command execution or file modification during use.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
This workflow instructs operators to send task content and status updates to Discord threads, which is an external service, without any warning about data sensitivity, retention, or access scope. Sensitive prompts, internal project details, or user data could be disclosed to channel members or stored externally without informed consent.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The instruction to archive the thread and report to a public chat channel encourages redistribution of task results to a broader audience without any disclosure warning or access control guidance. If outputs contain sensitive code, research, operational details, or user information, this step can amplify exposure beyond the original need-to-know group.

Session Persistence

Medium
Category
Rogue Agent
Content
- Check disk space

**If Thread creation fails:**
- Verify the bot has **Create Public Threads** permission in the target Forum channel
- Check that `channelId` points to a Forum channel (not a regular text channel)
- Confirm the bot is a member of the server with correct roles
Confidence
60% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The generated worker instructions explicitly require the recipient to confirm receipt using the Chinese phrase "收到任务,开始执行." This imposes a specific language choice in natural-language output and does not offer any user or agent opt-in or alternative locale handling.

Context-Inappropriate Capability

Medium
Confidence
87% confidence
Finding
The manifest describes orchestrating multi-agent workflows using JSON tickets and A2A coordination between agents. This code additionally executes the local `openclaw` binary via `subprocess.run` to discover session context, which is a broader host-execution capability than the stated purpose requires and is not declared in the manifest description.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# This subprocess call is intentional — it's the only way to detect
        # the current session context when running outside the OpenClaw runtime.
        try:
            result = subprocess.run(
                ["openclaw", "sessions", "list", "--json"],
                capture_output=True,
                text=True,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The module and method text explicitly state that sub-agents are spawned and run in the background, but the implementation only prints a `sessions_spawn(...)` string and never performs the launch. In an orchestration skill, this mismatch can cause operators or upstream agents to assume delegated work is executing when it is not, leading to silent task loss, broken monitoring, and incorrect trust in completion or progress state.

Excessive Permissions

Low
Category
Privilege Escalation
Content
Each agent's Discord bot **must** have these permissions configured in the Discord server. Without them, Thread creation and messaging will fail silently.

**Required Bot Permissions:**
- **Send Messages** — reply in channels and threads
- **Send Messages in Threads** — post inside Forum threads
- **Create Public Threads** — create new Forum posts programmatically
Confidence
80% confidence
Finding
Skill requests more permissions than appear necessary for its stated functionality. Review if elevated access is justified.

Static analysis

No suspicious patterns detected.