Back to skill

Security audit

Openclaw Skills

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for making Instagram content, but it handles sensitive media and logged-in Instagram automation with unsafe helper scripts that need review before use.

Review before installing. Use only with trusted local media, trusted content-plan JSON, and a non-sensitive working directory. Do not run posting commands with --post unless you have inspected the preview and caption, and avoid exposing the OpenClaw CDP browser port to untrusted users. Be aware that transcription uploads audio to OpenAI and preview screenshots are written under /tmp.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/extract-frames.sh:19
Finding

Python Code Injection Through Shell-Interpolated Arguments

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/transcribe.sh:68
Finding

Python Code Injection Through Transcription Paths and API Data

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/extract-frames.sh:27
Finding

Arbitrary File Writes Through Unvalidated Frame Identifiers

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/extract-reel.sh:9
Finding

Shell Arithmetic Injection Through Reel Timestamp Arguments

Content
View full analysis
}" OUTPUT_PATH="${2:?Usage: extract-reel.sh }" START="${3:?Provide start time in seconds}" DURATION="${4:-90}" echo "==> Extracting reel: ${START}s to $((START + DURATION))s..." ``` ### Technical Analysis `START` and `DURATION` are accepted as arbitrary strings and then evaluated inside Bash arithmetic expansion. Bash arithmetic operands are expressions rather than strictly parsed numbers. Untrusted expressions can trigger recursive variable and array-index evaluation and can reach shell-expansion behavior in unsafe constructions. Quoting the later ffmpeg arguments does not mitigate the earlier arithmetic evaluation performed by `$((START + DURATION))`. ### Attack Path 1. An attacker controls a Reel start or duration value in the content plan or invocation. 2. The value is assigned to `START` or `DURATION` without numeric validation. 3. Bash evaluates the value as part of the arithmetic expression used by the status message. 4. A malicious arithmetic expression triggers unintended shell evaluation. 5. Commands execute with the privileges of the Skill runner before ffmpeg processes the media. ### Impact Assessment Successful exploitation permits command execution as the account running the Skill. This can expose environment variables, API credentials, local files, generated media, and other resources available to the Agent. ]]>
Remediation
View remediation
&2 exit 1 } [[ "$DURATION" =~ ^[0-9]+$ ]] || { echo "Invalid duration" >&2 exit 1 } ``` - Pass validated values to ffmpeg as separate quoted arguments. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/generate-carousel.js:68
Finding

Carousel Identifier Path Traversal

Content
View full analysis
({ id: c.id, slideCount: c.slides.length, dir: path.join(slidesDir, c.id), slides: c.slides.map((s, i) => path.join(slidesDir, c.id, `slide_${String(i + 1).padStart(2, '0')}.png`) ) })); ``` ### Technical Analysis The content plan's `carousel.id` is used as a directory component without validation or a containment check. An ID containing sufficient `../` components can cause `path.join` to resolve the carousel directory outside `outputDir/slides`. The generator then creates that directory recursively and writes PNG files into it. The generated manifest also records the escaped paths as if they were legitimate outputs. ### Attack Path 1. An attacker supplies or influences `content-plan.json`. 2. A carousel ID contains parent-directory traversal components. 3. The generator joins that ID to `slidesDir`. 4. The resulting path escapes the intended output directory. 5. Directories and generated slide files are written to the attacker-selected writable location. ### Impact Assessment The attacker can create directories and overwrite predictably named PNG files outside the intended episode workspace. This can corrupt other project outputs, poison files consumed by later automation, or wr ...[truncated 47 chars]
Remediation
View remediation

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/capture-instagram-preview.js:38
Finding

Instagram-Origin Validation Bypass for CDP Target Selection

