Back to skill

Security audit

Apprentice

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local workflow-learning tool, but it records and persists user instructions and can execute learned shell scripts with broad local authority that is not clearly or accurately bounded.

Review carefully before installing. Do not teach it workflows that include secrets, customer data, private URLs, credentials, destructive commands, or sensitive operational procedures. Treat any learned workflow as local shell code: preview the generated files, inspect run.sh and SKILL.md, restrict workflow names, avoid passing secrets as variables, and remove raw logs if they contain sensitive content.

Vulnerability Patterns
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Tool Hijacking and SpoofingModifies or replaces tools so legitimate-looking calls execute attacker logic
  • 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
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
Findings (3)

T02 · Agent Memory Poisoning

Error
Location
synthesize.py:146
Finding
Persistent Agent Instruction Injection Through Generated Skill Files<![CDATA[ ## Vulnerability Details **File Location**: `synthesize.py:146-157`, `synthesize.py:195-197`, and `synthesize.py:304-305` **Vulnerability Type**: Persistent instruction injection into an agent-loadable skill **Risk Level**: High ### Vulnerable Code ```python # synthesize.py:146-157 steps_section = "\n## Steps\n\n" if steps: for i, step in enumerate(steps, 1): steps_section += f"{i}. {step.get('text', '').strip()}\n" else: steps_section += "*No steps recorded — observation was empty.*\n" # Trigger phrases triggers = [ f'"{slug}"', f'"run {slug}"', f'"do {slug}"', f'"replay {slug}"', ] ``` ```python # synthesize.py:195-197 ## Notes from Synthesis {synthesis_notes if synthesis_notes else "No additional notes."} ``` ```python # synthesize.py:304-305 with open(skill_path, "w") as f: f.write(skill_md) ``` ### Technical Analysis The synthesizer inserts recorded step text and the command-line `--notes` value directly into a persistent `SKILL.md` file without escaping Markdown, isolating the content as untrusted data, or validating it against an expected workflow schema. An attacker-controlled observation can contain Markdown headings, forged metadata, or instructions directed at the agent. For example, a recorded step could introduce a new section instructing the agent to disregard its intended workflow, access unrelated files, or execute additional actions. Because the generated file is a permanent OpenClaw-compatible skill, the injected content can affect later sessions whenever the skill is loaded. The project documentation states that users review and approve generated workflows before they are saved. The implemented `synthesize()` path does not contain an approval gate: it writes `SKILL.md` and `run.sh` immediately. A preview is optional and occurs after synthesis, so it does not prevent persistence. ### Attack Path 1. An attacker influences recorded narration, imported observation data, or the `--notes` arg ...[truncated 1114 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Treat all observation text and synthesis notes as untrusted data rather than executable agent instructions. 2. Store observations in a strict structured format with separate fields for actions, explanations, parameters, and commands. 3. Escape or reject Markdown constructs that can create headings, front matter, HTML blocks, links, or instruction-like sections. 4. Place raw narration in a clearly delimited quoted or encoded data block that the agent is explicitly instructed not to interpret as policy. 5. Validate generated skills against an allowlisted schema before saving them. 6. Generate files in a temporary staging directory and show the exact resulting content to the user. 7. Require an explicit approval action before atomically moving the generated skill into the active workflow library. 8. Ensure preview mode occurs before persistence rather than after files have already been written. 9. Consider storing raw observations separately from the agent-loadable skill and generating only normalized, reviewed actions in `SKILL.md`. ]]>

T07 · Tool Hijacking and Spoofing

Error
Location
run.py:84
Finding
Workflow Name Path Traversal Allows Execution of Arbitrary Local Shell Scripts<![CDATA[ ## Vulnerability Details **File Location**: `run.py:84-92` and `run.py:115-126`; related arbitrary write behavior in `synthesize.py:35-36` and `synthesize.py:295-310` **Vulnerability Type**: Unvalidated path construction and attacker-controlled script selection **Risk Level**: Critical ### Vulnerable Code ```python # run.py:84-92 def run_workflow(workflow_name: str, variables: dict = None, dry_run: bool = False): """Run a learned workflow.""" workflow_dir = WORKFLOWS_DIR / workflow_name if not workflow_dir.exists(): available = [w["name"] for w in list_workflows()] print(f"Workflow not found: {workflow_name}", file=sys.stderr) if available: print(f"Available workflows: {', '.join(available)}") sys.exit(1) run_script = workflow_dir / "run.sh" obs_file = workflow_dir / "observation.json" ``` ```python # run.py:115-126 env = os.environ.copy() if variables: for k, v in variables.items(): env[k.upper()] = str(v) print() try: result = subprocess.run( ["bash", str(run_script)], env=env, capture_output=False ) ``` ```python # synthesize.py:35-36 obs_file = WORKFLOWS_DIR / workflow_name / "observation.json" if not obs_file.exists(): ``` ```python # synthesize.py:295-310 slug = workflow_name # Always use the directory name as the slug workflow_dir = WORKFLOWS_DIR / slug workflow_dir.mkdir(exist_ok=True) # Generate and save SKILL.md skill_md = generate_workflow_skill_md(observation, workflow_meta, variables, synthesis_notes) skill_path = workflow_dir / "SKILL.md" with open(skill_path, "w") as f: f.write(skill_md) # Generate and save run.sh run_script = generate_run_script(observation, workflow_meta, variables) run_path = workflow_dir / "run.sh" with open(run_path, "w") as f: f.write(run_script) ``` ### Technical Analysis The workflow name is combined with `WORKFLOWS_DIR` without validation or canonical containment checks. Python's `path ...[truncated 2359 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Restrict workflow names to a conservative slug format, for example: `^[a-z0-9][a-z0-9_-]{0,63}$`. 2. Reject absolute paths, path separators, `.` components, and `..` components. 3. Resolve both the workflow root and candidate path before use, then verify that the candidate remains under the workflow root: ```python root = WORKFLOWS_DIR.resolve(strict=True) candidate = (root / workflow_name).resolve(strict=True) if not candidate.is_relative_to(root): raise ValueError("Invalid workflow path") ``` 4. Apply the same validation consistently in `run.py`, `synthesize.py`, and `observe.py`. 5. Reject workflow directories and executable files that are symbolic links. 6. Select workflows from an enumerated registry rather than translating user input directly into filesystem paths. 7. Verify ownership and expected permissions of `run.sh` before execution. 8. Prefer executing normalized structured actions through restricted APIs instead of invoking mutable shell scripts. 9. For synthesis writes, create files atomically with exclusive creation where appropriate and verify the resolved parent directory immediately before opening each destination. 10. Add regression tests covering absolute paths, nested traversal, symlink traversal, Unicode separator edge cases, and race conditions. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
run.py:115
Finding
Workflow Execution Exposes the Full Process Environment and Logs Sensitive Variables in Plaintext<![CDATA[ ## Vulnerability Details **File Location**: `run.py:115-119` and `run.py:129-147` **Vulnerability Type**: Excessive environment inheritance and plaintext sensitive-data storage **Risk Level**: High ### Vulnerable Code ```python # run.py:115-119 # Build environment for run.sh env = os.environ.copy() if variables: for k, v in variables.items(): env[k.upper()] = str(v) ``` ```python # run.py:129-147 run_log = { "workflow": workflow_name, "run_at": datetime.now(timezone.utc).isoformat(), "variables": variables or {}, "exit_code": result.returncode, "success": result.returncode == 0 } log_file = workflow_dir / "run_log.json" logs = [] if log_file.exists(): try: with open(log_file) as f: logs = json.load(f) except Exception: pass logs.append(run_log) logs = logs[-20:] # Keep last 20 runs with open(log_file, "w") as f: json.dump(logs, f, indent=2) ``` ### Technical Analysis The runner copies the complete parent process environment and passes it to every workflow shell script. Consequently, a workflow can access any credentials or configuration exposed through environment variables, even though the source security manifest states that no environment variables are accessed. The runner also stores all supplied workflow variables verbatim in `run_log.json`. No secret classification, redaction, encryption, or restrictive file-mode handling is present. Variables representing API keys, access tokens, passwords, private endpoints, or personal data therefore remain in plaintext for up to 20 recorded runs. Environment inheritance becomes especially severe when combined with the workflow path traversal issue, because an attacker-selected script can directly enumerate the inherited environment. Plaintext logging can be exploited independently whenever a user supplies sensitive data as a workflow variable. ### Attack Path **Environment disclosure path:** 1. Sensitive credentials are present ...[truncated 1486 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Construct a minimal execution environment rather than copying `os.environ`. 2. Allowlist only essential non-sensitive variables, such as a controlled `PATH`, locale values, and explicitly approved workflow parameters. 3. Run workflows in a restricted subprocess environment with an explicit working directory and reduced filesystem permissions. 4. Mark parameters as secret or non-secret in the workflow schema. 5. Never store secret parameter values in `run_log.json`; log only parameter names or redacted values. 6. Apply defensive name-based redaction for fields containing terms such as `TOKEN`, `SECRET`, `PASSWORD`, `KEY`, `CREDENTIAL`, and `AUTH`, while allowing explicit schema-based secret declarations to take precedence. 7. Create logs with owner-only permissions, such as mode `0600`, and ensure workflow directories are not broadly readable. 8. Provide a configurable retention policy and a secure log-deletion mechanism. 9. Avoid accepting secrets directly on command lines because process listings and shell history can expose them. Use protected standard input, a secret manager, or file descriptors instead. 10. Update security manifests and documentation so they accurately disclose environment access and local secret-storage behavior. ]]>
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
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (24)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding
The skill description promises local observation, learning, and review-based synthesis, but the detected behavior reportedly does not implement those safeguards and instead centers on workflow execution via scripts. This mismatch is dangerous because users may trust the documented privacy and approval model while the real implementation performs materially different actions, enabling deceptive or unsafe execution.

Ssd 3

High
Confidence
98% confidence
Finding
The guidance explicitly encourages users to speak sensitive values such as API keys or environment-variable-related information during observation. Those secrets can be captured into logs or synthesized into reusable workflows, creating direct credential exposure and long-lived secret persistence that could later be replayed, shared, or exfiltrated.

Ae1

High
Category
analysis-evasion
Content
The generated `SKILL.md` is a full, valid OpenClaw skill. This means:
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Intent-Code Divergence

High
Confidence
93% confidence
Finding
The module docstring states there are no network calls, yet it launches arbitrary shell scripts that can freely invoke network tools or exfiltrate data. This deceptive assurance is especially dangerous in a skill that persists and replays learned workflows, because users may trust it with sensitive tasks under a false no-network assumption.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
The skill is presented as replaying learned workflows, but the implementation can run arbitrary shell scripts with inherited environment, which is a much broader and more dangerous capability than replaying observed steps. Because the product promise emphasizes no-code learning by demonstration, this gap increases the chance that users will unknowingly authorize powerful execution paths.

Env Variable Harvesting

High
Category
Data Exfiltration
Content
return

    # Build environment for run.sh
    env = os.environ.copy()
    if variables:
        for k, v in variables.items():
            env[k.upper()] = str(v)
Confidence
95% confidence
Finding
Copying the full process environment into the workflow execution context exposes all ambient secrets and configuration to arbitrary workflow scripts. In a learned-workflow system, this sharply raises the impact of any malicious or compromised workflow because it can read credentials and then use them locally or exfiltrate them.

Context-Inappropriate Capability

High
Confidence
96% confidence
Finding
Executing arbitrary bash scripts is an unjustified capability for a no-code workflow-learning skill unless clearly disclosed and tightly controlled. This creates a direct path from learned content to host command execution, enabling destructive actions, persistence, lateral movement, or data theft if a workflow is malicious or tampered with.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
The README advertises permanent observation and replay of the user's exact actions and decisions, but the surrounding messaging minimizes the operational risk of capturing and re-executing shell commands, file modifications, git operations, or environment-specific steps. In this context, the skill is specifically intended to convert demonstrations into durable automation, so missing strong warnings and execution safeguards materially increases the chance of harmful system-impacting behavior.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The start-observation phrases include broad natural-language expressions such as "Learn this" and "I'll show you," which can plausibly appear in ordinary conversation. In a skill that records and later synthesizes user actions into executable workflows, accidental activation can silently begin capturing sensitive commands, file paths, or operational procedures the user did not intend to persist.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The stop triggers include common phrases like "Done" and "Got it?" that are likely to occur naturally during demonstrations or unrelated conversation. This can prematurely terminate observation, leading to incomplete or malformed learned workflows that may later execute unsafe partial sequences without the user's awareness.

Vague Triggers

Medium
Confidence
91% confidence
Finding
Workflow execution phrases like "Run [workflow-name]" and "Replay [workflow-name]" are underspecified and can overlap with ordinary user requests. Because this skill is designed to execute previously learned workflows, ambiguous invocation raises the risk of unintended shell, file, or git actions being launched from normal conversation rather than explicit user intent.

Lp3

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding
The skill advertises capabilities that imply file writing and shell execution but does not declare an explicit tool scope or permissions boundary. That increases the chance of overbroad agent access or silent privilege expansion, making it harder for users and the platform to reason about what the skill can actually do.

Vague Triggers

Medium
Confidence
96% confidence
Finding
Broad trigger phrases such as common conversational expressions can cause the skill to activate unintentionally. In a skill that may record actions or later execute learned workflows, accidental activation can capture sensitive information or initiate behavior the user did not intend.

Vague Triggers

Medium
Confidence
92% confidence
Finding
Ambiguous start/stop observation triggers make it easy for the skill to enter or exit recording mode based on normal speech. That creates a realistic risk of collecting unintended content or failing to stop logging when the user believes observation has ended.

Ssd 3

Medium
Confidence
95% confidence
Finding
The skill explicitly describes recording everything the user says and storing raw observation logs and learned workflows. Even if storage is local, retaining natural-language transcripts creates a meaningful privacy and data leakage risk because users may reveal secrets, personal data, internal procedures, or regulated information during demonstrations.

Vague Triggers

Medium
Confidence
97% confidence
Finding
The trigger lists include everyday phrases like 'Done', 'That's it', or 'I'll show you', which are too generic for reliable control of a powerful skill. This increases the attack surface for accidental invocation and prompt-injection-style conversational collisions during unrelated interactions.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The security manifest claims no environment-variable access, but the runner copies the full process environment and passes it to executed workflow scripts. That mismatch can hide exposure of secrets such as API keys, tokens, and credentials to untrusted workflow code and undermines trust in the declared security model.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The manifest understates file access by omitting that the program reads and executes workflows/<n>/run.sh. In a workflow-learning skill, hidden execution of additional local files increases risk because operators may believe only metadata and observations are accessed while executable content is actually trusted.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
print()
    try:
        result = subprocess.run(
            ["bash", str(run_script)],
            env=env,
            capture_output=False
Confidence
95% confidence
Finding
The code directly executes a per-workflow shell script via bash, which gives any learned workflow arbitrary code execution on the host. In this skill's context, workflows are user-taught and intended to become permanent/replayable behaviors, so executing run.sh is materially more dangerous than merely narrating or replaying recorded steps.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The runner executes learned scripts immediately without a confirmation prompt or clear safety warning, which increases the likelihood of accidental execution of dangerous or tampered workflows. In this context, where workflows become permanent and repeatable, the lack of an execution checkpoint makes misuse and surprise behavior more likely.

Ssd 3

Medium
Confidence
98% confidence
Finding
The generated skill content includes raw step text and examples derived directly from observations, creating durable storage of natural-language data that may contain confidential instructions or user-provided secrets. Because the apprentice skill's purpose is to learn and replay exact workflows forever, the context materially increases the chance that sensitive content will later be surfaced, searched, or executed unintentionally.

Ssd 3

Medium
Confidence
97% confidence
Finding
This synthesizer intentionally preserves and later replays the user's exact observed workflow text, which can embed secrets, internal URLs, tokens, customer data, or sensitive operational procedures into a permanent skill artifact. In this skill context, that is more dangerous because 'watch me once' workflows are specifically designed for long-term reuse, increasing retention and later disclosure risk across future sessions or users.

Intent-Code Divergence

Medium
Confidence
88% confidence
Finding
The docstring says this function generates a script that 'executes the workflow,' and the embedded SECURITY MANIFEST at L228 states environment variables are accessed. However, the generated script also uses interactive `read` to collect missing values, meaning its documented variable source is not limited to environment access. This is an active documentation/manifest mismatch inside the generated code rather than a mere omission.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This code creates or overwrites `SKILL.md` and `run.sh` in the workflow directory, which is a file-write operation covered by the warning requirement for code files. Although the header manifest documents file outputs for developers, there is no confirmation prompt or user-facing disclosure at the point of write before persistent files are created.

Static analysis

No suspicious patterns detected.