mirror of
https://github.com/J3vb/OwnCord.git
synced 2026-09-03 03:50:00 +03:00
chore(claude): session-start + pre-bash hooks, deny .env reads; durationcheck + noImplicitOverride (#1463)
* chore(claude): session-start and pre-bash hooks, deny .env reads scripts/claude-hook.mjs, wired in .claude/settings.json: - SessionStart warns when core.hooksPath is not .githooks, so a clone or a new machine cannot silently run without the repo git hooks. - PreToolUse on Bash refuses a top-level cd: the tool's shell is persistent, so a cd leaks into every later command and a gate can report green from the wrong directory. Subshells, git -C and root-relative paths pass. permissions.deny gains Read(**/.env): the gitignored env files never enter the model's context. Server/.env.example stays readable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jwaz4CHGAz85Rpypjvto5a * chore(lint): durationcheck on the server, noImplicitOverride on the client Both measured at zero hits on dev, so they cost nothing today and only block regressions: a Duration multiplied by a Duration-typed value, and an override left behind when its base method is renamed. rowserrcheck and sqlclosecheck were measured too and rejected: their six production hits are all correct code (rows.Err is checked inside scanEventRows behind the rowsScanner interface; the three Close sites close on every path by hand). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Jwaz4CHGAz85Rpypjvto5a --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
+25
-1
@@ -8,7 +8,31 @@
|
||||
"Edit(Client/src/lib/protocolTypes.ts)",
|
||||
"Write(Client/src/lib/protocolTypes.ts)",
|
||||
"Edit(Client/src/generated/**)",
|
||||
"Write(Client/src/generated/**)"
|
||||
"Write(Client/src/generated/**)",
|
||||
"Read(**/.env)"
|
||||
]
|
||||
},
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PROJECT_DIR}/scripts/claude-hook.mjs\" session-start"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PROJECT_DIR}/scripts/claude-hook.mjs\" pre-bash"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
@@ -28,6 +28,7 @@ linters:
|
||||
# Silent-drop and sentinel-error classes (added 2026-08-30).
|
||||
- exhaustive # a switch over an enum-like type with no default that misses a member
|
||||
- errorlint # errors.Is/As instead of == and type assertions on wrapped errors; %w in Errorf
|
||||
- durationcheck # time.Duration multiplied by a Duration-typed value, e.g. d*time.Second where d is already a Duration
|
||||
|
||||
settings:
|
||||
exhaustive:
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Claude Code hooks for this repository, wired in .claude/settings.json.
|
||||
//
|
||||
// session-start warn when the repo git hooks are not installed, so a clone
|
||||
// or a new machine cannot silently run without them.
|
||||
// pre-bash refuse a top-level `cd`. The Bash tool's shell is
|
||||
// persistent, so a `cd` leaks its directory into every later
|
||||
// command; a relative path then resolves somewhere else and a
|
||||
// gate can report a green result from the wrong directory.
|
||||
// Use a subshell `( cd DIR && ... )`, `git -C DIR`, or paths
|
||||
// from the repository root instead.
|
||||
//
|
||||
// Exit code 2 from a PreToolUse hook blocks the tool call and shows stderr to
|
||||
// the model; anything else lets it through.
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { readFileSync } from "node:fs";
|
||||
|
||||
const mode = process.argv[2];
|
||||
|
||||
if (mode === "session-start") {
|
||||
let hooksPath = "";
|
||||
try {
|
||||
hooksPath = execFileSync("git", ["config", "core.hooksPath"], {
|
||||
encoding: "utf8",
|
||||
stdio: ["ignore", "pipe", "ignore"],
|
||||
}).trim();
|
||||
} catch {
|
||||
// unset: git exits 1
|
||||
}
|
||||
if (hooksPath !== ".githooks") {
|
||||
console.log(
|
||||
"WARNING: the repo git hooks are not installed (core.hooksPath is not .githooks). Run `npm run hooks:install` at the repository root.",
|
||||
);
|
||||
}
|
||||
} else if (mode === "pre-bash") {
|
||||
let command = "";
|
||||
try {
|
||||
command = JSON.parse(readFileSync(0, "utf8")).tool_input?.command ?? "";
|
||||
} catch {
|
||||
// no or malformed input: nothing to check
|
||||
}
|
||||
// A `cd` that starts the command or follows a chain operator or newline is a
|
||||
// top-level statement; `( cd DIR && ... )` is not matched.
|
||||
if (/(^|\n|&&|\|\||;)\s*cd(\s|$)/.test(command)) {
|
||||
console.error(
|
||||
"Blocked: a top-level `cd` leaks the persistent shell cwd into every later command. Use a subshell `( cd DIR && ... )`, `git -C DIR`, or paths from the repository root.",
|
||||
);
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user