Back to skill

Security audit

LinkedIn Automation Enhanced

Security checks for vulnerabilities and agentic risk

Overview

This LinkedIn automation skill is mostly purpose-related, but it needs review because it can act on a logged-in LinkedIn session, includes under-scoped public posting workflows, targets a hard-coded profile during analytics extraction, and contains an unsafe shell reporting helper.

Review carefully before installing. Only use this skill if you are comfortable giving it access to a logged-in LinkedIn browser session, and require manual approval before any post, article, comment, like, or scheduled post is executed. The hard-coded analytics profile should be replaced with the current user's profile or an explicit user-supplied target, the Discord webhook should be documented and opt-in, and the eval-based report writer should be fixed before routine use.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/analytics-report.sh:69
Finding

Arbitrary Command Execution Through Analytics Report Generation

Content
View full analysis

Vulnerability Details

File Location: scripts/analytics-report.sh:69-78, scripts/analytics-report.sh:225-239, and scripts/analytics-report.sh:293-363
Vulnerability Type: Shell command injection through unsafe use of eval
Risk Level: High

Vulnerable Code

The retry helper accepts a command as a string and executes it with eval:

bash
retry_file_operation() {
    local attempt=1
    local operation="$1"
    local description="$2"
    
    while [[ $attempt -le $MAX_RETRIES ]]; do
        log_info "File operation attempt $attempt/$MAX_RETRIES: $description"
        
        if eval "$operation"; then
            log_info "File operation successful on attempt $attempt"
            return 0
        fi
        
        if [[ $attempt -lt $MAX_RETRIES ]]; then
            local delay=${RETRY_DELAYS[$((attempt-1))]}
            log_warn "File operation failed. Retrying in ${delay}s..."
            sleep $delay
        fi
        
        ((attempt++))
    done
    
    log_error "File operation failed after $MAX_RETRIES attempts: $description"
    return 1
}

Values are read from the analytics JSON file without restricting them to safe shell characters:

bash
read_json_field() {
    local field="$1"
    local default="$2"
    local result
    
    result=$(jq -r ".$field // \"$default\"" "$INPUT_FILE" 2>/dev/null || echo "$default")
    echo "$result"
}

TOTAL_IMPRESSIONS=$(read_json_field "summary.total_impressions" "0")
TOTAL_ENGAGEMENTS=$(read_json_field "summary.total_engagements" "0")
AVG_ER=$(read_json_field "summary.engagement_rate" "0")
AVG_CR=$(read_json_field "summary.comment_rate" "0")
EXTRACTION_STATUS=$(read_json_field "status" "unknown")
POSTS_COUNT=$(read_json_field "posts_count" "0")

