Back to skill

Security audit

Linkedin Ads CLI

Security checks for vulnerabilities and agentic risk

Overview

This skill is purpose-aligned for LinkedIn Ads reporting, but it needs Review because it uses LinkedIn credentials, can retrieve lead submissions, and asks users to install an unpinned global npm package.

Review before installing. Prefer a pinned, reviewed, local installation of `linkedin-ads-cli`; grant only the LinkedIn OAuth scopes needed for the task; keep tokens out of chat and logs; and retrieve raw lead-form responses only when authorized and necessary, since they may contain personal data.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (1)

T08 · Insecure Dependencies

Error
Location
SKILL.md:32
Finding

Unpinned Global Installation of an Unaudited Third-Party Package

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, lines 32-36
Vulnerability Type: Unsecured third-party dependency installation
Risk Level: High

Vulnerable Code:

markdown
If the CLI is not installed, install it:

```bash
npm install -g linkedin-ads-cli
text

### Technical Analysis

The Skill instructs the Agent to install `linkedin-ads-cli` globally from the npm registry without specifying a reviewed version, integrity hash, lockfile, or provenance requirement. Consequently, the installed code can change independently of the audited Skill.

npm packages may execute lifecycle scripts during installation. Because the command uses global installation, package code and lifecycle scripts execute with the permissions of the user running the Agent. The implementation of this package is not included in the audited project, so its installation behavior and runtime handling of credentials cannot be verified from the supplied artifact.

This installation is relevant to the Skill's functionality, but it exceeds the minimum safe approach because it implicitly trusts the latest registry release and modifies the user's global package environment. The dependency also handles a LinkedIn OAuth token resolved from `LINKEDIN_ADS_ACCESS_TOKEN`, an explicit credentials path, or `~/.config/linkedin-ads-cli/credentials.json`, increasing the potential consequences of a compromised release.

### Attack Path

1. An attacker compromises the npm package, its publisher account, or its upstream release process.
2. The attacker publishes a malicious release under the expected `linkedin-ads-cli` package name.
3. An Agent follows `SKILL.md` and runs `npm install -g linkedin-ads-cli`.
4. npm downloads the latest attacker-controlled release because no version or integrity value is pinned.
5. Malicious code executes through an npm lifecycle script during installation or when the CLI is subsequently invoked.
6. The code operates with
...[truncated 1040 chars]
Remediation
View remediation

Remediation Suggestions

  1. Pin the dependency to a specific, security-reviewed version instead of installing the latest release:
    bash
    npm install --global linkedin-ads-cli@REVIEWED_VERSION
    
  2. Verify the selected release's registry provenance, publisher identity, source repository, integrity metadata, and published package contents before approving it.
  3. Use a lockfile or independently maintained checksum where the installation workflow supports it.
  4. Prefer a project-local installation in an isolated environment over modifying the user's global npm environment.
  5. Disable npm lifecycle scripts during installation where compatible:
    bash
    npm install --ignore-scripts linkedin-ads-cli@REVIEWED_VERSION
    
    Any required lifecycle behavior should be separately reviewed before being enabled.
  6. Run the CLI under a dedicated, least-privileged account or sandbox with access only to the required credential and output locations.
  7. Grant the LinkedIn token only the OAuth scopes required for the requested operation and avoid exposing the token to unrelated processes, logs, or command output.
  8. Require restrictive filesystem permissions on ~/.config/linkedin-ads-cli/credentials.json and avoid storing tokens in broadly inherited environments.
  9. Document a trusted upgrade and re-audit process so dependency versions are changed only after review.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (7)

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
1. `--credentials <path>` flag (per-command)
2. Environment variable: `LINKEDIN_ADS_ACCESS_TOKEN`
3. Auto-detected file: `~/.config/linkedin-ads-cli/credentials.json`

Required OAuth scopes depend on the commands used:
- `r_ads` -- read ad accounts and campaigns

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
1. `--credentials <path>` flag (per-command)
2. Environment variable: `LINKEDIN_ADS_ACCESS_TOKEN`
3. Auto-detected file: `~/.config/linkedin-ads-cli/credentials.json`

Required OAuth scopes depend on the commands used:
- `r_ads` -- read ad accounts and campaigns

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
## Error handling

- **Authentication errors** -- ask the user to verify their access token and OAuth scopes
- **"No credentials found"** -- the user needs to set `LINKEDIN_ADS_ACCESS_TOKEN`, use `--credentials`, or place credentials at `~/.config/linkedin-ads-cli/credentials.json`
- **Empty analytics** -- check the date range, ensure the account had active campaigns in the period, and verify the pivot dimension is appropriate
- **Permission errors** -- different commands require different scopes (`r_ads`, `r_ads_reporting`, `r_organization_social`); check the required scopes list above

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill repeatedly describes the CLI as 'read-only,' but it also explicitly supports retrieving lead form submissions, which commonly contain personally identifiable information such as names, emails, and other submitted data. That mismatch can cause operators to underestimate the sensitivity of the data being accessed and disclose or process personal data without appropriate caution.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The documentation promotes retrieval of lead form submissions without any privacy notice, minimization guidance, or warning that the output may include personal data. In a skill context, this increases the chance that an agent will fetch and expose sensitive lead information in chat or logs without the user's informed intent.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

The authentication section instructs users to provide OAuth bearer tokens through environment variables or credential files but does not warn that these secrets must not be pasted into chat, logs, or shared transcripts. While common operational guidance, omission of safe-handling instructions in an agent-facing skill can lead to accidental credential exposure.

Content

No source excerpt is available for this finding.

Scope Creep

Low
Category
Excessive Agency
Confidence
88% confidence
Finding

The skill states that --fields and --pivot are passed directly to the LinkedIn API without validation and that additional undocumented values may be available. This expands the accessible data surface beyond what the skill documents, increasing the risk of over-collection, unexpected sensitive fields, or bypass of intended constraints through agent-supplied parameters.

Content

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

md
Default metric fields (when `--fields` is not specified): `impressions`, `clicks`, `costInLocalCurrency`, `costInUsd`, `externalWebsiteConversions`, `likes`, `comments`, `shares`, `follows`, `videoViews`

The CLI passes `--fields` and `--pivot` values directly to the LinkedIn API without validation. Additional fields and pivot values beyond the documented defaults may be available. Refer to the [LinkedIn Ad Analytics API docs](https://learn.microsoft.com/en-us/linkedin/marketing/integrations/ads-reporting/ads-reporting) for the full list.

### Audiences & targeting