Back to skill

Security audit

Linkedin Outreach

Security checks for vulnerabilities and agentic risk

Overview

This LinkedIn outreach skill mostly does what it advertises, but it handles account credentials, stores reusable login state, and can send real outreach in bulk without enough safeguards.

Review this carefully before installing. Use it only with an account and environment where you are comfortable storing LinkedIn session cookies and outreach data locally, and manually verify every recipient and message before running connect or follow-up commands. Prefer a hardened or dedicated local user profile, update dependencies, and add owner-only permissions, deletion/logout support, CSV sanitization, and explicit send confirmations before production 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/linkedin-api.js:84
Finding
LinkedIn Session State Stored Without Restrictive File Permissions## Vulnerability Details **File Location**: `scripts/linkedin-api.js:84-89` **Vulnerability Type**: Insecure storage of reusable authentication material **Risk Level**: High **Vulnerable Code**: ```js saveSession() { if (this.context) { this.ensureConfigDir(); this.context.storageState().then(state => { fs.writeFileSync(SESSION_FILE, JSON.stringify(state)); }); } } ``` ### Technical Analysis Playwright storage state can contain authenticated LinkedIn cookies and other reusable browser authentication data. The application writes this state to `~/.config/linkedin-outreach/session.json` using `fs.writeFileSync` without specifying an owner-only file mode. When a new file is created, Node.js uses default permissions subject to the process umask. Depending on the host configuration, the session file may consequently be readable by other local users or processes. The containing configuration directory is also created without an explicit owner-only mode. No permission validation is performed when an existing session file is loaded. Possession of valid session cookies may allow authentication without knowing the account password or completing multi-factor authentication. ### Attack Path 1. The victim runs the `linkedin login` command and successfully authenticates to LinkedIn. 2. The Skill obtains the authenticated Playwright storage state. 3. The storage state is written to `~/.config/linkedin-outreach/session.json` with permissions determined by the environment's default umask. 4. Another local user or compromised process reads the session file if filesystem permissions permit it. 5. The attacker imports the captured cookies and browser state into a compatible browser context. 6. If the session remains valid and LinkedIn does not reject it, the attacker accesses the victim's LinkedIn account as the victim. ### Impact Assessment Successful exploitation could disclose reusable LinkedIn ...[truncated 486 chars]
Remediation
## Remediation Suggestions - Create `~/.config/linkedin-outreach` with owner-only permissions, such as mode `0700`. - Write `session.json` with an explicit mode of `0600`. - Write the state to a securely created temporary file, apply restrictive permissions, and atomically rename it into place. - Check and correct the permissions of existing session files before loading them. - Reject session paths that are symbolic links or otherwise resolve outside the expected configuration directory. - Add a logout or session-deletion command that securely removes stored authentication state. - Consider using an operating-system credential store or encrypted secret-storage facility instead of a plaintext JSON file. - Apply equivalent owner-only protections to `data.json`, which contains collected profile and outreach-message data.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/linkedin-api.js:310
Finding
Spreadsheet Formula Injection and Incorrect Escaping in CSV Reports## Vulnerability Details **File Location**: `scripts/linkedin-api.js:310-319` **Vulnerability Type**: CSV injection and malformed CSV generation **Risk Level**: Medium **Vulnerable Code**: ```js const headers = ['Name', 'URN', 'Profile URL', 'Message', 'Sent At']; const rows = contacts.map(c => [ c.name || '', c.urn || '', c.profileUrl || '', c.message || '', c.sentAt || '' ]); return [headers, ...rows].map(row => row.map(cell => `"${cell}"`).join(',')).join('\n'); ``` ### Technical Analysis The report generator places profile-derived and user-controlled values directly into quoted CSV cells. It does not escape embedded double-quote characters by converting each `"` to `""`. A malicious or unusual field containing quotes can therefore corrupt the generated CSV structure. More importantly, the implementation does not neutralize cells beginning with spreadsheet formula indicators such as `=`, `+`, `-`, or `@`. Quoting a value in CSV does not reliably prevent spreadsheet applications from interpreting it as a formula. Relevant input sources include names and profile metadata scraped from LinkedIn, profile URLs, locally supplied outreach messages, and previously persisted contact records. If a formula-bearing value reaches a generated report and the report is opened in spreadsheet software, the spreadsheet may evaluate it according to its security settings. ### Attack Path 1. An attacker places a formula-like value in a LinkedIn profile field that the Skill collects, or a malicious value otherwise enters the local contact data. 2. The victim searches for the profile, causing the value to be saved in `data.json`. 3. The victim generates a CSV report with the `linkedin report --format csv` command. 4. The report generator wraps the value in double quotes but does not neutralize its formula prefix or correctly escape embedded quotes. 5. The victim opens the CSV file in spreadsheet software. 6. The s ...[truncated 734 chars]
Remediation
## Remediation Suggestions - Use the declared `csv-stringify` dependency or another well-maintained CSV serializer instead of manually constructing CSV output. - Correctly escape embedded double quotes by doubling them according to CSV rules. - Convert every cell to a string before validation and serialization. - Neutralize cells whose first non-whitespace character is `=`, `+`, `-`, or `@`, for example by prefixing an apostrophe where compatible with the intended spreadsheet workflow. - Consider also treating tab, carriage-return, and line-feed prefixes as dangerous because spreadsheet import behavior varies. - Validate profile URLs and normalize all exported fields. - Add tests covering embedded quotes, commas, newlines, formula prefixes, empty values, and Unicode content. - Document that reports contain untrusted profile data and should be imported with formula evaluation disabled.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (24)

