Back to skill

Security audit

Npm Publish

Security checks for vulnerabilities and agentic risk

Overview

This npm publishing skill is purpose-related, but it asks the agent to handle raw npm credentials and create a persistent write-capable npm token on disk.

Review carefully before installing. This skill should not be used unless you are comfortable with an agent accessing npm credentials, automating login, creating a write-capable npm token, and modifying `~/.npmrc`. Prefer native `npm login`, avoid putting passwords in generated scripts, use scoped or temporary tokens, preserve existing npm config, and revoke tokens after publishing.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:139
Finding

Overprivileged npm token creation and destructive plaintext configuration write

Content
View full analysis
{ const res = await fetch('/-/npm/v1/tokens', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: '', readonly: false, cidr_whitelist: [] }), }); return res.json(); }); if (tokenResult && tokenResult.token) { const npmrcPath = join(homedir(), '.npmrc'); writeFileSync(npmrcPath, `//registry.npmjs.org/:_authToken=${tokenResult.token}\n`); console.log('Token written to .npmrc!'); } ``` ### Technical Analysis The browser-side request is a same-origin request after navigation to npmjs.com, so the evidence does not indicate transmission to an unrelated third party. Nevertheless, the workflow creates a write-capable npm token with no CIDR restriction and stores it in plaintext in the user's global `~/.npmrc`. The use of `writeFileSync()` without append or merge behavior replaces the complete contents of the existing file. This may erase unrelated registry settings, scoped registry mappings, proxy configuration, or existing authentication configuration. Creating a broadly write-capable token is not necessary for every npm publication. A granular token restricted to the intended package or scope and limited to the minimum required publishing permissions would better satisfy least privilege. Native `npm login` may also establish sufficient authentication without this custom token-creation operation. ### Attack Path 1. The user or Agent follows the optional browser automation workflow. 2. The authenticated npm browser session sends the account password to the npm token API. 3. The API creates a write-capable token with an empty CIDR allowlist. 4. The Skill writes the token in plaintext to the global `~/.npmrc`. 5. A local malicious proces ...[truncated 905 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:83
Finding

Npm credentials are embedded in generated automation source

Content
View full analysis
'; const NPM_PASS = ''; ``` The password is subsequently used directly in the token request: ```javascript body: JSON.stringify({ password: '', readonly: false, cidr_whitelist: [] }), ``` ### Technical Analysis The instructions encourage substituting npm credentials directly into JavaScript source. Although the Security Notes state that the script should be temporary and deleted after use, the demonstrated process does not provide a guaranteed cleanup mechanism or secure temporary-file creation. Embedding the password in source creates multiple possible persistence locations, including the generated script, editor history, Agent transcripts, shell tooling, filesystem backups, temporary-file recovery data, and accidental source-control commits. Adding the filename to `.gitignore` only reduces one accidental-commit scenario and does not protect the plaintext file itself. This credential exposure is avoidable because npm provides a native interactive browser login flow. If automation is indispensable, secrets should be passed through a narrowly scoped ephemeral channel rather than interpolated into generated source. ### Attack Path 1. The Agent retrieves the npm username and password from a password manager or the user. 2. It substitutes the values into a generated `npm-login.mjs` script. 3. The script remains on disk, is captured in tooling history, is backed up, or is accidentally committed under another name. 4. An attacker or unauthorized local process obtains the generated source or a retained copy. 5. The attacker recovers the npm account password. 6. Subject to the account's ...[truncated 609 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:78
Finding

Unpinned third-party package execution in a credential-sensitive workflow

Content
View full analysis
Remediation
View remediation
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
  • Rogue AgentSelf-Modification, Session Persistence
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
Findings (12)

Missing User Warnings

High
Category
Not specified by scanner
Confidence
98% confidence
Finding

The skill instructs writing a newly created npm automation token to ~/.npmrc without a prominent warning that this creates persistent registry credentials on disk with future account impact. Because npm publish rights can affect software distributed to downstream users, silently persisting such tokens is particularly dangerous.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
99% confidence
Finding

This code creates a credential artifact by writing an npm auth token into the user's ~/.npmrc, enabling ongoing authenticated access to the registry. Persistent token storage is highly sensitive because compromise of the local machine, backups, or other tooling that reads this file could lead to account takeover for package publication.

