Back to skill

Security audit

Linkedin Monitor

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent LinkedIn inbox monitor, but it asks for persistent access to a logged-in LinkedIn account and external messaging channels with under-scoped safeguards for credentials, automatic actions, and untrusted message content.

Review before installing. Use only a dedicated LinkedIn browser profile, avoid autonomous levels 2 and 3 unless you accept messages being sent as you, and do not store LinkedIn session cookies in plaintext. Confirm exactly which alert channel receives private message previews and drafts, and disable cron/remove local state when you no longer need monitoring.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/lk.py:145
Finding
LinkedIn Session Cookies Stored in Plaintext Without Enforced Access Controls<![CDATA[ ## Vulnerability Details **File Location**: `scripts/lk.py:145-158` **Vulnerability Type**: Plaintext credential storage with unsafe default file permissions **Risk Level**: High ### Vulnerable Code ```python li_at = input("Enter li_at cookie value: ").strip() jsessionid = input("Enter JSESSIONID cookie value: ").strip() # Save to config config_dir = os.path.expanduser('~/.clawdbot/linkedin-monitor') os.makedirs(config_dir, exist_ok=True) config_path = os.path.join(config_dir, 'credentials.json') with open(config_path, 'w') as f: json.dump({ 'li_at': li_at, 'jsessionid': jsessionid.strip('"'), 'updated_at': datetime.now().isoformat() }, f, indent=2) ``` ### Technical Analysis The Skill stores the LinkedIn `li_at` and `JSESSIONID` authentication cookies directly in `~/.clawdbot/linkedin-monitor/credentials.json`. These cookies are reusable authentication credentials capable of granting access to the user's LinkedIn session. The file is created with Python's default `open()` behavior. Neither the containing directory nor the credential file is assigned an explicit restrictive permission mode. Their effective permissions therefore depend on the process umask. Under a permissive or commonly used umask, other local users or processes may be able to read the credential file. The credentials are also stored without encryption, operating-system keychain integration, ownership validation, permission validation, or expiration management. Related private information may also be written to `state/drafts.json` by `scripts/state.sh:82-100`, including inbound messages and drafted replies, without explicit permission hardening. ### Attack Path 1. The user runs the interactive authentication setup. 2. The Skill writes valid LinkedIn session cookies to `~/.clawdbot/linkedin-monitor/credentials.json`. 3. The resulting permissions are inherited from the current umask rather than being explicitly restricted. 4. A malicious local ...[truncated 1014 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store authentication cookies in an operating-system credential manager, such as macOS Keychain, Windows Credential Manager, or a Linux Secret Service provider. 2. If file storage is unavoidable: - Create `~/.clawdbot/linkedin-monitor` with mode `0700`. - Create the credential file atomically with mode `0600`. - Verify that the file is owned by the current user. - Refuse to use the file if group or other permissions are present. 3. Set a restrictive umask, such as `0o077`, before creating any directory or file containing credentials or private message data. 4. Apply the same permission controls to state, draft, configuration, and log files that may contain private LinkedIn information. 5. Avoid writing temporary copies of sensitive files outside a protected directory. 6. Provide a command that securely deletes stored credentials and instructs the user to revoke the LinkedIn session. 7. Prefer short-lived authentication mechanisms where available and document that these cookies provide account-level access. ]]>

T01 · Skill Instruction Hijacking

Error
Location
CRON-PAYLOAD.md:12
Finding
Indirect Prompt Injection Through Attacker-Controlled LinkedIn Messages<![CDATA[ ## Vulnerability Details **File Location**: `CRON-PAYLOAD.md:12-30` **Vulnerability Type**: Untrusted webpage and message content processed as Agent instructions **Risk Level**: High ### Vulnerable Instructions ```text STEP 2: Check LinkedIn - Use browser tool (profile: clawd) - DO NOT CLOSE IT - Navigate to linkedin.com/messaging/ if not already there - Take snapshot STEP 3: Parse conversations - Extract each conversation from snapshot - For each: name, last message preview, timestamp, isFromMe - Identify INBOUND messages (where last message is NOT from me) STEP 4: Compare against state - For each inbound message, create unique ID: {name}_{timestamp} - Check if ID exists in seenIds - Collect only NEW messages (ID not in seenIds) STEP 5: If NEW messages found - Draft reply for each using USER.md communication style - Read alertChannel and alertTarget from ~/.clawdbot/linkedin-monitor/config.json - Post to the configured channel (Discord, Telegram, Slack, WhatsApp, etc.): ``` A substantially similar Agent instruction is emitted by `scripts/check-browser.sh:36-49`: ```sh cat << 'EOF' LINKEDIN_BROWSER_CHECK Instructions for Clawdbot: 1. Use browser tool (profile: clawd) to navigate to linkedin.com/messaging/ 2. Take snapshot 3. Parse conversation list for inbound messages (not from me) 4. Compare against state file: ~/.clawdbot/linkedin-monitor/state/messages.json 5. For each NEW inbound message: - Extract: name, message preview, timestamp - Add to results - Mark as seen in state file 6. If new messages found: - Draft replies using USER.md communication style - Post to #linkedin channel 7. Update lastCheck timestamp in state file EOF ``` ### Technical Analysis LinkedIn participant names, profiles, and message bodies are controlled by remote LinkedIn users. The Skill instructs an AI Agent to take a browser snapshot, interpret the snapshot, read `USER.md`, access local state and configuration, and use the messaging tool. The instr ...[truncated 2590 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add an explicit instruction that all LinkedIn pages, profiles, names, messages, previews, and snapshots are untrusted data. 2. State that the Agent must never follow commands, policies, tool requests, links, or file-access instructions found in LinkedIn content. 3. Parse browser output into a strict schema containing only required fields, with length and type limits. 4. Perform reply drafting in an isolated step that has no browser, filesystem, cron, or messaging tools. 5. Quote or delimit message text clearly and instruct the model to transform it only into a draft. 6. Require explicit user approval before every outbound LinkedIn message, destination change, file read outside an allowlist, or state-changing action beyond recording a message identifier. 7. Restrict readable files to the minimum required configuration and communication-style data. Do not make unrelated Agent memory or workspace files available. 8. Validate the configured alert channel independently of any data extracted from LinkedIn. 9. Sanitize outbound previews and prevent mentions, links, formatting, or control tokens from triggering behavior in downstream messaging systems. 10. Add adversarial tests using messages that contain instruction-override, data-exfiltration, tool-use, and state-manipulation prompts. ]]>

T08 · Insecure Dependencies

Warning
Location
SKILL.md:150
Finding
Unpinned and Globally Installed Third-Party Dependencies<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:150-153` **Vulnerability Type**: Unpinned dependency installation and mutable executable resolution **Risk Level**: Medium ### Vulnerable Dependency Instructions ```markdown ## Dependencies - `lk` CLI (LinkedIn CLI) — `npm install -g lk` - `jq` (JSON processor) — `brew install jq` ``` The bundled Python implementation similarly recommends an unpinned package at `scripts/lk.py:11-17`: ```python try: from linkedin_api import Linkedin except ImportError: print(json.dumps({"error": "linkedin-api not installed", "action": "pip3 install linkedin-api"})) sys.exit(1) ``` The runtime resolves `lk` from the ambient `PATH` at `scripts/check.sh:47-55`: ```sh if ! command -v lk &> /dev/null; then log "ERROR" "lk CLI not installed" echo '{"error": "lk CLI not installed", "action": "install lk: npm install -g lk"}' exit 1 fi if ! lk profile me --json 2>/dev/null | jq -e '.id' &>/dev/null; then ``` ### Technical Analysis The installation guidance identifies npm and pip packages only by mutable package name. It provides no exact version, lockfile, integrity hash, verified publisher identity, or reproducible environment. The npm package is installed globally, increasing its exposure and making it available outside this Skill. The implementation also contains an architectural inconsistency: documentation directs users to install the npm `lk` CLI, while `scripts/lk.py` uses the separate Python `linkedin-api` package. The packaged Python file is not explicitly bound to the `lk` command used by `check.sh`. At runtime, `check.sh` invokes whichever executable named `lk` appears first in the cron process's `PATH`. A compromised package release, package-name confusion, or local PATH-precedence attack could therefore substitute attacker-controlled behavior. ### Attack Path #### Package Supply-Chain Path 1. A user follows the documented `npm install -g lk` or suggested `pip3 install lin ...[truncated 1693 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Pin every dependency to a reviewed exact version. 2. Use lockfiles and registry-provided integrity hashes. 3. Verify package publisher identity and link dependencies to a specific reviewed source repository. 4. Avoid global npm and system-wide pip installation. 5. Install dependencies in a project-local virtual environment or other isolated runtime. 6. Invoke the dependency through a project-controlled absolute path rather than relying on ambient `PATH` resolution. 7. Reconcile the npm `lk` dependency with the bundled Python implementation and document one authoritative execution path. 8. Declare Python and npm dependencies in machine-readable manifests with reproducible installation procedures. 9. Disable lifecycle scripts during installation where feasible, or review required lifecycle scripts before allowing them. 10. Run the monitoring process under a dedicated, minimally privileged account with access only to the required browser profile, configuration, and state files. ]]>
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
  • Memory PoisoningPersistent Context Injection, Context Window Stuffing, Memory Manipulation
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
Findings (44)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
clawdhub uninstall linkedin-monitor

# (Optional) Remove data
rm -rf ~/.clawdbot/linkedin-monitor
```

---
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tool Parameter Abuse

High
Category
Tool Misuse
Content
clawdhub uninstall linkedin-monitor

# (Optional) Remove data
rm -rf ~/.clawdbot/linkedin-monitor
```

---
Confidence
90% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding
If the real behavior includes interactive credential harvesting or storage that is not disclosed in the description, that is a serious trust and transparency issue. Users may provide credentials under false assumptions about the skill’s scope, and undocumented account-access flows materially increase the risk of credential mishandling or abuse.

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding
If the real behavior includes interactive credential harvesting or storage that is not disclosed in the description, that is a serious trust and transparency issue. Users may provide credentials under false assumptions about the skill’s scope, and undocumented account-access flows materially increase the risk of credential mishandling or abuse.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
If the real behavior includes interactive credential harvesting or storage that is not disclosed in the description, that is a serious trust and transparency issue. Users may provide credentials under false assumptions about the skill’s scope, and undocumented account-access flows materially increase the risk of credential mishandling or abuse.

Memory Manipulation

High
Category
Memory Poisoning
Content
linkedin-monitor status     # Show current state
linkedin-monitor config     # View/edit configuration
linkedin-monitor logs       # View recent activity
linkedin-monitor reset      # Clear state (start fresh)
```

## Configuration
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
linkedin-monitor status     # Show current state
linkedin-monitor config     # View/edit configuration
linkedin-monitor logs       # View recent activity
linkedin-monitor reset      # Clear state (start fresh)
```

## Configuration
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Memory Manipulation

High
Category
Memory Poisoning
Content
linkedin-monitor status     # Show current state
linkedin-monitor config     # View/edit configuration
linkedin-monitor logs       # View recent activity
linkedin-monitor reset      # Clear state (start fresh)
```

## Configuration
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Missing User Warnings

High
Confidence
97% confidence
Finding
The guide says the alert includes a draft reply and that the user can reply with a command to approve and send, but it does not prominently warn that the skill can act on the user's behalf on LinkedIn. In an agent skill with progressive autonomy, insufficient disclosure of outbound autonomous or semi-autonomous messaging materially increases the risk of unauthorized, mistaken, or reputation-damaging communications.

Memory Manipulation

High
Category
Memory Poisoning
Content
# Check what the monitor sees
linkedin-monitor check --debug

# If needed, reset state
linkedin-monitor reset
```
Confidence
80% confidence
Finding
Skill manipulates agent memory, state, or stored context. Memory corruption can alter personality, override safety rules, or cause unpredictable behavior.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```bash
linkedin-monitor disable
linkedin-monitor reset
rm -rf ~/.clawdbot/linkedin-monitor
linkedin-monitor setup
linkedin-monitor enable
```
Confidence
99% confidence
Finding
This finding correctly identifies a destructive shell command operating on a user directory subtree. Even though the path is more specific than 'rm -rf ~', documenting such a command without strong guardrails is risky because copy-paste execution in support workflows can cause permanent deletion of configuration, logs, and state.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
```bash
linkedin-monitor disable
linkedin-monitor reset
rm -rf ~/.clawdbot/linkedin-monitor
linkedin-monitor setup
linkedin-monitor enable
```
Confidence
99% confidence
Finding
This finding correctly identifies a destructive shell command operating on a user directory subtree. Even though the path is more specific than 'rm -rf ~', documenting such a command without strong guardrails is risky because copy-paste execution in support workflows can cause permanent deletion of configuration, logs, and state.

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
# This script is called by the cron, which then invokes Clawdbot
# to do the browser work and parse results

# Output instruction for Clawdbot cron
cat << 'EOF'
LINKEDIN_BROWSER_CHECK
Instructions for Clawdbot:
Confidence
96% confidence
Finding
The script outputs a prompt-like instruction block for an agent to browse LinkedIn, parse messages, update state, draft replies in the user's voice, and post results. This is a true prompt-execution surface because unstructured instructions are being handed to downstream automation with authority to access external content and perform write actions, increasing the risk of prompt injection, unintended data extraction, and unauthorized autonomous messaging.

Credential Access

High
Category
Privilege Escalation
Content
if not li_at or not jsessionid:
        # Try loading from config
        config_path = os.path.expanduser('~/.clawdbot/linkedin-monitor/credentials.json')
        if os.path.exists(config_path):
            with open(config_path) as f:
                creds = json.load(f)
Confidence
90% confidence
Finding
This code reads authentication cookies from a local credentials file, which is credential access behavior even if done for the tool's stated purpose. In the context of a monitoring skill, consuming stored browser-session material is dangerous because any code path that can access the file can impersonate the user on LinkedIn.

Credential Access

High
Category
Privilege Escalation
Content
config_dir = os.path.expanduser('~/.clawdbot/linkedin-monitor')
    os.makedirs(config_dir, exist_ok=True)
    
    config_path = os.path.join(config_dir, 'credentials.json')
    with open(config_path, 'w') as f:
        json.dump({
            'li_at': li_at,
Confidence
98% confidence
Finding
This code persists LinkedIn session cookies to a local credentials file, creating a durable stash of reusable authentication material. Because these values are sufficient to act as the user, file theft or accidental exposure can directly lead to account takeover and unauthorized message access.

Session Persistence

Medium
Category
Rogue Agent
Content
- Identify INBOUND messages (where last message is NOT from me)

STEP 4: Compare against state
- For each inbound message, create unique ID: {name}_{timestamp}
- Check if ID exists in seenIds
- Collect only NEW messages (ID not in seenIds)
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.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The instructions direct the agent to forward message previews and drafted replies derived from private LinkedIn conversations to external messaging platforms, but they do not include a clear privacy warning or explicit consent step. This can leak personal, confidential, or regulated content into third-party systems with different retention, access control, and logging practices.

Description-Behavior Mismatch

Medium
Confidence
95% confidence
Finding
The skill description frames behavior as inbox monitoring and drafting, but the cron payload broadens data delivery to arbitrary third-party channels like Discord, Telegram, Slack, and WhatsApp. That scope expansion increases data exposure risk because LinkedIn message content and AI-generated replies are transmitted outside LinkedIn without clear limitation, creating an unexpected data-sharing surface.

Intent-Code Divergence

Medium
Confidence
89% confidence
Finding
The opening description emphasizes inbox monitoring and notifications, but later sections describe drafting replies, auto-replying, and booking meetings. This mismatch can mislead users about the skill's actual capabilities and trust boundary, especially because it operates on a logged-in browser session tied to a real LinkedIn account.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The README advertises hourly monitoring, draft generation, and approval workflows without a clear privacy warning that the skill continuously accesses LinkedIn message content. Users may not realize that private inbox data is being repeatedly read, processed, stored in state/logs, and potentially forwarded to external alerting channels.

Intent-Code Divergence

Medium
Confidence
92% confidence
Finding
The README claims 'nothing sent without your OK' while also documenting autonomy levels that can automatically reply and book meetings. This is dangerous because users may enable the skill under a false assumption that outbound actions always require approval, leading to unintended communications from their account.

Session Persistence

Medium
Category
Rogue Agent
Content
## Setup Guide

### Step 1: Create a Browser Profile

LinkedIn Monitor uses Clawdbot's browser tool to check your inbox. You need a browser profile that's logged into LinkedIn.
Confidence
84% confidence
Finding
The skill depends on a browser profile remaining logged into LinkedIn and staying open, which creates persistent authenticated session exposure. If the local environment, browser profile, or automation tooling is compromised, an attacker could inherit access to the user's LinkedIn account and messages.

Session Persistence

Medium
Category
Rogue Agent
Content
Drafts are generated using your communication style from `USER.md` in your Clawdbot workspace.

If you don't have a `USER.md`, create one with your preferences:

```markdown
# USER.md
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.

Lp3

Medium
Category
MCP Least Privilege
Confidence
73% confidence
Finding
The skill advertises behavior that would require filesystem access and likely environment interaction, but it does not declare any explicit tool scope or permissions boundary. That creates ambiguity about what the skill is allowed to access and makes it harder for a runtime or reviewer to enforce least privilege, increasing the chance of overbroad file writes or secret exposure.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill supports autonomous sending and booking actions affecting a user's LinkedIn account, calendar, and professional reputation, but the description does not prominently warn about those consequences. Users may enable higher autonomy without understanding that external communications and meetings could be initiated on their behalf.

Static analysis

No suspicious patterns detected.