Skip to content

Add PreToolUse:Bash hook for OPC script directory guard#160

Open
yurukusa wants to merge 1 commit intoparcadei:mainfrom
yurukusa:fix/opc-directory-guard
Open

Add PreToolUse:Bash hook for OPC script directory guard#160
yurukusa wants to merge 1 commit intoparcadei:mainfrom
yurukusa:fix/opc-directory-guard

Conversation

@yurukusa
Copy link
Copy Markdown

@yurukusa yurukusa commented Mar 14, 2026

Summary

Fixes #148

Adds a PreToolUse:Bash hook that prevents scripts/(mcp|core)/ from running outside the OPC directory. Without this guard, uv run misses opc/pyproject.toml and its dependencies, causing ModuleNotFoundError.

  • Detects Bash commands referencing scripts/(mcp|core)/ paths
  • Blocks execution unless prefixed with cd $CLAUDE_OPC_DIR && (or the resolved path)
  • Returns a deny message with the corrected command so Claude can retry immediately

Example: blocked

uv run python scripts/mcp/research.py --topic "foo"

Deny reason includes corrected command:

cd $CLAUDE_OPC_DIR && uv run python scripts/mcp/research.py --topic "foo"

Example: allowed

cd $CLAUDE_OPC_DIR && uv run python scripts/mcp/research.py --topic "foo"
cd /home/user/project/opc && uv run python scripts/core/init.py

Files changed

  • .claude/hooks/src/mcp-directory-guard.ts — hook implementation using shared getOpcDir() and PreToolUseHookOutput types
  • .claude/hooks/dist/mcp-directory-guard.mjs — esbuild bundle
  • .claude/settings.json — registered under PreToolUse with "matcher": "Bash"

Test plan

  • Run a Bash command with scripts/mcp/ path without cd prefix — should be denied with corrected suggestion
  • Run the same command with cd $CLAUDE_OPC_DIR && prefix — should pass through
  • Run a Bash command that doesn't reference scripts/(mcp|core)/ — should pass through (no-op)
  • Verify hook exits cleanly when OPC dir is not available (non-CC projects)

Summary by CodeRabbit

  • New Features
    • Added directory validation for script execution that prevents unintended execution from incorrect working directories and automatically corrects command paths when necessary.

Prevents running scripts/(mcp|core)/ from wrong directory by requiring
cd $CLAUDE_OPC_DIR && prefix. Without this, uv run misses pyproject.toml
and causes ModuleNotFoundError.

Fixes parcadei#148

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 14, 2026

PR author is not in the allowed authors list.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 14, 2026

📝 Walkthrough

Walkthrough

A new Bash pre-tool hook is introduced that prevents unintended execution of scripts in scripts/mcp/ or scripts/core/ directories. The hook validates that such commands are prefixed with a directory change to the OPC directory before allowing execution, or returns a corrected command otherwise. The hook is registered in the PreToolUse configuration.

Changes

Cohort / File(s) Summary
Bash Hook Implementation
.claude/hooks/src/mcp-directory-guard.ts
New TypeScript hook that reads PreToolUseInput from stdin, detects scripts/mcp/ or scripts/core/ references in Bash commands, and denies execution unless prefixed with cd $CLAUDE_OPC_DIR &&. Returns a corrected command with the required prefix or an empty object if the command is already properly prefixed or has no script references.
Hook Configuration
.claude/settings.json
Registers the new mcp-directory-guard hook in the PreToolUse configuration with a Bash matcher and 5-second timeout.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add PreToolUse:Bash hook for OPC script directory guard' directly describes the main change: introducing a new Bash pre-tool hook to guard OPC script directories.
Linked Issues check ✅ Passed The PR fully implements all requirements from #148: detects scripts/(mcp|core)/ references, blocks execution without cd $CLAUDE_OPC_DIR && prefix, returns deny messages with corrected commands, and registers the hook in settings.json.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the PreToolUse:Bash hook as specified in #148; no unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

CodeRabbit can enforce grammar and style rules using `languagetool`.

