- TypeScript 100%
| src | ||
| tests | ||
| .gitignore | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| security-permissions.example.json | ||
| tsconfig.json | ||
Security Permissions Plugin
A standalone, headless security plugin for Fractal Synapse agents that audits every tool call
before execution. Enforces three dimensions of access control from a security-permissions.json
file: tool-level permissions, file system path permissions, and bash command permissions.
When a permission is denied the tool call is aborted gracefully — the LLM receives the denial as a tool error and can adapt (explain the restriction, try a different approach, etc.) without crashing the agent loop.
Features
- Tool permissions — allow or deny specific tools entirely (e.g. block
bashoutright) - Path permissions — allow or deny file system paths with prefix matching (longest match wins)
- Bash command permissions — allow or deny bash commands by pattern, with subcommand splitting
(
cd /safe && rm -rf /checkscdandrmindependently — deny wins) - Inline path detection — scans bash command strings for embedded paths (
cat /etc/passwd) - Cross-platform path normalization — Windows, Unix, and Unix-style drive paths all compared correctly
- Self-protecting — the permissions file itself is hardcoded-off-limits to the agent; cannot be overridden by any rule
- Parent directory protection — blocks deletion of any directory containing the permissions file
- Hot reload — optional interval-based reload for development; preserves current rules if the file is missing or invalid
- Graceful degradation — runs in
defaultBehavior-only mode when no storage plugin is found
Installation
npm install @fractal-synapse/security-permissions
Usage
Basic Setup
import { SecurityPermissionsPlugin } from '@fractal-synapse/security-permissions';
const securityPlugin = new SecurityPermissionsPlugin({
defaultBehavior: 'allow' // 'allow' | 'deny' — applied when no rule matches
});
const pluginRegistry = new PluginRegistry();
pluginRegistry.register(securityPlugin); // Register first — must run before other plugins
const agent = new Agent({
agentDefinition: new AgentDefinition(
'My Agent', 'Description', 'System prompt', [],
'anthropic-claude-sonnet',
['security-permissions', 'storage-fs'] // security-permissions must be listed first
),
pluginRegistry
});
Permissions File
Place security-permissions.json in the agent's storage directory:
- Windows:
%LOCALAPPDATA%\FractalSynapse\<app-name>\security-permissions.json - macOS:
~/Library/Application Support/FractalSynapse/<app-name>/security-permissions.json - Linux:
~/.fractal-synapse/<app-name>/security-permissions.json
The plugin logs the resolved storage path on initialization so you know where to place the file.
{
"version": 1,
"defaultBehavior": "deny",
"tools": {
"bash": "allow",
"edit-file": "allow",
"create-file": "allow",
"read-file": "allow",
"list-files": "allow"
},
"paths": [
{ "path": "/home/user/projects/myapp", "permission": "allow" },
{ "path": "/etc", "permission": "deny" },
{ "path": "/home/user/.ssh", "permission": "deny" }
],
"commands": [
{ "pattern": "ls", "permission": "allow" },
{ "pattern": "cat", "permission": "allow" },
{ "pattern": "grep", "permission": "allow" },
{ "pattern": "git", "permission": "allow" },
{ "pattern": "npm", "permission": "allow" },
{ "pattern": "rm", "permission": "deny" },
{ "pattern": "curl", "permission": "deny" },
{ "pattern": "wget", "permission": "deny" },
{ "pattern": "sudo", "permission": "deny" }
]
}
See security-permissions.example.json in this package for a ready-to-copy template.
Rule Evaluation Order
For each tool call, rules are evaluated in this order — first match wins:
- Hardcoded self-protection — deny any access to the permissions file or its parent directory (unconditional, cannot be overridden)
- Tool rule — check if the tool name has an explicit
allow/denyrule - Path rules — check named path args (longest prefix match wins)
- Bash inline paths — scan the command string for embedded path tokens
- Command rules — split on
&&,||,;,|and check each subcommand; deny wins defaultBehavior— applied if no rule matched
Configuration
new SecurityPermissionsPlugin({
defaultBehavior?: 'allow' | 'deny'; // Default: 'deny'
permissionsFile?: string; // Filename in storage dir. Default: 'security-permissions.json'
hotReload?: boolean; // Reload file on interval. Default: false
hotReloadInterval?: number; // Interval in ms when hotReload is true. Default: 5000
})
Hot Reload
When hotReload: true, the plugin polls the permissions file every hotReloadInterval ms and
applies any changes immediately. If the file is missing or invalid at reload time, the current
rules stay active and an error is logged — the agent is never left unprotected.
Intended for development and debugging. Disable in production once human-middleware or other runtime controls are in place.
const securityPlugin = new SecurityPermissionsPlugin({
defaultBehavior: 'allow',
hotReload: true,
hotReloadInterval: 3000 // check every 3 seconds
});
Security Model
What this plugin enforces
- Named path arguments in fs tools (
filePath,directoryPath,workingDirectory) - Embedded paths in bash command strings (
cat /etc/passwd,cd /safe && rm /unsafe) - Bash command patterns, evaluated per subcommand in chained commands
- The permissions file itself — always protected, regardless of rules
Known limitations (v1)
- Quoted paths containing spaces in bash commands are not extracted
- Environment variable expansion (
$HOME/secret) is not resolved before path checking - The
askpermission value is reserved for future human-middleware integration; currently treated asdeny
API Reference
Constructor
new SecurityPermissionsPlugin(config?: SecurityPermissionsConfig)
Runtime Rule Management
These methods update rules in memory and persist them to storage. Intended for future human-middleware integration.
addPathRule(path: string, permission: 'allow' | 'deny'): Promise<void>
addToolRule(toolName: string, permission: 'allow' | 'deny'): Promise<void>
addCommandRule(pattern: string, permission: 'allow' | 'deny'): Promise<void>
getData(): PermissionsData
Requirements
@fractal-synapse/agent-core— core agent functionality andbefore-toolhook@fractal-synapse/storage-fs(or another storage plugin) — for loading the permissions file
License
ISC