Known Vulnerable Dependency: brace-expansion==1.1.12 — 4 advisory(ies): CVE-2026-13149 (brace-expansion: DoS via exponential-time expansion of consecutive non-expanding); CVE-2026-33750 (brace-expansion: Zero-step sequence causes process hang and memory exhaustion); CVE-2026-14257 (brace-expansion: DoS via unbounded expansion length causing an out-of-memory pro) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: browserslist==4.28.1 — 2 advisory(ies): CVE-2026-73088 (Browserslist: Uncaught crash / prototype write via untrusted browserslist-stats.); CVE-2026-73089 (Browserslist: Unbounded memory growth (no cache eviction) via distinct query res)

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: fast-uri==3.1.0 — 7 advisory(ies): CVE-2026-13676 (fast-uri vulnerable to host confusion via failed IDN canonicalization); CVE-2026-18446 (fast-uri vulnerable to host confusion via backslash authority introducer); CVE-2026-75975 (fast-uri vulnerable to server-side request forgery via malformed IPv6 normalizat) +4 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: js-yaml==3.14.2 — 4 advisory(ies): CVE-2026-84375 (js-yaml: maxTotalMergeKeys does not limit CPU use for empty merge sources); CVE-2026-59869 (js-yaml: YAML merge-key chains can force quadratic CPU consumption); GHSA-5p4m-2wfm-xmqj (JS-YAML: Quadratic CPU consumption in !!omap resolution (3.x and 4.x) — CVE-2026) +1 more

High
Category
Supply Chain
Confidence
80% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Missing User Warnings

High
Confidence
97% confidence
Finding
Bulk sending magnifies the risk of unintended external actions because a single invocation can issue many connection requests automatically. In the LinkedIn automation context, this is more dangerous due to spam/abuse potential, account restrictions, and the difficulty of reversing a mistaken batch once started.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill advertises capabilities that involve environment-variable access and network interaction, but it does not declare any explicit tool scope or permissions. That weakens reviewability and containment, because operators cannot easily tell what resources the skill expects to access before use, especially in a credential-handling automation context.

Missing User Warnings

Medium
Confidence
95% confidence
Finding
The skill instructs users to provide LinkedIn credentials through environment variables for auto-login, but it does not include adequate warnings or handling guidance for sensitive secrets. In a browser automation and session-reuse workflow, poor secret hygiene can lead to credential exposure, account takeover, or leakage through logs, config files, or debugging output.

Context-Inappropriate Capability

