Back to skill

Security audit

Candidate Sourcing

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent LinkedIn candidate-sourcing integration, but its setup guidance includes broader-than-needed remote tooling and an unpinned npm command that deserves review before use.

Review the Veezee service and npm package before installing. Prefer the narrower LinkedIn MCP endpoint or direct API-key minting, avoid the all-tools endpoint unless needed, and do not run unpinned `npx @veezee/sdk init` in a sensitive environment without pinning and reviewing the package first.

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

Warning
Location
SKILL.md:15
Finding

Unpinned Third-Party Package Download and Execution

Content
View full analysis

Vulnerability Details

File Location: SKILL.md, line 15
Vulnerability Type: Unpinned npm dependency execution through npx
Risk Level: Medium

Vulnerable Code Snippet:

markdown
- SDK: `import { VeezeeClient } from "@veezee/sdk"`; `const client = new VeezeeClient(); await client.mint();` mints and stores the free key the first time and reuses it on later runs. Platform methods live on the namespace (`client.linkedin.getProfile/searchPeople/getCompany/getPosts`); `client.resolveUrl` and `client.getUsage` are top-level. The client sends retries and Idempotency-Keys for you. The `veezee` CLI (`npx @veezee/sdk init`) mints and stores the same key.

Technical Analysis

The instruction npx @veezee/sdk init resolves and executes an npm package without specifying an exact version, lockfile, or integrity value. If the package is absent from the local cache, npx may retrieve it from the configured npm registry and immediately execute its CLI entry point. npm installation lifecycle scripts or the invoked CLI can run code with the permissions of the user executing the command.

Because the package version is not pinned, the code executed during a future Skill invocation may differ from the code available when the Skill was audited. A compromised publisher account, malicious package release, registry compromise, or compromised transitive dependency could therefore convert this documented setup operation into arbitrary local code execution.

No evidence establishes that the current @veezee/sdk package is malicious. The confirmed issue is the unsafe, unpinned download-and-execute pattern and its resulting supply-chain exposure.

Attack Path

  1. An attacker compromises the @veezee/sdk publishing account, its release pipeline, or a dependency incorporated into a later package release.
  2. The attacker publishes a malicious package version under the expected package name.
  3. A user or agent follows the setup instructions and runs `npx ...[truncated 1273 chars]
Remediation
View remediation

Remediation Suggestions

  1. Replace the unversioned invocation with an exact, reviewed release, for example:

    bash
    npx --yes @veezee/sdk@<audited-exact-version> init
    
  2. Record the dependency in a package manifest and lockfile rather than resolving an unspecified release during execution:

    bash
    npm install --save-exact @veezee/sdk@<audited-exact-version>
    npm ci
    npx --no-install veezee init
    
  3. Commit and review the generated lockfile. In CI, use npm ci so installation fails when the lockfile and manifest disagree.

  4. Verify package provenance, publisher identity, integrity metadata, release signatures where available, and transitive dependencies before approving a version.

  5. Configure trusted registries explicitly and enforce organizational package allowlists where possible.

  6. Disable lifecycle scripts during installation when they are not required, and review any scripts before enabling them:

    bash
    npm ci --ignore-scripts
    
  7. Run initialization under a least-privileged account or isolated environment without unrelated credentials or sensitive filesystem access.

  8. Prefer the documented direct API-key minting flow when CLI execution is unnecessary, reducing the need to download and execute third-party package code during setup.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (2)

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

md
Every call needs an API key. Minting one is free and human-free: no signup, no card. A call without a key fails with `KEY_REQUIRED` (401) carrying `mint_url`; the fix is the free mint below, never a payment. A fresh `vz_trial_` key runs under a free per-IP daily budget of 200 credits, cached data only, first page only on any cursor sequence. Two equivalent surfaces; pick whichever your environment has:

- MCP: add the server `https://mcp.veezee.io/linkedin` (streamable-http; `https://mcp.veezee.io/all` exposes every tool). Hosts that support MCP authorization (Claude Code, claude.ai) open a Veezee sign-in on connect (email code, no password): that is the whole auth step. Other hosts: mint a key with `POST https://api.veezee.io/v1/keys/mint` (empty body; the key is shown once) and put it in the connection's `Authorization: Bearer` header.
- SDK: `import { VeezeeClient } from "@veezee/sdk"`; `const client = new VeezeeClient(); await client.mint();` mints and stores the free key the first time and reuses it on later runs. Platform methods live on the namespace (`client.linkedin.getProfile/searchPeople/getCompany/getPosts`); `client.resolveUrl` and `client.getUsage` are top-level. The client sends retries and Idempotency-Keys for you. The `veezee` CLI (`npx @veezee/sdk init`) mints and stores the same key.

When the free budget or a trial cap runs out (`TRIAL_CAP_EXCEEDED`, `INSUFFICIENT_CREDITS`, `BUDGET_EXHAUSTED`), the error carries `upgrade_url` (https://veezee.io/upgrade) and a machine-readable `offer`. Hand that link to your human; purchases credit the same key directly and it keeps working unchanged. Note: one search plus a couple of profile fetches makes a real dent in the free daily budget, so sourcing runs at any real volume need purchased credits.

Rp1

Medium
Category
MCP Rug Pull
Confidence
93% confidence
Finding

The skill instructs users to run npx @veezee/sdk init without pinning a specific package version. That creates a supply-chain risk because the latest published package is fetched at execution time, so a compromised publisher account or malicious update could execute arbitrary code in the user's environment.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.