mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
test(hooks): guard PreToolUse gate shim against predictable temp file (#384)
The symlink-attack vector (predictable /tmp/cbm-code-discovery-gate-$PPID)
was removed in c29e6d5 when the blocking bash gate became a stateless
compiled augmenter writing to stdout only. Add a regression guard so a
predictable temp/state file can never be reintroduced: assert the emitted
gate shim contains no /tmp path and no $PPID, and delegates to hook-augment.
Export cbm_install_hook_gate_script (was static) so the security property
is directly testable.
This commit is contained in:
1 parent
f88d0315ac
commit
61453e928f
3 files changed
+33
-1
No files matched your search
+1
-1
@@ -1746,7 +1746,7 @@ int cbm_remove_claude_hooks(const char *settings_path) {
|
||||
* a missing/old/hung binary results in a silent exit 0 (issue #362/#288).
|
||||
* The legacy filename `cbm-code-discovery-gate` is retained so existing
|
||||
* settings.json entries and uninstall keep working with zero migration. */
|
||||
static void cbm_install_hook_gate_script(const char *home, const char *binary_path) {
|
||||
void cbm_install_hook_gate_script(const char *home, const char *binary_path) {
|
||||
if (!home || !binary_path) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -175,6 +175,12 @@ int cbm_upsert_claude_hooks(const char *settings_path);
|
||||
* Returns 0 on success. */
|
||||
int cbm_remove_claude_hooks(const char *settings_path);
|
||||
|
||||
/* Write the PreToolUse gate shim to <home>/.claude/hooks/. The shim is a thin
|
||||
* wrapper that invokes the compiled `hook-augment` and writes to stdout only —
|
||||
* it must never create a predictable temp/state file (issue #384). Exposed for
|
||||
* testing that security property. */
|
||||
void cbm_install_hook_gate_script(const char *home, const char *binary_path);
|
||||
|
||||
/* Upsert a BeforeTool hook in ~/.gemini/settings.json for Gemini CLI / Antigravity.
|
||||
* Returns 0 on success. */
|
||||
int cbm_upsert_gemini_hooks(const char *settings_path);
|
||||
|
||||
@@ -1988,6 +1988,31 @@ TEST(cli_upsert_claude_hook_fresh) {
|
||||
PASS();
|
||||
}
|
||||
|
||||
/* issue #384: the PreToolUse gate shim must never use a predictable /tmp
|
||||
* filename (the old `/tmp/cbm-code-discovery-gate-$PPID` was a symlink-attack
|
||||
* vector). The shim is now a stateless wrapper around the compiled augmenter. */
|
||||
TEST(cli_hook_gate_script_no_predictable_tmp_issue384) {
|
||||
char tmpdir[256];
|
||||
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-gate-XXXXXX");
|
||||
if (!cbm_mkdtemp(tmpdir))
|
||||
SKIP("cbm_mkdtemp failed");
|
||||
|
||||
cbm_install_hook_gate_script(tmpdir, "/usr/local/bin/codebase-memory-mcp");
|
||||
|
||||
char script_path[512];
|
||||
snprintf(script_path, sizeof(script_path), "%s/.claude/hooks/cbm-code-discovery-gate", tmpdir);
|
||||
const char *data = read_test_file(script_path);
|
||||
ASSERT_NOT_NULL(data);
|
||||
/* No predictable temp/state file and no PPID-derived path. */
|
||||
ASSERT(strstr(data, "/tmp") == NULL);
|
||||
ASSERT(strstr(data, "PPID") == NULL);
|
||||
/* It delegates to the stateless compiled augmenter (stdout only). */
|
||||
ASSERT(strstr(data, "hook-augment") != NULL);
|
||||
|
||||
test_rmdir_r(tmpdir);
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST(cli_upsert_claude_hook_existing) {
|
||||
char tmpdir[256];
|
||||
snprintf(tmpdir, sizeof(tmpdir), "/tmp/cli-hook-XXXXXX");
|
||||
@@ -2548,6 +2573,7 @@ SUITE(cli) {
|
||||
RUN_TEST(cli_agent_instructions_content);
|
||||
|
||||
/* Claude Code hooks (5 tests — group D) */
|
||||
RUN_TEST(cli_hook_gate_script_no_predictable_tmp_issue384);
|
||||
RUN_TEST(cli_upsert_claude_hook_fresh);
|
||||
RUN_TEST(cli_upsert_claude_hook_existing);
|
||||
RUN_TEST(cli_upsert_claude_hook_replace);
|
||||
|
||||
Reference in new issue
Block a user