Medium
Confidence
89% confidence
Finding
The code persists both LinkedIn session state and outreach/contact data under a user config directory on disk. While this supports automation convenience, storing authentication state and scraped/contact records locally increases exposure to credential theft, unauthorized reuse of the session, and privacy issues if the host is shared or compromised.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill writes harvested contact/outreach data to disk without any explicit user notice or consent flow. Because this data can include personal identifiers, profile URLs, and outreach history, silent persistence increases privacy, compliance, and local data exposure risks.

Missing User Warnings

Medium
Confidence
96% confidence
Finding
Persisting browser storage state to disk can capture active authenticated session tokens for LinkedIn. If another local process or user accesses that file, they may be able to hijack the LinkedIn account without needing the password or 2FA again.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The function sends a real external action—LinkedIn connection invitations—immediately once invoked, with no user confirmation or dry-run safeguard. This can cause accidental outreach, spam-like behavior, reputational harm, and potential account enforcement if triggered unintentionally or by another component.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The code automatically sends LinkedIn messages once it identifies or selects a recipient, without a final confirmation step. Automatic outbound messaging can be abused for spam or cause accidental disclosure of sensitive or incorrect content, particularly in an outreach automation skill.

Missing User Warnings

Medium
Confidence
89% confidence
Finding
The script creates a per-user config directory and usage file under the home directory and later writes usage data there without clearly informing the user. In this context, local persistence is expected for CLI tools, but undisclosed storage becomes more sensitive because the same tool also handles LinkedIn authentication/session state, making users less able to assess privacy and account-security implications.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
The CLI directly prompts for LinkedIn email and password and passes them to the automation API, but provides no clear notice about how credentials are handled, stored, or protected. In this skill context, the risk is elevated because the tool also supports session persistence and browser automation, so users may unknowingly provide high-value account credentials to software that stores or reuses access artifacts.

Known Vulnerable Dependency: @babel/core==7.29.0 — 1 advisory(ies): CVE-2026-49356 (@babel/core: Arbitrary File Read via sourceMappingURL Comment)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Known Vulnerable Dependency: baseline-browser-mapping==2.10.0 — 1 advisory(ies): CVE-2026-45819 (baseline-browser-mapping process termination on invalid input causes denial of s)

Low
Category
Supply Chain
Confidence
60% confidence
Finding
Dependency has known vulnerabilities (CVEs). Using packages with unpatched security flaws exposes the environment to known exploits.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"author": "CEO Claw",
  "license": "MIT",
  "dependencies": {
    "playwright": "^1.40.0",
    "inquirer": "^9.2.12",
    "csv-stringify": "^6.4.4",
    "conf": "^12.0.0",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"license": "MIT",
  "dependencies": {
    "playwright": "^1.40.0",
    "inquirer": "^9.2.12",
    "csv-stringify": "^6.4.4",
    "conf": "^12.0.0",
    "ora": "^7.0.1",
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"dependencies": {
    "playwright": "^1.40.0",
    "inquirer": "^9.2.12",
    "csv-stringify": "^6.4.4",
    "conf": "^12.0.0",
    "ora": "^7.0.1",
    "chalk": "^5.3.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"playwright": "^1.40.0",
    "inquirer": "^9.2.12",
    "csv-stringify": "^6.4.4",
    "conf": "^12.0.0",
    "ora": "^7.0.1",
    "chalk": "^5.3.0"
  },
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"inquirer": "^9.2.12",
    "csv-stringify": "^6.4.4",
    "conf": "^12.0.0",
    "ora": "^7.0.1",
    "chalk": "^5.3.0"
  },
  "devDependencies": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"csv-stringify": "^6.4.4",
    "conf": "^12.0.0",
    "ora": "^7.0.1",
    "chalk": "^5.3.0"
  },
  "devDependencies": {
    "jest": "^29.7.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"chalk": "^5.3.0"
  },
  "devDependencies": {
    "jest": "^29.7.0"
  },
  "engines": {
    "node": ">=18.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Context-Inappropriate Capability

Low
Confidence
75% confidence
Finding
The code accesses process.env.HOME or process.env.USERPROFILE to derive a filesystem path. For a LinkedIn outreach skill, interacting with LinkedIn is expected, but reading host environment context is an additional capability unrelated to outreach logic itself.

Static analysis

No suspicious patterns detected.