Skip to content

Commit 75d0c60

Browse files
committed
fix(gatekeeper): handle invalid JSON input and unhandled rejections
- Fix crash on valid JSON non-object input (e.g. null, 42, []) - Report invalid JSON to stderr with exit code 1 instead of silent exit 0 - Add .catch() handler to main() for unhandled promise rejections - Document empty stdin passthrough intent with comment
1 parent 8e6e857 commit 75d0c60

2 files changed

Lines changed: 33 additions & 10 deletions

File tree

plugins/gatekeeper/dist/pre-tool-use.js

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,19 +86,31 @@ async function main() {
8686
if (!raw.trim()) {
8787
process.exit(0);
8888
}
89-
let input;
89+
let parsed;
9090
try {
91-
input = JSON.parse(raw);
92-
} catch {
93-
process.exit(0);
91+
parsed = JSON.parse(raw);
92+
} catch (err) {
93+
process.stderr.write(`gatekeeper: invalid JSON input: ${err instanceof Error ? err.message : String(err)}
94+
`);
95+
process.exit(1);
96+
}
97+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
98+
process.stderr.write(`gatekeeper: expected JSON object, got ${parsed === null ? "null" : typeof parsed}
99+
`);
100+
process.exit(1);
94101
}
102+
const input = parsed;
95103
const decision = evaluate(input);
96104
if (decision) {
97105
process.stdout.write(JSON.stringify(decision));
98106
}
99107
process.exit(0);
100108
}
101-
main();
109+
main().catch((err) => {
110+
process.stderr.write(`gatekeeper: unexpected error: ${err instanceof Error ? err.message : String(err)}
111+
`);
112+
process.exit(1);
113+
});
102114
export {
103115
makeDecision,
104116
isGitPushNonForce,

plugins/gatekeeper/src/pre-tool-use.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,18 +128,26 @@ function readStdin(): Promise<string> {
128128

129129
async function main(): Promise<void> {
130130
const raw = await readStdin()
131+
// Empty stdin means no hook input; passthrough is correct per Claude Code hook protocol
131132
if (!raw.trim()) {
132133
process.exit(0)
133134
}
134135

135-
let input: PreToolUseHookInput
136+
let parsed: unknown
136137
try {
137-
input = JSON.parse(raw)
138+
parsed = JSON.parse(raw)
138139
}
139-
catch {
140-
process.exit(0)
140+
catch (err) {
141+
process.stderr.write(`gatekeeper: invalid JSON input: ${err instanceof Error ? err.message : String(err)}\n`)
142+
process.exit(1)
143+
}
144+
145+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
146+
process.stderr.write(`gatekeeper: expected JSON object, got ${parsed === null ? 'null' : typeof parsed}\n`)
147+
process.exit(1)
141148
}
142149

150+
const input = parsed as PreToolUseHookInput
143151
const decision = evaluate(input)
144152
if (decision) {
145153
process.stdout.write(JSON.stringify(decision))
@@ -148,4 +156,7 @@ async function main(): Promise<void> {
148156
process.exit(0)
149157
}
150158

151-
main()
159+
main().catch((err) => {
160+
process.stderr.write(`gatekeeper: unexpected error: ${err instanceof Error ? err.message : String(err)}\n`)
161+
process.exit(1)
162+
})

0 commit comments

Comments
 (0)