codiedev 0.10.0-beta.4 → 0.10.0-beta.6
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 +11 -5
- package/assets/tui-brand-plugin.tsx +14 -2
- package/dist/backup/cache.js +1 -5
- package/dist/backup/match.js +0 -6
- package/dist/backup/run.js +3 -35
- package/dist/captureExclusions.js +0 -12
- package/dist/captureLog.js +0 -28
- package/dist/captureReadiness.js +0 -58
- package/dist/captureSignals.js +68 -3
- package/dist/cli.js +75 -64
- package/dist/codexHookTrust.js +125 -6
- package/dist/codexLimitScan.js +2 -109
- package/dist/codie.js +50 -114
- package/dist/codieBrandInstall.js +0 -18
- package/dist/codieLauncher.js +20 -92
- package/dist/codieRuntime.js +19 -23
- package/dist/codieRuntimeInstall.js +108 -23
- package/dist/codieUsage.js +38 -41
- package/dist/commands/agentTemplates.generated.js +136 -0
- package/dist/commands/agents.js +1066 -0
- package/dist/commands/agentsSdk.generated.js +7 -0
- package/dist/commands/ask.js +0 -3
- package/dist/commands/capture.js +0 -2
- package/dist/commands/createTicket.js +0 -10
- package/dist/commands/doctor.js +53 -120
- package/dist/commands/evals.js +187 -0
- package/dist/commands/inbox.js +0 -2
- package/dist/commands/library.js +0 -3
- package/dist/commands/pack.js +573 -0
- package/dist/commands/ping.js +0 -2
- package/dist/commands/pull.js +6 -10
- package/dist/commands/push.js +2 -12
- package/dist/commands/search.js +0 -4
- package/dist/commands/send.js +0 -5
- package/dist/commands/share.js +0 -4
- package/dist/commands/shared.js +0 -35
- package/dist/commands/traces.js +344 -0
- package/dist/commands/tracesLabel.js +348 -0
- package/dist/configDir.js +0 -10
- package/dist/connect.js +19 -116
- package/dist/connectFlow.js +0 -9
- package/dist/copilotHooks.js +391 -0
- package/dist/copilotInstallation.js +64 -0
- package/dist/cursorHooks.js +38 -30
- package/dist/cursorLifecycle.js +109 -0
- package/dist/detection.js +0 -30
- package/dist/harnessDirs.js +77 -0
- package/dist/hook.js +182 -469
- package/dist/inboxFormat.js +0 -24
- package/dist/launchContext.js +150 -0
- package/dist/launcher.js +140 -0
- package/dist/limitEvent.js +41 -35
- package/dist/limitEventReporter.js +1 -1
- package/dist/managedPi.js +240 -0
- package/dist/mcp.js +3 -384
- package/dist/nativeSteering.js +24 -0
- package/dist/packs/codie.js +182 -0
- package/dist/packs/harnesses.js +411 -0
- package/dist/packs/hookGate.js +256 -0
- package/dist/packs/install.js +286 -0
- package/dist/packs/manifest.js +126 -0
- package/dist/packs/paths.js +81 -0
- package/dist/packs/shim.js +214 -0
- package/dist/packs/store.js +155 -0
- package/dist/packs/sync.js +170 -0
- package/dist/piHooks.js +216 -0
- package/dist/piRuntime.js +120 -0
- package/dist/repoResolver.js +0 -19
- package/dist/secret.js +0 -3
- package/dist/selfUpdate.js +376 -0
- package/dist/skillMarker.js +23 -0
- package/dist/subagents.js +135 -0
- package/dist/teamSkillContext.js +10 -0
- package/dist/transcriptModels.js +6 -8
- package/dist/upgradeNudge.js +0 -30
- package/dist/utils.js +494 -824
- package/dist/version.js +0 -10
- package/dist/versionCheck.js +0 -33
- package/package.json +8 -9
- package/dist/backup/cache.d.ts +0 -14
- package/dist/backup/match.d.ts +0 -2
- package/dist/backup/run.d.ts +0 -25
- package/dist/captureExclusions.d.ts +0 -30
- package/dist/captureLog.d.ts +0 -20
- package/dist/captureReadiness.d.ts +0 -65
- package/dist/captureSignals.d.ts +0 -19
- package/dist/cli.d.ts +0 -16
- package/dist/codexHookTrust.d.ts +0 -34
- package/dist/codexLimitScan.d.ts +0 -64
- package/dist/codie.d.ts +0 -2
- package/dist/codieBrandInstall.d.ts +0 -11
- package/dist/codieLauncher.d.ts +0 -86
- package/dist/codieRuntime.d.ts +0 -29
- package/dist/codieRuntimeInstall.d.ts +0 -28
- package/dist/codieStartup.d.ts +0 -26
- package/dist/codieStartup.js +0 -30
- package/dist/codieUsage.d.ts +0 -57
- package/dist/commands/ask.d.ts +0 -1
- package/dist/commands/capture.d.ts +0 -14
- package/dist/commands/createTicket.d.ts +0 -27
- package/dist/commands/delete.d.ts +0 -1
- package/dist/commands/doctor.d.ts +0 -1
- package/dist/commands/inbox.d.ts +0 -3
- package/dist/commands/library.d.ts +0 -2
- package/dist/commands/note.d.ts +0 -1
- package/dist/commands/ping.d.ts +0 -1
- package/dist/commands/promote.d.ts +0 -1
- package/dist/commands/promote.js +0 -53
- package/dist/commands/pull.d.ts +0 -2
- package/dist/commands/push.d.ts +0 -33
- package/dist/commands/react.d.ts +0 -2
- package/dist/commands/react.js +0 -40
- package/dist/commands/reverseTicket.d.ts +0 -2
- package/dist/commands/reverseTicket.js +0 -477
- package/dist/commands/search.d.ts +0 -2
- package/dist/commands/security.d.ts +0 -1
- package/dist/commands/security.js +0 -156
- package/dist/commands/send.d.ts +0 -2
- package/dist/commands/share.d.ts +0 -2
- package/dist/commands/shared.d.ts +0 -64
- package/dist/configDir.d.ts +0 -1
- package/dist/connect.d.ts +0 -1
- package/dist/connectFlow.d.ts +0 -13
- package/dist/cursorHooks.d.ts +0 -23
- package/dist/detection.d.ts +0 -30
- package/dist/hook.d.ts +0 -6
- package/dist/inboxFormat.d.ts +0 -38
- package/dist/limitEvent.d.ts +0 -16
- package/dist/limitEventReporter.d.ts +0 -21
- package/dist/mcp.d.ts +0 -12
- package/dist/prUrl.d.ts +0 -9
- package/dist/prUrl.js +0 -33
- package/dist/repoResolver.d.ts +0 -17
- package/dist/secret.d.ts +0 -1
- package/dist/security.d.ts +0 -102
- package/dist/security.js +0 -437
- package/dist/securityDeepScan.d.ts +0 -41
- package/dist/securityDeepScan.js +0 -248
- package/dist/securityDoctor.d.ts +0 -32
- package/dist/securityDoctor.js +0 -63
- package/dist/securityFeature.d.ts +0 -5
- package/dist/securityFeature.js +0 -7
- package/dist/securityInstall.d.ts +0 -63
- package/dist/securityInstall.js +0 -239
- package/dist/securityInventory.d.ts +0 -54
- package/dist/securityInventory.js +0 -556
- package/dist/securityPilot.d.ts +0 -19
- package/dist/securityPilot.js +0 -61
- package/dist/securityPolicy.d.ts +0 -33
- package/dist/securityPolicy.js +0 -188
- package/dist/securitySession.d.ts +0 -6
- package/dist/securitySession.js +0 -72
- package/dist/transcriptModels.d.ts +0 -18
- package/dist/upgradeNudge.d.ts +0 -25
- package/dist/utils.d.ts +0 -346
- package/dist/version.d.ts +0 -1
- package/dist/versionCheck.d.ts +0 -68
package/dist/security.js
DELETED
|
@@ -1,437 +0,0 @@
|
|
|
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.SECURITY_RUNTIME_VERSION = void 0;
|
|
37
|
-
exports.withEndpointIdentity = withEndpointIdentity;
|
|
38
|
-
exports.evaluatePromptSecurity = evaluatePromptSecurity;
|
|
39
|
-
exports.evaluateToolSecurity = evaluateToolSecurity;
|
|
40
|
-
exports.securityInputHasSensitivePath = securityInputHasSensitivePath;
|
|
41
|
-
exports.buildSecurityWorkflowEvent = buildSecurityWorkflowEvent;
|
|
42
|
-
exports.buildSecurityPromptEvent = buildSecurityPromptEvent;
|
|
43
|
-
exports.buildBlockedSecurityPromptEvent = buildBlockedSecurityPromptEvent;
|
|
44
|
-
const crypto = __importStar(require("crypto"));
|
|
45
|
-
// Advances only when endpoint enforcement behavior changes. This is
|
|
46
|
-
// intentionally independent from the package version so security coverage can
|
|
47
|
-
// remain current across unrelated CLI releases.
|
|
48
|
-
exports.SECURITY_RUNTIME_VERSION = 1;
|
|
49
|
-
function withEndpointIdentity(event, identity) {
|
|
50
|
-
return {
|
|
51
|
-
...event,
|
|
52
|
-
...(identity.endpointId ? { endpointId: identity.endpointId.slice(0, 240) } : {}),
|
|
53
|
-
...(identity.endpointName ? { endpointName: identity.endpointName.slice(0, 240) } : {}),
|
|
54
|
-
...(identity.cliVersion ? { cliVersion: identity.cliVersion.slice(0, 80) } : {}),
|
|
55
|
-
...(typeof identity.securityRuntimeVersion === "number"
|
|
56
|
-
&& Number.isInteger(identity.securityRuntimeVersion)
|
|
57
|
-
&& identity.securityRuntimeVersion > 0
|
|
58
|
-
? { securityRuntimeVersion: identity.securityRuntimeVersion }
|
|
59
|
-
: {}),
|
|
60
|
-
...(typeof identity.securityPolicyVersion === "number"
|
|
61
|
-
&& Number.isInteger(identity.securityPolicyVersion)
|
|
62
|
-
&& identity.securityPolicyVersion > 0
|
|
63
|
-
? { securityPolicyVersion: identity.securityPolicyVersion }
|
|
64
|
-
: {}),
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
function scopeMatches(value, candidate) {
|
|
68
|
-
const escaped = value.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
69
|
-
try {
|
|
70
|
-
return new RegExp(`^${escaped}$`, "i").test(candidate);
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
function effectivePolicyAction(policy, category, fallback, source, input) {
|
|
77
|
-
if (!policy)
|
|
78
|
-
return fallback;
|
|
79
|
-
const toolName = input?.tool_name ?? input?.toolName ?? "";
|
|
80
|
-
const cwd = input?.cwd ?? "";
|
|
81
|
-
const excepted = policy.exceptions.some((exception) => {
|
|
82
|
-
if (exception.scope === "company" || exception.scope === "user")
|
|
83
|
-
return true;
|
|
84
|
-
if (exception.scope === "category")
|
|
85
|
-
return scopeMatches(exception.scopeValue, category);
|
|
86
|
-
if (exception.scope === "source")
|
|
87
|
-
return !!source && scopeMatches(exception.scopeValue, source);
|
|
88
|
-
if (exception.scope === "tool")
|
|
89
|
-
return !!toolName && scopeMatches(exception.scopeValue, toolName);
|
|
90
|
-
if (exception.scope === "repository")
|
|
91
|
-
return !!cwd && scopeMatches(exception.scopeValue, cwd);
|
|
92
|
-
return false;
|
|
93
|
-
});
|
|
94
|
-
if (excepted)
|
|
95
|
-
return "allow";
|
|
96
|
-
const configured = policy.rules[category] ?? fallback;
|
|
97
|
-
return policy.enforcementMode === "alert" && configured === "block" ? "alert" : configured;
|
|
98
|
-
}
|
|
99
|
-
function policyActionPrefix(action) {
|
|
100
|
-
if (action === "block")
|
|
101
|
-
return "Codie blocked";
|
|
102
|
-
if (action === "alert")
|
|
103
|
-
return "Codie detected";
|
|
104
|
-
return "Codie detected but allowed";
|
|
105
|
-
}
|
|
106
|
-
const SECRET_KEY = /(?:api[_-]?key|access[_-]?token|auth[_-]?token|password|passwd|secret|private[_-]?key|client[_-]?secret)/i;
|
|
107
|
-
const SECRET_VALUE_PATTERNS = [
|
|
108
|
-
/\b(?:sk|rk|pk)-(?:live|test|proj)?[-_A-Za-z0-9]{16,}\b/g,
|
|
109
|
-
/\bgh[opusr]_[A-Za-z0-9]{20,}\b/g,
|
|
110
|
-
/\bAKIA[0-9A-Z]{16}\b/g,
|
|
111
|
-
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g,
|
|
112
|
-
/\bBearer\s+[A-Za-z0-9._~+\/-]{16,}=*\b/gi,
|
|
113
|
-
];
|
|
114
|
-
const SECRET_ASSIGNMENT = /\b([A-Z][A-Z0-9_]*(?:API_KEY|ACCESS_KEY|TOKEN|SECRET|PASSWORD|PASSWD|PRIVATE_KEY|CLIENT_SECRET|DATABASE_URL))\s*[:=]\s*["']?([^\s"'`]{8,})/gi;
|
|
115
|
-
const SENSITIVE_PATH = /(?:~\/\.ssh\/(?:id_[a-z0-9_-]+)|(?:^|\/)\.env(?:\.[a-z0-9_-]+)?|(?:^|\/)\.aws\/credentials|(?:^|\/)\.config\/gcloud\/application_default_credentials\.json)(?=$|[\/\s"'])/i;
|
|
116
|
-
const PROTECTED_AGENT_CONFIG = /(?:^|[\/\s"'])\.(?:claude\/(?:settings(?:\.local)?\.json|hooks)|codex\/(?:config\.toml|hooks\.json)|cursor\/(?:mcp\.json|rules)|config\/opencode\/(?:opencode\.json|plugins))(?=$|[\s"'\/])/i;
|
|
117
|
-
function looksLikePlaceholder(value) {
|
|
118
|
-
return (/^\$\{?[A-Z][A-Z0-9_]*\}?$/.test(value) ||
|
|
119
|
-
/^(?:your|example|sample|dummy|fake|replace|redacted|changeme)[-_\[]/i.test(value) ||
|
|
120
|
-
/^\[(?:redacted|secret|value)\]$/i.test(value) ||
|
|
121
|
-
/^<[^>]+>$/.test(value));
|
|
122
|
-
}
|
|
123
|
-
function containsSecretValue(text) {
|
|
124
|
-
for (const pattern of SECRET_VALUE_PATTERNS) {
|
|
125
|
-
pattern.lastIndex = 0;
|
|
126
|
-
if (pattern.test(text)) {
|
|
127
|
-
pattern.lastIndex = 0;
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
pattern.lastIndex = 0;
|
|
131
|
-
}
|
|
132
|
-
SECRET_ASSIGNMENT.lastIndex = 0;
|
|
133
|
-
let match;
|
|
134
|
-
while ((match = SECRET_ASSIGNMENT.exec(text)) !== null) {
|
|
135
|
-
if (!looksLikePlaceholder(match[2])) {
|
|
136
|
-
SECRET_ASSIGNMENT.lastIndex = 0;
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
SECRET_ASSIGNMENT.lastIndex = 0;
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Runs before a user prompt reaches the model. This is intentionally narrow:
|
|
145
|
-
* block credential material, but allow safe references such as
|
|
146
|
-
* `$OPENAI_API_KEY` so developers can ask agents to use environment variables
|
|
147
|
-
* without pasting their values.
|
|
148
|
-
*/
|
|
149
|
-
function evaluatePromptSecurity(prompt, policy, source) {
|
|
150
|
-
if (!prompt || !containsSecretValue(prompt))
|
|
151
|
-
return { action: "allow" };
|
|
152
|
-
const action = effectivePolicyAction(policy, "secret_in_prompt", "block", source);
|
|
153
|
-
return {
|
|
154
|
-
action,
|
|
155
|
-
category: "secret_in_prompt",
|
|
156
|
-
reason: policyActionPrefix(action) + " this prompt because it appears to contain an API key, password, or credential. Remove the secret value and reference the environment variable by name instead (for example, $OPENAI_API_KEY).",
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
function serializedToolInput(input) {
|
|
160
|
-
const value = input.tool_input ?? input.toolInput;
|
|
161
|
-
if (typeof value === "string")
|
|
162
|
-
return value;
|
|
163
|
-
try {
|
|
164
|
-
return JSON.stringify(value ?? "");
|
|
165
|
-
}
|
|
166
|
-
catch {
|
|
167
|
-
return "";
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
function toolCommand(input) {
|
|
171
|
-
const value = input.tool_input ?? input.toolInput;
|
|
172
|
-
if (value && typeof value === "object" && typeof value.command === "string") {
|
|
173
|
-
return value.command;
|
|
174
|
-
}
|
|
175
|
-
return typeof value === "string" ? value : "";
|
|
176
|
-
}
|
|
177
|
-
function isWriteOperation(input, serialized) {
|
|
178
|
-
const tool = String(input.tool_name ?? input.toolName ?? "").toLowerCase();
|
|
179
|
-
return /(?:^|[_-])(?:write|edit|patch|move|rename)(?:$|[_-])/.test(tool) ||
|
|
180
|
-
(/\b(?:tee|cp|mv|install)\b|(?:^|[^>])>{1,2}(?:[^>]|$)/.test(serialized) && /(?:bash|shell|terminal|exec)/.test(tool));
|
|
181
|
-
}
|
|
182
|
-
function networkHosts(text) {
|
|
183
|
-
const hosts = [];
|
|
184
|
-
for (const match of text.matchAll(/https?:\/\/([^\s/"'`]+)/gi))
|
|
185
|
-
hosts.push(match[1].replace(/:\d+$/, "").toLowerCase());
|
|
186
|
-
return [...new Set(hosts)];
|
|
187
|
-
}
|
|
188
|
-
function allowedByPatterns(candidate, patterns) {
|
|
189
|
-
return Boolean(patterns?.some((pattern) => scopeMatches(pattern, candidate)));
|
|
190
|
-
}
|
|
191
|
-
function mcpServerName(input) {
|
|
192
|
-
const tool = String(input.tool_name ?? input.toolName ?? "");
|
|
193
|
-
return tool.match(/^mcp__(.+?)__/)?.[1] ?? tool.match(/^mcp[.:/]([^.:/]+)/i)?.[1] ?? null;
|
|
194
|
-
}
|
|
195
|
-
function rootCommand(command) {
|
|
196
|
-
const cleaned = command.trim().replace(/^(?:sudo\s+|env\s+(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)*)/, "");
|
|
197
|
-
return cleaned.match(/^([A-Za-z0-9_./+-]+)/)?.[1]?.split("/").pop() ?? null;
|
|
198
|
-
}
|
|
199
|
-
function destructiveTargetOutsideWorkspace(command, cwd) {
|
|
200
|
-
const match = command.match(/\brm\s+(?:-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*|-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*)\s+(?:--\s+)?([^\s;&|]+)/);
|
|
201
|
-
if (!match)
|
|
202
|
-
return false;
|
|
203
|
-
const target = match[1].replace(/^['"]|['"]$/g, "");
|
|
204
|
-
if (["/", "~", "$HOME", "${HOME}", "..", "../"].includes(target))
|
|
205
|
-
return true;
|
|
206
|
-
if (target.startsWith("~/") || target.startsWith("../"))
|
|
207
|
-
return true;
|
|
208
|
-
if (!target.startsWith("/") || !cwd)
|
|
209
|
-
return false;
|
|
210
|
-
const normalizedCwd = cwd.replace(/\/+$/, "");
|
|
211
|
-
return target !== normalizedCwd && !target.startsWith(`${normalizedCwd}/`);
|
|
212
|
-
}
|
|
213
|
-
/** Runs synchronously before a tool executes. No network or model is involved. */
|
|
214
|
-
function evaluateToolSecurity(input, phase, policy, source, sessionContext) {
|
|
215
|
-
if (phase !== "pre")
|
|
216
|
-
return { action: "allow" };
|
|
217
|
-
const serialized = serializedToolInput(input);
|
|
218
|
-
if (!serialized)
|
|
219
|
-
return { action: "allow" };
|
|
220
|
-
if (destructiveTargetOutsideWorkspace(serialized, input.cwd)) {
|
|
221
|
-
const action = effectivePolicyAction(policy, "destructive_outside_workspace", "block", source, input);
|
|
222
|
-
return {
|
|
223
|
-
action,
|
|
224
|
-
category: "destructive_outside_workspace",
|
|
225
|
-
reason: policyActionPrefix(action) + " a destructive command targeting files outside the approved workspace.",
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
const hasNetworkEgress = /\b(?:curl|wget)\b|https?:\/\//i.test(serialized);
|
|
229
|
-
if (hasNetworkEgress && containsSecretValue(serialized)) {
|
|
230
|
-
const action = effectivePolicyAction(policy, "secret_exfiltration", "block", source, input);
|
|
231
|
-
return {
|
|
232
|
-
action,
|
|
233
|
-
category: "secret_exfiltration",
|
|
234
|
-
reason: policyActionPrefix(action) + " a command that would send credential material to an external destination.",
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
if (hasNetworkEgress &&
|
|
238
|
-
sessionContext?.sensitiveReadAt &&
|
|
239
|
-
(sessionContext.now ?? Date.now()) - sessionContext.sensitiveReadAt <= 5 * 60_000) {
|
|
240
|
-
return {
|
|
241
|
-
action: effectivePolicyAction(policy, "sensitive_read_then_egress", "alert", source, input),
|
|
242
|
-
category: "sensitive_read_then_egress",
|
|
243
|
-
reason: "Codie observed network egress shortly after this session read a credential-bearing file.",
|
|
244
|
-
};
|
|
245
|
-
}
|
|
246
|
-
const writeOperation = isWriteOperation(input, serialized);
|
|
247
|
-
if (writeOperation && SENSITIVE_PATH.test(serialized)) {
|
|
248
|
-
const action = effectivePolicyAction(policy, "sensitive_file_write", "block", source, input);
|
|
249
|
-
return {
|
|
250
|
-
action,
|
|
251
|
-
category: "sensitive_file_write",
|
|
252
|
-
reason: policyActionPrefix(action) + " a write to a credential-bearing file.",
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
if (writeOperation && PROTECTED_AGENT_CONFIG.test(serialized)) {
|
|
256
|
-
const action = effectivePolicyAction(policy, "protected_agent_config_write", "block", source, input);
|
|
257
|
-
return {
|
|
258
|
-
action,
|
|
259
|
-
category: "protected_agent_config_write",
|
|
260
|
-
reason: policyActionPrefix(action) + " an agent modifying its own hooks, policy, MCP, or instruction configuration.",
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
const server = mcpServerName(input);
|
|
264
|
-
if (server && policy?.constraints?.allowedMcpServers?.length && !allowedByPatterns(server, policy.constraints.allowedMcpServers)) {
|
|
265
|
-
return {
|
|
266
|
-
action: effectivePolicyAction(policy, "untrusted_mcp_tool", "alert", source, input),
|
|
267
|
-
category: "untrusted_mcp_tool",
|
|
268
|
-
reason: `Codie observed a tool call from MCP server “${server}”, which is not in the approved component list.`,
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
const hosts = networkHosts(serialized);
|
|
272
|
-
const unapprovedHost = hosts.find((host) => policy?.constraints?.allowedHosts?.length && !allowedByPatterns(host, policy.constraints.allowedHosts));
|
|
273
|
-
if (unapprovedHost) {
|
|
274
|
-
return {
|
|
275
|
-
action: effectivePolicyAction(policy, "unapproved_network_egress", "alert", source, input),
|
|
276
|
-
category: "unapproved_network_egress",
|
|
277
|
-
reason: `Codie observed network egress to unapproved host “${unapprovedHost}”.`,
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
const command = toolCommand(input);
|
|
281
|
-
if (/\b(?:base64\s+(?:-[a-z]*d|--decode)|openssl\s+enc\s+-d)[^|;&]*(?:\||&&)\s*(?:sh|bash|zsh)\b|\beval\s+["']?\$\(/i.test(command)) {
|
|
282
|
-
return {
|
|
283
|
-
action: effectivePolicyAction(policy, "obfuscated_shell_command", "alert", source, input),
|
|
284
|
-
category: "obfuscated_shell_command",
|
|
285
|
-
reason: "Codie observed an encoded or dynamically evaluated shell payload.",
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
if (/\b(?:npm|pnpm|yarn|pipx?|uv|gem|cargo)\s+(?:add|install|i)\b/i.test(command)) {
|
|
289
|
-
return {
|
|
290
|
-
action: effectivePolicyAction(policy, "package_install", "alert", source, input),
|
|
291
|
-
category: "package_install",
|
|
292
|
-
reason: "Codie observed a package installation. Verify the package source and pinned version.",
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
const commandName = rootCommand(command);
|
|
296
|
-
if (commandName && policy?.constraints?.allowedCommands?.length && !allowedByPatterns(commandName, policy.constraints.allowedCommands)) {
|
|
297
|
-
return {
|
|
298
|
-
action: effectivePolicyAction(policy, "unapproved_command", "alert", source, input),
|
|
299
|
-
category: "unapproved_command",
|
|
300
|
-
reason: `Codie observed command “${commandName}”, which is not in the approved command list.`,
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
if (SENSITIVE_PATH.test(serialized)) {
|
|
304
|
-
return {
|
|
305
|
-
action: effectivePolicyAction(policy, "sensitive_file_read", "alert", source, input),
|
|
306
|
-
category: "sensitive_file_read",
|
|
307
|
-
reason: "Codie observed access to a sensitive credential file. This policy is alert-only.",
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
return { action: "allow" };
|
|
311
|
-
}
|
|
312
|
-
function securityInputHasSensitivePath(input) {
|
|
313
|
-
return SENSITIVE_PATH.test(serializedToolInput(input));
|
|
314
|
-
}
|
|
315
|
-
function fingerprint(value) {
|
|
316
|
-
return crypto.createHash("sha256").update(value).digest("hex").slice(0, 12);
|
|
317
|
-
}
|
|
318
|
-
function redactString(value) {
|
|
319
|
-
let count = 0;
|
|
320
|
-
let redacted = value;
|
|
321
|
-
for (const pattern of SECRET_VALUE_PATTERNS) {
|
|
322
|
-
redacted = redacted.replace(pattern, (match) => {
|
|
323
|
-
count += 1;
|
|
324
|
-
return `[REDACTED_SECRET:${fingerprint(match)}]`;
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
// Keep hook payloads bounded. Full transcripts already use Codie's existing
|
|
328
|
-
// capture path; the detector only needs immediate causal context here.
|
|
329
|
-
if (redacted.length > 16_384)
|
|
330
|
-
redacted = `${redacted.slice(0, 16_384)}[TRUNCATED]`;
|
|
331
|
-
return { value: redacted, count };
|
|
332
|
-
}
|
|
333
|
-
function redact(value, key = "", depth = 0) {
|
|
334
|
-
if (depth > 8)
|
|
335
|
-
return { value: "[TRUNCATED_DEPTH]", count: 0 };
|
|
336
|
-
if (SECRET_KEY.test(key) && value !== null && value !== undefined) {
|
|
337
|
-
const text = typeof value === "string" ? value : JSON.stringify(value);
|
|
338
|
-
return { value: `[REDACTED_SECRET:${fingerprint(text)}]`, count: 1 };
|
|
339
|
-
}
|
|
340
|
-
if (typeof value === "string")
|
|
341
|
-
return redactString(value);
|
|
342
|
-
if (Array.isArray(value)) {
|
|
343
|
-
let count = 0;
|
|
344
|
-
const next = value.slice(0, 200).map((item) => {
|
|
345
|
-
const result = redact(item, key, depth + 1);
|
|
346
|
-
count += result.count;
|
|
347
|
-
return result.value;
|
|
348
|
-
});
|
|
349
|
-
return { value: next, count };
|
|
350
|
-
}
|
|
351
|
-
if (value && typeof value === "object") {
|
|
352
|
-
let count = 0;
|
|
353
|
-
const next = {};
|
|
354
|
-
for (const [childKey, childValue] of Object.entries(value).slice(0, 200)) {
|
|
355
|
-
const result = redact(childValue, childKey, depth + 1);
|
|
356
|
-
count += result.count;
|
|
357
|
-
next[childKey] = result.value;
|
|
358
|
-
}
|
|
359
|
-
return { value: next, count };
|
|
360
|
-
}
|
|
361
|
-
return { value, count: 0 };
|
|
362
|
-
}
|
|
363
|
-
function buildSecurityWorkflowEvent(input, phase, source, now = Date.now) {
|
|
364
|
-
const sessionId = input.session_id ?? input.sessionId ?? "";
|
|
365
|
-
const toolName = input.tool_name ?? input.toolName ?? "";
|
|
366
|
-
if (!sessionId || !toolName)
|
|
367
|
-
return null;
|
|
368
|
-
const rawPayload = {
|
|
369
|
-
input: input.tool_input ?? input.toolInput,
|
|
370
|
-
...(phase === "post" ? { response: input.tool_response ?? input.toolResponse } : {}),
|
|
371
|
-
...(phase === "failure" ? { error: input.error ?? input.tool_response ?? input.toolResponse } : {}),
|
|
372
|
-
};
|
|
373
|
-
const sanitized = redact(rawPayload);
|
|
374
|
-
const duration = input.duration_ms ?? input.durationMs;
|
|
375
|
-
return {
|
|
376
|
-
schemaVersion: 1,
|
|
377
|
-
source,
|
|
378
|
-
phase,
|
|
379
|
-
sessionId,
|
|
380
|
-
toolName,
|
|
381
|
-
toolUseId: input.tool_use_id ?? input.toolUseId,
|
|
382
|
-
cwd: input.cwd,
|
|
383
|
-
observedAt: now(),
|
|
384
|
-
toolDurationMs: typeof duration === "number" && Number.isFinite(duration) ? duration : undefined,
|
|
385
|
-
payload: sanitized.value,
|
|
386
|
-
redactionCount: sanitized.count,
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
/**
|
|
390
|
-
* Captures the intent side of the causal chain without weakening the local
|
|
391
|
-
* privacy boundary. A prompt that the deterministic policy blocks never
|
|
392
|
-
* becomes an uploadable event; allowed prompts are redacted and bounded using
|
|
393
|
-
* the same sanitizer as tool payloads.
|
|
394
|
-
*/
|
|
395
|
-
function buildSecurityPromptEvent(input, source, now = Date.now, policy) {
|
|
396
|
-
const sessionId = input.session_id ?? input.sessionId ?? "";
|
|
397
|
-
if (!sessionId || !input.prompt) {
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
const decision = evaluatePromptSecurity(input.prompt, policy, source);
|
|
401
|
-
if (decision.action === "block")
|
|
402
|
-
return null;
|
|
403
|
-
const sanitized = redact({ prompt: input.prompt });
|
|
404
|
-
return {
|
|
405
|
-
schemaVersion: 1,
|
|
406
|
-
source,
|
|
407
|
-
phase: "pre",
|
|
408
|
-
sessionId,
|
|
409
|
-
toolName: "UserPrompt",
|
|
410
|
-
cwd: input.cwd,
|
|
411
|
-
observedAt: now(),
|
|
412
|
-
payload: sanitized.value,
|
|
413
|
-
redactionCount: sanitized.count,
|
|
414
|
-
localDecision: decision.action === "alert" ? "alert" : undefined,
|
|
415
|
-
localCategory: decision.action === "alert" ? decision.category : undefined,
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
/** Audit-safe evidence for a blocked prompt. The secret-bearing prompt is
|
|
419
|
-
* intentionally omitted; administrators still see that prevention occurred. */
|
|
420
|
-
function buildBlockedSecurityPromptEvent(input, source, category, now = Date.now) {
|
|
421
|
-
const sessionId = input.session_id ?? input.sessionId ?? "";
|
|
422
|
-
if (!sessionId)
|
|
423
|
-
return null;
|
|
424
|
-
return {
|
|
425
|
-
schemaVersion: 1,
|
|
426
|
-
source,
|
|
427
|
-
phase: "pre",
|
|
428
|
-
sessionId,
|
|
429
|
-
toolName: "UserPrompt",
|
|
430
|
-
cwd: input.cwd,
|
|
431
|
-
observedAt: now(),
|
|
432
|
-
payload: { blocked: true, content: "[REDACTED_BLOCKED_PROMPT]" },
|
|
433
|
-
redactionCount: 1,
|
|
434
|
-
localDecision: "block",
|
|
435
|
-
localCategory: category,
|
|
436
|
-
};
|
|
437
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { type SpawnSyncReturns } from "child_process";
|
|
2
|
-
import type { SecurityFindingSeverity, SecurityInventoryReport } from "./securityInventory";
|
|
3
|
-
export interface ExternalSecurityFinding {
|
|
4
|
-
scanner: string;
|
|
5
|
-
componentKey?: string;
|
|
6
|
-
severity: SecurityFindingSeverity;
|
|
7
|
-
ruleId?: string;
|
|
8
|
-
title: string;
|
|
9
|
-
}
|
|
10
|
-
export interface ExternalScannerRun {
|
|
11
|
-
scanner: string;
|
|
12
|
-
status: "passed" | "findings" | "unavailable" | "error" | "skipped";
|
|
13
|
-
detail: string;
|
|
14
|
-
findings: ExternalSecurityFinding[];
|
|
15
|
-
}
|
|
16
|
-
export interface DeepSecurityScanReport {
|
|
17
|
-
schemaVersion: 1;
|
|
18
|
-
generatedAt: string;
|
|
19
|
-
runs: ExternalScannerRun[];
|
|
20
|
-
findings: ExternalSecurityFinding[];
|
|
21
|
-
}
|
|
22
|
-
/** Adds component-addressable OSS scanner findings to the uploadable inventory.
|
|
23
|
-
* Workspace-level findings (for example OSV dependency results) stay in the
|
|
24
|
-
* deep-scan report because they do not identify one agent component. */
|
|
25
|
-
export declare function mergeDeepScanFindings(report: SecurityInventoryReport, deep: DeepSecurityScanReport): SecurityInventoryReport;
|
|
26
|
-
type Runner = (command: string, args: string[], timeoutMs: number) => SpawnSyncReturns<string>;
|
|
27
|
-
/**
|
|
28
|
-
* Runs optional open-source scanners. No scanner is downloaded implicitly.
|
|
29
|
-
* MCP server connection is a separate opt-in because scanning a live stdio
|
|
30
|
-
* server starts third-party code; the normal deep scan remains static.
|
|
31
|
-
*/
|
|
32
|
-
export declare function runDeepSecurityScan(report: SecurityInventoryReport, options?: {
|
|
33
|
-
homeDir?: string;
|
|
34
|
-
workspace?: string;
|
|
35
|
-
connectMcp?: boolean;
|
|
36
|
-
supplyChain?: boolean;
|
|
37
|
-
timeoutMs?: number;
|
|
38
|
-
runner?: Runner;
|
|
39
|
-
now?: () => number;
|
|
40
|
-
}): DeepSecurityScanReport;
|
|
41
|
-
export {};
|