codiedev 0.10.0-beta.1 → 0.10.0-beta.11
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/opencode-plugin.mjs +6 -0
- package/assets/session-key.mjs +48 -0
- package/assets/tui-brand-plugin.tsx +73 -0
- 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 +184 -0
- package/dist/cli.js +135 -72
- package/dist/cliSession.js +456 -0
- package/dist/codexHookTrust.js +125 -6
- package/dist/codexLimitScan.js +359 -0
- package/dist/codie.js +77 -130
- package/dist/codieBrandInstall.js +80 -0
- package/dist/codieLauncher.js +76 -79
- package/dist/codieRuntime.js +19 -23
- package/dist/codieRuntimeInstall.js +108 -23
- package/dist/codieUsage.js +181 -0
- package/dist/commands/agentTemplates.generated.js +136 -0
- package/dist/commands/agents.js +1101 -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 +18 -11
- package/dist/connect.js +55 -118
- package/dist/connectFlow.js +35 -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 +256 -445
- package/dist/inboxFormat.js +0 -24
- package/dist/launchContext.js +150 -0
- package/dist/launcher.js +168 -0
- package/dist/limitEvent.js +44 -13
- package/dist/limitEventReporter.js +1 -1
- package/dist/managedPi.js +256 -0
- package/dist/mcp.js +5 -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 +205 -0
- package/dist/upgradeNudge.js +0 -30
- package/dist/utils.js +502 -828
- 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/cli.d.ts +0 -16
- package/dist/codexHookTrust.d.ts +0 -34
- package/dist/codie.d.ts +0 -2
- package/dist/codieLauncher.d.ts +0 -53
- 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/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/upgradeNudge.d.ts +0 -25
- package/dist/utils.d.ts +0 -342
- package/dist/version.d.ts +0 -1
- package/dist/versionCheck.d.ts +0 -68
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
export type SecurityComponentKind = "mcp-server" | "skill" | "extension" | "plugin" | "hook" | "instruction";
|
|
2
|
-
export type SecurityFindingSeverity = "low" | "medium" | "high" | "critical";
|
|
3
|
-
export interface SecurityInventoryFinding {
|
|
4
|
-
code: string;
|
|
5
|
-
severity: SecurityFindingSeverity;
|
|
6
|
-
detail: string;
|
|
7
|
-
}
|
|
8
|
-
export interface SecurityComponent {
|
|
9
|
-
componentKey: string;
|
|
10
|
-
kind: SecurityComponentKind;
|
|
11
|
-
name: string;
|
|
12
|
-
version?: string;
|
|
13
|
-
source: string;
|
|
14
|
-
scope: "user" | "workspace";
|
|
15
|
-
sha256: string;
|
|
16
|
-
findings: SecurityInventoryFinding[];
|
|
17
|
-
metadata: Record<string, string | number | boolean>;
|
|
18
|
-
}
|
|
19
|
-
export interface SecurityInventoryReport {
|
|
20
|
-
schemaVersion: 1;
|
|
21
|
-
generatedAt: string;
|
|
22
|
-
endpointId: string;
|
|
23
|
-
workspace: string;
|
|
24
|
-
components: SecurityComponent[];
|
|
25
|
-
drift: Array<{
|
|
26
|
-
componentKey: string;
|
|
27
|
-
name: string;
|
|
28
|
-
previousSha256: string;
|
|
29
|
-
currentSha256: string;
|
|
30
|
-
}>;
|
|
31
|
-
}
|
|
32
|
-
interface InventoryOptions {
|
|
33
|
-
homeDir?: string;
|
|
34
|
-
workspace?: string;
|
|
35
|
-
stateDir?: string;
|
|
36
|
-
now?: () => number;
|
|
37
|
-
persist?: boolean;
|
|
38
|
-
}
|
|
39
|
-
export declare function readSecurityInventory(stateDir?: string): SecurityInventoryReport | null;
|
|
40
|
-
export declare function securityInventoryDue(stateDir?: string, maxAgeMs?: number, now?: number): boolean;
|
|
41
|
-
export declare function buildSecurityInventory(options?: InventoryOptions): SecurityInventoryReport;
|
|
42
|
-
export declare function scannerAvailability(): Array<{
|
|
43
|
-
name: string;
|
|
44
|
-
available: boolean;
|
|
45
|
-
purpose: string;
|
|
46
|
-
}>;
|
|
47
|
-
export declare function uploadSecurityInventory(report: SecurityInventoryReport, config: {
|
|
48
|
-
backendUrl: string;
|
|
49
|
-
token: string;
|
|
50
|
-
}, timeoutMs?: number): Promise<{
|
|
51
|
-
ok: boolean;
|
|
52
|
-
accepted: number;
|
|
53
|
-
}>;
|
|
54
|
-
export {};
|
|
@@ -1,556 +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.readSecurityInventory = readSecurityInventory;
|
|
37
|
-
exports.securityInventoryDue = securityInventoryDue;
|
|
38
|
-
exports.buildSecurityInventory = buildSecurityInventory;
|
|
39
|
-
exports.scannerAvailability = scannerAvailability;
|
|
40
|
-
exports.uploadSecurityInventory = uploadSecurityInventory;
|
|
41
|
-
const crypto = __importStar(require("crypto"));
|
|
42
|
-
const fs = __importStar(require("fs"));
|
|
43
|
-
const http = __importStar(require("http"));
|
|
44
|
-
const https = __importStar(require("https"));
|
|
45
|
-
const os = __importStar(require("os"));
|
|
46
|
-
const path = __importStar(require("path"));
|
|
47
|
-
const child_process_1 = require("child_process");
|
|
48
|
-
const configDir_1 = require("./configDir");
|
|
49
|
-
const INVENTORY_FILE = "security-inventory.json";
|
|
50
|
-
const MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
51
|
-
const MAX_COMPONENTS = 5_000;
|
|
52
|
-
const SKIP_DIRS = new Set([".git", "node_modules", ".next", "dist", "build", ".venv"]);
|
|
53
|
-
function hash(value) {
|
|
54
|
-
return crypto.createHash("sha256").update(value).digest("hex");
|
|
55
|
-
}
|
|
56
|
-
function stableJson(value) {
|
|
57
|
-
if (Array.isArray(value))
|
|
58
|
-
return `[${value.map(stableJson).join(",")}]`;
|
|
59
|
-
if (value && typeof value === "object") {
|
|
60
|
-
return `{${Object.entries(value)
|
|
61
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
62
|
-
.map(([key, child]) => `${JSON.stringify(key)}:${stableJson(child)}`)
|
|
63
|
-
.join(",")}}`;
|
|
64
|
-
}
|
|
65
|
-
return JSON.stringify(value) ?? "null";
|
|
66
|
-
}
|
|
67
|
-
function readFileBounded(filePath) {
|
|
68
|
-
try {
|
|
69
|
-
const stat = fs.statSync(filePath);
|
|
70
|
-
if (!stat.isFile() || stat.size > MAX_FILE_BYTES)
|
|
71
|
-
return null;
|
|
72
|
-
return fs.readFileSync(filePath);
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function displayPath(filePath, homeDir, workspace) {
|
|
79
|
-
const resolved = path.resolve(filePath);
|
|
80
|
-
const resolvedWorkspace = path.resolve(workspace);
|
|
81
|
-
const resolvedHome = path.resolve(homeDir);
|
|
82
|
-
if (resolved === resolvedWorkspace)
|
|
83
|
-
return ".";
|
|
84
|
-
if (resolved.startsWith(`${resolvedWorkspace}${path.sep}`)) {
|
|
85
|
-
return path.relative(resolvedWorkspace, resolved) || ".";
|
|
86
|
-
}
|
|
87
|
-
if (resolved === resolvedHome)
|
|
88
|
-
return "~";
|
|
89
|
-
if (resolved.startsWith(`${resolvedHome}${path.sep}`)) {
|
|
90
|
-
return `~/${path.relative(resolvedHome, resolved)}`;
|
|
91
|
-
}
|
|
92
|
-
// Never upload arbitrary absolute paths. The basename plus a short digest is
|
|
93
|
-
// enough to distinguish two external locations without exposing usernames.
|
|
94
|
-
return `[external:${hash(resolved).slice(0, 10)}]/${path.basename(resolved)}`;
|
|
95
|
-
}
|
|
96
|
-
function fileScope(filePath, workspace) {
|
|
97
|
-
const resolved = path.resolve(filePath);
|
|
98
|
-
const root = path.resolve(workspace);
|
|
99
|
-
return resolved === root || resolved.startsWith(`${root}${path.sep}`) ? "workspace" : "user";
|
|
100
|
-
}
|
|
101
|
-
function fileModeFinding(filePath) {
|
|
102
|
-
try {
|
|
103
|
-
const mode = fs.statSync(filePath).mode & 0o777;
|
|
104
|
-
return (mode & 0o002) !== 0
|
|
105
|
-
? [{ code: "world-writable", severity: "high", detail: "Component is writable by every local user." }]
|
|
106
|
-
: [];
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
109
|
-
return [];
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
function secretLike(text) {
|
|
113
|
-
return /(?:api[_-]?key|token|secret|password)["']?\s*[=:]\s*["']?(?!\$\{|\{env:|<)[A-Za-z0-9_./+\-=]{12,}/i.test(text);
|
|
114
|
-
}
|
|
115
|
-
function commandFindings(text) {
|
|
116
|
-
const findings = [];
|
|
117
|
-
if (/\b(?:curl|wget)\b[^\n|;&]*(?:\||&&)\s*(?:sudo\s+)?(?:sh|bash|zsh)\b/i.test(text)) {
|
|
118
|
-
findings.push({ code: "remote-shell-pipe", severity: "critical", detail: "Downloads content and pipes it directly to a shell." });
|
|
119
|
-
}
|
|
120
|
-
const insecureRemoteUrl = [...text.matchAll(/\bhttp:\/\/([^\s/"'`]+)/gi)]
|
|
121
|
-
.some((match) => !/^(?:localhost|127(?:\.\d{1,3}){3}|\[::1\])(?::\d+)?$/i.test(match[1]));
|
|
122
|
-
if (insecureRemoteUrl) {
|
|
123
|
-
findings.push({ code: "insecure-transport", severity: "high", detail: "Uses an unencrypted HTTP endpoint." });
|
|
124
|
-
}
|
|
125
|
-
if (/\b(?:npx|bunx|uvx)\b(?![^\n]*@[0-9a-f]{7,40}\b)(?![^\n\s]*@\d+(?:\.\d+){1,2}\b)/i.test(text)) {
|
|
126
|
-
findings.push({ code: "unpinned-executable", severity: "medium", detail: "Runs a package without a pinned version or commit." });
|
|
127
|
-
}
|
|
128
|
-
if (secretLike(text)) {
|
|
129
|
-
findings.push({ code: "embedded-secret", severity: "critical", detail: "Configuration appears to contain a literal credential." });
|
|
130
|
-
}
|
|
131
|
-
return findings;
|
|
132
|
-
}
|
|
133
|
-
function makeComponent(args) {
|
|
134
|
-
const source = displayPath(args.sourcePath, args.homeDir, args.workspace);
|
|
135
|
-
const componentKey = hash(`${args.kind}\0${source}\0${args.name}`).slice(0, 32);
|
|
136
|
-
const findings = [...fileModeFinding(args.sourcePath), ...(args.findings ?? [])];
|
|
137
|
-
const uniqueFindings = [...new Map(findings.map((finding) => [`${finding.code}:${finding.detail}`, finding])).values()];
|
|
138
|
-
return {
|
|
139
|
-
componentKey,
|
|
140
|
-
kind: args.kind,
|
|
141
|
-
name: args.name.slice(0, 240),
|
|
142
|
-
...(args.version ? { version: args.version.slice(0, 120) } : {}),
|
|
143
|
-
source,
|
|
144
|
-
scope: fileScope(args.sourcePath, args.workspace),
|
|
145
|
-
sha256: hash(args.content),
|
|
146
|
-
findings: uniqueFindings,
|
|
147
|
-
metadata: args.metadata ?? {},
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
function jsoncToJson(text) {
|
|
151
|
-
let output = "";
|
|
152
|
-
let inString = false;
|
|
153
|
-
let escaped = false;
|
|
154
|
-
let lineComment = false;
|
|
155
|
-
let blockComment = false;
|
|
156
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
157
|
-
const char = text[index];
|
|
158
|
-
const next = text[index + 1];
|
|
159
|
-
if (lineComment) {
|
|
160
|
-
if (char === "\n") {
|
|
161
|
-
lineComment = false;
|
|
162
|
-
output += char;
|
|
163
|
-
}
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
if (blockComment) {
|
|
167
|
-
if (char === "*" && next === "/") {
|
|
168
|
-
blockComment = false;
|
|
169
|
-
index += 1;
|
|
170
|
-
}
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (inString) {
|
|
174
|
-
output += char;
|
|
175
|
-
if (escaped)
|
|
176
|
-
escaped = false;
|
|
177
|
-
else if (char === "\\")
|
|
178
|
-
escaped = true;
|
|
179
|
-
else if (char === '"')
|
|
180
|
-
inString = false;
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
if (char === '"') {
|
|
184
|
-
inString = true;
|
|
185
|
-
output += char;
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (char === "/" && next === "/") {
|
|
189
|
-
lineComment = true;
|
|
190
|
-
index += 1;
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
if (char === "/" && next === "*") {
|
|
194
|
-
blockComment = true;
|
|
195
|
-
index += 1;
|
|
196
|
-
continue;
|
|
197
|
-
}
|
|
198
|
-
output += char;
|
|
199
|
-
}
|
|
200
|
-
// JSONC permits trailing commas. Comments are already gone, so remove a
|
|
201
|
-
// comma only when the next non-whitespace token closes an array or object.
|
|
202
|
-
let withoutTrailingCommas = "";
|
|
203
|
-
inString = false;
|
|
204
|
-
escaped = false;
|
|
205
|
-
for (let index = 0; index < output.length; index += 1) {
|
|
206
|
-
const char = output[index];
|
|
207
|
-
if (inString) {
|
|
208
|
-
withoutTrailingCommas += char;
|
|
209
|
-
if (escaped)
|
|
210
|
-
escaped = false;
|
|
211
|
-
else if (char === "\\")
|
|
212
|
-
escaped = true;
|
|
213
|
-
else if (char === '"')
|
|
214
|
-
inString = false;
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (char === '"') {
|
|
218
|
-
inString = true;
|
|
219
|
-
withoutTrailingCommas += char;
|
|
220
|
-
continue;
|
|
221
|
-
}
|
|
222
|
-
if (char === ",") {
|
|
223
|
-
let cursor = index + 1;
|
|
224
|
-
while (cursor < output.length && /\s/.test(output[cursor]))
|
|
225
|
-
cursor += 1;
|
|
226
|
-
if (output[cursor] === "}" || output[cursor] === "]")
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
withoutTrailingCommas += char;
|
|
230
|
-
}
|
|
231
|
-
return withoutTrailingCommas;
|
|
232
|
-
}
|
|
233
|
-
function safeJson(filePath) {
|
|
234
|
-
const raw = readFileBounded(filePath);
|
|
235
|
-
if (!raw)
|
|
236
|
-
return null;
|
|
237
|
-
try {
|
|
238
|
-
const text = raw.toString("utf8");
|
|
239
|
-
const parsed = JSON.parse(filePath.endsWith(".jsonc") ? jsoncToJson(text) : text);
|
|
240
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
241
|
-
? parsed
|
|
242
|
-
: null;
|
|
243
|
-
}
|
|
244
|
-
catch {
|
|
245
|
-
return null;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
function mcpMaps(parsed) {
|
|
249
|
-
const maps = [];
|
|
250
|
-
const direct = parsed.mcpServers;
|
|
251
|
-
if (direct && typeof direct === "object" && !Array.isArray(direct))
|
|
252
|
-
maps.push(direct);
|
|
253
|
-
const mcp = parsed.mcp;
|
|
254
|
-
if (mcp && typeof mcp === "object" && !Array.isArray(mcp)) {
|
|
255
|
-
const mcpRecord = mcp;
|
|
256
|
-
const servers = mcpRecord.servers;
|
|
257
|
-
if (servers && typeof servers === "object" && !Array.isArray(servers))
|
|
258
|
-
maps.push(servers);
|
|
259
|
-
else
|
|
260
|
-
maps.push(mcpRecord);
|
|
261
|
-
}
|
|
262
|
-
return maps;
|
|
263
|
-
}
|
|
264
|
-
function collectJsonConfig(filePath, homeDir, workspace, components) {
|
|
265
|
-
const parsed = safeJson(filePath);
|
|
266
|
-
if (!parsed)
|
|
267
|
-
return;
|
|
268
|
-
for (const servers of mcpMaps(parsed)) {
|
|
269
|
-
for (const [name, definition] of Object.entries(servers)) {
|
|
270
|
-
const serialized = stableJson(definition);
|
|
271
|
-
components.push(makeComponent({
|
|
272
|
-
kind: "mcp-server", name, sourcePath: filePath, homeDir, workspace,
|
|
273
|
-
content: serialized, findings: commandFindings(serialized),
|
|
274
|
-
metadata: { transport: /https?:\/\//i.test(serialized) ? "remote" : "stdio" },
|
|
275
|
-
}));
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
const hooks = parsed.hooks;
|
|
279
|
-
if (hooks && typeof hooks === "object" && !Array.isArray(hooks)) {
|
|
280
|
-
for (const [event, entries] of Object.entries(hooks)) {
|
|
281
|
-
const serialized = stableJson(entries);
|
|
282
|
-
components.push(makeComponent({
|
|
283
|
-
kind: "hook", name: event, sourcePath: filePath, homeDir, workspace,
|
|
284
|
-
content: serialized, findings: commandFindings(serialized), metadata: { event },
|
|
285
|
-
}));
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
const plugins = parsed.plugin;
|
|
289
|
-
if (Array.isArray(plugins)) {
|
|
290
|
-
for (const plugin of plugins) {
|
|
291
|
-
const serialized = stableJson(plugin);
|
|
292
|
-
components.push(makeComponent({
|
|
293
|
-
kind: "plugin", name: typeof plugin === "string" ? plugin : serialized.slice(0, 160),
|
|
294
|
-
sourcePath: filePath, homeDir, workspace, content: serialized,
|
|
295
|
-
findings: commandFindings(serialized), metadata: { declared: true },
|
|
296
|
-
}));
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
const enabledPlugins = parsed.enabledPlugins;
|
|
300
|
-
if (enabledPlugins && typeof enabledPlugins === "object" && !Array.isArray(enabledPlugins)) {
|
|
301
|
-
for (const [name, enabled] of Object.entries(enabledPlugins)) {
|
|
302
|
-
if (enabled !== true)
|
|
303
|
-
continue;
|
|
304
|
-
components.push(makeComponent({
|
|
305
|
-
kind: "plugin", name, sourcePath: filePath, homeDir, workspace,
|
|
306
|
-
content: name, findings: commandFindings(name), metadata: { declared: true, enabled: true },
|
|
307
|
-
}));
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
function collectCodexToml(filePath, homeDir, workspace, components) {
|
|
312
|
-
const raw = readFileBounded(filePath);
|
|
313
|
-
if (!raw)
|
|
314
|
-
return;
|
|
315
|
-
const text = raw.toString("utf8");
|
|
316
|
-
const lines = text.split(/\r?\n/);
|
|
317
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
318
|
-
const section = lines[index].match(/^\s*\[mcp_servers\.(?:"([^"]+)"|'([^']+)'|([^\]]+))\]\s*(?:#.*)?$/);
|
|
319
|
-
if (!section)
|
|
320
|
-
continue;
|
|
321
|
-
const name = (section[1] ?? section[2] ?? section[3] ?? "").trim();
|
|
322
|
-
const bodyLines = [];
|
|
323
|
-
for (index += 1; index < lines.length && !/^\s*\[/.test(lines[index]); index += 1) {
|
|
324
|
-
bodyLines.push(lines[index]);
|
|
325
|
-
}
|
|
326
|
-
index -= 1;
|
|
327
|
-
const body = bodyLines.join("\n").trim();
|
|
328
|
-
components.push(makeComponent({
|
|
329
|
-
kind: "mcp-server", name, sourcePath: filePath, homeDir, workspace,
|
|
330
|
-
content: body, findings: commandFindings(body),
|
|
331
|
-
metadata: { transport: /\burl\s*=/.test(body) ? "remote" : "stdio" },
|
|
332
|
-
}));
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
function walkFiles(root, matcher, maxDepth = 5) {
|
|
336
|
-
const found = [];
|
|
337
|
-
const visit = (dir, depth) => {
|
|
338
|
-
if (depth > maxDepth || found.length >= MAX_COMPONENTS)
|
|
339
|
-
return;
|
|
340
|
-
let entries;
|
|
341
|
-
try {
|
|
342
|
-
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
343
|
-
}
|
|
344
|
-
catch {
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
for (const entry of entries) {
|
|
348
|
-
if (found.length >= MAX_COMPONENTS)
|
|
349
|
-
break;
|
|
350
|
-
const candidate = path.join(dir, entry.name);
|
|
351
|
-
if (entry.isDirectory()) {
|
|
352
|
-
if (!SKIP_DIRS.has(entry.name))
|
|
353
|
-
visit(candidate, depth + 1);
|
|
354
|
-
}
|
|
355
|
-
else if (entry.isFile() && matcher(candidate, entry.name)) {
|
|
356
|
-
found.push(candidate);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
};
|
|
360
|
-
visit(root, 0);
|
|
361
|
-
return found;
|
|
362
|
-
}
|
|
363
|
-
function frontmatterValue(text, field) {
|
|
364
|
-
const match = text.match(new RegExp(`^${field}:\\s*["']?([^\\n"']+)`, "mi"));
|
|
365
|
-
return match?.[1]?.trim();
|
|
366
|
-
}
|
|
367
|
-
function collectSkills(roots, homeDir, workspace, components) {
|
|
368
|
-
for (const root of [...new Set(roots)]) {
|
|
369
|
-
for (const filePath of walkFiles(root, (_candidate, name) => name === "SKILL.md", 6)) {
|
|
370
|
-
const raw = readFileBounded(filePath);
|
|
371
|
-
if (!raw)
|
|
372
|
-
continue;
|
|
373
|
-
const text = raw.toString("utf8");
|
|
374
|
-
const name = frontmatterValue(text, "name") ?? path.basename(path.dirname(filePath));
|
|
375
|
-
components.push(makeComponent({
|
|
376
|
-
kind: "skill", name, version: frontmatterValue(text, "version"), sourcePath: filePath,
|
|
377
|
-
homeDir, workspace, content: raw, findings: commandFindings(text),
|
|
378
|
-
metadata: { description: (frontmatterValue(text, "description") ?? "").slice(0, 240) },
|
|
379
|
-
}));
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
function collectExtensions(roots, homeDir, workspace, components) {
|
|
384
|
-
for (const root of roots) {
|
|
385
|
-
let entries;
|
|
386
|
-
try {
|
|
387
|
-
entries = fs.readdirSync(root, { withFileTypes: true });
|
|
388
|
-
}
|
|
389
|
-
catch {
|
|
390
|
-
continue;
|
|
391
|
-
}
|
|
392
|
-
for (const entry of entries) {
|
|
393
|
-
if (!entry.isDirectory())
|
|
394
|
-
continue;
|
|
395
|
-
const packagePath = path.join(root, entry.name, "package.json");
|
|
396
|
-
const parsed = safeJson(packagePath);
|
|
397
|
-
if (!parsed)
|
|
398
|
-
continue;
|
|
399
|
-
const publisher = typeof parsed.publisher === "string" ? parsed.publisher : "unknown";
|
|
400
|
-
const packageName = typeof parsed.name === "string" ? parsed.name : entry.name;
|
|
401
|
-
const version = typeof parsed.version === "string" ? parsed.version : undefined;
|
|
402
|
-
const raw = readFileBounded(packagePath);
|
|
403
|
-
if (!raw)
|
|
404
|
-
continue;
|
|
405
|
-
components.push(makeComponent({
|
|
406
|
-
kind: "extension", name: `${publisher}.${packageName}`, version, sourcePath: packagePath,
|
|
407
|
-
homeDir, workspace, content: raw, findings: commandFindings(raw.toString("utf8")),
|
|
408
|
-
metadata: { publisher },
|
|
409
|
-
}));
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
function collectLooseFiles(paths, kind, homeDir, workspace, components) {
|
|
414
|
-
for (const filePath of [...new Set(paths)]) {
|
|
415
|
-
const raw = readFileBounded(filePath);
|
|
416
|
-
if (!raw)
|
|
417
|
-
continue;
|
|
418
|
-
components.push(makeComponent({
|
|
419
|
-
kind, name: kind === "instruction" ? path.basename(filePath) : path.basename(filePath, path.extname(filePath)),
|
|
420
|
-
sourcePath: filePath, homeDir, workspace, content: raw,
|
|
421
|
-
findings: commandFindings(raw.toString("utf8")), metadata: {},
|
|
422
|
-
}));
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
function endpointId(homeDir) {
|
|
426
|
-
return hash(`${os.hostname()}\0${homeDir}`).slice(0, 32);
|
|
427
|
-
}
|
|
428
|
-
function readSecurityInventory(stateDir = (0, configDir_1.codiedevStateDir)()) {
|
|
429
|
-
try {
|
|
430
|
-
const parsed = JSON.parse(fs.readFileSync(path.join(stateDir, INVENTORY_FILE), "utf8"));
|
|
431
|
-
return parsed?.schemaVersion === 1 ? parsed : null;
|
|
432
|
-
}
|
|
433
|
-
catch {
|
|
434
|
-
return null;
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
function securityInventoryDue(stateDir = (0, configDir_1.codiedevStateDir)(), maxAgeMs = 24 * 60 * 60 * 1_000, now = Date.now()) {
|
|
438
|
-
const previous = readSecurityInventory(stateDir);
|
|
439
|
-
const generatedAt = previous ? Date.parse(previous.generatedAt) : NaN;
|
|
440
|
-
return !Number.isFinite(generatedAt) || now - generatedAt >= maxAgeMs;
|
|
441
|
-
}
|
|
442
|
-
function buildSecurityInventory(options = {}) {
|
|
443
|
-
const homeDir = path.resolve(options.homeDir ?? os.homedir());
|
|
444
|
-
const workspace = path.resolve(options.workspace ?? process.cwd());
|
|
445
|
-
const stateDir = options.stateDir ?? (0, configDir_1.codiedevStateDir)();
|
|
446
|
-
const components = [];
|
|
447
|
-
const jsonConfigs = [
|
|
448
|
-
path.join(homeDir, ".claude.json"), path.join(homeDir, ".claude", "settings.json"),
|
|
449
|
-
path.join(homeDir, ".claude", "settings.local.json"),
|
|
450
|
-
path.join(homeDir, ".cursor", "mcp.json"), path.join(homeDir, ".config", "opencode", "opencode.json"),
|
|
451
|
-
path.join(homeDir, ".config", "opencode", "opencode.jsonc"),
|
|
452
|
-
path.join(homeDir, ".config", "Code", "User", "mcp.json"),
|
|
453
|
-
path.join(homeDir, ".config", "Cursor", "User", "mcp.json"),
|
|
454
|
-
path.join(homeDir, ".codex", "hooks.json"),
|
|
455
|
-
path.join(homeDir, "Library", "Application Support", "Code", "User", "mcp.json"),
|
|
456
|
-
path.join(homeDir, "Library", "Application Support", "Cursor", "User", "mcp.json"),
|
|
457
|
-
path.join(homeDir, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
458
|
-
path.join(workspace, ".mcp.json"), path.join(workspace, ".cursor", "mcp.json"),
|
|
459
|
-
path.join(workspace, ".vscode", "mcp.json"),
|
|
460
|
-
path.join(workspace, ".codex", "hooks.json"),
|
|
461
|
-
path.join(workspace, ".claude", "settings.json"), path.join(workspace, ".claude", "settings.local.json"),
|
|
462
|
-
path.join(workspace, "opencode.json"), path.join(workspace, "opencode.jsonc"),
|
|
463
|
-
];
|
|
464
|
-
for (const filePath of jsonConfigs)
|
|
465
|
-
collectJsonConfig(filePath, homeDir, workspace, components);
|
|
466
|
-
for (const filePath of [path.join(homeDir, ".codex", "config.toml"), path.join(workspace, ".codex", "config.toml")]) {
|
|
467
|
-
collectCodexToml(filePath, homeDir, workspace, components);
|
|
468
|
-
}
|
|
469
|
-
collectSkills([
|
|
470
|
-
path.join(homeDir, ".claude", "skills"), path.join(homeDir, ".codex", "skills"),
|
|
471
|
-
path.join(homeDir, ".agents", "skills"), path.join(homeDir, ".cursor", "skills"),
|
|
472
|
-
path.join(workspace, ".claude", "skills"), path.join(workspace, ".codex", "skills"),
|
|
473
|
-
path.join(workspace, ".agents", "skills"), path.join(workspace, ".cursor", "skills"),
|
|
474
|
-
], homeDir, workspace, components);
|
|
475
|
-
collectExtensions([
|
|
476
|
-
path.join(homeDir, ".vscode", "extensions"), path.join(homeDir, ".cursor", "extensions"),
|
|
477
|
-
], homeDir, workspace, components);
|
|
478
|
-
const pluginDirs = [path.join(homeDir, ".config", "opencode", "plugins"), path.join(workspace, ".opencode", "plugins")];
|
|
479
|
-
collectLooseFiles(pluginDirs.flatMap((dir) => walkFiles(dir, (_file, name) => /\.(?:js|mjs|cjs|ts)$/.test(name), 2)), "plugin", homeDir, workspace, components);
|
|
480
|
-
collectLooseFiles([
|
|
481
|
-
path.join(homeDir, ".claude", "CLAUDE.md"), path.join(homeDir, ".codex", "AGENTS.md"),
|
|
482
|
-
path.join(workspace, "CLAUDE.md"), path.join(workspace, "AGENTS.md"),
|
|
483
|
-
...walkFiles(path.join(workspace, ".cursor", "rules"), (_file, name) => /\.(?:md|mdc)$/.test(name), 3),
|
|
484
|
-
], "instruction", homeDir, workspace, components);
|
|
485
|
-
const unique = [...new Map(components.slice(0, MAX_COMPONENTS).map((component) => [component.componentKey, component])).values()]
|
|
486
|
-
.sort((a, b) => a.kind.localeCompare(b.kind) || a.name.localeCompare(b.name));
|
|
487
|
-
const previous = readSecurityInventory(stateDir);
|
|
488
|
-
const previousByKey = new Map((previous?.components ?? []).map((component) => [component.componentKey, component]));
|
|
489
|
-
const drift = unique.flatMap((component) => {
|
|
490
|
-
const before = previousByKey.get(component.componentKey);
|
|
491
|
-
return before && before.sha256 !== component.sha256
|
|
492
|
-
? [{ componentKey: component.componentKey, name: component.name, previousSha256: before.sha256, currentSha256: component.sha256 }]
|
|
493
|
-
: [];
|
|
494
|
-
});
|
|
495
|
-
const report = {
|
|
496
|
-
schemaVersion: 1,
|
|
497
|
-
generatedAt: new Date((options.now ?? Date.now)()).toISOString(),
|
|
498
|
-
endpointId: endpointId(homeDir),
|
|
499
|
-
workspace: displayPath(workspace, homeDir, workspace),
|
|
500
|
-
components: unique,
|
|
501
|
-
drift,
|
|
502
|
-
};
|
|
503
|
-
if (options.persist !== false) {
|
|
504
|
-
fs.mkdirSync(stateDir, { recursive: true, mode: 0o700 });
|
|
505
|
-
const target = path.join(stateDir, INVENTORY_FILE);
|
|
506
|
-
const temporary = `${target}.${process.pid}.tmp`;
|
|
507
|
-
fs.writeFileSync(temporary, `${JSON.stringify(report, null, 2)}\n`, { mode: 0o600 });
|
|
508
|
-
fs.renameSync(temporary, target);
|
|
509
|
-
}
|
|
510
|
-
return report;
|
|
511
|
-
}
|
|
512
|
-
function scannerAvailability() {
|
|
513
|
-
const scanners = [
|
|
514
|
-
{ name: "atr", purpose: "Agent Threat Rules for skills and runtime artifacts" },
|
|
515
|
-
{ name: "skill-scanner", purpose: "Cisco AI Defense skill behavioral scanning" },
|
|
516
|
-
{ name: "mcp-scanner", purpose: "Cisco AI Defense MCP tool and server scanning" },
|
|
517
|
-
{ name: "guarddog", purpose: "Datadog extension and package supply-chain scanning" },
|
|
518
|
-
{ name: "osv-scanner", purpose: "OSV dependency vulnerability intelligence" },
|
|
519
|
-
];
|
|
520
|
-
return scanners.map((scanner) => ({
|
|
521
|
-
...scanner,
|
|
522
|
-
available: (0, child_process_1.spawnSync)(scanner.name, ["--version"], { stdio: "ignore", timeout: 2_000 }).status === 0,
|
|
523
|
-
}));
|
|
524
|
-
}
|
|
525
|
-
async function uploadSecurityInventory(report, config, timeoutMs = 10_000) {
|
|
526
|
-
const url = new URL("/api/cli/securityInventory", config.backendUrl);
|
|
527
|
-
const body = JSON.stringify(report);
|
|
528
|
-
const transport = url.protocol === "https:" ? https : http;
|
|
529
|
-
return await new Promise((resolve, reject) => {
|
|
530
|
-
const request = transport.request(url, {
|
|
531
|
-
method: "POST",
|
|
532
|
-
headers: {
|
|
533
|
-
Authorization: `Bearer ${config.token}`,
|
|
534
|
-
"Content-Type": "application/json",
|
|
535
|
-
"Content-Length": Buffer.byteLength(body),
|
|
536
|
-
},
|
|
537
|
-
}, (response) => {
|
|
538
|
-
let raw = "";
|
|
539
|
-
response.setEncoding("utf8");
|
|
540
|
-
response.on("data", (chunk) => { raw += chunk; });
|
|
541
|
-
response.on("end", () => {
|
|
542
|
-
if ((response.statusCode ?? 500) >= 300)
|
|
543
|
-
return reject(new Error(`inventory_http_${response.statusCode}`));
|
|
544
|
-
try {
|
|
545
|
-
resolve(JSON.parse(raw));
|
|
546
|
-
}
|
|
547
|
-
catch {
|
|
548
|
-
reject(new Error("invalid inventory response"));
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
});
|
|
552
|
-
request.setTimeout(timeoutMs, () => request.destroy(new Error(`inventory upload timeout after ${timeoutMs}ms`)));
|
|
553
|
-
request.on("error", reject);
|
|
554
|
-
request.end(body);
|
|
555
|
-
});
|
|
556
|
-
}
|
package/dist/securityPilot.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { type AgentSurface, type LocalSecurityAction } from "./security";
|
|
2
|
-
export interface SecurityPilotCase {
|
|
3
|
-
surface: AgentSurface;
|
|
4
|
-
name: string;
|
|
5
|
-
expected: LocalSecurityAction;
|
|
6
|
-
actual: LocalSecurityAction;
|
|
7
|
-
passed: boolean;
|
|
8
|
-
}
|
|
9
|
-
export interface SecurityPilotReport {
|
|
10
|
-
schemaVersion: 1;
|
|
11
|
-
runAt: string;
|
|
12
|
-
ok: boolean;
|
|
13
|
-
containsRealSecrets: false;
|
|
14
|
-
surfaces: AgentSurface[];
|
|
15
|
-
cases: SecurityPilotCase[];
|
|
16
|
-
}
|
|
17
|
-
/** Safe, side-effect-free policy validation. No command is executed and none
|
|
18
|
-
* of the synthetic values are real credentials. */
|
|
19
|
-
export declare function runSyntheticSecurityPilot(cwd?: string, now?: () => number): SecurityPilotReport;
|