codiedev 0.9.35 → 0.9.37
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/dist/captureLog.d.ts +1 -1
- package/dist/cli.js +11 -1
- package/dist/commands/doctor.js +5 -0
- package/dist/commands/security.d.ts +1 -0
- package/dist/commands/security.js +138 -0
- package/dist/connect.js +41 -0
- package/dist/hook.js +165 -2
- package/dist/security.d.ts +97 -0
- package/dist/security.js +406 -0
- package/dist/securityDeepScan.d.ts +41 -0
- package/dist/securityDeepScan.js +248 -0
- package/dist/securityDoctor.d.ts +32 -0
- package/dist/securityDoctor.js +63 -0
- package/dist/securityInstall.d.ts +61 -0
- package/dist/securityInstall.js +217 -0
- package/dist/securityInventory.d.ts +54 -0
- package/dist/securityInventory.js +546 -0
- package/dist/securityPilot.d.ts +19 -0
- package/dist/securityPilot.js +61 -0
- package/dist/securityPolicy.d.ts +23 -0
- package/dist/securityPolicy.js +161 -0
- package/dist/securitySession.d.ts +6 -0
- package/dist/securitySession.js +72 -0
- package/dist/utils.js +56 -2
- package/package.json +9 -2
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.trustedPolicyKeysFromEnvironment = trustedPolicyKeysFromEnvironment;
|
|
37
|
+
exports.verifyEndpointSecurityPolicySignature = verifyEndpointSecurityPolicySignature;
|
|
38
|
+
exports.readEndpointSecurityPolicy = readEndpointSecurityPolicy;
|
|
39
|
+
exports.inspectEndpointSecurityPolicy = inspectEndpointSecurityPolicy;
|
|
40
|
+
exports.writeEndpointSecurityPolicy = writeEndpointSecurityPolicy;
|
|
41
|
+
exports.syncEndpointSecurityPolicy = syncEndpointSecurityPolicy;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const crypto = __importStar(require("crypto"));
|
|
44
|
+
const http = __importStar(require("http"));
|
|
45
|
+
const https = __importStar(require("https"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const configDir_1 = require("./configDir");
|
|
48
|
+
const POLICY_FILE = "security-policy.json";
|
|
49
|
+
function stableJson(value) {
|
|
50
|
+
if (Array.isArray(value))
|
|
51
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
52
|
+
if (value && typeof value === "object") {
|
|
53
|
+
return `{${Object.entries(value)
|
|
54
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
55
|
+
.map(([key, child]) => `${JSON.stringify(key)}:${stableJson(child)}`)
|
|
56
|
+
.join(",")}}`;
|
|
57
|
+
}
|
|
58
|
+
return JSON.stringify(value) ?? "null";
|
|
59
|
+
}
|
|
60
|
+
function trustedPolicyKeysFromEnvironment(env = process.env) {
|
|
61
|
+
const raw = env.CODIEDEV_SECURITY_POLICY_PUBLIC_KEYS;
|
|
62
|
+
if (!raw)
|
|
63
|
+
return {};
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(raw);
|
|
66
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
67
|
+
return {};
|
|
68
|
+
return Object.fromEntries(Object.entries(parsed)
|
|
69
|
+
.filter((entry) => typeof entry[1] === "string"));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return {};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function verifyEndpointSecurityPolicySignature(policy, trustedKeys = trustedPolicyKeysFromEnvironment()) {
|
|
76
|
+
const configuredKeys = Object.keys(trustedKeys);
|
|
77
|
+
if (configuredKeys.length === 0) {
|
|
78
|
+
return { ok: true, trusted: false, detail: "no endpoint policy public keys are pinned" };
|
|
79
|
+
}
|
|
80
|
+
const envelope = policy.signature;
|
|
81
|
+
if (!envelope || envelope.algorithm !== "Ed25519") {
|
|
82
|
+
return { ok: false, trusted: false, detail: "policy signature is missing" };
|
|
83
|
+
}
|
|
84
|
+
const publicKey = trustedKeys[envelope.keyId];
|
|
85
|
+
if (!publicKey)
|
|
86
|
+
return { ok: false, trusted: false, detail: `policy signing key ${envelope.keyId} is not trusted` };
|
|
87
|
+
const unsigned = { ...policy };
|
|
88
|
+
delete unsigned.signature;
|
|
89
|
+
try {
|
|
90
|
+
const ok = crypto.verify(null, Buffer.from(`${envelope.signedAt}.${stableJson(unsigned)}`), crypto.createPublicKey(publicKey), Buffer.from(envelope.value, "base64"));
|
|
91
|
+
return ok
|
|
92
|
+
? { ok: true, trusted: true, detail: `verified with ${envelope.keyId}` }
|
|
93
|
+
: { ok: false, trusted: false, detail: "policy signature does not match its content" };
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return { ok: false, trusted: false, detail: `policy signature could not be verified: ${error.message}` };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function readEndpointSecurityPolicy(stateDir = (0, configDir_1.codiedevStateDir)(), trustedKeys = trustedPolicyKeysFromEnvironment()) {
|
|
100
|
+
const inspection = inspectEndpointSecurityPolicy(stateDir, trustedKeys);
|
|
101
|
+
return inspection.verification.ok ? inspection.policy : undefined;
|
|
102
|
+
}
|
|
103
|
+
function inspectEndpointSecurityPolicy(stateDir = (0, configDir_1.codiedevStateDir)(), trustedKeys = trustedPolicyKeysFromEnvironment()) {
|
|
104
|
+
const target = path.join(stateDir, POLICY_FILE);
|
|
105
|
+
if (!fs.existsSync(target)) {
|
|
106
|
+
return { exists: false, verification: { ok: true, trusted: false, detail: "no cached endpoint policy" } };
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const value = JSON.parse(fs.readFileSync(target, "utf8"));
|
|
110
|
+
if (value?.schemaVersion !== 1 || !["alert", "block"].includes(value.enforcementMode)) {
|
|
111
|
+
return { exists: true, verification: { ok: false, trusted: false, detail: "cached endpoint policy has an invalid schema" } };
|
|
112
|
+
}
|
|
113
|
+
const policy = value;
|
|
114
|
+
return { exists: true, policy, verification: verifyEndpointSecurityPolicySignature(policy, trustedKeys) };
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
return { exists: true, verification: { ok: false, trusted: false, detail: `cached endpoint policy could not be read: ${error.message}` } };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function writeEndpointSecurityPolicy(policy, stateDir = (0, configDir_1.codiedevStateDir)(), trustedKeys = trustedPolicyKeysFromEnvironment()) {
|
|
121
|
+
const verification = verifyEndpointSecurityPolicySignature(policy, trustedKeys);
|
|
122
|
+
if (!verification.ok)
|
|
123
|
+
throw new Error(`Refusing untrusted endpoint policy: ${verification.detail}`);
|
|
124
|
+
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
125
|
+
const target = path.join(stateDir, POLICY_FILE);
|
|
126
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
127
|
+
fs.writeFileSync(temporary, `${JSON.stringify(policy, null, 2)}\n`, { mode: 0o600 });
|
|
128
|
+
fs.chmodSync(temporary, 0o600);
|
|
129
|
+
fs.renameSync(temporary, target);
|
|
130
|
+
}
|
|
131
|
+
async function syncEndpointSecurityPolicy(config, source, timeoutMs = 3_000) {
|
|
132
|
+
const url = new URL("/api/cli/securityPolicy", config.backendUrl);
|
|
133
|
+
if (source)
|
|
134
|
+
url.searchParams.set("source", source);
|
|
135
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
136
|
+
const policy = await new Promise((resolve, reject) => {
|
|
137
|
+
const request = transport.request(url, {
|
|
138
|
+
method: "GET",
|
|
139
|
+
headers: { Authorization: `Bearer ${config.token}`, Accept: "application/json" },
|
|
140
|
+
}, (response) => {
|
|
141
|
+
let raw = "";
|
|
142
|
+
response.setEncoding("utf8");
|
|
143
|
+
response.on("data", (chunk) => { raw += chunk; });
|
|
144
|
+
response.on("end", () => {
|
|
145
|
+
if ((response.statusCode ?? 500) >= 300)
|
|
146
|
+
return reject(new Error(`policy_http_${response.statusCode}`));
|
|
147
|
+
try {
|
|
148
|
+
resolve(JSON.parse(raw));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
reject(new Error("invalid policy response"));
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
request.setTimeout(timeoutMs, () => request.destroy(new Error(`policy sync timeout after ${timeoutMs}ms`)));
|
|
156
|
+
request.on("error", reject);
|
|
157
|
+
request.end();
|
|
158
|
+
});
|
|
159
|
+
writeEndpointSecurityPolicy(policy);
|
|
160
|
+
return policy;
|
|
161
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
interface SessionSignal {
|
|
2
|
+
sensitiveReadAt?: number;
|
|
3
|
+
}
|
|
4
|
+
export declare function readSessionSecuritySignal(sessionId: string | undefined, stateDir?: string, now?: number): SessionSignal | undefined;
|
|
5
|
+
export declare function recordSensitiveReadSignal(sessionId: string | undefined, stateDir?: string, now?: number): void;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.readSessionSecuritySignal = readSessionSecuritySignal;
|
|
37
|
+
exports.recordSensitiveReadSignal = recordSensitiveReadSignal;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const configDir_1 = require("./configDir");
|
|
41
|
+
const SIGNAL_FILE = "security-session-signals.json";
|
|
42
|
+
const MAX_AGE_MS = 2 * 60 * 60 * 1_000;
|
|
43
|
+
function readSignals(stateDir, now) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(fs.readFileSync(path.join(stateDir, SIGNAL_FILE), "utf8"));
|
|
46
|
+
if (parsed.schemaVersion !== 1 || !parsed.sessions || typeof parsed.sessions !== "object")
|
|
47
|
+
throw new Error("invalid signals");
|
|
48
|
+
parsed.sessions = Object.fromEntries(Object.entries(parsed.sessions)
|
|
49
|
+
.filter(([, signal]) => signal.sensitiveReadAt && now - signal.sensitiveReadAt <= MAX_AGE_MS)
|
|
50
|
+
.slice(-250));
|
|
51
|
+
return parsed;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return { schemaVersion: 1, sessions: {} };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function readSessionSecuritySignal(sessionId, stateDir = (0, configDir_1.codiedevStateDir)(), now = Date.now()) {
|
|
58
|
+
if (!sessionId)
|
|
59
|
+
return undefined;
|
|
60
|
+
return readSignals(stateDir, now).sessions[sessionId];
|
|
61
|
+
}
|
|
62
|
+
function recordSensitiveReadSignal(sessionId, stateDir = (0, configDir_1.codiedevStateDir)(), now = Date.now()) {
|
|
63
|
+
if (!sessionId)
|
|
64
|
+
return;
|
|
65
|
+
const signals = readSignals(stateDir, now);
|
|
66
|
+
signals.sessions[sessionId] = { sensitiveReadAt: now };
|
|
67
|
+
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
68
|
+
const target = path.join(stateDir, SIGNAL_FILE);
|
|
69
|
+
const temporary = `${target}.${process.pid}.tmp`;
|
|
70
|
+
fs.writeFileSync(temporary, `${JSON.stringify(signals)}\n`, { mode: 0o600 });
|
|
71
|
+
fs.renameSync(temporary, target);
|
|
72
|
+
}
|
package/dist/utils.js
CHANGED
|
@@ -1054,6 +1054,11 @@ function installOpenCodeCapturePlugin() {
|
|
|
1054
1054
|
const start = resolveHookInvocation("session-start-opencode");
|
|
1055
1055
|
const capture = resolveHookInvocation("capture-opencode");
|
|
1056
1056
|
const finish = resolveHookInvocation("capture-opencode-final");
|
|
1057
|
+
const backup = resolveHookInvocation("backup");
|
|
1058
|
+
const prompt = resolveHookInvocation("discover-codie");
|
|
1059
|
+
const securityPre = resolveHookInvocation("security-pre-codie");
|
|
1060
|
+
const securityPost = resolveHookInvocation("security-post-codie");
|
|
1061
|
+
const securityFailure = resolveHookInvocation("security-failure-codie");
|
|
1057
1062
|
const source = `// Managed by codiedev connect. Safe to remove and reinstall.\n` +
|
|
1058
1063
|
`import { spawn, spawnSync } from "node:child_process";\n` +
|
|
1059
1064
|
`import { mkdir, writeFile } from "node:fs/promises";\n` +
|
|
@@ -1062,6 +1067,11 @@ function installOpenCodeCapturePlugin() {
|
|
|
1062
1067
|
`const START = ${JSON.stringify(start)};\n` +
|
|
1063
1068
|
`const CAPTURE = ${JSON.stringify(capture)};\n` +
|
|
1064
1069
|
`const FINISH = ${JSON.stringify(finish)};\n` +
|
|
1070
|
+
`const BACKUP = ${JSON.stringify(backup)};\n` +
|
|
1071
|
+
`const PROMPT = ${JSON.stringify(prompt)};\n` +
|
|
1072
|
+
`const SECURITY_PRE = ${JSON.stringify(securityPre)};\n` +
|
|
1073
|
+
`const SECURITY_POST = ${JSON.stringify(securityPost)};\n` +
|
|
1074
|
+
`const SECURITY_FAILURE = ${JSON.stringify(securityFailure)};\n` +
|
|
1065
1075
|
`const fingerprints = new Map();\n\n` +
|
|
1066
1076
|
`const transcriptPaths = new Map();\n\n` +
|
|
1067
1077
|
`function sessionId(event) {\n` +
|
|
@@ -1077,6 +1087,18 @@ function installOpenCodeCapturePlugin() {
|
|
|
1077
1087
|
`function dispatchSync(invocation, payload) {\n` +
|
|
1078
1088
|
` try { spawnSync(invocation.command, invocation.args, { input: JSON.stringify(payload), stdio: ["pipe", "ignore", "ignore"], timeout: 1500 }); } catch {}\n` +
|
|
1079
1089
|
`}\n\n` +
|
|
1090
|
+
`function enforceSync(invocation, payload) {\n` +
|
|
1091
|
+
` const result = spawnSync(invocation.command, invocation.args, { input: JSON.stringify(payload), encoding: "utf8", stdio: ["pipe", "pipe", "ignore"], timeout: 1500 });\n` +
|
|
1092
|
+
` if (result.error) return;\n` +
|
|
1093
|
+
` try {\n` +
|
|
1094
|
+
` const decision = JSON.parse(result.stdout || "{}");\n` +
|
|
1095
|
+
` const denied = decision?.decision === "block" || decision?.hookSpecificOutput?.permissionDecision === "deny";\n` +
|
|
1096
|
+
` if (denied) throw new Error(decision?.reason || decision?.hookSpecificOutput?.permissionDecisionReason || "Codie security policy blocked this action");\n` +
|
|
1097
|
+
` } catch (error) {\n` +
|
|
1098
|
+
` if (error instanceof SyntaxError) return;\n` +
|
|
1099
|
+
` throw error;\n` +
|
|
1100
|
+
` }\n` +
|
|
1101
|
+
`}\n\n` +
|
|
1080
1102
|
`export const CodiedevCapture = async ({ client, directory, worktree }) => {\n` +
|
|
1081
1103
|
` const finalizeAll = () => {\n` +
|
|
1082
1104
|
` for (const [id, transcriptPath] of transcriptPaths) {\n` +
|
|
@@ -1086,6 +1108,21 @@ function installOpenCodeCapturePlugin() {
|
|
|
1086
1108
|
` };\n` +
|
|
1087
1109
|
` process.once("exit", finalizeAll);\n` +
|
|
1088
1110
|
` return ({\n` +
|
|
1111
|
+
` "chat.message": async (input, output) => {\n` +
|
|
1112
|
+
` const id = input?.sessionID ?? input?.sessionId ?? "unknown";\n` +
|
|
1113
|
+
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1114
|
+
` const prompt = Array.isArray(output?.parts) ? output.parts.filter((part) => part?.type === "text").map((part) => part.text || "").join("\\n") : "";\n` +
|
|
1115
|
+
` enforceSync(PROMPT, { sessionId: id, cwd, prompt });\n` +
|
|
1116
|
+
` },\n` +
|
|
1117
|
+
` "tool.execute.before": async (input, output) => {\n` +
|
|
1118
|
+
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1119
|
+
` enforceSync(SECURITY_PRE, { sessionId: input?.sessionID ?? input?.sessionId ?? "unknown", cwd, toolName: input?.tool ?? "unknown", toolUseId: input?.callID, toolInput: output?.args ?? output });\n` +
|
|
1120
|
+
` },\n` +
|
|
1121
|
+
` "tool.execute.after": async (input, output) => {\n` +
|
|
1122
|
+
` const cwd = worktree || directory || process.cwd();\n` +
|
|
1123
|
+
` const payload = { sessionId: input?.sessionID ?? input?.sessionId ?? "unknown", cwd, toolName: input?.tool ?? "unknown", toolUseId: input?.callID, toolInput: input?.args, toolResponse: output };\n` +
|
|
1124
|
+
` dispatch(output?.error ? SECURITY_FAILURE : SECURITY_POST, payload);\n` +
|
|
1125
|
+
` },\n` +
|
|
1089
1126
|
` event: async ({ event }) => {\n` +
|
|
1090
1127
|
` const id = sessionId(event);\n` +
|
|
1091
1128
|
` if (!id) return;\n` +
|
|
@@ -1117,6 +1154,7 @@ function installOpenCodeCapturePlugin() {
|
|
|
1117
1154
|
` await writeFile(transcriptPath, JSON.stringify(list), { mode: 0o600 });\n` +
|
|
1118
1155
|
` transcriptPaths.set(id, transcriptPath);\n` +
|
|
1119
1156
|
` dispatch(CAPTURE, { session_id: id, transcript_path: transcriptPath, cwd, workspace_roots: [cwd] });\n` +
|
|
1157
|
+
` dispatch(BACKUP, { session_id: id, cwd, workspace_roots: [cwd] });\n` +
|
|
1120
1158
|
` } catch {}\n` +
|
|
1121
1159
|
` },\n` +
|
|
1122
1160
|
` });\n` +
|
|
@@ -1196,6 +1234,11 @@ function installHook() {
|
|
|
1196
1234
|
const hooks = settings.hooks;
|
|
1197
1235
|
// SessionEnd → capture (upload the finished session for the team library).
|
|
1198
1236
|
upsertCodiedevHook(hooks, "SessionEnd", "capture");
|
|
1237
|
+
// Tool boundaries → ADR shadow detection. PreToolUse is the eventual
|
|
1238
|
+
// enforcement point; alert mode always returns allow.
|
|
1239
|
+
upsertCodiedevHook(hooks, "PreToolUse", "security-pre");
|
|
1240
|
+
upsertCodiedevHook(hooks, "PostToolUse", "security-post");
|
|
1241
|
+
upsertCodiedevHook(hooks, "PostToolUseFailure", "security-failure");
|
|
1199
1242
|
// UserPromptSubmit → discover (surface a matching team skill for the task).
|
|
1200
1243
|
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover");
|
|
1201
1244
|
// Stop → backup (auto-back-up changed artifact files to your private library).
|
|
@@ -1305,6 +1348,9 @@ function ensureAllCodiedevHooksInstalled() {
|
|
|
1305
1348
|
const hooks = (settings.hooks ?? {});
|
|
1306
1349
|
const wanted = [
|
|
1307
1350
|
["SessionEnd", "capture"],
|
|
1351
|
+
["PreToolUse", "security-pre"],
|
|
1352
|
+
["PostToolUse", "security-post"],
|
|
1353
|
+
["PostToolUseFailure", "security-failure"],
|
|
1308
1354
|
["UserPromptSubmit", "discover"],
|
|
1309
1355
|
["Stop", "backup"],
|
|
1310
1356
|
["StopFailure", "limit", "rate_limit"],
|
|
@@ -1767,9 +1813,8 @@ function installCodexHook() {
|
|
|
1767
1813
|
hooksFile.hooks = {};
|
|
1768
1814
|
}
|
|
1769
1815
|
const hooks = hooksFile.hooks;
|
|
1770
|
-
if (!hooks.Stop)
|
|
1816
|
+
if (!hooks.Stop)
|
|
1771
1817
|
hooks.Stop = [];
|
|
1772
|
-
}
|
|
1773
1818
|
const existing = hooks.Stop;
|
|
1774
1819
|
const filtered = existing.filter((hook) => {
|
|
1775
1820
|
const inner = hook.hooks;
|
|
@@ -1797,6 +1842,15 @@ function installCodexHook() {
|
|
|
1797
1842
|
],
|
|
1798
1843
|
});
|
|
1799
1844
|
hooks.Stop = filtered;
|
|
1845
|
+
// Codex uses the same Claude-style matcher-group shape. Keep these as
|
|
1846
|
+
// separate entries because Codex hook coverage is still evolving and a
|
|
1847
|
+
// malformed/unavailable post hook must not interfere with PreToolUse.
|
|
1848
|
+
upsertCodiedevHook(hooks, "PreToolUse", "security-pre-codex");
|
|
1849
|
+
upsertCodiedevHook(hooks, "PostToolUse", "security-post-codex");
|
|
1850
|
+
upsertCodiedevHook(hooks, "PostToolUseFailure", "security-failure-codex");
|
|
1851
|
+
// Prompt guard + skill discovery. The guard runs locally before any prompt
|
|
1852
|
+
// content can reach either the model or Codiedev's discovery endpoint.
|
|
1853
|
+
upsertCodiedevHook(hooks, "UserPromptSubmit", "discover-codex");
|
|
1800
1854
|
if (!fs.existsSync(CODEX_DIR)) {
|
|
1801
1855
|
fs.mkdirSync(CODEX_DIR, { recursive: true });
|
|
1802
1856
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codiedev",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.37",
|
|
4
4
|
"description": "Connect Claude Code, Codex, Cursor, OpenCode, or VS Code Copilot to Codiedev for team session capture and artifact collaboration — and run the governed Codie harness",
|
|
5
5
|
"bin": {
|
|
6
6
|
"codiedev": "dist/cli.js",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"dev": "tsc --watch",
|
|
13
13
|
"prepublishOnly": "tsc",
|
|
14
14
|
"test": "vitest run",
|
|
15
|
+
"test:security-pilot": "vitest run src/__tests__/security.test.ts src/__tests__/securityInstall.test.ts src/__tests__/securityPilot.test.ts src/__tests__/codieSecurityAdapterWiring.test.ts && tsc && node dist/cli.js security pilot --json",
|
|
15
16
|
"test:watch": "vitest"
|
|
16
17
|
},
|
|
17
18
|
"files": [
|
|
@@ -35,12 +36,18 @@
|
|
|
35
36
|
"session-capture"
|
|
36
37
|
],
|
|
37
38
|
"homepage": "https://codiedev.com",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/Signal-and-Code/codiedev.git",
|
|
42
|
+
"directory": "packages/codiedev-cli"
|
|
43
|
+
},
|
|
38
44
|
"license": "MIT",
|
|
39
45
|
"engines": {
|
|
40
46
|
"node": ">=18"
|
|
41
47
|
},
|
|
42
48
|
"publishConfig": {
|
|
43
|
-
"access": "public"
|
|
49
|
+
"access": "public",
|
|
50
|
+
"provenance": true
|
|
44
51
|
},
|
|
45
52
|
"devDependencies": {
|
|
46
53
|
"@types/node": "^20.0.0",
|