@hasna/hooks 0.11.7 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +129 -4
- package/bin/hooks-mcp.js +39 -7
- package/bin/index.js +739 -129
- package/bin/native-safety-entry.js +15 -1718
- package/bin/serve.js +36 -9
- package/dist/index.js +51 -17
- package/dist/lib/codex-safety-check.d.ts +3 -0
- package/dist/lib/native-safety-registration.d.ts +49 -6
- package/dist/lib/registry.d.ts +15 -0
- package/dist/native-safety.d.ts +4 -0
- package/dist/native-safety.js +8 -4
- package/dist/sdk/index.js +1 -1
- package/hooks/hook-signed-link-guard/README.md +228 -0
- package/hooks/hook-signed-link-guard/package.json +12 -0
- package/hooks/hook-signed-link-guard/src/classify.ts +1280 -0
- package/hooks/hook-signed-link-guard/src/hook.ts +72 -0
- package/hooks/hook-signed-link-guard/src/jq.ts +545 -0
- package/hooks/hook-signed-link-guard/src/links.ts +136 -0
- package/hooks/hook-signed-link-output/README.md +47 -0
- package/hooks/hook-signed-link-output/package.json +12 -0
- package/hooks/hook-signed-link-output/src/hook.ts +111 -0
- package/hooks/hook-trash-guard/src/hook.ts +204 -80
- package/hooks/native-safety-entry.ts +28 -7
- package/package.json +2 -2
- package/scripts/validate-package.ts +12 -2
package/bin/serve.js
CHANGED
|
@@ -131,6 +131,7 @@ function getExplicitDbPath(env = process.env) {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
// src/lib/registry.ts
|
|
134
|
+
var CLAUDE_TRASH_GUARD_MATCHER = "^(Bash|Monitor|apply_patch|ApplyPatch|functions\\.apply_patch)$";
|
|
134
135
|
var HOOKS = [
|
|
135
136
|
{
|
|
136
137
|
name: "gitguard",
|
|
@@ -181,6 +182,8 @@ var HOOKS = [
|
|
|
181
182
|
category: "Git Safety",
|
|
182
183
|
event: "PreToolUse",
|
|
183
184
|
matcher: "^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$",
|
|
185
|
+
targetMatchers: { claude: "^(Bash|Monitor|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$" },
|
|
186
|
+
legacyMatchers: ["^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
|
|
184
187
|
tags: ["workspace", "repos", "structure", "guard", "safety", "orgs", "multi-agent"]
|
|
185
188
|
},
|
|
186
189
|
{
|
|
@@ -191,6 +194,8 @@ var HOOKS = [
|
|
|
191
194
|
category: "Git Safety",
|
|
192
195
|
event: "PreToolUse",
|
|
193
196
|
matcher: "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$",
|
|
197
|
+
targetMatchers: { claude: CLAUDE_TRASH_GUARD_MATCHER },
|
|
198
|
+
legacyMatchers: ["^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
|
|
194
199
|
tags: ["rm", "delete", "trash", "recoverable", "guard", "safety", "bash"],
|
|
195
200
|
rewritesInput: true,
|
|
196
201
|
timeoutSeconds: 10
|
|
@@ -295,6 +300,28 @@ var HOOKS = [
|
|
|
295
300
|
matcher: "Bash",
|
|
296
301
|
tags: ["security", "credentials", "secrets", "detection", "audit"]
|
|
297
302
|
},
|
|
303
|
+
{
|
|
304
|
+
name: "signed-link-guard",
|
|
305
|
+
displayName: "Signed Link Guard",
|
|
306
|
+
description: "Refuses gh reads that would print composite GitHub content (bodies, comments, reviews, commit messages, check links and details, raw issue/PR/check API objects) where signed action links appear; bounded scalar projections, diffs, lists and writes pass",
|
|
307
|
+
version: "0.1.0",
|
|
308
|
+
category: "Security",
|
|
309
|
+
event: "PreToolUse",
|
|
310
|
+
matcher: "^(Bash|Monitor)$",
|
|
311
|
+
tags: ["github", "gh", "signed-links", "capabilities", "transcript", "guard", "security"],
|
|
312
|
+
timeoutSeconds: 10
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "signed-link-output",
|
|
316
|
+
displayName: "Signed Link Output",
|
|
317
|
+
description: "Claude PostToolUse backstop: replaces signed-link URLs in tool output with a redaction marker (updatedToolOutput, where the running Claude Code supports it) and tells the model not to repeat or store the link. It runs after the tool, so it cannot undo display or other persistence",
|
|
318
|
+
version: "0.1.0",
|
|
319
|
+
category: "Security",
|
|
320
|
+
event: "PostToolUse",
|
|
321
|
+
matcher: "^(Bash|WebFetch|mcp__.*)$",
|
|
322
|
+
tags: ["github", "signed-links", "capabilities", "redaction", "transcript", "security"],
|
|
323
|
+
timeoutSeconds: 10
|
|
324
|
+
},
|
|
298
325
|
{
|
|
299
326
|
name: "phonenotify",
|
|
300
327
|
displayName: "Phone Notify",
|
|
@@ -676,7 +703,7 @@ function getHook(name) {
|
|
|
676
703
|
import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, realpathSync, writeFileSync } from "fs";
|
|
677
704
|
import { basename, dirname, isAbsolute, join as join3, normalize, resolve as resolve2, sep } from "path";
|
|
678
705
|
|
|
679
|
-
//
|
|
706
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
|
|
680
707
|
var exports_external = {};
|
|
681
708
|
__export(exports_external, {
|
|
682
709
|
void: () => voidType,
|
|
@@ -788,7 +815,7 @@ __export(exports_external, {
|
|
|
788
815
|
BRAND: () => BRAND
|
|
789
816
|
});
|
|
790
817
|
|
|
791
|
-
//
|
|
818
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
|
|
792
819
|
var util;
|
|
793
820
|
(function(util2) {
|
|
794
821
|
util2.assertEqual = (_) => {};
|
|
@@ -919,7 +946,7 @@ var getParsedType = (data) => {
|
|
|
919
946
|
}
|
|
920
947
|
};
|
|
921
948
|
|
|
922
|
-
//
|
|
949
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
|
|
923
950
|
var ZodIssueCode = util.arrayToEnum([
|
|
924
951
|
"invalid_type",
|
|
925
952
|
"invalid_literal",
|
|
@@ -1038,7 +1065,7 @@ ZodError.create = (issues) => {
|
|
|
1038
1065
|
return error;
|
|
1039
1066
|
};
|
|
1040
1067
|
|
|
1041
|
-
//
|
|
1068
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
|
|
1042
1069
|
var errorMap = (issue, _ctx) => {
|
|
1043
1070
|
let message;
|
|
1044
1071
|
switch (issue.code) {
|
|
@@ -1141,7 +1168,7 @@ var errorMap = (issue, _ctx) => {
|
|
|
1141
1168
|
};
|
|
1142
1169
|
var en_default = errorMap;
|
|
1143
1170
|
|
|
1144
|
-
//
|
|
1171
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
|
|
1145
1172
|
var overrideErrorMap = en_default;
|
|
1146
1173
|
function setErrorMap(map) {
|
|
1147
1174
|
overrideErrorMap = map;
|
|
@@ -1149,7 +1176,7 @@ function setErrorMap(map) {
|
|
|
1149
1176
|
function getErrorMap() {
|
|
1150
1177
|
return overrideErrorMap;
|
|
1151
1178
|
}
|
|
1152
|
-
//
|
|
1179
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
1153
1180
|
var makeIssue = (params) => {
|
|
1154
1181
|
const { data, path, errorMaps, issueData } = params;
|
|
1155
1182
|
const fullPath = [...path, ...issueData.path || []];
|
|
@@ -1255,14 +1282,14 @@ var isAborted = (x) => x.status === "aborted";
|
|
|
1255
1282
|
var isDirty = (x) => x.status === "dirty";
|
|
1256
1283
|
var isValid = (x) => x.status === "valid";
|
|
1257
1284
|
var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;
|
|
1258
|
-
//
|
|
1285
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
|
|
1259
1286
|
var errorUtil;
|
|
1260
1287
|
(function(errorUtil2) {
|
|
1261
1288
|
errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
|
|
1262
1289
|
errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
|
|
1263
1290
|
})(errorUtil || (errorUtil = {}));
|
|
1264
1291
|
|
|
1265
|
-
//
|
|
1292
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
|
|
1266
1293
|
class ParseInputLazyPath {
|
|
1267
1294
|
constructor(parent, value, path, key) {
|
|
1268
1295
|
this._cachedPath = [];
|
|
@@ -5362,7 +5389,7 @@ import { readFileSync as readFileSync5 } from "fs";
|
|
|
5362
5389
|
import { dirname as dirname4, join as join8 } from "path";
|
|
5363
5390
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
5364
5391
|
|
|
5365
|
-
//
|
|
5392
|
+
// ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
|
|
5366
5393
|
import { spawnSync } from "child_process";
|
|
5367
5394
|
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync22 } from "fs";
|
|
5368
5395
|
import { O_NOFOLLOW as O_NOFOLLOW2, O_NONBLOCK as O_NONBLOCK2, O_RDONLY as O_RDONLY2 } from "constants";
|
package/dist/index.js
CHANGED
|
@@ -581,7 +581,7 @@ var init_redact = __esm(() => {
|
|
|
581
581
|
];
|
|
582
582
|
});
|
|
583
583
|
|
|
584
|
-
//
|
|
584
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/util.js
|
|
585
585
|
var util, objectUtil, ZodParsedType, getParsedType = (data) => {
|
|
586
586
|
const t = typeof data;
|
|
587
587
|
switch (t) {
|
|
@@ -712,7 +712,7 @@ var init_util = __esm(() => {
|
|
|
712
712
|
]);
|
|
713
713
|
});
|
|
714
714
|
|
|
715
|
-
//
|
|
715
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/ZodError.js
|
|
716
716
|
var ZodIssueCode, quotelessJson = (obj) => {
|
|
717
717
|
const json = JSON.stringify(obj, null, 2);
|
|
718
718
|
return json.replace(/"([^"]+)":/g, "$1:");
|
|
@@ -833,7 +833,7 @@ var init_ZodError = __esm(() => {
|
|
|
833
833
|
};
|
|
834
834
|
});
|
|
835
835
|
|
|
836
|
-
//
|
|
836
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/locales/en.js
|
|
837
837
|
var errorMap = (issue, _ctx) => {
|
|
838
838
|
let message;
|
|
839
839
|
switch (issue.code) {
|
|
@@ -940,7 +940,7 @@ var init_en = __esm(() => {
|
|
|
940
940
|
en_default = errorMap;
|
|
941
941
|
});
|
|
942
942
|
|
|
943
|
-
//
|
|
943
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/errors.js
|
|
944
944
|
function setErrorMap(map) {
|
|
945
945
|
overrideErrorMap = map;
|
|
946
946
|
}
|
|
@@ -953,7 +953,7 @@ var init_errors = __esm(() => {
|
|
|
953
953
|
overrideErrorMap = en_default;
|
|
954
954
|
});
|
|
955
955
|
|
|
956
|
-
//
|
|
956
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js
|
|
957
957
|
function addIssueToContext(ctx, issueData) {
|
|
958
958
|
const overrideMap = getErrorMap();
|
|
959
959
|
const issue = makeIssue({
|
|
@@ -1058,10 +1058,10 @@ var init_parseUtil = __esm(() => {
|
|
|
1058
1058
|
});
|
|
1059
1059
|
});
|
|
1060
1060
|
|
|
1061
|
-
//
|
|
1061
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/typeAliases.js
|
|
1062
1062
|
var init_typeAliases = () => {};
|
|
1063
1063
|
|
|
1064
|
-
//
|
|
1064
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js
|
|
1065
1065
|
var errorUtil;
|
|
1066
1066
|
var init_errorUtil = __esm(() => {
|
|
1067
1067
|
(function(errorUtil2) {
|
|
@@ -1070,7 +1070,7 @@ var init_errorUtil = __esm(() => {
|
|
|
1070
1070
|
})(errorUtil || (errorUtil = {}));
|
|
1071
1071
|
});
|
|
1072
1072
|
|
|
1073
|
-
//
|
|
1073
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/types.js
|
|
1074
1074
|
class ParseInputLazyPath {
|
|
1075
1075
|
constructor(parent, value, path, key) {
|
|
1076
1076
|
this._cachedPath = [];
|
|
@@ -4421,7 +4421,7 @@ var init_types = __esm(() => {
|
|
|
4421
4421
|
NEVER = INVALID;
|
|
4422
4422
|
});
|
|
4423
4423
|
|
|
4424
|
-
//
|
|
4424
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/v3/external.js
|
|
4425
4425
|
var exports_external = {};
|
|
4426
4426
|
__export(exports_external, {
|
|
4427
4427
|
void: () => voidType,
|
|
@@ -4541,7 +4541,7 @@ var init_external = __esm(() => {
|
|
|
4541
4541
|
init_ZodError();
|
|
4542
4542
|
});
|
|
4543
4543
|
|
|
4544
|
-
//
|
|
4544
|
+
// ../../node_modules/.bun/zod@3.25.76/node_modules/zod/index.js
|
|
4545
4545
|
var init_zod = __esm(() => {
|
|
4546
4546
|
init_external();
|
|
4547
4547
|
init_external();
|
|
@@ -5101,7 +5101,7 @@ var init_profiles = __esm(() => {
|
|
|
5101
5101
|
init_app_home();
|
|
5102
5102
|
});
|
|
5103
5103
|
|
|
5104
|
-
//
|
|
5104
|
+
// ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
|
|
5105
5105
|
import { isIP } from "net";
|
|
5106
5106
|
import { spawnSync } from "child_process";
|
|
5107
5107
|
import { closeSync as closeSync22, fstatSync as fstatSync22, openSync as openSync22, readFileSync as readFileSync22 } from "fs";
|
|
@@ -6979,6 +6979,10 @@ var HOOK_EVENTS = [
|
|
|
6979
6979
|
"UserPromptSubmit",
|
|
6980
6980
|
"SubagentStart"
|
|
6981
6981
|
];
|
|
6982
|
+
var CLAUDE_TRASH_GUARD_MATCHER = "^(Bash|Monitor|apply_patch|ApplyPatch|functions\\.apply_patch)$";
|
|
6983
|
+
function matcherFor(meta, target) {
|
|
6984
|
+
return meta.targetMatchers?.[target] ?? meta.matcher;
|
|
6985
|
+
}
|
|
6982
6986
|
var CATEGORIES = [
|
|
6983
6987
|
"Git Safety",
|
|
6984
6988
|
"Code Quality",
|
|
@@ -7041,6 +7045,8 @@ var HOOKS = [
|
|
|
7041
7045
|
category: "Git Safety",
|
|
7042
7046
|
event: "PreToolUse",
|
|
7043
7047
|
matcher: "^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$",
|
|
7048
|
+
targetMatchers: { claude: "^(Bash|Monitor|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$" },
|
|
7049
|
+
legacyMatchers: ["^(Bash|Write|Edit|MultiEdit|NotebookEdit|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
|
|
7044
7050
|
tags: ["workspace", "repos", "structure", "guard", "safety", "orgs", "multi-agent"]
|
|
7045
7051
|
},
|
|
7046
7052
|
{
|
|
@@ -7051,6 +7057,8 @@ var HOOKS = [
|
|
|
7051
7057
|
category: "Git Safety",
|
|
7052
7058
|
event: "PreToolUse",
|
|
7053
7059
|
matcher: "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$",
|
|
7060
|
+
targetMatchers: { claude: CLAUDE_TRASH_GUARD_MATCHER },
|
|
7061
|
+
legacyMatchers: ["^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$"],
|
|
7054
7062
|
tags: ["rm", "delete", "trash", "recoverable", "guard", "safety", "bash"],
|
|
7055
7063
|
rewritesInput: true,
|
|
7056
7064
|
timeoutSeconds: 10
|
|
@@ -7155,6 +7163,28 @@ var HOOKS = [
|
|
|
7155
7163
|
matcher: "Bash",
|
|
7156
7164
|
tags: ["security", "credentials", "secrets", "detection", "audit"]
|
|
7157
7165
|
},
|
|
7166
|
+
{
|
|
7167
|
+
name: "signed-link-guard",
|
|
7168
|
+
displayName: "Signed Link Guard",
|
|
7169
|
+
description: "Refuses gh reads that would print composite GitHub content (bodies, comments, reviews, commit messages, check links and details, raw issue/PR/check API objects) where signed action links appear; bounded scalar projections, diffs, lists and writes pass",
|
|
7170
|
+
version: "0.1.0",
|
|
7171
|
+
category: "Security",
|
|
7172
|
+
event: "PreToolUse",
|
|
7173
|
+
matcher: "^(Bash|Monitor)$",
|
|
7174
|
+
tags: ["github", "gh", "signed-links", "capabilities", "transcript", "guard", "security"],
|
|
7175
|
+
timeoutSeconds: 10
|
|
7176
|
+
},
|
|
7177
|
+
{
|
|
7178
|
+
name: "signed-link-output",
|
|
7179
|
+
displayName: "Signed Link Output",
|
|
7180
|
+
description: "Claude PostToolUse backstop: replaces signed-link URLs in tool output with a redaction marker (updatedToolOutput, where the running Claude Code supports it) and tells the model not to repeat or store the link. It runs after the tool, so it cannot undo display or other persistence",
|
|
7181
|
+
version: "0.1.0",
|
|
7182
|
+
category: "Security",
|
|
7183
|
+
event: "PostToolUse",
|
|
7184
|
+
matcher: "^(Bash|WebFetch|mcp__.*)$",
|
|
7185
|
+
tags: ["github", "signed-links", "capabilities", "redaction", "transcript", "security"],
|
|
7186
|
+
timeoutSeconds: 10
|
|
7187
|
+
},
|
|
7158
7188
|
{
|
|
7159
7189
|
name: "phonenotify",
|
|
7160
7190
|
displayName: "Phone Notify",
|
|
@@ -8106,7 +8136,7 @@ function writeHookSettingsPreimage(path, settings, before) {
|
|
|
8106
8136
|
// src/lib/claude-skills-coordination.ts
|
|
8107
8137
|
import { createHash as createHash6 } from "crypto";
|
|
8108
8138
|
|
|
8109
|
-
//
|
|
8139
|
+
// ../../node_modules/.bun/@hasna+skills@0.9.17+a00d3064559565bb/node_modules/@hasna/skills/dist/index.js
|
|
8110
8140
|
import { existsSync as existsSync10 } from "fs";
|
|
8111
8141
|
import { homedir as homedir5 } from "os";
|
|
8112
8142
|
import { join as join9, resolve as resolve3 } from "path";
|
|
@@ -39789,6 +39819,7 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
|
|
|
39789
39819
|
if (uniqueEventKeys.length === 0) {
|
|
39790
39820
|
throw new Error(`Hook '${name}' has no installable events for target '${target}'`);
|
|
39791
39821
|
}
|
|
39822
|
+
const matcher = matcherFor(meta3, target);
|
|
39792
39823
|
const settings = readSettings2(scope, target);
|
|
39793
39824
|
if (!settings.hooks)
|
|
39794
39825
|
settings.hooks = {};
|
|
@@ -39823,10 +39854,12 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
|
|
|
39823
39854
|
}
|
|
39824
39855
|
}
|
|
39825
39856
|
if (positions.length) {
|
|
39826
|
-
if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== (meta3.matcher ?? ""))) {
|
|
39857
|
+
if (positions.length !== uniqueEventKeys.length || uniqueEventKeys.some((event) => positions.filter((position) => position.event === event).length !== 1) || positions.some((position) => (position.entry.matcher ?? "") !== matcher && !(meta3.legacyMatchers ?? []).includes(position.entry.matcher ?? ""))) {
|
|
39827
39858
|
throw new Error("Codex hook events, matchers or duplicate registrations require explicit reconciliation; no settings were changed.");
|
|
39828
39859
|
}
|
|
39829
39860
|
for (const { entry, index } of positions) {
|
|
39861
|
+
if (matcher)
|
|
39862
|
+
entry.matcher = matcher;
|
|
39830
39863
|
entry.hooks[index] = { ...entry.hooks[index], type: "command", command: hookCommand };
|
|
39831
39864
|
if (typeof meta3.timeoutSeconds === "number" && meta3.timeoutSeconds > 0)
|
|
39832
39865
|
entry.hooks[index].timeout = meta3.timeoutSeconds;
|
|
@@ -39846,8 +39879,8 @@ function registerHook(name, scope = "global", target = "claude", profile, mement
|
|
|
39846
39879
|
const entry = {
|
|
39847
39880
|
hooks: [hookEntry]
|
|
39848
39881
|
};
|
|
39849
|
-
if (
|
|
39850
|
-
entry.matcher =
|
|
39882
|
+
if (matcher) {
|
|
39883
|
+
entry.matcher = matcher;
|
|
39851
39884
|
}
|
|
39852
39885
|
settings.hooks[eventKey].push(entry);
|
|
39853
39886
|
}
|
|
@@ -41351,7 +41384,8 @@ async function verifyNativeSafetyCommand(command, home = homedir7()) {
|
|
|
41351
41384
|
}
|
|
41352
41385
|
|
|
41353
41386
|
// src/lib/codex-safety-check.ts
|
|
41354
|
-
var
|
|
41387
|
+
var SUPPORTED_CODEX_SAFETY_VERSIONS = ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.154.0-alpha.6.1", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1", "codex-cli 0.158.0", "codex-cli 0.159.0", "codex-cli 0.159.2"];
|
|
41388
|
+
var versions = new Set(SUPPORTED_CODEX_SAFETY_VERSIONS);
|
|
41355
41389
|
var matcher = "^(Bash|apply_patch|ApplyPatch|functions\\.apply_patch)$";
|
|
41356
41390
|
|
|
41357
41391
|
class CodexSafetyError extends Error {
|
|
@@ -41896,7 +41930,7 @@ async function verifyClaudeSafetyConfiguration(options = {}, dependencies = {})
|
|
|
41896
41930
|
need8(!/^(?:hooks run trash-guard|hook-trash-guard)(?:\s|$)/.test(handler.command.trim()), "guard_definition_invalid");
|
|
41897
41931
|
if (binding?.name !== "trash-guard")
|
|
41898
41932
|
continue;
|
|
41899
|
-
need8(binding.home === home && event === "PreToolUse" && handler.type === "command" && group.matcher ===
|
|
41933
|
+
need8(binding.home === home && event === "PreToolUse" && handler.type === "command" && group.matcher === CLAUDE_TRASH_GUARD_MATCHER && handler.timeout === 10 && (handler.async === undefined || handler.async === false) && handler.asyncRewake !== true, "guard_definition_invalid");
|
|
41900
41934
|
guards.push({ command: handler.command, path: file2.path });
|
|
41901
41935
|
}
|
|
41902
41936
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { verifyNativeSafetyCommand } from "../native-safety.js";
|
|
2
|
+
/** Exact `codex --version` outputs whose app-server protocol was measured
|
|
3
|
+
* against the real native binary. Anything else refuses as native_unsupported. */
|
|
4
|
+
export declare const SUPPORTED_CODEX_SAFETY_VERSIONS: readonly ["codex-cli 0.153.0", "codex-cli 0.154.0", "codex-cli 0.154.0-alpha.6.1", "codex-cli 0.155.0", "codex-cli 0.155.1", "codex-cli 0.156.1", "codex-cli 0.157.0", "codex-cli 0.157.1", "codex-cli 0.158.0", "codex-cli 0.159.0", "codex-cli 0.159.2"];
|
|
2
5
|
export type CodexSafetyCode = "native_unavailable" | "native_unsafe_executable" | "native_executable_required" | "native_unsupported" | "native_failed" | "native_discovery_failed" | "hooks_disabled" | "guard_missing" | "guard_ambiguous" | "guard_definition_invalid" | "guard_disabled" | "guard_untrusted" | "guard_changed" | "guard_failed";
|
|
3
6
|
export declare class CodexSafetyError extends Error {
|
|
4
7
|
readonly code: CodexSafetyCode;
|
|
@@ -7,9 +7,12 @@ export declare class NativeRegistrationError extends Error {
|
|
|
7
7
|
export interface NativeRegistrationOptions {
|
|
8
8
|
target: "codex" | "claude";
|
|
9
9
|
}
|
|
10
|
+
export interface NativeReadinessRegistrationOptions {
|
|
11
|
+
target: "codex" | "claude" | "sumi";
|
|
12
|
+
}
|
|
10
13
|
export interface NativeRegistrationPlan {
|
|
11
14
|
schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
|
|
12
|
-
target: "codex" | "claude";
|
|
15
|
+
target: "codex" | "claude" | "sumi";
|
|
13
16
|
settingsPath: string;
|
|
14
17
|
resolvedPath: string;
|
|
15
18
|
beforeSHA256: string;
|
|
@@ -24,6 +27,21 @@ export interface NativeRegistrationPlan {
|
|
|
24
27
|
interface Dependencies {
|
|
25
28
|
verify?: typeof verifyNativeSafetyCommand;
|
|
26
29
|
}
|
|
30
|
+
/** Sumi observes startup inside its own process through the bundled
|
|
31
|
+
* `@hasna/hooks/native-readiness` SDK, enabled by Sumi's own
|
|
32
|
+
* `experimental.trash_readiness` setting, so there is no startup command to
|
|
33
|
+
* write into a harness settings file. The Hooks-owned Sumi registration is the
|
|
34
|
+
* native guard record `~/.hasna/hooks/native/sumi-trash-guard.json`: it pins a
|
|
35
|
+
* Hooks runtime and worker, and Sumi's startup check requires it (Trash runs
|
|
36
|
+
* `hooks safety verify --target sumi --execution`). Planning re-points that
|
|
37
|
+
* record at this installed package without writing. Applying requires the exact
|
|
38
|
+
* plan digest, keeps the previous record as `<path>.before-<sha256>` and journals
|
|
39
|
+
* the operation. Sumi's own configuration is never read or written here. */
|
|
40
|
+
export interface SumiRegistrationDependencies {
|
|
41
|
+
home?: string;
|
|
42
|
+
command?: string;
|
|
43
|
+
verify?: typeof verifyNativeSafetyCommand;
|
|
44
|
+
}
|
|
27
45
|
export declare const planNativeSafetyRegistration: (options: NativeRegistrationOptions, dependencies?: Dependencies) => Promise<NativeRegistrationPlan>;
|
|
28
46
|
export declare const applyNativeSafetyRegistration: (options: NativeRegistrationOptions & {
|
|
29
47
|
expectedPlanDigest: string;
|
|
@@ -38,7 +56,7 @@ export declare const applyNativeSafetyRegistration: (options: NativeRegistration
|
|
|
38
56
|
guardVerified: boolean;
|
|
39
57
|
} | {
|
|
40
58
|
schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
|
|
41
|
-
target: "codex" | "claude";
|
|
59
|
+
target: "codex" | "claude" | "sumi";
|
|
42
60
|
settingsPath: string;
|
|
43
61
|
resolvedPath: string;
|
|
44
62
|
beforeSHA256: string;
|
|
@@ -52,10 +70,11 @@ export declare const applyNativeSafetyRegistration: (options: NativeRegistration
|
|
|
52
70
|
ok: boolean;
|
|
53
71
|
changed: boolean;
|
|
54
72
|
}>;
|
|
55
|
-
|
|
56
|
-
export declare const
|
|
73
|
+
/** `sumiDependencies` is a test seam for the Sumi target only. */
|
|
74
|
+
export declare const planNativeReadinessRegistration: (options: NativeReadinessRegistrationOptions, sumiDependencies?: SumiRegistrationDependencies) => Promise<NativeRegistrationPlan>;
|
|
75
|
+
export declare const applyNativeReadinessRegistration: (options: NativeReadinessRegistrationOptions & {
|
|
57
76
|
expectedPlanDigest: string;
|
|
58
|
-
}) => Promise<{
|
|
77
|
+
}, sumiDependencies?: SumiRegistrationDependencies) => Promise<{
|
|
59
78
|
nativeAdoptionVerified: boolean;
|
|
60
79
|
readinessRunnerVerified?: boolean | undefined;
|
|
61
80
|
ok: boolean;
|
|
@@ -66,7 +85,31 @@ export declare const applyNativeReadinessRegistration: (options: NativeRegistrat
|
|
|
66
85
|
guardVerified: boolean;
|
|
67
86
|
} | {
|
|
68
87
|
schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
|
|
69
|
-
target: "codex" | "claude";
|
|
88
|
+
target: "codex" | "claude" | "sumi";
|
|
89
|
+
settingsPath: string;
|
|
90
|
+
resolvedPath: string;
|
|
91
|
+
beforeSHA256: string;
|
|
92
|
+
desiredSHA256: string;
|
|
93
|
+
commandSHA256: string;
|
|
94
|
+
action: "unchanged" | "register";
|
|
95
|
+
planDigest: string;
|
|
96
|
+
guardVerified: boolean;
|
|
97
|
+
readinessRunnerVerified?: true;
|
|
98
|
+
nativeAdoptionVerified: false;
|
|
99
|
+
ok: boolean;
|
|
100
|
+
changed: boolean;
|
|
101
|
+
}> | Promise<{
|
|
102
|
+
nativeAdoptionVerified: boolean;
|
|
103
|
+
backup?: string | undefined;
|
|
104
|
+
ok: boolean;
|
|
105
|
+
changed: boolean;
|
|
106
|
+
operationId: `${string}-${string}-${string}-${string}-${string}`;
|
|
107
|
+
planDigest: string;
|
|
108
|
+
settingsSHA256: string;
|
|
109
|
+
guardVerified: boolean;
|
|
110
|
+
} | {
|
|
111
|
+
schema: "hasna.hooks.native-safety-registration/v1" | "hasna.hooks.native-readiness-registration/v1";
|
|
112
|
+
target: "codex" | "claude" | "sumi";
|
|
70
113
|
settingsPath: string;
|
|
71
114
|
resolvedPath: string;
|
|
72
115
|
beforeSHA256: string;
|
package/dist/lib/registry.d.ts
CHANGED
|
@@ -21,6 +21,17 @@ export interface HookMeta {
|
|
|
21
21
|
* reports it.
|
|
22
22
|
*/
|
|
23
23
|
rewritesInput?: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* A different matcher for one harness. Claude Code's `Monitor` tool runs a
|
|
26
|
+
* shell command and streams its stdout to the model, so the bundled guards
|
|
27
|
+
* add it on Claude; Codex has no such tool and keeps its verified matcher.
|
|
28
|
+
*/
|
|
29
|
+
targetMatchers?: Partial<Record<"claude" | "codex", string>>;
|
|
30
|
+
/**
|
|
31
|
+
* Earlier matchers of an installed native registration that an in-place
|
|
32
|
+
* update may replace with the current one.
|
|
33
|
+
*/
|
|
34
|
+
legacyMatchers?: string[];
|
|
24
35
|
/**
|
|
25
36
|
* Timeout (seconds) written into the agent's settings entry for this hook.
|
|
26
37
|
* The harness default is 600s, and a TIMED-OUT HOOK DOES NOT BLOCK: a guard
|
|
@@ -28,6 +39,10 @@ export interface HookMeta {
|
|
|
28
39
|
*/
|
|
29
40
|
timeoutSeconds?: number;
|
|
30
41
|
}
|
|
42
|
+
/** The Claude Code matcher of the bundled Trash guard registration (Bash, Monitor and patch tools). */
|
|
43
|
+
export declare const CLAUDE_TRASH_GUARD_MATCHER = "^(Bash|Monitor|apply_patch|ApplyPatch|functions\\.apply_patch)$";
|
|
44
|
+
/** The matcher a hook is registered with for one harness. */
|
|
45
|
+
export declare function matcherFor(meta: HookMeta, target: string): string;
|
|
31
46
|
export declare const CATEGORIES: readonly ["Git Safety", "Code Quality", "Security", "Notifications", "Context Management", "Workflow Automation", "Environment", "Permissions", "Observability", "Agent Teams"];
|
|
32
47
|
export type Category = (typeof CATEGORIES)[number];
|
|
33
48
|
export declare const HOOKS: HookMeta[];
|
package/dist/native-safety.d.ts
CHANGED
|
@@ -38,10 +38,14 @@ export type NativeSafetyRecord = {
|
|
|
38
38
|
harness: "sumi";
|
|
39
39
|
command: string;
|
|
40
40
|
};
|
|
41
|
+
/** The exact bytes every writer stores for a Sumi guard record. Planners use
|
|
42
|
+
* the same bytes to compute a desired digest before any write. */
|
|
43
|
+
export declare function nativeSafetyRecordText(command: string): string;
|
|
41
44
|
export declare function readNativeSafetyRegistration(home?: string): {
|
|
42
45
|
record: NativeSafetyRecord;
|
|
43
46
|
sha256: string;
|
|
44
47
|
path: string;
|
|
48
|
+
text: string;
|
|
45
49
|
};
|
|
46
50
|
/** Bounded, non-leaking explanation for a refused binding: which integrity
|
|
47
51
|
* property the saved record fails. A binding that a third party could rewrite
|
package/dist/native-safety.js
CHANGED
|
@@ -199,11 +199,16 @@ function syncDirectory(path) {
|
|
|
199
199
|
closeSync2(fd);
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
|
+
function nativeSafetyRecordText(command) {
|
|
203
|
+
const record = { schema: "hasna.hooks.native-safety.v1", harness: "sumi", command };
|
|
204
|
+
return JSON.stringify(record) + `
|
|
205
|
+
`;
|
|
206
|
+
}
|
|
202
207
|
function readNativeSafetyRegistration(home = homedir2()) {
|
|
203
208
|
try {
|
|
204
209
|
home = canonicalHome(home);
|
|
205
210
|
const path = join2(home, ".hasna/hooks/native", filename), text = readOwned(path);
|
|
206
|
-
return { record: parseRecord(text, home), sha256: hash(text), path };
|
|
211
|
+
return { record: parseRecord(text, home), sha256: hash(text), path, text };
|
|
207
212
|
} catch (error) {
|
|
208
213
|
if (error instanceof NativeSafetyError)
|
|
209
214
|
throw error;
|
|
@@ -229,9 +234,7 @@ function nativeSafetyBindingIntegrityReason(home = homedir2()) {
|
|
|
229
234
|
}
|
|
230
235
|
function registerNativeSafety(options = {}) {
|
|
231
236
|
const home = canonicalHome(options.home ?? homedir2());
|
|
232
|
-
const
|
|
233
|
-
const text = JSON.stringify(record) + `
|
|
234
|
-
`;
|
|
237
|
+
const text = nativeSafetyRecordText(options.command ?? installedNativeSafetyCommand("trash-guard"));
|
|
235
238
|
parseRecord(text, home);
|
|
236
239
|
for (const dir of [join2(home, ".hasna"), join2(home, ".hasna/hooks"), join2(home, ".hasna/hooks/native")]) {
|
|
237
240
|
try {
|
|
@@ -412,6 +415,7 @@ export {
|
|
|
412
415
|
verifyNativeSafetyCommand,
|
|
413
416
|
registerNativeSafety,
|
|
414
417
|
readNativeSafetyRegistration,
|
|
418
|
+
nativeSafetyRecordText,
|
|
415
419
|
nativeSafetyBindingIntegrityReason,
|
|
416
420
|
evaluateNativeSafetyForExecution,
|
|
417
421
|
evaluateNativeSafety,
|
package/dist/sdk/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
//
|
|
2
|
+
// ../../node_modules/.bun/@hasna+contracts@1.2.2+944b86d8024e79fa/node_modules/@hasna/contracts/dist/client/transport.js
|
|
3
3
|
import { isIP } from "net";
|
|
4
4
|
import { spawnSync } from "child_process";
|
|
5
5
|
import { closeSync as closeSync2, fstatSync as fstatSync2, openSync as openSync2, readFileSync as readFileSync2 } from "fs";
|