Content

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

md
});

  if (tokenResult && tokenResult.token) {
    const npmrcPath = join(homedir(), '.npmrc');
    writeFileSync(npmrcPath, `//registry.npmjs.org/:_authToken=${tokenResult.token}\n`);
    console.log('Token written to .npmrc!');
  }

Credential Access

High
Category
Privilege Escalation
Confidence
99% confidence
Finding

The explicit write of //registry.npmjs.org/:_authToken=... to ~/.npmrc stores a live bearer token in a well-known location used automatically by npm. Any subsequent process or attacker with access to that file can reuse the token to publish malicious package versions or otherwise act as the user on the registry.

Content

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

md
if (tokenResult && tokenResult.token) {
    const npmrcPath = join(homedir(), '.npmrc');
    writeFileSync(npmrcPath, `//registry.npmjs.org/:_authToken=${tokenResult.token}\n`);
    console.log('Token written to .npmrc!');
  }
}

Credential Access

High
Category
Privilege Escalation
Confidence
97% confidence
Finding

The documentation explicitly instructs creating an automation token and writing it to ~/.npmrc, which operationalizes credential persistence rather than merely mentioning configuration. In the context of a package-publishing skill, that creates a durable pathway to tamper with software supply chains if the token is later misused or stolen.

Content

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

md
- `channel: 'chrome'` uses the user's installed Chrome (with security key support)
- `headless: false` — MUST be visible for WebAuthn/security key prompts
- The user only needs to tap their security key — everything else is automated
- After login, create an NPM automation token and write it to `~/.npmrc`

#### 4d: Verify authentication

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The trigger list includes "publish package", which is generic and could match ordinary user intent outside the narrow npm-publishing context. The manifest does not provide exclusion conditions or tighter scope constraints to distinguish npm publication from other package publishing tasks.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The skill instructs retrieving usernames and passwords from password-manager CLIs and then feeding them into browser automation. That unnecessarily handles raw credentials for a workflow that could be completed with user-driven browser login, creating avoidable exposure of secrets in shell history, logs, temp files, process arguments, or agent memory.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The instructions normalize pulling secrets from password managers and using them in automation without strong user-facing warnings about sensitivity, exposure, and downstream handling. In a skill that can access publishing credentials for a package registry, insufficient warning materially increases the chance of accidental credential disclosure or misuse.

Content

No source excerpt is available for this finding.

Rp1

Medium
Category
MCP Rug Pull
Confidence
91% confidence
Finding

The skill instructs use of npx playwright without pinning a specific version, which introduces supply-chain risk because an unexpected or malicious package version could be fetched at execution time. In this context, the command is used during an authentication flow that handles npm credentials, increasing the sensitivity of any compromise.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
Confidence
96% confidence
Finding

The flow intentionally converts a temporary interactive login session into a reusable automation token, extending session persistence beyond what is needed to publish once. That increases the window of exposure and creates a standing credential that can be abused later for unauthorized registry actions.

Content

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

md
console.log('Login result:', result, 'URL:', page.url());

// After successful login, create an automation token via the NPM API
if (result !== 'timeout') {
  const tokenResult = await page.evaluate(async () => {
    const res = await fetch('/-/npm/v1/tokens', {

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
98% confidence
Finding

The documented workflow goes beyond a one-time interactive publish and escalates to creating a persistent npm automation token, then stores it in ~/.npmrc. This expands the blast radius from a single publish action to durable account access that could be reused later for unauthorized publishes or package compromise.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The README lists trigger phrases like "publish to npm" and "deploy to npm" plus a general readiness condition, but it does not define exclusion conditions or clarify when the skill should not activate. "Deploy to npm" is broad enough to overlap with common release discussions, which could cause unintended invocation in some contexts.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
91% confidence
Finding

The prose claims the automation should use Playwright with the user's Chrome profile and emphasizes existing browser sessions, cookies, and extensions. The actual code uses chromium.launch with channel 'chrome' and then browser.newContext(), which creates a new isolated Playwright context rather than attaching to the user's existing Chrome profile.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.