Back to skill

Security audit

Loom Workflow

Security checks for vulnerabilities and agentic risk

Overview

This skill has a coherent Loom workflow purpose, but it under-discloses sensitive video data handling and can generate unsafe executable workflows from untrusted analysis content.

Review carefully before installing. Only process Loom recordings you are authorized to analyze, inspect and redact extracted frames/transcripts before any vision-model upload, avoid running generated .lobster workflows without manual review and dry-run testing, and treat analysis JSON as untrusted input because it can influence executable commands and output paths.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/generate-lobster.py:47
Finding
Shell Command Injection Through Untrusted Workflow Analysis Fields<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate-lobster.py:47-52, 81-90` **Vulnerability Type**: Command injection through unsafe shell-command construction **Risk Level**: High ### Vulnerable Code ```python else: # Generic placeholder lobster_step["command"] = f"echo 'TODO: Implement {step.get('action', 'action')}'" lobster_step["_todo"] = f"Tool: {step.get('tool')} | Action: {step.get('action')}" ``` ```python def generate_browser_command(step: dict) -> str: """Generate browser automation command.""" action = step.get("action", "").lower() ui_element = step.get("ui_element", "") if "click" in action: return f"openclaw.invoke --tool browser --action act --args-json '{{\"kind\": \"click\", \"ref\": \"{ui_element}\"}}'" elif "type" in action or "enter" in action: return f"openclaw.invoke --tool browser --action act --args-json '{{\"kind\": \"type\", \"ref\": \"{ui_element}\", \"text\": \"${{input}}\"}}'" ``` ### Technical Analysis The generator treats fields from `analysis.json` as trusted and interpolates them directly into strings intended to be executed as shell commands. These fields can originate from a vision model analyzing attacker-controlled frames and transcripts, or from a directly supplied analysis file. In the generic command, a single quote in `action` can terminate the quoted `echo` argument and append shell syntax. In browser commands, `ui_element` is embedded inside nested JSON and shell quoting without JSON-safe serialization or shell-safe argument handling. YAML serialization does not remove this vulnerability. It only serializes the resulting string; when Lobster executes the `command` value through a shell, shell metacharacters remain effective. ### Attack Path 1. An attacker controls or influences a Loom recording, transcript, frame content, or supplied analysis JSON. 2. The resulting model output places shell syntax in `action` or `ui_element`. 3. `gener ...[truncated 991 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Treat every model-generated field as untrusted input. - Do not represent tool operations as interpolated shell strings. Emit structured commands with separately encoded arguments. - Define a strict schema for supported operation types and reject unknown actions. - Validate UI references as opaque identifiers using a restrictive allowlist. - Serialize JSON arguments with `json.dumps()` rather than manually constructing JSON. - If a subprocess is required, invoke it with an argument array and `shell=False`. - If shell execution cannot be eliminated, apply context-appropriate shell escaping and reject control characters and shell metacharacters. Escaping alone should not be the primary defense. - Require review and approval before executing any model-generated workflow, not only steps labeled ambiguous. - Add tests using quotes, newlines, command substitutions, redirections, and shell separators in every model-controlled field. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate-lobster.py:15
Finding
Output Path Traversal Through AI-Generated Workflow Title<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate-lobster.py:15-18, 169-182` **Vulnerability Type**: Path traversal and arbitrary file creation or overwrite **Risk Level**: Medium ### Vulnerable Code ```python workflow = { "name": analysis.get("title", "untitled-workflow").lower().replace(" ", "-"), "description": analysis.get("summary", "Auto-generated workflow from Loom recording"), "args": {}, "steps": [] } ``` ```python # Generate Lobster workflow workflow = generate_lobster_workflow(analysis) workflow_name = workflow["name"] lobster_path = Path(output_dir) / f"{workflow_name}.lobster" with open(lobster_path, "w") as f: yaml.dump(workflow, f, default_flow_style=False, sort_keys=False) print(f"[lobster] Generated: {lobster_path}") # Generate summary markdown summary = generate_summary_markdown(analysis, workflow) summary_path = Path(output_dir) / f"{workflow_name}-summary.md" with open(summary_path, "w") as f: f.write(summary) ``` ### Technical Analysis The workflow title is used as a filename after only lowercasing it and replacing spaces with hyphens. This transformation does not remove absolute-path prefixes, directory separators, `..` traversal components, drive prefixes, or other filesystem-significant characters. Because `title` may be generated from untrusted recording content or supplied in an untrusted analysis file, an attacker can influence both output paths. `Path(output_dir) / attacker_controlled_name` does not guarantee confinement beneath `output_dir`; traversal components can escape it, and an absolute child path can supersede the parent path. Both files are opened using write mode, so existing writable targets can be truncated and replaced. ### Attack Path 1. The attacker causes the analysis title to contain traversal components, such as `../../attacker-selected-name`, or directly supplies a manipulated analysis JSON file. 2. The generator performs only lowercase and space replacem ...[truncated 818 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Convert model-generated titles to strict slugs allowing only lowercase ASCII letters, digits, `_`, and `-`. - Reject path separators, `..`, absolute paths, control characters, drive prefixes, and empty names. - Resolve the output directory and candidate destination with `Path.resolve()`. - Verify that the resolved destination is a child of the resolved output directory before opening it. - Use exclusive creation mode where practical to avoid silently overwriting existing files. - Generate filenames from a trusted internal identifier rather than directly from model output. - Apply the same confinement check independently to both the Lobster and Markdown output paths. - Add regression tests for relative traversal, absolute paths, Windows-style separators, Unicode separator lookalikes, and symbolic-link edge cases. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
test-output/video.info.json:1
Finding
Signed Loom Media URLs and Recording Metadata Stored in Repository Fixture<![CDATA[ ## Vulnerability Details **File Location**: `test-output/video.info.json:1` **Vulnerability Type**: Exposure of bearer-style signed media URLs and recording metadata **Risk Level**: Medium ### Vulnerable Data Structure The single-line JSON fixture contains complete signed Loom CDN URLs in fields such as the following. Authorization values are redacted in this report to avoid redistributing bearer material: ```json { "formats": [ { "url": "https://luna.loom.com/id/a79ecc143cad4dae992eb366dd209edd/.../mediaplaylist-audio.m3u8?Policy=[REDACTED]&Signature=[REDACTED]&Key-Pair-Id=[REDACTED]", "manifest_url": "https://luna.loom.com/id/a79ecc143cad4dae992eb366dd209edd/.../playlist.m3u8?Policy=[REDACTED]&Signature=[REDACTED]&Key-Pair-Id=[REDACTED]", "extra_param_to_segment_url": "Policy=[REDACTED]&Signature=[REDACTED]&Key-Pair-Id=[REDACTED]" }, { "url": "https://cdn.loom.com/sessions/transcoded/a79ecc143cad4dae992eb366dd209edd.mp4?Policy=[REDACTED]&Key-Pair-Id=[REDACTED]&Signature=[REDACTED]" } ], "title": "Processo de Registro e Cálculo de Tipos de Trabalho", "uploader": "Mauricio Sobral", "webpage_url": "https://www.loom.com/share/a79ecc143cad4dae992eb366dd209edd" } ``` ### Technical Analysis Signed CDN URLs operate as bearer capabilities while valid: possession of the complete URL may be sufficient to retrieve the protected resource. The fixture stores authorization query parameters alongside the recording identifier, uploader, title, description, chapter names, and source webpage. Although these URLs appear time-limited and may now be expired, committing such metadata creates a repeatable disclosure pattern. Repository history and caches can preserve the values after deletion. ### Attack Path 1. An attacker obtains access to the repository, an archive, a build artifact, a log, or version-control history. 2. The attacker extracts the complete signed CDN URL from `test-output/video.info.json`. 3. If ...[truncated 880 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the fixture from the current tree and version-control history. - Invalidate or rotate exposed share links and signed resources where the provider supports it. - Replace real downloader output with synthetic fixtures. - Strip URL query strings and authorization fields before saving diagnostic metadata. - Redact recording IDs, uploader names, descriptions, titles, original URLs, and other identifying metadata. - Add secret scanning rules for signed URL parameters such as `Policy`, `Signature`, and `Key-Pair-Id`. - Add generated downloader metadata and media output directories to `.gitignore`. - Establish retention controls so temporary downloader metadata is deleted after processing. ]]>

other

Warning
Location
SKILL.md:63
Finding
Potentially Sensitive Recording Content Is Sent to an External Vision Service Without Privacy Controls<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:63-70` **Vulnerability Type**: Privacy-sensitive external data transfer **Risk Level**: Medium ### Vulnerable Instruction ```bash # The prompt is at: output/workflow-analysis-prompt.md # Attach frames from: output/frames/ # Example with Claude: cat output/workflow-analysis-prompt.md | claude --images output/frames/*.jpg ``` ### Technical Analysis The documented command sends the generated analysis prompt and all extracted screenshot frames to an external vision-model service. The prompt contains transcript-derived context, while screenshots from business-process recordings may display personal information, financial records, credentials, customer data, email, internal URLs, access tokens, or confidential application state. External vision analysis is relevant to the Skill’s declared functionality, so the transfer is not inherently malicious. However, the Skill provides no explicit confirmation step, data classification, secret or PII redaction, destination disclosure beyond the example command, retention warning, or local-only alternative. The instruction also uses a wildcard that submits every generated JPEG in the frame directory, rather than selecting only the minimum frames necessary for analysis. ### Attack Path 1. A user processes a Loom recording containing confidential screen content or narration. 2. The extraction pipeline creates screenshots and a prompt containing transcript context. 3. The user or agent follows the documented Claude command. 4. Every matching frame and the prompt are transmitted to the external model provider. 5. Sensitive content is processed and potentially retained according to provider and account policies. No additional attacker action is required; the risk arises from insufficiently disclosed and insufficiently minimized data transfer. ### Impact Assessment The transferred data can include information visible to the recording creator, including materi ...[truncated 372 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Require explicit user confirmation immediately before external upload. - Clearly identify the service receiving the data and describe exactly which prompt, transcript content, and images will be transferred. - Add automated detection and redaction for secrets, credentials, access tokens, personal data, financial data, and sensitive screen regions. - Default to local transcription and local vision analysis for confidential recordings. - Select the minimum required frames rather than uploading all files through a wildcard. - Provide a review manifest listing every file scheduled for upload. - Document applicable provider retention, training, geographic-processing, and access policies. - Allow users to configure an approved enterprise endpoint or disable external analysis entirely. - Delete temporary frames, audio, transcripts, and prompts according to a documented retention policy. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • YARA SignaturesMalware Match, Webshell Match, Cryptominer Match
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (21)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
The supplied code chunk is a media preprocessing utility, not a workflow analyzer. It transcribes audio, detects scene changes, chooses timestamps, extracts image frames, and writes an extraction manifest. While this could support a larger Loom-analysis pipeline, the code itself does not perform the declared core functions of workflow breakdown, ambiguity detection, human intervention identification, or Lobster workflow generation. There are no undeclared sensitive capabilities, but there is a material mismatch between the declared primary purpose and the actual implemented behavior.

Missing User Warnings

High
Confidence
98% confidence
Finding
The skill explicitly instructs users to send extracted frames and prompt content to an external vision model, which can disclose screen contents, transcripts, business workflows, credentials, customer information, or other sensitive data to a third party. In the context of Loom workflow analysis, this is especially dangerous because recordings often capture enterprise systems and internal procedures, making unannounced external transmission a significant confidentiality and compliance risk.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
"automation_potential": <0.0-1.0>
}
"""
    return prompt

def main():
    if len(sys.argv) < 2:
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

YARA rule 'agent_skill_mcp_tool_poisoning_metadata': MCP/tool metadata poisoning indicators in tool schemas or skill manifests [agent_skills]

High
Category
YARA Match
Content
Verificando Horas de Felipe", "end_time": 1405.0}, {"start_time": 1405.0, "title": "Ajustando Horas de Trabalho", "end_time": 1549.0}, {"start_time": 1549.0, "title": "Conferindo Total de Horas", "end_time": 1652.0}, {"start_time": 1652.0, "title": "Analisando Registro de Miami", "end_time": 3943}], "formats": [{"format_id": "hls-cdn-audio-audio", "format_note": "audio", "url": "https://luna.loom.com/id/a79ecc143cad4dae992eb366dd209edd/rev/8bfaaed66683b3471ce008927eb78f5a6519bf48b399a965ccc8bd254225ee846/resource/hls/mediaplaylist-audio.m3u8?Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9sdW5hLmxvb20uY29tL2lkL2E3OWVjYzE0M2NhZDRkYWU5OTJlYjM2NmRkMjA5ZWRkL3Jldi84YmZhYWVkNjY2ODNiMzQ3MWNlMDA4OTI3ZWI3OGY1YTY1MTliZjQ4YjM5OWE5NjVjY2M4YmQyNTQyMjVlZTg0Ni9yZXNvdXJjZS8qIiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNzcwMjU5MTM4fX19XX0_&Signature=tkrBZbATXVJQ%7E1jDhj5PmawPlHxtnYRQ8fZOJTxZy1oj%7Eh-gIUnaFYitpNilNTtiLLGSw%7Eyh9om9sSX-anObUfUwofXU5I6-QSIhQJzfzCAWyBXEruJOua%7EApF
Confidence
80% confidence
Finding
YARA rule matched a hack tool or exploit indicator (offensive tools, reconnaissance, privilege escalation, or exploit frameworks).

Intent-Code Divergence

Medium
Confidence
94% confidence
Finding
The Vision section says the agent 'autonomously' performs the full pipeline and explicitly states 'No human intervention required.' Later sections describe inserting `approve` gates, generating clarifying questions, and marking steps `requires_guidance: true`, which means human input may be required for completion. This is an active contradiction in the documented intent.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The design explicitly automates downloading Loom videos, extracting frames, transcribing audio, and analyzing on-screen content without any documented privacy, consent, retention, or sensitive-data handling controls. Because Loom recordings often contain credentials, personal data, internal business processes, and customer information, this omission can lead to unauthorized collection, persistent storage, and downstream disclosure of sensitive content.

Lp3

Medium
Category
MCP Least Privilege
Confidence
95% confidence
Finding
The skill advertises shell execution and file-writing behavior through its documented commands, but it declares no explicit tool scope or permissions boundary. That creates an authorization ambiguity where an agent or user may invoke powerful local capabilities without clear least-privilege constraints, increasing the chance of unsafe file modification or command execution.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The documentation encourages downloading and processing Loom videos, which commonly contain internal business processes, credentials, customer data, or other sensitive visual content, but provides no privacy or data-handling warning. Users may inadvertently ingest confidential material into local tooling and derived workflow artifacts without understanding retention, access, or compliance implications.

Description-Behavior Mismatch

Medium
Confidence
98% confidence
Finding
The module docstring says it 'uses multimodal LLM' to identify tools, actions, ambiguities, and build a structured workflow. In reality, the executable path in main() just reads a manifest, writes a prompt markdown file, and emits a pending placeholder JSON, while analyze_frame_batch() also returns a placeholder structure rather than invoking any model.

Missing User Warnings

Medium
Confidence
93% confidence
Finding
The script can generate a live email-sending command (`openclaw.invoke --tool message --action send`) directly from analyzed workflow content, with no explicit confirmation gate, dry-run default, or warning that external messages may actually be sent. In this skill’s context, the input is derived from Loom workflow analysis, which may be incomplete or inaccurate, so automatically turning a detected 'send email' step into an executable command increases the risk of unintended outbound communication, data leakage, or accidental spam.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
# Extract audio first
    audio_path = os.path.join(output_dir, "audio.mp3")
    subprocess.run([
        "ffmpeg", "-y", "-i", video_path,
        "-vn", "-acodec", "mp3", "-q:a", "2",
        audio_path
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
], capture_output=True)
    
    # Run whisper with JSON output
    result = subprocess.run([
        "whisper", audio_path,
        "--model", model,
        "--output_format", "json",
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The script stores transcription output containing spoken content on disk automatically, but provides no user-facing notice, consent flow, retention guidance, or option to avoid persistence. In a workflow-analysis tool for Loom recordings, transcripts may contain credentials, personal data, business secrets, or regulated information, so silent storage increases confidentiality and compliance risk.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
"""Use ffmpeg scene detection to find visual changes."""
    print(f"[scene] Detecting scene changes (threshold={threshold})...")
    
    result = subprocess.run([
        "ffmpeg", "-i", video_path,
        "-vf", f"select='gt(scene,{threshold})',showinfo",
        "-f", "null", "-"
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def extract_frame(video_path: str, timestamp: float, output_path: str) -> bool:
    """Extract a single frame at the given timestamp."""
    result = subprocess.run([
        "ffmpeg", "-y",
        "-ss", str(timestamp),
        "-i", video_path,
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

subprocess module call

Medium
Category
Dangerous Code Execution
Content
def get_video_duration(video_path: str) -> float:
    """Get video duration in seconds."""
    result = subprocess.run([
        "ffprobe", "-v", "error",
        "-show_entries", "format=duration",
        "-of", "json", video_path
Confidence
70% confidence
Finding
subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The script's CLI flow and surrounding code imply it will save an extraction manifest for the video just processed, but the manifest construction uses `video_path` and `output_dir`, which are not defined in `main()`. This contradicts the documented behavior of the tool because the advertised manifest-writing step will fail rather than recording results for `args.video_path` and `args.output_dir`.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The manifest writes frame paths, timestamps, and transcript snippets to disk without clear disclosure or controls. Given this skill’s purpose—analyzing screen recordings of business workflows—those snippets and associated frames can expose sensitive operational details, personal data, or confidential UI content long after processing is complete.

Missing User Warnings

Low
Confidence
80% confidence
Finding
The document shows agent actions and CLI commands that create files such as video.mp4, transcript.json, workflow-analysis.json, and workflow.lobster, but it does not warn users that local files will be written. For a markdown skill description, file creation affecting user storage should be disclosed when presenting operational behavior.

Intent-Code Divergence

Low
Confidence
97% confidence
Finding
The docstring for analyze_frame_batch() explicitly states 'Uses the oracle CLI for LLM calls.' However, the function only constructs a prompt and returns placeholder metadata with needs_vision_analysis=True; there is no subprocess call, API call, or oracle CLI invocation.

Natural-Language Policy Violations

Low
Confidence
92% confidence
Finding
The JSON embeds repeated HTTP header values with "Accept-Language": "en-us,en;q=0.5" on L1, which hard-codes an English locale preference. Because this is a natural-language locale setting and the file provides no opt-in or region-specific justification, it fits the language/locale policy violation category.

Static analysis

No suspicious patterns detected.