Configure the reviews.tools.languagetool setting to enable/disable rules and categories. Refer to the LanguageTool Community to learn more.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/hooks/src/mcp-directory-guard.ts:
- Around line 33-44: The regex in buildCdPrefixPattern currently doesn't allow
cd targets wrapped in quotes and will fail to recognize commands like cd
"$CLAUDE_OPC_DIR" && ...; update the pattern to accept optional single or double
quotes around the variants (both '\\$CLAUDE_OPC_DIR', '\\$\\{CLAUDE_OPC_DIR\\}'
and escapedDir) so quoted forms match, and wherever the code constructs/suggests
a corrected cd target (the place that uses escapedDir to emit a replacement),
ensure the resolved OPC path is properly escaped and wrapped in quotes so paths
with spaces produce a valid cd "path" && ... suggestion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a3243b87-50d4-4533-bc12-20eb22d73065

📥 Commits

Reviewing files that changed from the base of the PR and between d07ff4b and 8847924.

⛔ Files ignored due to path filters (1)
  • .claude/hooks/dist/mcp-directory-guard.mjs is excluded by !**/dist/**, !**/dist/**, !**/.claude/hooks/dist/**
📒 Files selected for processing (2)
  • .claude/hooks/src/mcp-directory-guard.ts
  • .claude/settings.json

Comment on lines +33 to +44
function buildCdPrefixPattern(opcDir: string | null): RegExp {
const escapedDir = opcDir ? opcDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') : '';
// Match: cd <opc-dir-variant> && (with flexible whitespace)
const variants = [
'\\$CLAUDE_OPC_DIR',
'\\$\\{CLAUDE_OPC_DIR\\}',
];
if (escapedDir) {
variants.push(escapedDir);
}
return new RegExp(`^\\s*cd\\s+(${variants.join('|')})\\s*&&`);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Allow quoted cd prefixes and quote corrected cd target.

Line 43 currently rejects valid forms like cd "$CLAUDE_OPC_DIR" && ..., and Line 87 can emit a broken command when the resolved OPC path contains spaces. This can deny correct commands and suggest unusable retries.

🔧 Proposed fix
 function buildCdPrefixPattern(opcDir: string | null): RegExp {
   const escapedDir = opcDir ? opcDir.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') : '';
   // Match: cd <opc-dir-variant> && (with flexible whitespace)
   const variants = [
     '\\$CLAUDE_OPC_DIR',
     '\\$\\{CLAUDE_OPC_DIR\\}',
   ];
   if (escapedDir) {
     variants.push(escapedDir);
   }
-  return new RegExp(`^\\s*cd\\s+(${variants.join('|')})\\s*&&`);
+  const target = `(?:${variants.join('|')})`;
+  // allow both: cd $CLAUDE_OPC_DIR && ... and cd "$CLAUDE_OPC_DIR" && ...
+  return new RegExp(`^\\s*cd\\s+(?:(["'])${target}\\1|${target})\\s*&&`);
 }
+
+function quoteShellArg(value: string): string {
+  return "'" + value.replace(/'/g, "'\\''") + "'";
+}
@@
-  const dirRef = opcDir || '$CLAUDE_OPC_DIR';
+  const dirRef = opcDir ? quoteShellArg(opcDir) : '"$CLAUDE_OPC_DIR"';
   const corrected = `cd ${dirRef} && ${command.trimStart()}`;

Also applies to: 86-87

🧰 Tools
🪛 ast-grep (0.41.1)

[warning] 42-42: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(^\\s*cd\\s+(${variants.join('|')})\\s*&&)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/hooks/src/mcp-directory-guard.ts around lines 33 - 44, The regex in
buildCdPrefixPattern currently doesn't allow cd targets wrapped in quotes and
will fail to recognize commands like cd "$CLAUDE_OPC_DIR" && ...; update the
pattern to accept optional single or double quotes around the variants (both
'\\$CLAUDE_OPC_DIR', '\\$\\{CLAUDE_OPC_DIR\\}' and escapedDir) so quoted forms
match, and wherever the code constructs/suggests a corrected cd target (the
place that uses escapedDir to emit a replacement), ensure the resolved OPC path
is properly escaped and wrapped in quotes so paths with spaces produce a valid
cd "path" && ... suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PreToolUse:Bash hook - OPC script directory guard

1 participant