Content
View full analysis
= 0 ? process.argv[targetIndex + 1] : ''; const cdpUrlIndex = process.argv.indexOf('--cdp-url'); const cdpUrl = cdpUrlIndex >= 0 ? process.argv[cdpUrlIndex + 1] : 'http://127.0.0.1:18800'; ``` ```javascript async function resolvePreviewPage(browser) { if (targetId) { const targeted = await findPageByTargetId(browser, targetId); if (targeted) { return targeted; } } for (const context of browser.contexts()) { for (const page of context.pages()) { try { const state = await readState(page); if (!state.url.startsWith('https://www.instagram.com/')) { continue; } if ( state.textareaCount || state.editableCount || state.hasShare || state.hasNext ) { return page; } } catch {} } } return null; } ``` ```javascript const browser = await chromium.connectOverCDP(cdpUrl); try { const page = await resolvePreviewPage(browser); if (!page) { throw new Error('Could not find an Instagram composer page to capture.'); } await page.bringToFront(); await page.waitForTimeout(500); const screenshotPath = path.join( '/tmp', `ig-preview-${Date.now()}.png` ); await page.screenshot({ path: screenshotPath }); console.log(JSON.stringify({ ok: true, screenshotPath, state: await readState(page) }, null, 2)); } ``` The video-draft helper repeats the same trust pattern: ```javascript async function resolveDraftPage(browser) { if (targetId) { const targeted = await findPageB ...[truncated 1653 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/post-to-instagram.js:789
Finding

Instagram Publishing Proceeds When Preview Capture Fails

Content
View full analysis
Capturing preview screenshot...'); const preview = capturePreview(targetId); const screenshot = preview.ok ? preview.screenshot : null; if (screenshot) { console.log(` Preview: ${screenshot}`); } else { console.log(` Preview capture skipped: ${preview.error}`); } if (!doPost) { console.log('\n========================================'); console.log('==> STOPPED BEFORE POSTING'); console.log('==> Review the preview screenshot path above'); console.log('==> Run with --post flag to actually publish'); console.log('========================================'); return; } console.log('==> Waiting for Share/Post button...'); browser( ['wait', '--text', 'Share', '--timeout-ms', '60000'], { allowFailure: true } ); console.log('==> Publishing...'); const shareResult = clickShare(); console.log(` Share result: ${shareResult}`); if (shareResult === 'not-found') { throw new Error( 'Could not find Share/Post/Publish button in the Instagram composer.' ); } ``` ### Technical Analysis Preview capture failure is logged but is not treated as a blocking condition. If the caller supplies `--post`, the script continues to the authenticated Instagram Share action even when no preview screenshot was produced. This conflicts with the Skill's strict workflow requiring a preview and explicit approval before posting. The script treats possession of a command-line flag as sufficient authorization and does not bind approval to the exact caption, media files, account, or preview. ### Attack Path 1. A caller invokes the script with `--post`. 2. Preview capture fails because of a helper error, target mismatch, filesystem error, or browser-state issue. 3. The script logs that the preview was skipped. 4. Execution continues bec ...[truncated 449 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (20)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises handling YouTube/webpage URLs and generating multiple Instagram asset types, but the detected behavior for this portion appears limited and does not support those claims. Security-wise, this is risky because users may authorize broader access or supply sensitive sources expecting deterministic processing, while the actual code path may behave differently or fail into ad hoc manual/browser-assisted flows that are less controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The skill advertises handling YouTube/webpage URLs and generating multiple Instagram asset types, but the detected behavior for this portion appears limited and does not support those claims. Security-wise, this is risky because users may authorize broader access or supply sensitive sources expecting deterministic processing, while the actual code path may behave differently or fail into ad hoc manual/browser-assisted flows that are less controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises handling YouTube/webpage URLs and generating multiple Instagram asset types, but the detected behavior for this portion appears limited and does not support those claims. Security-wise, this is risky because users may authorize broader access or supply sensitive sources expecting deterministic processing, while the actual code path may behave differently or fail into ad hoc manual/browser-assisted flows that are less controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding

The skill advertises handling YouTube/webpage URLs and generating multiple Instagram asset types, but the detected behavior for this portion appears limited and does not support those claims. Security-wise, this is risky because users may authorize broader access or supply sensitive sources expecting deterministic processing, while the actual code path may behave differently or fail into ad hoc manual/browser-assisted flows that are less controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The skill advertises handling YouTube/webpage URLs and generating multiple Instagram asset types, but the detected behavior for this portion appears limited and does not support those claims. Security-wise, this is risky because users may authorize broader access or supply sensitive sources expecting deterministic processing, while the actual code path may behave differently or fail into ad hoc manual/browser-assisted flows that are less controlled.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The skill advertises handling YouTube/webpage URLs and generating multiple Instagram asset types, but the detected behavior for this portion appears limited and does not support those claims. Security-wise, this is risky because users may authorize broader access or supply sensitive sources expecting deterministic processing, while the actual code path may behave differently or fail into ad hoc manual/browser-assisted flows that are less controlled.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Script: `scripts/generate-carousel.js`

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
Script: `scripts/post-to-instagram.js`

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · scripts/transcribe.sh (reported line 48)May include surrounding context.

sh
ffmpeg -y -i "$AUDIO_FILE" -ss "$START" -t "$CHUNK_DURATION" -acodec libmp3lame -ar 16000 -ac 1 -b:a 64k "$CHUNK_FILE" 2>/dev/null
        
        # Transcribe chunk
        RESPONSE=$(curl -s https://api.openai.com/v1/audio/transcriptions \
            -H "Authorization: Bearer $OPENAI_API_KEY" \
            -F file="@$CHUNK_FILE" \
            -F model="whisper-1" \

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · scripts/transcribe.sh (reported line 98)May include surrounding context.

sh
else
    echo "==> Audio file is ${FILESIZE} bytes, transcribing in one shot..."
    
    RESPONSE=$(curl -s https://api.openai.com/v1/audio/transcriptions \
        -H "Authorization: Bearer $OPENAI_API_KEY" \
        -F file="@$AUDIO_FILE" \
        -F model="whisper-1" \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The README explicitly describes Instagram draft automation using a logged-in browser session, which means the skill can perform authenticated actions on the user's account. Failing to clearly warn that the automation may post, upload, or otherwise affect the account reduces informed consent and can lead users to run account-impacting actions they did not fully expect.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
83% confidence
Finding

The skill describes shell execution and file-writing behavior but does not declare any explicit tool scope or allowed-tools boundary. That makes the effective execution surface broader and less auditable, increasing the risk of unintended command execution, filesystem modification, and abuse if the skill is invoked with untrusted inputs or implemented differently than documented.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The script captures screenshots of the Instagram composer and stores them in /tmp, which may include captions, media previews, account details, and other sensitive user content. In a multi-process or shared environment, these artifacts can persist longer than expected and be accessible to other users, logs, backup systems, or later forensic collection, creating unnecessary data exposure.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

When the script is run with --post, it performs the final Instagram share action immediately through browser automation, with no in-script confirmation, dry-run guard, or explicit acknowledgement step. In this skill's context, that is more dangerous because the tool is specifically designed to transform and publish social media content, so a mistaken flag, mis-targeted browser tab, or prompt injection in upstream workflow could cause unintended public posting from a real account.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

This is a true external-transmission finding because the code sends audio chunks to api.openai.com. In the context of a media-processing skill, this behavior is expected functionally, but it still has security significance because it exports potentially sensitive content off-host to a third party.

Content

Scanner excerpt · scripts/transcribe.sh (reported line 48)May include surrounding context.

sh
ffmpeg -y -i "$AUDIO_FILE" -ss "$START" -t "$CHUNK_DURATION" -acodec libmp3lame -ar 16000 -ac 1 -b:a 64k "$CHUNK_FILE" 2>/dev/null
        
        # Transcribe chunk
        RESPONSE=$(curl -s https://api.openai.com/v1/audio/transcriptions \
            -H "Authorization: Bearer $OPENAI_API_KEY" \
            -F file="@$CHUNK_FILE" \
            -F model="whisper-1" \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script uploads user-provided audio content to OpenAI's external transcription API without any explicit notice, consent check, or privacy gating in the script itself. Because podcast/video episodes may contain sensitive, copyrighted, or private speech, silent remote transmission creates a real data-exposure risk even if the destination service is legitimate.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
90% confidence
Finding

The one-shot transcription branch also performs external transmission of user audio to OpenAI. While not inherently malicious, this is a real privacy and compliance concern because the transfer is automatic and may surprise users who expect local processing.

Content

Scanner excerpt · scripts/transcribe.sh (reported line 98)May include surrounding context.

sh
else
    echo "==> Audio file is ${FILESIZE} bytes, transcribing in one shot..."
    
    RESPONSE=$(curl -s https://api.openai.com/v1/audio/transcriptions \
        -H "Authorization: Bearer $OPENAI_API_KEY" \
        -F file="@$AUDIO_FILE" \
        -F model="whisper-1" \

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

In the single-shot path, the script sends the entire extracted audio file to a remote API without any explicit warning or consent mechanism. This is materially the same privacy/security issue as the chunked path: user content is externally transmitted, which is risky in a skill intended to process full podcast/video episodes that may contain sensitive material.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 17)May include surrounding context.

json
],
  "license": "MIT",
  "dependencies": {
    "canvas": "^3.2.3"
  }
}

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The script reads and uses the OPENAI_API_KEY environment variable to authenticate outbound API requests. While the header comment lists the variable as required, it does not provide a clear user-facing warning that a credential will be used for external transmission to a third-party service.

Content

No source excerpt is available for this finding.