security: add prompt injection guards, path traversal prevention, and input validation
Defense-in-depth security hardening for a codebase where markdown files become LLM system prompts. Adds centralized security module, PreToolUse hook for injection detection, and CI-ready codebase scan. New files: - security.cjs: path traversal prevention, prompt injection scanner/sanitizer, safe JSON parsing, field name validation, shell arg validation - gsd-prompt-guard.js: PreToolUse hook scans .planning/ writes for injection - security.test.cjs: 62 unit tests for all security functions - prompt-injection-scan.test.cjs: CI scan of all agent/workflow/command files Hardened code paths: - readTextArgOrFile: path traversal guard (--prd, --text-file) - cmdStateUpdate/Patch: field name validation prevents regex injection - cmdCommit: sanitizeForPrompt strips invisible chars from commit messages - gsd-tools --fields: safeJsonParse wraps unprotected JSON.parse - cmdFrontmatterGet/Set: null byte rejection - cmdVerifyPathExists: null byte rejection - install.js: registers prompt guard hook, updates uninstaller Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3123,7 +3123,7 @@ function uninstall(isGlobal, runtime = 'claude') {
|
||||
// 4. Remove GSD hooks
|
||||
const hooksDir = path.join(targetDir, 'hooks');
|
||||
if (fs.existsSync(hooksDir)) {
|
||||
const gsdHooks = ['gsd-statusline.js', 'gsd-check-update.js', 'gsd-check-update.sh', 'gsd-context-monitor.js'];
|
||||
const gsdHooks = ['gsd-statusline.js', 'gsd-check-update.js', 'gsd-check-update.sh', 'gsd-context-monitor.js', 'gsd-prompt-guard.js'];
|
||||
let hookCount = 0;
|
||||
for (const hook of gsdHooks) {
|
||||
const hookPath = path.join(hooksDir, hook);
|
||||
@@ -3214,6 +3214,27 @@ function uninstall(isGlobal, runtime = 'claude') {
|
||||
}
|
||||
}
|
||||
|
||||
// Remove GSD hooks from PreToolUse (prompt injection guard)
|
||||
if (settings.hooks && settings.hooks.PreToolUse) {
|
||||
const before = settings.hooks.PreToolUse.length;
|
||||
settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(entry => {
|
||||
if (entry.hooks && Array.isArray(entry.hooks)) {
|
||||
const hasGsdHook = entry.hooks.some(h =>
|
||||
h.command && h.command.includes('gsd-prompt-guard')
|
||||
);
|
||||
return !hasGsdHook;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (settings.hooks.PreToolUse.length < before) {
|
||||
settingsModified = true;
|
||||
console.log(` ${green}✓${reset} Removed prompt injection guard hook from settings`);
|
||||
}
|
||||
if (settings.hooks.PreToolUse.length === 0) {
|
||||
delete settings.hooks.PreToolUse;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up empty hooks object
|
||||
if (settings.hooks && Object.keys(settings.hooks).length === 0) {
|
||||
delete settings.hooks;
|
||||
@@ -4007,6 +4028,9 @@ function install(isGlobal, runtime = 'claude') {
|
||||
const contextMonitorCommand = isGlobal
|
||||
? buildHookCommand(targetDir, 'gsd-context-monitor.js')
|
||||
: 'node ' + dirName + '/hooks/gsd-context-monitor.js';
|
||||
const promptGuardCommand = isGlobal
|
||||
? buildHookCommand(targetDir, 'gsd-prompt-guard.js')
|
||||
: 'node ' + dirName + '/hooks/gsd-prompt-guard.js';
|
||||
|
||||
// Enable experimental agents for Gemini CLI (required for custom sub-agents)
|
||||
if (isGemini) {
|
||||
@@ -4086,6 +4110,30 @@ function install(isGlobal, runtime = 'claude') {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Configure PreToolUse hook for prompt injection detection
|
||||
const preToolEvent = 'PreToolUse';
|
||||
if (!settings.hooks[preToolEvent]) {
|
||||
settings.hooks[preToolEvent] = [];
|
||||
}
|
||||
|
||||
const hasPromptGuardHook = settings.hooks[preToolEvent].some(entry =>
|
||||
entry.hooks && entry.hooks.some(h => h.command && h.command.includes('gsd-prompt-guard'))
|
||||
);
|
||||
|
||||
if (!hasPromptGuardHook) {
|
||||
settings.hooks[preToolEvent].push({
|
||||
matcher: 'Write|Edit',
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: promptGuardCommand,
|
||||
timeout: 5
|
||||
}
|
||||
]
|
||||
});
|
||||
console.log(` ${green}✓${reset} Configured prompt injection guard hook`);
|
||||
}
|
||||
}
|
||||
|
||||
return { settingsPath, settings, statuslineCommand, runtime };
|
||||
|
||||
Reference in New Issue
Block a user