Files
fleet-dotfiles-template/dot_claude/hooks/executable_validate-path.js
T
Anthony Cardinale ebccdda936 Initial public release
A chezmoi-based fleet-dotfiles template for macOS workstations:

- Two-way auto-sync via launchd watcher + 5-min puller
- Mesh SSH via modify_authorized_keys driven by .chezmoidata/fleet.yaml
- age-encrypted secrets file
- Bundled Claude Code agentic team (11 agents) + /lite + /lite-sub commands
- Verify-before-claiming Stop hook
- Generic statusline + project-boundary validate-path hook
- Reference launchd plist for cross-fleet task-durations aggregation
  (companion repo: gitea.tojo.team/cardinale/task-durations)
- AGENTS.md walks an agent through the entire setup Q&A interactively
- docs/ covers architecture, security model, fleet onboarding
2026-05-02 17:26:32 -04:00

44 lines
1.3 KiB
JavaScript

#!/usr/bin/env node
// Hook script to validate file paths are within PROJECT_PATH
// Generic project-boundary enforcement: requires PROJECT_PATH env var to be set;
const path = require('path');
const projectPath = process.env.PROJECT_PATH;
if (!projectPath) process.exit(0);
let input = '';
process.stdin.setEncoding('utf8');
process.stdin.on('data', chunk => { input += chunk; });
process.stdin.on('end', () => {
try {
const data = JSON.parse(input);
const tool = data.tool_name || data.tool || '';
const toolInput = data.tool_input || {};
let filePath = '';
if (['Write', 'Edit', 'Read'].includes(tool)) {
filePath = toolInput.file_path || '';
} else {
process.exit(0);
}
if (!filePath) process.exit(0);
const resolvedFile = path.resolve(filePath);
const resolvedProject = path.resolve(projectPath);
if (resolvedFile.startsWith(resolvedProject + path.sep) || resolvedFile === resolvedProject) {
process.exit(0);
}
if (resolvedFile.startsWith('/tmp/') || resolvedFile.startsWith('/tmp')) {
process.exit(0);
}
process.stderr.write('Security: Cannot access "' + filePath + '" - outside project directory "' + projectPath + '"\n');
process.exit(2);
} catch (e) {
process.exit(0);
}
});