@ssweens/pi-leash
Security hooks for Pi to reduce accidental destructive actions and secret-file access.
Package details
Install @ssweens/pi-leash from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@ssweens/pi-leash- Package
@ssweens/pi-leash- Version
1.2.1- Published
- May 30, 2026
- Downloads
- 534/mo · 41/wk
- Author
- ssweens
- License
- MIT
- Types
- extension
- Size
- 202.2 KB
- Dependencies
- 0 dependencies · 4 peers
Pi manifest JSON
{
"extensions": [
"./src/index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
Pi Leash
Security hooks for Pi to reduce accidental destructive actions and secret-file access.
Forked from @aliou/pi-guardrails (MIT) with added sudo mode for secure password handling and an opt-in in-memory password cache. See Credits & Attribution below for details.
Install
pi install npm:@ssweens/pi-leash
Or from git:
pi install git:github.com/ssweens/pi-leash
What it does
- policies: named file-protection rules with per-rule protection levels.
- permission-gate: detects dangerous bash commands and asks for confirmation.
- path-access (opt-in): restricts tool access to the current working directory with allow/ask/block modes.
- optional command explainer: can call a small LLM to explain a dangerous command inline in the confirmation dialog.
- sudo mode (opt-in): securely handles sudo commands by prompting for passwords and executing with
sudo -S.
Configuration
Pi Leash reads from:
~/.pi/agent/settings/pi-leash.json
Create this file to customize settings. All fields are optional — sensible defaults are used when not specified.
Example config
{
"enabled": true,
"features": {
"policies": true,
"permissionGate": true,
"pathAccess": true
},
"pathAccess": {
"mode": "ask",
"allowedPaths": ["~/shared-libs/"]
},
"permissionGate": {
"sudoMode": {
"enabled": true,
"timeout": 30000,
"preserveEnv": false
}
}
}
Current schema
{
"enabled": true,
"features": {
"policies": true,
"permissionGate": true,
"pathAccess": false
},
"policies": {
"rules": [
{
"id": "secret-files",
"description": "Files containing secrets",
"patterns": [
{ "pattern": ".env" },
{ "pattern": ".env.local" },
{ "pattern": ".env.production" },
{ "pattern": ".env.prod" },
{ "pattern": ".dev.vars" }
],
"allowedPatterns": [
{ "pattern": ".env.example" },
{ "pattern": ".env.sample" },
{ "pattern": ".env.test" },
{ "pattern": "*.example.env" },
{ "pattern": "*.sample.env" },
{ "pattern": "*.test.env" }
],
"protection": "noAccess",
"onlyIfExists": true
}
]
},
"pathAccess": {
"mode": "ask",
"allowedPaths": []
},
"permissionGate": {
"patterns": [
{ "pattern": "rm -rf", "description": "recursive force delete" },
{ "pattern": "sudo", "description": "superuser command" },
{ "pattern": "git checkout", "description": "branch switch or discard uncommitted changes" }
],
"requireConfirmation": true,
"allowedPatterns": [],
"autoDenyPatterns": [],
"explainCommands": false,
"explainModel": null,
"explainTimeout": 5000,
"sudoMode": {
"enabled": false,
"timeout": 30000,
"preserveEnv": false
}
}
}
All fields optional. Missing fields use defaults.
Policies
Each rule has:
id: stable identifierpatterns: files to match (glob by default, regex ifregex: true). Patterns with/match full path; patterns without/match basename only.allowedPatterns: exceptionsprotection:noAccess: blockread,write,edit,bash,grep,find,lsreadOnly: blockwrite,edit,bashnone: no protection
onlyIfExists(default true)blockMessagewith{file}placeholderenabled(default true)
When multiple rules match, strongest protection wins: noAccess > readOnly > none.
Permission gate
Detects dangerous bash commands and prompts user confirmation.
Built-in dangerous patterns are matched structurally (AST-based):
rm -rf(recursive force delete)sudo,doas,pkexec(privilege escalation)dd of=(disk write operation)mkfs,mkfs.*(filesystem format)shred(secure file overwrite)wipefs(filesystem signature wipe)blkdiscard(block device discard)fdisk,sfdisk,cfdisk,parted,sgdisk(disk partitioning)chmod -R 777/0777/a+rwx/ugo+rwx(insecure recursive permissions)chown -R(recursive ownership change)docker/podman run --privileged,--pid=host, socket mounts, root mounts (container escapes)git checkout(branch switch or discard uncommitted changes)
You can add custom dangerous patterns via permissionGate.patterns.
When prompted, you can choose:
- Allow (
y/ Enter) — allow this command now - Allow for session (
a) — allow this exact command string for the current session - Allow cwd file ops this session (
c) — shown only for cwd-scoped, allowlisted file operations; allows future dangerous file-based commands in the currentcwdfor this session - Allow eligible cmds for 5 min (
w) — shown only for cwd-scoped, allowlisted operations; temporarily bypasses prompts for similar eligible dangerous commands - Allow eligible cmds for session (
s) — same as above, but until session end - Deny (
n/ Esc)
w/s/c are intentionally restricted to non-evil categories only:
rm -rfchmod -R ...(world-writable patterns)chown -R ...
They do not bypass for privilege escalation, disk/filesystem tools (sudo, dd, mkfs, wipefs, partitioning, etc.), shred, or dangerous container flags.
Danger detection and cwd-scope checks parse full shell structure (AST), including pipelines and shell heredoc payloads (e.g. bash <<'EOF' ... EOF).
The approval prompt now shows:
- Reason (danger category)
- Source (
built-in structural,custom pattern, orfallback substring (parse failed)) - Trigger (exact token/pattern that matched, e.g.
rm -rf)
Explain commands (opt-in)
If enabled, guardrails calls an LLM before showing the confirmation dialog and displays a short explanation.
Config fields:
permissionGate.explainCommands(boolean)permissionGate.explainModel(provider/model-id)permissionGate.explainTimeout(ms)
Failures/timeouts degrade gracefully: dialog still shows without explanation.
Sudo mode (opt-in)
When enabled, sudo commands prompt for the sudo password and execute securely using sudo -S. The password is masked during input and cleared from memory immediately after execution.
Config fields:
permissionGate.sudoMode.enabled(boolean) - Enable sudo modepermissionGate.sudoMode.timeout(number, ms) - Command timeout (default: 30000)permissionGate.sudoMode.preserveEnv(boolean) - Preserve environment withsudo -E(default: false)permissionGate.sudoMode.maxRetries(number) - Maximum password attempts before failing (default: 3). Mirrors realsudobehavior — on a mistyped password the dialog re-appears with an error message and remaining-attempt count.
Example config:
{
"permissionGate": {
"sudoMode": {
"enabled": true,
"timeout": 60000,
"preserveEnv": false
}
}
}
Security notes:
- Passwords are never logged or stored to disk
- Password input is masked (••••)
- Password buffer is overwritten with asterisks after use
- Uses
sudo -Sfor secure stdin-based password delivery - Failed authentication re-prompts up to
maxRetriestimes before failing
Path access (opt-in)
Restricts tool access to the current working directory. When enabled, any tool call targeting a path outside cwd is checked against the configured mode:
- allow: no restrictions
- ask: prompt with options to grant access (file or directory, for session or always)
- block: deny all outside access
{
"features": { "pathAccess": true },
"pathAccess": {
"mode": "ask",
"allowedPaths": ["~/code/shared-libs/", "~/.config/myapp"]
}
}
When prompted, the user can choose:
- Allow once — this invocation only
- Allow file/dir this session — in-memory, gone on restart
- Allow file/dir always — persisted to
pi-leash.json
Entries in allowedPaths use trailing / for directory grants and exact paths for file grants.
Limitations:
- Symlinks are not resolved (lexical path comparison only).
- Bash path extraction is best-effort (AST-based heuristics).
- In non-interactive mode,
askmode degrades toblock.
Events
Pi Leash emits events for other extensions:
leash:blocked
interface LeashBlockedEvent {
feature: "policies" | "permissionGate" | "pathAccess";
toolName: string;
input: Record<string, unknown>;
reason: string;
userDenied?: boolean;
}
leash:dangerous
interface LeashDangerousEvent {
command: string;
description: string;
pattern: string;
}
Credits & Attribution
Pi Leash is a fork of @aliou/pi-guardrails by @aliou, used under the terms of the MIT License. The original package provided the file-protection policy engine, the dangerous-command permission gate, and the optional command-explainer integration that this fork inherits.
This fork adds:
- Path access control: opt-in CWD boundary enforcement with allow/ask/block modes and session or persistent grants.
- Expanded dangerous command matchers:
shred,doas,pkexec,wipefs,blkdiscard,fdisk/sfdisk/cfdisk,parted/sgdisk, and Docker/Podman container escape detection. - Secure sudo mode with masked password input and
sudo -Sexecution. - Opt-in in-memory sudo password caching with a per-prompt
Remember for N mintoggle. - Self-contained shell parser: the
@aliou/shparser used by structural command matching is vendored intosrc/vendor/aliou-sh(also MIT, seeNOTICE.md) so local path installs do not depend on external module resolution. - Comprehensive test suite for dangerous command matchers, path utilities, path access decisions, command argument classification, and bash path extraction.
- Various stability fixes to the sudo approval flow, dialog key handling, and dangerous-pattern detection.
Both pi-leash and the upstream @aliou/pi-guardrails are MIT-licensed. See LICENSE for the full text.