Skip to content

Commit 79ec176

Browse files
fix(integrity): adapt verify_hook + rtk verify for native rtk hook claude
- verify_hook() now checks ~/.claude/settings.json for "rtk hook claude" instead of looking for the rtk-rewrite.sh file + SHA-256 hash - Verified = rtk hook claude present in PreToolUse hooks - NoBaseline = legacy rtk-rewrite.sh detected (still works, needs migration) - NotInstalled = neither hook registered - run_verify() output updated to reflect settings.json check - runtime_check() simplified (all settings.json states are non-blocking) - show_claude_config() (rtk init --show) now reports hook presence from settings.json instead of the old file-based integrity check - verify_hook_at() + read_stored_hash() gated #[cfg(test)] (legacy SHA-256 logic only needed by existing hash-based tests) - 7 new tests for verify_hook_in_settings() covering: native hook, legacy hook, missing file, empty object, no hooks key, no PreToolUse, unrelated cmd Fixes: rtk verify reporting "SKIP RTK hook not installed" even when rtk hook claude is registered; rtk init --show showing stale hook file status Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Signed-off-by: Florian BRUNIAUX <florian@bruniaux.com>
1 parent 9f9c022 commit 79ec176

File tree

2 files changed

+209
-157
lines changed

2 files changed

+209
-157
lines changed

src/init.rs

Lines changed: 16 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,54 +1788,24 @@ fn show_claude_config() -> Result<()> {
17881788

17891789
println!("rtk Configuration:\n");
17901790

1791-
// Check hook
1792-
if hook_path.exists() {
1793-
#[cfg(unix)]
1794-
{
1795-
use std::os::unix::fs::PermissionsExt;
1796-
let metadata = fs::metadata(&hook_path)?;
1797-
let perms = metadata.permissions();
1798-
let is_executable = perms.mode() & 0o111 != 0;
1799-
1800-
let hook_content = fs::read_to_string(&hook_path)?;
1801-
let has_guards =
1802-
hook_content.contains("command -v rtk") && hook_content.contains("command -v jq");
1803-
let is_thin_delegator = hook_content.contains("rtk rewrite");
1804-
let hook_version = crate::hook_check::parse_hook_version(&hook_content);
1805-
1806-
if !is_executable {
1807-
println!(
1808-
"[warn] Hook: {} (NOT executable - run: chmod +x)",
1809-
hook_path.display()
1810-
);
1811-
} else if !is_thin_delegator {
1812-
println!(
1813-
"[warn] Hook: {} (outdated — inline logic, not thin delegator)",
1814-
hook_path.display()
1815-
);
1816-
println!(
1817-
" → Run `rtk init --global` to upgrade to the single source of truth hook"
1818-
);
1819-
} else if is_executable && has_guards {
1820-
println!(
1821-
"[ok] Hook: {} (thin delegator, version {})",
1822-
hook_path.display(),
1823-
hook_version
1824-
);
1825-
} else {
1826-
println!(
1827-
"[warn] Hook: {} (no guards - outdated)",
1828-
hook_path.display()
1829-
);
1830-
}
1791+
// Check hook presence in settings.json
1792+
match integrity::verify_hook() {
1793+
Ok(integrity::IntegrityStatus::Verified) => {
1794+
println!("[ok] Hook: rtk hook claude registered in settings.json");
18311795
}
1832-
1833-
#[cfg(not(unix))]
1834-
{
1835-
println!("[ok] Hook: {} (exists)", hook_path.display());
1796+
Ok(integrity::IntegrityStatus::NoBaseline) => {
1797+
println!("[warn] Hook: legacy rtk-rewrite.sh detected (run: rtk init -g to migrate)");
1798+
}
1799+
Ok(integrity::IntegrityStatus::NotInstalled) => {
1800+
println!("[--] Hook: not registered in settings.json (run: rtk init -g)");
1801+
}
1802+
Ok(integrity::IntegrityStatus::Tampered { .. })
1803+
| Ok(integrity::IntegrityStatus::OrphanedHash) => {
1804+
// Cannot occur from settings.json check
1805+
}
1806+
Err(_) => {
1807+
println!("[warn] Hook: settings.json check failed");
18361808
}
1837-
} else {
1838-
println!("[--] Hook: not found");
18391809
}
18401810

18411811
// Check RTK.md
@@ -1845,26 +1815,6 @@ fn show_claude_config() -> Result<()> {
18451815
println!("[--] RTK.md: not found");
18461816
}
18471817

1848-
// Check hook integrity
1849-
match integrity::verify_hook_at(&hook_path) {
1850-
Ok(integrity::IntegrityStatus::Verified) => {
1851-
println!("[ok] Integrity: hook hash verified");
1852-
}
1853-
Ok(integrity::IntegrityStatus::Tampered { .. }) => {
1854-
println!("[FAIL] Integrity: hook modified outside rtk init (run: rtk verify)");
1855-
}
1856-
Ok(integrity::IntegrityStatus::NoBaseline) => {
1857-
println!("[warn] Integrity: no baseline hash (run: rtk init -g to establish)");
1858-
}
1859-
Ok(integrity::IntegrityStatus::NotInstalled)
1860-
| Ok(integrity::IntegrityStatus::OrphanedHash) => {
1861-
// Don't show integrity line if hook isn't installed
1862-
}
1863-
Err(_) => {
1864-
println!("[warn] Integrity: check failed");
1865-
}
1866-
}
1867-
18681818
// Check global CLAUDE.md
18691819
if global_claude_md.exists() {
18701820
let content = fs::read_to_string(&global_claude_md)?;

0 commit comments

Comments
 (0)