The JSON-derived values are interpolated into report content and then embedded in a command string passed to the vulnerable helper: ...[truncated 2919 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove eval entirely. Write the report directly:

    bash
    printf '%s\n' "$REPORT_CONTENT" > "$OUTPUT_FILE"
    
  2. If retries are required, implement them around a normal function call rather than accepting command strings:

    bash
    write_report() {
        local output_file="$1"
        local content="$2"
        printf '%s\n' "$content" > "$output_file"
    }
    
  3. Pass operations and arguments as separate array elements. Never concatenate data into executable shell syntax.

  4. Enforce JSON types before using values. Require metric fields to be JSON numbers and restrict status to an explicit allowlist such as success, failed, or unknown.

  5. Treat analytics files as untrusted input even if they are normally generated locally. Restrict directory and file permissions so unrelated users and processes cannot modify them.

  6. Add regression tests containing shell metacharacters, command substitutions, quotes, newlines, and redirections, and verify that these values are rendered literally without execution.

other

Note
Location
scripts/analytics-extract.sh:247
Finding

Analytics Extraction Is Hard-Coded to an Unrelated LinkedIn Profile

Content
View full analysis

Vulnerability Details

File Location: scripts/analytics-extract.sh:247-261
Vulnerability Type: Undeclared third-party data collection and target-scope violation
Risk Level: Low

Vulnerable Code

bash
# Step 5: Navigate to recent activity in SAME tab (no new tab created)
log_info "5. Navegando para recent activity..."
openclaw browser navigate --targetId "$LINKEDIN_TAB_LABEL" https://www.linkedin.com/in/renatomaluhy/recent-activity/all/
sleep 5

# Human-like scroll for posts in current tab
log_info "6. Scroll para carregar posts..."
for i in 1 2 3; do
    openclaw browser evaluate --targetId "$LINKEDIN_TAB_LABEL" --fn "window.scrollBy(0, 500)" 2>/dev/null || log_warn "Post scroll $i failed (non-critical)"
    sleep 2
done

# Step 6: Capture snapshot for extraction from current tab
log_info "7. Capturando snapshot..."
SNAPSHOT=$(openclaw browser snapshot --targetId "$LINKEDIN_TAB_LABEL" --limit 200 2>&1)

Technical Analysis

The Skill is described as tracking the logged-in user’s LinkedIn presence and recent posts. However, the extractor does not resolve the current user or use LinkedIn’s /in/me/ route. It always navigates to the fixed profile renatomaluhy.

The subsequent scrolling, snapshot collection, and post parsing therefore operate on that fixed profile rather than necessarily operating on the authenticated user’s profile. This creates a mismatch between the declared analytics scope and the actual browser target.

The behavior is not hidden code execution, but it can cause users to collect and store data about an unintended third party while believing that the generated analytics apply to their own account.

Attack Path

  1. A user installs the Skill and runs the analytics extractor while authenticated to LinkedIn.
  2. The script verifies that a LinkedIn session exists.
  3. Instead of opening the authenticated user’s activity page, it navigates to the hard-coded `renat ...[truncated 970 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace the hard-coded profile URL with LinkedIn’s current-user route:

    bash
    openclaw browser navigate --targetId "$LINKEDIN_TAB_LABEL" \
      https://www.linkedin.com/in/me/recent-activity/all/
    
  2. If third-party profile analytics are an intended feature, require the target profile to be supplied explicitly by the user.

  3. Validate user-supplied profile identifiers against a strict LinkedIn profile format and reject arbitrary URLs.

  4. Display the resolved profile name and URL before extraction and require confirmation when it is not the authenticated user.

  5. Record the target profile identifier in generated analytics files so reports cannot silently misattribute the collected data.

  6. Update the Skill documentation to distinguish first-party account analytics from analysis of publicly visible third-party activity.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The code is related to LinkedIn automation, but it does not implement most of the declared feature set. Its actual purpose is narrowly focused on extracting analytics and recent post metrics from LinkedIn via browser automation, then storing structured JSON locally. It does not create content, schedule posts, publish posts, generate ideas, comment on posts, or actively build audience presence. Additionally, it includes an undeclared integration by sending alerts to a Discord webhook and writes structured outputs/logs to local storage. While engagement tracking is part of the declared description, the overall description materially overstates and misrepresents the code’s real behavior.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The declared description claims a broad LinkedIn automation suite covering posting, scheduling, analytics, idea generation, commenting, and audience growth. In contrast, this code chunk has a much narrower purpose: it accepts title/subtitle/content/image inputs and outputs step-by-step instructions for an agent to publish a LinkedIn article via the browser. Its primary behavior is article-publishing guidance, not a general LinkedIn management system. This is a material description-behavior mismatch because multiple advertised capabilities are absent, and even the posting behavior is indirect rather than implemented automation.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding

The declared description presents a broad LinkedIn automation skill for creating content, posting, scheduling, tracking engagement, generating ideas, commenting, and growing an audience using browser access. The supplied code chunk merely echoes a static workflow document describing how a user should manually engage with posts. It accepts simple positional arguments for a limit/topic and inserts them into the printed guidance, but it does not log into LinkedIn, control a browser, click buttons, post comments, collect metrics, schedule content, or generate/analyze content. This is a material description-behavior mismatch because the primary purpose and actual capabilities are much narrower and non-automated compared with the declared automation-focused functionality.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description presents a broad LinkedIn automation skill with browser-based interaction for posting, scheduling, engagement analysis, commenting, and growth. The supplied code chunk is much narrower: it is a helper script that formats a scheduled-post summary and suggests how to create an OpenClaw cron job. There is no LinkedIn API or browser automation, no actual scheduling side effect beyond printed guidance, and none of the analytics, commenting, or growth functionality described. This is a material description-to-behavior mismatch in primary purpose and capabilities.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · scripts/analytics-extract.sh (reported line 52)May include surrounding context.

sh
MAX_RETRIES=3
RETRY_DELAYS=(2 4 8)  # Exponential backoff: 2s, 4s, 8s

# Discord webhook (optional - set in .env or leave empty)
DISCORD_WEBHOOK="${DISCORD_WEBHOOK:-}"

# =============================================================================

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/article.sh (reported line 42)May include surrounding context.

sh
exit 1
fi

# Output instructions for the agent to execute via browser tool
cat << EOF
📝 LINKEDIN POST WORKFLOW
═══════════════════════════════════════

Direct Prompt Extraction

High
Category
System Prompt Leakage
Confidence
85% confidence
Finding

Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Content

Scanner excerpt · scripts/post.sh (reported line 29)May include surrounding context.

sh
exit 1
fi

# Output instructions for the agent to execute via browser tool
cat << EOF
📝 LINKEDIN POST WORKFLOW
═══════════════════════════════════════

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding

The skill embeds shell-capable command examples and operational guidance for executing scripts, but it does not declare an explicit tool scope such as allowed tools or permissions. That creates ambiguity about what the skill is authorized to do and increases the chance an agent may invoke shell capabilities more broadly than intended when operating on a logged-in browser session.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The description is broad enough to trigger on many ordinary LinkedIn-related requests, which can cause an agent to activate the skill in situations the user did not intend. In a skill that may operate against a logged-in LinkedIn session, overbroad activation increases the risk of unintended browsing, posting, commenting, or account actions.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The top-level summary lacks tight activation boundaries and presents the skill as a general-purpose LinkedIn automation tool. Because the context involves a logged-in browser session and potentially public actions, ambiguous triggering materially raises the chance of accidental or excessive account activity.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The documentation describes automated posting and article publication to LinkedIn without a strong warning that these are public, potentially irreversible actions performed from an authenticated account. In this context, a mistaken trigger or misunderstood workflow could immediately publish content to a real professional profile, causing reputational harm or unauthorized communications.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The script can transmit operational details about LinkedIn analytics failures to an external Discord webhook, which is a third-party destination outside the local workflow. Even though the message content is short, it still creates an outbound data path from a browser-automation script handling authenticated LinkedIn activity, and the alert is sent automatically without an explicit user confirmation at the call site.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
94% confidence
Finding

The curl call posts data to an externally supplied webhook URL, creating an explicit exfiltration channel from a script that operates in an authenticated LinkedIn browser context. While the current payload appears limited to failure text, any outbound network transmission from such a skill is sensitive because it can expose workflow state and can be expanded later to leak more data.

Content

Scanner excerpt · scripts/analytics-extract.sh (reported line 144)May include surrounding context.

sh
send_discord_alert() {
    local message="$1"
    if [[ -n "$DISCORD_WEBHOOK" ]]; then
        curl -s -X POST "$DISCORD_WEBHOOK" \
            -H "Content-Type: application/json" \
            -d "{\"content\": \"🚨 LinkedIn Analytics Alert: $message\"}" \
            >> /dev/null 2>&1 || true

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The script emits user-facing logs in Portuguese and relies on Portuguese UI text such as "Entrar", "Cadastre-se", "impressões", "reações", and "comentários" for control flow and extraction. This effectively forces a specific language/locale without opt-in, which matches the language-policy violation criteria.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The script emits browser-automation instructions that proceed directly to the final 'Publish' action without requiring an explicit confirmation step from the user at the point of commitment. In an agentic/browser-automation context, this increases the risk of accidental irreversible posting of public content, especially because LinkedIn articles are persistent and broadly visible.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script generates step-by-step browser automation instructions that will publish content directly to LinkedIn, but it does not present a strong, explicit warning that this action posts to an external public service and may have irreversible reputational impact. In this skill context, the risk is heightened because the tool is specifically designed to operate on a logged-in LinkedIn session, so a user or downstream agent could trigger real posting with insufficient confirmation.

Content

No source excerpt is available for this finding.

Natural-Language Policy Violations

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The JSON includes natural-language values in Portuguese such as "Últimos 7 dias" and "Versão inicial - parser completo em desenvolvimento". Because the file provides no indication that the skill is region-specific or that language selection is user-configurable, this may violate the policy against forcing a specific language or locale without opt-in.

Content

No source excerpt is available for this finding.