2026-02-04 · Authensor

How to Prevent AI Agents from Publishing Packages

SafeClaw by Authensor blocks all package publishing commands by default, preventing AI agents from releasing code to npm, PyPI, RubyGems, crates.io, or any other package registry. Install SafeClaw with npx @authensor/safeclaw and every publish attempt — npm publish, twine upload, gem push, cargo publish — is denied and recorded in a hash-chained audit log.

Why Package Publishing Is Dangerous When AI Agents Do It

Publishing a package is a public, often irreversible action. An npm package published under your organization's scope is immediately downloadable by anyone. A PyPI package can be installed by every project that lists it as a dependency. An agent that publishes can ship code containing bugs, vulnerabilities, leaked secrets, hallucinated implementations, or malicious payloads to your downstream consumers. Version numbers, once published, often cannot be reused even after unpublishing. Depending on the registry, published packages may be cached by mirrors and CDNs indefinitely. Registry credentials (npm tokens, PyPI API tokens) are typically stored in environment variables or .npmrc/.pypirc files — both accessible to agents with file read permissions.

The Exact SafeClaw Policy to Block Package Publishing

Add these rules to .safeclaw/policy.yaml:

rules:
  # Block npm publish
  - id: deny-npm-publish
    action: shell.exec
    match:
      command: "npm publish*"
    effect: deny
    audit: true
    message: "npm publish is permanently denied for AI agents."

# Block yarn publish
- id: deny-yarn-publish
action: shell.exec
match:
command: "yarn publish*"
effect: deny
audit: true
message: "yarn publish is permanently denied."

# Block pnpm publish
- id: deny-pnpm-publish
action: shell.exec
match:
command: "pnpm publish*"
effect: deny
audit: true
message: "pnpm publish is permanently denied."

# Block Python package publishing
- id: deny-twine-upload
action: shell.exec
match:
command: "twine upload*"
effect: deny
audit: true
message: "PyPI package upload is permanently denied."

- id: deny-python-setup-upload
action: shell.exec
match:
command: "python setup.py upload"
effect: deny
audit: true
message: "setup.py upload is permanently denied."

# Block Ruby gem publishing
- id: deny-gem-push
action: shell.exec
match:
command: "gem push*"
effect: deny
audit: true
message: "gem push is permanently denied."

# Block Rust crate publishing
- id: deny-cargo-publish
action: shell.exec
match:
command: "cargo publish*"
effect: deny
audit: true
message: "cargo publish is permanently denied."

# Block NuGet publishing
- id: deny-nuget-push
action: shell.exec
match:
command: "nuget push*"
effect: deny
audit: true
message: "nuget push is permanently denied."

- id: deny-dotnet-nuget-push
action: shell.exec
match:
command: "dotnet nuget push*"
effect: deny
audit: true
message: "dotnet nuget push is permanently denied."

# Protect registry credential files
- id: deny-read-npmrc
action: file.read
match:
path: "*/.npmrc"
effect: deny
audit: true
message: "Reading .npmrc (contains npm tokens) is denied."

- id: deny-read-pypirc
action: file.read
match:
path: "*/.pypirc"
effect: deny
audit: true
message: "Reading .pypirc (contains PyPI tokens) is denied."

This policy covers the six major package ecosystems: npm/yarn/pnpm (JavaScript), twine/setup.py (Python), gem (Ruby), cargo (Rust), and nuget (C#/.NET). Registry credential files are also protected from being read.

What Happens When the Agent Tries

When an agent attempts npm publish --access public:

  1. SafeClaw intercepts the shell.exec action.
  2. The deny-npm-publish rule matches npm publish*.
  3. The command is blocked. No package is uploaded. No registry API call is made.
  4. Audit entry:
{
  "timestamp": "2026-02-13T15:40:33Z",
  "action": "shell.exec",
  "command": "npm publish --access public",
  "effect": "deny",
  "rule": "deny-npm-publish",
  "agent": "release-agent-01",
  "hash": "o4q1t7...chain"
}

If the agent tries to read .npmrc to extract the auth token for a manual HTTP publish request, the deny-read-npmrc rule catches that attempt separately.

How to Allow Publishing with Approval

For CI/CD agents that need to publish releases after human review:

rules:
  # Protect credentials (always deny)
  - id: deny-read-npmrc
    action: file.read
    match:
      path: "*/.npmrc"
    effect: deny
    audit: true
    message: "Reading .npmrc is denied."

# Approval-gated publishing
- id: approve-npm-publish
action: shell.exec
match:
command: "npm publish*"
effect: approval
audit: true
approvers:
- role: release-manager
timeout: 600
message: "npm publish requires release manager approval. Version and scope logged."

# Deny other registries
- id: deny-twine-upload
action: shell.exec
match:
command: "twine upload*"
effect: deny
audit: true
message: "PyPI publishing is denied."

The release manager sees the full command — including any --tag, --access, or --registry flags — before approving. The 600-second timeout gives time to review the package contents, verify the version number, and check the changelog.

For maximum safety, combine with a pre-publish verification step:

rules:
  - id: allow-npm-pack
    action: shell.exec
    match:
      command: "npm pack*"
    effect: allow
    audit: true

- id: approve-npm-publish
action: shell.exec
match:
command: "npm publish*"
effect: approval
audit: true
approvers:
- role: release-manager
timeout: 600
message: "Review npm pack output before approving publish."

This lets the agent run npm pack (which creates a local tarball without uploading) so the release manager can inspect the exact contents before approving the publish.

Verification

npx @authensor/safeclaw simulate --action 'shell.exec' --command 'npm publish --access public'

Expected: deny, rule: deny-npm-publish

npx @authensor/safeclaw simulate --action 'file.read' --path '/home/user/.npmrc'

Expected: deny, rule: deny-read-npmrc

Related Pages

Try SafeClaw

Action-level gating for AI agents. Set it up in your browser in 60 seconds.

$ npx @authensor/safeclaw