
Research
Shai-Hulud Descends to Hades: Miasma Worm Campaign Spreads with New PyPI Wave
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.
cc-safety-net
Advanced tools
A coding agent CLI hook - block destructive git and filesystem commands before execution
A Coding Agent CLI plugin that acts as a safety net, catching destructive git and filesystem commands before they execute.
We learned the hard way that instructions aren't enough to keep AI agents in check.
After Claude Code silently wiped out hours of progress with a single rm -rf ~/ or git checkout --, it became evident that soft rules in an CLAUDE.md or AGENTS.md file cannot replace hard technical constraints.
The current approach is to use a dedicated hook to programmatically prevent agents from running destructive commands.
Claude Code's .claude/settings.json supports deny rules with wildcard matching (e.g., Bash(git reset --hard:*)). Here's how this plugin differs:
| Permission Deny Rules | CC Safety Net | |
|---|---|---|
| Setup | Manual configuration required | Works out of the box |
| Parsing | Wildcard pattern matching | Semantic command analysis |
| Execution order | Runs second | Runs first (PreToolUse hook) |
| Shell wrappers | Not handled automatically (must match wrapper forms) | Recursively analyzed (up to 10 levels) |
| Interpreter one-liners | Not handled automatically (must match interpreter forms) | Detected and blocked |
Even with wildcard matching, Bash permission patterns are intentionally limited and can be bypassed in many ways:
| Bypass Method | Example |
|---|---|
| Options before value | curl -X GET http://evil.com bypasses Bash(curl http://evil.com:*) |
| Shell variables | URL=http://evil.com && curl $URL bypasses URL pattern |
| Flag reordering | rm -r -f / bypasses Bash(rm -rf:*) |
| Extra whitespace | rm -rf / (double space) bypasses pattern |
| Shell wrappers | sh -c "rm -rf /" bypasses Bash(rm:*) entirely |
| Scenario | Permission Rules | CC Safety Net |
|---|---|---|
git checkout -b feature (safe) | Blocked by Bash(git checkout:*) | Allowed |
git checkout -- file (dangerous) | Blocked by Bash(git checkout:*) | Blocked |
rm -rf /tmp/cache (safe) | Blocked by Bash(rm -rf:*) | Allowed |
rm -r -f / (dangerous) | Allowed (flag order) | Blocked |
bash -c 'git reset --hard' | Allowed (wrapper) | Blocked |
python -c 'os.system("rm -rf /")' | Allowed (interpreter) | Blocked |
PreToolUse hooks run before the permission system. This means CC Safety Net inspects every command first, regardless of your permission configuration. Even if you misconfigure deny rules, CC Safety Net provides a fallback layer of protection.
Use both together: Permission deny rules for quick, user-configurable blocks; CC Safety Net for robust, bypass-resistant protection that works out of the box.
Claude Code offers native sandboxing that provides OS-level filesystem and network isolation. Here's how it compares to CC Safety Net:
| Sandboxing | CC Safety Net | |
|---|---|---|
| Enforcement | OS-level (Seatbelt/bubblewrap) | Application-level (PreToolUse hook) |
| Approach | Containment — restricts filesystem + network access | Command analysis — blocks destructive operations |
| Filesystem | Writes restricted (default: cwd); reads are broad by default | Only destructive operations blocked |
| Network | Domain-based proxy filtering | None |
| Git awareness | None | Explicit rules for destructive git operations |
| Bypass resistance | High — OS enforces boundaries | Lower — analyzes command strings only |
Sandboxing restricts filesystem + network access, but it doesn't understand whether an operation is destructive within those boundaries. These commands are not blocked by the sandbox boundary:
[!NOTE] Whether they're auto-run or require confirmation depends on your sandbox mode (auto-allow vs regular permissions), and network access still depends on your allowed-domain policy. Claude Code can also retry a command outside the sandbox via
dangerouslyDisableSandbox(with user permission); this can be disabled withallowUnsandboxedCommands: false.
| Command | Sandboxing | CC Safety Net |
|---|---|---|
git reset --hard | Allowed (within cwd) | Blocked |
git checkout -- . | Allowed (within cwd) | Blocked |
git stash clear | Allowed (within cwd) | Blocked |
git push --force | Allowed (if remote domain is allowed) | Blocked |
rm -rf . | Allowed (within cwd) | Blocked |
Sandboxing sees git reset --hard as a safe operation—it only modifies files within the current directory. But you just lost all uncommitted work.
Sandboxing is the better choice when your primary concern is:
They protect against different threats:
Running both together provides defense-in-depth. Sandboxing handles unknown threats; CC Safety Net handles known destructive patterns that sandboxing permits.
~/.codex/config.toml:[features]
plugin_hooks = true
codex plugin marketplace add kenryu42/cc-marketplace
/plugins.[cc-marketplace]./hooks and select the safety-net PreToolUse hook and press t to trust it./plugin marketplace add kenryu42/cc-marketplace
/plugin install safety-net@cc-marketplace
/reload-plugins
/plugin → Select Marketplaces → Choose cc-marketplace → Enable auto-updategemini extensions install https://github.com/kenryu42/gemini-safety-net
/plugin install kenryu42/copilot-safety-net
Install CC Safety Net into your Kimi CLI config:
npx -y cc-safety-net hook install --kimi-cli
Install CC Safety Net with OpenCode's native plugin command:
opencode plugin -g cc-safety-net
[!NOTE] OpenCode can sometimes keep using a stale cached plugin version. See anomalyco/opencode#25293 for the current tracking issue.
To force OpenCode to reinstall
cc-safety-net, remove its cached package and install the version you want:rm -rf ~/.cache/opencode/packages/cc-safety-net@latest opencode plugin -g -f cc-safety-net@latest If you prefer pinning a specific version: rm -rf ~/.cache/opencode/packages/cc-safety-net@latest opencode plugin -g -f cc-safety-net@<version> Restart OpenCode after updating so the plugin is loaded from the refreshed cache.
Install CC Safety Net with Pi's package installer:
pi install npm:cc-safety-net
CC Safety Net can display its status in Claude Code's status line, showing whether protection is active and which modes are enabled.
Add the following to your ~/.claude/settings.json:
Using Bun (recommended):
{
"statusLine": {
"type": "command",
"command": "bunx cc-safety-net statusline --claude-code"
}
}
Using Claude X:
{
"statusLine": {
"type": "command",
"command": "BUN_BE_BUN=1 claude x cc-safety-net statusline --claude-code"
}
}
[!NOTE] The
claude xcommand is only compatible with the native version of Claude Code. If you installed via npm, please usenpxorbunxinstead.
Using NPM:
{
"statusLine": {
"type": "command",
"command": "npx -y cc-safety-net statusline --claude-code"
}
}
Piping with existing status line:
If you already have a status line command, you can pipe CC Safety Net at the end:
{
"statusLine": {
"type": "command",
"command": "your-existing-command | bunx cc-safety-net statusline --claude-code"
}
}
Changes take effect immediately — no restart needed.
The status line displays different emojis based on the current configuration:
| Status | Display | Meaning |
|---|---|---|
| Plugin disabled | 🛡️ CC Safety Net ❌ | CC Safety Net plugin is not enabled |
| Default mode | 🛡️ CC Safety Net ✅ | Protection active with default settings |
| Strict mode | 🛡️ CC Safety Net 🔒 | CC_SAFETY_NET_STRICT=1 — fail-closed on unparseable commands |
| Paranoid mode | 🛡️ CC Safety Net 👁️ | CC_SAFETY_NET_PARANOID=1 — all paranoid checks enabled |
| Paranoid RM only | 🛡️ CC Safety Net 🗑️ | CC_SAFETY_NET_PARANOID_RM=1 — blocks rm -rf even within cwd |
| Paranoid interpreters only | 🛡️ CC Safety Net 🐚 | CC_SAFETY_NET_PARANOID_INTERPRETERS=1 — blocks interpreter one-liners |
| Worktree mode | 🛡️ CC Safety Net 🌳 | CC_SAFETY_NET_WORKTREE=1 — relax local git discards inside linked worktrees |
| Strict + Paranoid | 🛡️ CC Safety Net 🔒👁️ | Both strict and paranoid modes enabled |
Multiple mode emojis are combined when multiple environment variables are set. Mode flags use CC_SAFETY_NET_* names; legacy SAFETY_NET_* names are still accepted.
Run the diagnostic command to verify your installation and troubleshoot issues:
npx cc-safety-net doctor
# or with bun
bunx cc-safety-net doctor
The doctor command checks:
| Check | Description |
|---|---|
| Hook Integration | Verifies the plugin is properly configured for each supported platform |
| Self-Test | Runs sample commands to confirm blocking works correctly |
| Configuration | Validates custom rules in user and project configs |
| Environment | Shows status of mode flags (CC_SAFETY_NET_STRICT, CC_SAFETY_NET_PARANOID, etc.; legacy SAFETY_NET_* also listed when set) |
| Recent Activity | Summarizes blocked commands from the last 7 days |
| System Info | Displays versions of all relevant tools |
| Update Check | Checks if a newer version is available |
| Flag | Description |
|---|---|
--json | Output in JSON format (useful for sharing in bug reports) |
--skip-update-check | Skip the npm version check |
Trace how CC Safety Net analyzes a command step-by-step. Useful for debugging why a command is blocked or allowed, or when developing custom rules.
npx cc-safety-net explain "git reset --hard"
# or with bun
bunx cc-safety-net explain "git reset --hard"
| Flag | Description |
|---|---|
--json | Output analysis as JSON |
--cwd <path> | Use custom working directory for analysis |
npx cc-safety-net explain "rm -rf /"
npx cc-safety-net explain --json "git checkout -- file.txt"
npx cc-safety-net explain --cwd /tmp "git status"
| Command Pattern | Why It's Dangerous |
|---|---|
| git checkout -- files | Discards uncommitted changes permanently |
| git checkout <ref> -- <path> | Overwrites working tree with ref version |
| git checkout <ref> <path> | May overwrite working tree when Git disambiguates ref vs pathspec |
| git restore files | Discards uncommitted changes |
| git restore --worktree | Explicitly discards working tree changes |
| git switch --discard-changes | Discards uncommitted changes when switching branches |
| git switch --force / -f | Discards uncommitted changes (force switch) |
| git reset --hard | Destroys all uncommitted changes |
| git reset --merge | Can lose uncommitted changes |
| git clean -f | Removes untracked files permanently |
| git push --force / -f | Destroys remote history |
| git branch -D | Force-deletes branch without merge check |
| git stash drop | Permanently deletes stashed changes |
| git stash clear | Deletes ALL stashed changes |
| git worktree remove --force | Force-deletes worktree without checking for changes |
| rm -rf (destructive targets) | Recursive file deletion of root, home, parent, absolute, or non-temp paths outside cwd |
| rm -rf / or ~ or $HOME | Root/home deletion is extremely dangerous |
| find ... -delete | Permanently removes files matching criteria |
| xargs rm -rf | Dynamic input makes targets unpredictable |
| xargs <shell> -c | Can execute arbitrary commands |
| parallel rm -rf | Dynamic input makes targets unpredictable |
| parallel <shell> -c | Can execute arbitrary commands |
| dd writing to block devices | Can overwrite disks or partitions |
| mkfs on block devices | Formats disks or partitions |
| shred | Permanently destroys file contents |
| Command Pattern | Why It's Safe |
|---|---|
| git checkout -b branch | Creates new branch |
| git checkout --orphan | Creates orphan branch |
| git restore --staged | Only unstages, doesn't discard |
| git restore --help/--version | Help/version output |
| git branch -d | Safe delete with merge check |
| git clean -n / --dry-run | Preview only |
| git push --force-with-lease | Safe force push |
| rm -rf /tmp/... | Temp directories are ephemeral |
| rm -rf /var/tmp/... | System temp directory |
| rm -rf $TMPDIR/... | User's temp directory |
| rm -rf ./... (within cwd) | Limited to current working directory |
| git restore / checkout -- / reset --hard / clean -f (in linked worktree) | Relaxed only when CC_SAFETY_NET_WORKTREE=1 and cwd is a linked worktree |
When a destructive command is detected, the plugin blocks the tool execution and provides a reason.
Example output:
BLOCKED by CC Safety Net
Reason: git checkout -- discards uncommitted changes permanently. Use 'git stash' first.
Command: git checkout -- src/main.py
If this operation is truly needed, ask the user for explicit permission and have them run the command manually.
You can manually test the hook by attempting to run blocked commands in Claude Code:
# This should be blocked
git checkout -- README.md
# This should be allowed
git checkout -b test-branch
[!WARNING] The custom rules system has moved from legacy inline config files to a rulebook-based layout. Legacy inline config files (
.safety-net.jsonand~/.cc-safety-net/config.json) are no longer loaded at runtime. If they contain rules, commands now fail closed (stay blocked) until you migrate.
.safety-net.json (project scope) or ~/.cc-safety-net/config.json (user scope).| Legacy file state | New behavior |
|---|---|
| Empty legacy file | Silently ignored — built-in rules only |
| Legacy file with rules | Fail closed until migrated with rule migrate |
| Invalid legacy file | Fail closed until fixed and migrated, or removed |
"Fail closed" means commands stay blocked until the legacy rules are migrated to the new layout.
# Convert legacy inline rules into the new rulebook layout
npx -y cc-safety-net rule migrate
# Optionally delete verified legacy files after migration
npx -y cc-safety-net rule migrate --cleanup
# Validate the migrated rules
npx -y cc-safety-net rule verify
npx -y cc-safety-net rule test
Before — a single inline config with rules embedded:
.safety-net.json # project rules (inline)
~/.cc-safety-net/config.json # user rules (inline)
After — rule migrate creates a rulebook-based layout automatically:
.cc-safety-net/rules/rule.json # project rulebook sources + overrides
.cc-safety-net/rules/project-rules/rulebook.json # migrated project rules
~/.cc-safety-net/rules/rule.json # user rulebook sources + overrides
~/.cc-safety-net/rules/user-rules/rulebook.json # migrated user rules
See Custom Rules for the full authoring guide and Error Handling for fail-closed details.
Beyond the built-in protections, you can define your own blocking rules to enforce team conventions or project-specific safety policies.
[!TIP] The best way to create custom rules is to use the
/cc-safety-netskill to create custom rules interactively with natural language.
/cc-safety-net read my package.json and suggest blocking rules
/cc-safety-net set up rules to block all terraform destroy commands
/cc-safety-net verify my rules and fix any errors
[!NOTE] If your agent does not support skills, prompt it with:
run npx -y cc-safety-net rule doc and help me set up custom rules
Create a starter project rule config and rulebook:
npx -y cc-safety-net rule init
This creates .cc-safety-net/rules/rule.json:
{
"version": 1,
"rules": ["project-rules"],
"overrides": {}
}
Rule definitions live in .cc-safety-net/rules/project-rules/rulebook.json:
{
"rulebook_version": 1,
"name": "project-rules",
"version": "1.0.0",
"description": "Project-specific CC Safety Net rules.",
"author": "project",
"allowed_commands": ["git"],
"rules": [
{
"name": "block-git-add-all",
"command": "git",
"subcommand": "add",
"block_args": ["-A", "--all", "."],
"reason": "Use 'git add <specific-files>' instead of blanket add."
}
],
"tests": [
{
"command": "git add -A",
"expect": "blocked",
"rule": "block-git-add-all"
},
{
"command": "git add README.md",
"expect": "allowed"
}
]
}
After editing rulebooks, run:
npx -y cc-safety-net rule sync
npx -y cc-safety-net rule verify
npx -y cc-safety-net rule test
Now git add -A, git add --all, and git add . will be blocked with your custom message.
Config files are loaded from two scopes and merged:
~/.cc-safety-net/rules/rule.json (use rule init --global).cc-safety-net/rules/rule.json in the current working directoryLocal rulebook sources are bare names like project-rules. GitHub rulebook sources use owner/repo#ref/<rulebook-name> and point to .cc-safety-net/rules/<rulebook-name>/rulebook.json in that repository.
Legacy inline config files (.safety-net.json and ~/.cc-safety-net/config.json) are no longer loaded at runtime. Empty legacy files are ignored, but legacy files with rules and invalid legacy files fail closed until migrated or fixed. Convert existing legacy rules with npx -y cc-safety-net rule migrate; use npx -y cc-safety-net rule migrate --cleanup if you also want to delete verified legacy files after migration. See Breaking Change: Custom Rules Migration for the full upgrade guide.
Merging behavior:
<rulebook-name>/<rule-name> keyThis allows you to define personal defaults in user scope while letting projects disable or replace reasons for specific rules.
If no config file is found in either location, only built-in rules apply.
| Field | Type | Required | Description |
|---|---|---|---|
version | integer | Yes | Schema version (must be 1) |
rules | array | No | List of rulebook source strings (defaults to empty) |
overrides | object | No | Rule overrides keyed by <rulebook-name>/<rule-name> |
Override values are either "off" to disable a rule or { "reason": "..." } to replace the rule reason.
| Field | Type | Required | Description |
|---|---|---|---|
rulebook_version | integer | Yes | Rulebook schema version (must be 1) |
name | string | Yes | Rulebook name; must match the local directory name or GitHub source name |
version | string | Yes | Rulebook version |
description | string | No | Human-readable description |
author | string | No | Rulebook author |
allowed_commands | array | Yes | Commands this rulebook is allowed to define rules for |
rules | array | Yes | Custom blocking rules |
tests | array | Yes | Rulebook fixtures |
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique within the rulebook (letters, numbers, hyphens, underscores; max 64 chars) |
command | string | Yes | Base command to match; must be listed in allowed_commands |
subcommand | string | No | Subcommand to match (e.g., add, install). If omitted, matches any. |
block_args | array | Yes | Arguments that trigger the block (at least one required) |
reason | string | Yes | Message shown when blocked (max 256 chars) |
| Field | Type | Required | Description |
|---|---|---|---|
command | string | Yes | Shell command fixture |
expect | string | Yes | Either blocked or allowed |
rule | string | For blocked fixtures | Rule expected to block the command |
Every rule must have at least one blocked fixture. Add allowed fixtures for close-but-safe commands.
/usr/bin/git → git)block_args is present-Ap matches -A (bundled flags are unbundled)--all-files does NOT match --all-Cfoo is treated as -C -f -o -o, not -C foo. Blocking -f may false-positive on attached option values..cc-safety-net/rules/rule.json:
{
"version": 1,
"rules": ["project-rules"],
"overrides": {}
}
.cc-safety-net/rules/project-rules/rulebook.json:
{
"rulebook_version": 1,
"name": "project-rules",
"version": "1.0.0",
"allowed_commands": ["npm"],
"rules": [
{
"name": "block-npm-global",
"command": "npm",
"subcommand": "install",
"block_args": ["-g", "--global"],
"reason": "Global npm installs can cause version conflicts. Use npx or local install."
}
],
"tests": [
{
"command": "npm install -g typescript",
"expect": "blocked",
"rule": "block-npm-global"
},
{
"command": "npm install typescript",
"expect": "allowed"
}
]
}
{
"rulebook_version": 1,
"name": "project-rules",
"version": "1.0.0",
"allowed_commands": ["docker"],
"rules": [
{
"name": "block-docker-system-prune",
"command": "docker",
"subcommand": "system",
"block_args": ["prune"],
"reason": "docker system prune removes all unused data. Use targeted cleanup instead."
}
],
"tests": [
{
"command": "docker system prune",
"expect": "blocked",
"rule": "block-docker-system-prune"
},
{
"command": "docker ps",
"expect": "allowed"
}
]
}
{
"rulebook_version": 1,
"name": "project-rules",
"version": "1.0.0",
"allowed_commands": ["git", "npm"],
"rules": [
{
"name": "block-git-add-all",
"command": "git",
"subcommand": "add",
"block_args": ["-A", "--all", ".", "-u", "--update"],
"reason": "Use 'git add <specific-files>' instead of blanket add."
},
{
"name": "block-npm-global",
"command": "npm",
"subcommand": "install",
"block_args": ["-g", "--global"],
"reason": "Use npx or local install instead of global."
}
],
"tests": [
{
"command": "git add -A",
"expect": "blocked",
"rule": "block-git-add-all"
},
{
"command": "npm install -g typescript",
"expect": "blocked",
"rule": "block-npm-global"
}
]
}
Rulebook-backed custom rules fail closed when configured rulebooks cannot be loaded safely:
| Scenario | Behavior |
|---|---|
| Config file not found | Silent — use built-in rules only |
| Invalid rule config | Fail closed until fixed |
| Empty legacy config | Silent — use built-in rules only |
| Legacy config with rules and no migrated rule config | Fail closed until rule migrate creates the new rule config |
| Invalid legacy config | Fail closed until fixed or removed |
| Missing or stale lock/cache | Fail closed until rule sync repairs it |
| Invalid local rulebook | Fail closed until the rulebook is fixed and synced |
| Invalid GitHub rulebook | Fail closed until the source is fixed or removed |
[!IMPORTANT]
If you add or modify custom rules manually, always validate them withnpx -y cc-safety-net rule verifyandnpx -y cc-safety-net rule test.
When a custom rule blocks a command, the output includes the rule name:
BLOCKED by CC Safety Net
Reason: [block-git-add-all] Use 'git add <specific-files>' instead of blanket add.
Command: git add -A
Mode and debug flags use CC_SAFETY_NET_* environment variables. Older SAFETY_NET_* names (without the CC_ prefix) still work for strict, paranoid, and worktree toggles.
Malformed or missing hook input JSON always fails closed. By default, ambiguous shell
command parsing is allowed through. Enable strict mode to fail closed when a shell
command cannot be safely analyzed (e.g., unterminated quotes or malformed bash -c
wrappers):
export CC_SAFETY_NET_STRICT=1
Paranoid mode enables stricter safety checks that may be disruptive to normal workflows. You can enable it globally or via focused toggles:
# Enable all paranoid checks
export CC_SAFETY_NET_PARANOID=1
# Or enable specific paranoid checks
export CC_SAFETY_NET_PARANOID_RM=1
export CC_SAFETY_NET_PARANOID_INTERPRETERS=1
Paranoid behavior:
rm -rf even within the current working directory.python -c, node -e, ruby -e,
and perl -e (these can hide destructive commands).Linked git worktrees are designed as disposable, isolated workspaces — discarding changes inside one doesn't risk the main working tree. Worktree mode relaxes local-discard rules when (and only when) the command is proven to run inside a linked worktree:
export CC_SAFETY_NET_WORKTREE=1
When enabled, these commands are allowed inside a linked worktree:
git restore <file> and git restore --worktree <file>git checkout -- <file>, git checkout <ref> -- <file>, git checkout --force,
and ambiguous multi-positional checkout formsgit switch --discard-changes and git switch -f / --forcegit reset --hard and git reset --mergegit clean -f (and combined short flags like -fd)These remain blocked even in linked worktrees because they reach beyond the local working tree:
git push --force (affects remote)git branch -D (affects shared refs)git stash drop / git stash clear (stash is shared across worktrees)git worktree remove --force (could delete another worktree)Detection is fail-closed and mostly filesystem-based:
.git file containing gitdir: whose
resolved git directory contains a commondir file. Main worktrees and
submodules don't satisfy this and are not relaxed.realpath so symlinked paths resolve correctly.git -C <path> (including chained -C and attached -Cpath) is honored;
unresolved targets keep the command blocked.cd/pushd),
if --git-dir / --work-tree is passed, or if GIT_DIR / GIT_WORK_TREE
/ GIT_COMMON_DIR is set in the environment.The guard recursively analyzes commands wrapped in shells:
bash -c 'git reset --hard' # Blocked
sh -lc 'rm -rf /' # Blocked
bash -c 'git stash drop' # Blocked
Detects destructive commands hidden in Python/Node/Ruby/Perl one-liners:
python -c 'import os; os.system("rm -rf /")' # Blocked
python -c 'import os; os.system("git stash drop")' # Blocked
python -c 'import os; os.system("dd if=/dev/zero of=/dev/sda")' # Blocked
python -c 'import os; os.system("mkfs.ext4 /dev/sda1")' # Blocked
python -c 'import os; os.system("shred -u secret.txt")' # Blocked
Block messages automatically redact sensitive data (tokens, passwords, API keys) to prevent leaking secrets in logs.
All blocked commands are logged to ~/.cc-safety-net/logs/<session_id>.jsonl for audit purposes:
{"ts": "2025-01-15T10:30:00Z", "command": "git reset --hard", "segment": "git reset --hard", "reason": "...", "cwd": "/path/to/project"}
Sensitive data in log entries is automatically redacted.
See CONTRIBUTING.md for details on how to contribute to this project.
MIT
FAQs
A coding agent CLI hook - block destructive git and filesystem commands before execution
The npm package cc-safety-net receives a total of 1,175 weekly downloads. As such, cc-safety-net popularity was classified as popular.
We found that cc-safety-net demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
Socket found 37 malicious PyPI wheels that abuse Python startup hooks to launch a Bun-powered credential stealer tied to Mini Shai-Hulud/Miasma.

Security News
RubyGems and Bundler 4.0.13 introduced an opt-in cooldown feature that delays newly published gems during dependency resolution.

Security News
pnpm 11.5 now recognizes npm staged publish approvals in release metadata, preventing those releases from being mistaken for lower-trust package publishes.