From 337ca46c2515a8ed2ca684257bab5a52b3cf592f Mon Sep 17 00:00:00 2001 From: timoconnellaus Date: Tue, 8 Jul 2025 13:17:45 +1000 Subject: [PATCH 1/2] updated new-hook command --- .claude/commands/new-hook.md | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/.claude/commands/new-hook.md b/.claude/commands/new-hook.md index ec2ebf1..4b86ad9 100644 --- a/.claude/commands/new-hook.md +++ b/.claude/commands/new-hook.md @@ -86,7 +86,16 @@ Hooks can return structured responses: suppressOutput?: boolean } -// Other hooks can return: +// Stop/SubagentStop can return: +{ + decision?: 'block', + reason?: string, + continue?: boolean, + stopReason?: string, + suppressOutput?: boolean +} + +// Notification can return: { continue?: boolean, stopReason?: string, @@ -139,22 +148,31 @@ export { blockDangerousCommands } from './hooks/blockDangerousCommands'; - Each hook type has its own specific input type - Share common logic between related hooks when appropriate -### 5. Update Documentation +### 5. Update Exports + +After creating a new hook, update the `src/index.ts` file to export it: + +```typescript +// Add your export to src/index.ts +export { myHookName } from './hooks/myHookName'; +``` + +### 6. Update Documentation -After creating a new hook, update the README.md file to document it: +After creating and exporting the hook, update the README.md file to document it: 1. Add the new hook to the "Predefined Hook Utilities" section 2. Include usage examples showing how to import and use the hook 3. Document any configuration options 4. Show example output if the hook produces logs or other artifacts -### 6. Using the Hook +### 7. Using the Hook Users can then import and use the hook: ```typescript // .claude/hooks/hooks.ts -import { defineHooks, blockDangerousCommands } from 'define-claude-code-hooks'; +import { defineHooks, blockDangerousCommands } from '@timoaus/define-claude-code-hooks'; export default defineHooks({ PreToolUse: [ @@ -164,7 +182,7 @@ export default defineHooks({ }); ``` -### 7. Common Patterns and Best Practices +### 8. Common Patterns and Best Practices **File I/O in hooks:** - Use `process.cwd()` to get the current working directory for file paths @@ -198,7 +216,7 @@ export default defineHooks({ - Clean up test artifacts after running - Test edge cases like file size limits and error conditions -### 8. Advanced Hook Patterns +### 9. Advanced Hook Patterns **Tool hooks with matchers:** - Tool hooks (PreToolUse, PostToolUse) require both `matcher` and `handler` From eb36e9b9533400de41ed70cfd6d9ebc7a267a02f Mon Sep 17 00:00:00 2001 From: timoconnellaus Date: Tue, 8 Jul 2025 13:23:18 +1000 Subject: [PATCH 2/2] feat: add blockEnvFiles hook to prevent access to .env files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Creates new predefined hook that blocks reading/writing of .env files - Allows access to example env files (.env.example, .env.sample, etc.) - Works with Read, Write, Edit, and MultiEdit tools - Includes comprehensive documentation and usage examples 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 24 ++++++++++++++++ src/hooks/blockEnvFiles.ts | 59 ++++++++++++++++++++++++++++++++++++++ src/index.ts | 1 + 3 files changed, 84 insertions(+) create mode 100644 src/hooks/blockEnvFiles.ts diff --git a/README.md b/README.md index 5052a6f..3f69236 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ The library includes several predefined hook utilities for common logging scenar | **`logStopEvents`**
Logs main agent stop events | • `maxEventsStored` (default: 100)
• `logFileName` (default: 'hook-log.stop.json') | | **`logSubagentStopEvents`**
Logs subagent stop events | • `maxEventsStored` (default: 100)
• `logFileName` (default: 'hook-log.stop.json') | | **`logNotificationEvents`**
Logs notification messages | • `maxEventsStored` (default: 100)
• `logFileName` (default: 'hook-log.notification.json') | +| **`blockEnvFiles`**
Blocks access to .env files | No options - blocks all .env file variants except example files | All predefined hooks: - Create JSON log files in your current working directory @@ -326,6 +327,27 @@ export default defineHooks({ }); ``` +### Environment File Protection + +```typescript +import { + defineHooks, + blockEnvFiles, +} from "@timoaus/define-claude-code-hooks"; + +export default defineHooks({ + PreToolUse: [ + blockEnvFiles, // Blocks access to .env files while allowing .env.example + ], +}); +``` + +The `blockEnvFiles` hook: +- Blocks reading or writing to `.env` files and variants (`.env.local`, `.env.production`, etc.) +- Allows access to example env files (`.env.example`, `.env.sample`, `.env.template`, `.env.dist`) +- Works with `Read`, `Write`, `Edit`, and `MultiEdit` tools +- Provides clear error messages when access is blocked + ### Combining Multiple Hooks ```typescript @@ -334,10 +356,12 @@ import { logStopEvents, logPreToolUseEvents, logPostToolUseEvents, + blockEnvFiles, } from "@timoaus/define-claude-code-hooks"; export default defineHooks({ PreToolUse: [ + blockEnvFiles, // Security: prevent .env file access logPreToolUseEvents({ logFileName: "hook-log.tool-use.json" }), // Add your custom hooks here { diff --git a/src/hooks/blockEnvFiles.ts b/src/hooks/blockEnvFiles.ts new file mode 100644 index 0000000..fefa490 --- /dev/null +++ b/src/hooks/blockEnvFiles.ts @@ -0,0 +1,59 @@ +import { defineHook } from '../index'; +import { PreToolUseInput } from '../types'; + +/** + * Blocks reading or writing of .env files and variants (e.g., .env.local, .env.production) + * while allowing example env files (e.g., .env.example, .env.sample) + * + * @example + * ```typescript + * import { defineHooks, blockEnvFiles } from 'define-claude-code-hooks'; + * + * export default defineHooks({ + * PreToolUse: [ + * blockEnvFiles, + * // other hooks... + * ] + * }); + * ``` + */ +export const blockEnvFiles = defineHook('PreToolUse', { + matcher: 'Read|Write|Edit|MultiEdit', + handler: async (input: PreToolUseInput) => { + const toolName = input.tool_name; + let filePath: string | undefined; + + // Extract file path based on tool + if (toolName === 'Read' || toolName === 'Write') { + filePath = input.tool_input.file_path; + } else if (toolName === 'Edit' || toolName === 'MultiEdit') { + filePath = input.tool_input.file_path; + } + + if (!filePath) { + return; // No file path to check + } + + // Normalize the file path for consistent checking + const normalizedPath = filePath.toLowerCase(); + + // Check if this is an example env file (allowed) + const isExampleFile = /\.env\.(example|sample|template|dist)$/i.test(filePath) || + /\.env\..*\.(example|sample|template|dist)$/i.test(filePath); + + if (isExampleFile) { + return; // Allow example env files + } + + // Check if this is a .env file or variant (blocked) + const isEnvFile = /\.env$/i.test(filePath) || // .env + /\.env\.[^.]+$/i.test(filePath); // .env.local, .env.production, etc. + + if (isEnvFile) { + return { + decision: 'block' as const, + reason: `Access to .env files is not allowed. File: ${filePath}. If you need to show an example, use .env.example or .env.sample instead.` + }; + } + } +}); \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ca1127e..2230ab4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ export { logPreToolUseEvents, logPostToolUseEvents } from './hooks/logToolUseEvents'; +export { blockEnvFiles } from './hooks/blockEnvFiles'; /** * Define a typed hook handler for Claude Code