imageplus / claude-guardrails
Claude Code guardrails for ImagePlus Laravel projects: permission deny rules + PreToolUse hooks that block Vapor commands, .env access and WordPress/Android config and credential files, and self-install into each project's .claude/settings.json.
Package info
github.com/imageplus/claude-guardrails
Type:composer-plugin
pkg:composer/imageplus/claude-guardrails
Requires
- php: >=8.1
- composer-plugin-api: ^2.0
Requires (Dev)
- composer/composer: ^2.0
README
Centralised Claude Code guardrails for ImagePlus Laravel projects.
Installing this package into a project automatically wires four protections into
that project's .claude/settings.json:
- Vapor is blocked. Claude cannot run any
vaporcommand (deploy or otherwise). Deploys stay a manual, human-run action. .envis protected. Claude cannot read, edit, copy orsourcea real.envfile via its built-in tools or Bash. Template files (.env.example,.env.sample,.env.dist,.env.template) remain readable.- WordPress credentials are protected.
wp-config.phpand its per-environment variants (wp-config-local.php,wp-config-staging.php, and any otherwp-config-*.php),local-config.php,wp-salt.php,wp-cli.local.yml,~/.wp-cli/and.htpasswdare off-limits — they carry DB credentials, auth salts and host aliases.wp-config-sample.phpremains readable. - Android secrets are protected.
secrets.properties, are off-limits. A project's owngradle.properties,local.defaults.propertiesand the Gradle build files stay readable.
Enforcement is layered: static deny rules as a first line, plus PreToolUse
hooks (which block at exit code 2, before permission rules are even evaluated)
as the reliable line.
Installation
This is a private, first-party package. Add your internal repository to the
project's composer.json, then require it as a dev dependency:
composer require --dev imageplus/claude-guardrails
Because it is a Composer plugin, Composer 2.2+ will ask you to allow it to
run. Approve it, or pre-approve it in the project's composer.json:
{
"config": {
"allow-plugins": {
"imageplus/claude-guardrails": true
}
}
}
On the next composer install / composer update, the plugin merges its rules
into .claude/settings.json, creating the file (and the .claude/ directory)
if they don't exist. Commit .claude/settings.json so every teammate inherits
the same guardrails.
What lands in the project
.claude/settings.json gains (merged, not overwritten):
{
"permissions": {
"deny": [
"Bash(*vapor*)",
"Read(.env)",
"Read(**/.env)",
"Write(.claude/**)",
"Edit(.claude/**)",
"Write(vendor/imageplus/claude-guardrails/**)",
"Edit(vendor/imageplus/claude-guardrails/**)"
]
},
"hooks": {
"PreToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "php $CLAUDE_PROJECT_DIR/vendor/imageplus/claude-guardrails/hooks/block-vapor.php" }] },
{ "matcher": "Read|Edit|Write|Bash", "hooks": [{ "type": "command", "command": "php $CLAUDE_PROJECT_DIR/vendor/imageplus/claude-guardrails/hooks/protect-env.php" }] }
]
}
}
A sidecar file, .claude/.guardrails-managed.json, records which deny rules the
package owns so it can keep them in sync on future updates. Leave it in place.
Updating
Bump the version and run composer update imageplus/claude-guardrails across
your projects. The plugin re-syncs on every install/update: it strips the hook
entries and deny rules it previously added and writes the current set, so
changes here propagate everywhere without hand-editing any project.
Extending / changing what's blocked
Edit config/guardrails.json in this package:
deny— plain Claude Code permission strings. Use the__PACKAGE_PATH__token where you need this package's install path (the plugin substitutes the real relative path).hooks— each entry is{ "matcher": "...", "script": "..." }, wherescriptis a file inhooks/. Add a new PHP hook file and reference it here.
Hook scripts read the tool-call JSON from stdin (tool_name,
tool_input.command, tool_input.file_path) and exit 2 to block.
Verifying it works
In a project after install:
/permissions # confirm the deny rules loaded
/hooks # confirm both PreToolUse hooks are registered
Then ask Claude to run ./vendor/bin/vapor deploy production (should block) and
to cat .env (should block) versus cat .env.example (should succeed). On a
WordPress project, cat wp-config.php should block while
cat wp-config-sample.php succeeds.
Limitations (be honest about these)
- Hooks can't see a filename that never appears in the command — e.g.
php artisan config:cachereads.envinternally. That doesn't expose the contents to Claude, so it's fine, but it means "no process ever touches .env" is not what this provides. For OS-level enforcement, use Claude Code's sandbox. - Referenced scripts live in
vendor/, which Claude can normally write to; that's why the package denies writes/edits to its own directory. If you want the scripts inside the already-protected.claude/dir instead, switch the plugin to copy them rather than reference them (you lose one-command updates). denyrules have a history of reliability bugs in Claude Code; the hooks are the dependable layer and the reason they exist.- This package ships code that auto-executes in the dev loop. Keep it private and first-party, and review changes like any other security tooling.