cc-safety-net 1.0.3 → 1.0.4
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/bin/cc-safety-net.js +2 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +66 -1
- package/package.json +1 -1
|
@@ -7927,7 +7927,7 @@ import { existsSync as existsSync14 } from "node:fs";
|
|
|
7927
7927
|
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
|
7928
7928
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
7929
7929
|
import { delimiter, extname, join as join11 } from "node:path";
|
|
7930
|
-
var CURRENT_VERSION = "1.0.
|
|
7930
|
+
var CURRENT_VERSION = "1.0.4";
|
|
7931
7931
|
var VERSION_FETCH_TIMEOUT_MS = 2000;
|
|
7932
7932
|
var PI_PROBE_TIMEOUT_MS = 5000;
|
|
7933
7933
|
var PI_SENTINEL_COMMAND = "cc-safety-net";
|
|
@@ -9436,7 +9436,7 @@ function formatTraceJson(result) {
|
|
|
9436
9436
|
return JSON.stringify(result, null, 2);
|
|
9437
9437
|
}
|
|
9438
9438
|
// src/bin/help.ts
|
|
9439
|
-
var version = "1.0.
|
|
9439
|
+
var version = "1.0.4";
|
|
9440
9440
|
var INDENT = " ";
|
|
9441
9441
|
var PROGRAM_NAME = "cc-safety-net";
|
|
9442
9442
|
function formatOptionFlags(option) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,15 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
1
|
+
import type { PluginInput } from '@opencode-ai/plugin';
|
|
2
|
+
type CCSafetyNetPluginInput = PluginInput & {
|
|
3
|
+
homeDir?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare const CCSafetyNetPlugin: ({ directory, homeDir }: CCSafetyNetPluginInput) => Promise<{
|
|
6
|
+
config: (opencodeConfig: Record<string, unknown>) => Promise<void>;
|
|
7
|
+
'tool.execute.before': (input: {
|
|
8
|
+
tool: string;
|
|
9
|
+
sessionID: string;
|
|
10
|
+
callID: string;
|
|
11
|
+
}, output: {
|
|
12
|
+
args: any;
|
|
13
|
+
}) => Promise<void>;
|
|
14
|
+
}>;
|
|
15
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -6187,6 +6187,66 @@ function analyzeCommand(command2, options2 = {}) {
|
|
|
6187
6187
|
return analyzeCommandInternal(command2, 0, { ...options2, config });
|
|
6188
6188
|
}
|
|
6189
6189
|
|
|
6190
|
+
// src/core/audit.ts
|
|
6191
|
+
import { appendFileSync, existsSync as existsSync10, mkdirSync as mkdirSync3 } from "node:fs";
|
|
6192
|
+
import { homedir as homedir3 } from "node:os";
|
|
6193
|
+
import { join as join8 } from "node:path";
|
|
6194
|
+
function sanitizeSessionIdForFilename(sessionId) {
|
|
6195
|
+
const raw = sessionId.trim();
|
|
6196
|
+
if (!raw) {
|
|
6197
|
+
return null;
|
|
6198
|
+
}
|
|
6199
|
+
let safe = raw.replace(/[^A-Za-z0-9_.-]+/g, "_");
|
|
6200
|
+
safe = safe.replace(/^[._-]+|[._-]+$/g, "").slice(0, 128);
|
|
6201
|
+
if (!safe || safe === "." || safe === "..") {
|
|
6202
|
+
return null;
|
|
6203
|
+
}
|
|
6204
|
+
return safe;
|
|
6205
|
+
}
|
|
6206
|
+
function writeAuditLog(sessionId, command2, segment, reason, cwd, options2 = {}) {
|
|
6207
|
+
const safeSessionId = sanitizeSessionIdForFilename(sessionId);
|
|
6208
|
+
if (!safeSessionId) {
|
|
6209
|
+
return;
|
|
6210
|
+
}
|
|
6211
|
+
const home = options2.homeDir ?? process.env.HOME ?? homedir3();
|
|
6212
|
+
const logsDir = join8(home, ".cc-safety-net", "logs");
|
|
6213
|
+
try {
|
|
6214
|
+
if (!existsSync10(logsDir)) {
|
|
6215
|
+
mkdirSync3(logsDir, { recursive: true });
|
|
6216
|
+
}
|
|
6217
|
+
const logFile = join8(logsDir, `${safeSessionId}.jsonl`);
|
|
6218
|
+
const entry = {
|
|
6219
|
+
ts: new Date().toISOString(),
|
|
6220
|
+
decision: options2.decision ?? "deny",
|
|
6221
|
+
command: redactSecrets(command2).slice(0, 300),
|
|
6222
|
+
segment: redactSecrets(segment).slice(0, 300),
|
|
6223
|
+
reason,
|
|
6224
|
+
cwd
|
|
6225
|
+
};
|
|
6226
|
+
appendFileSync(logFile, `${JSON.stringify(entry)}
|
|
6227
|
+
`, "utf-8");
|
|
6228
|
+
} catch {}
|
|
6229
|
+
}
|
|
6230
|
+
function redactSecrets(text) {
|
|
6231
|
+
let result = text;
|
|
6232
|
+
result = result.replace(/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g, "<redacted>");
|
|
6233
|
+
result = result.replace(/\b((?:DATABASE|POSTGRES|POSTGRESQL|MYSQL|MARIADB|REDIS|MONGO(?:DB)?|DB)_URL)=([^\s]+)/gi, "$1=<redacted>");
|
|
6234
|
+
result = result.replace(/\b([A-Z0-9_]*(?:TOKEN|SECRET|PASSWORD|PASS|KEY|CREDENTIALS)[A-Z0-9_]*)=([^\s]+)/gi, "$1=<redacted>");
|
|
6235
|
+
result = result.replace(/(['"]?\s*(?:authorization|cookie|x-api-key|api-key)\s*:\s*)([^'"\r\n]+)(['"]?)/gi, "$1<redacted>$3");
|
|
6236
|
+
result = result.replace(/(['"]?\s*authorization\s*:\s*)([^'"]+)(['"]?)/gi, "$1<redacted>$3");
|
|
6237
|
+
result = result.replace(/(authorization\s*:\s*)([^\s"']+)(\s+[^\s"']+)?/gi, "$1<redacted>");
|
|
6238
|
+
result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)([^\s/:@]+):([^\s@/]+)@/gi, "$1<redacted>:<redacted>@");
|
|
6239
|
+
result = result.replace(/\b([a-z][a-z0-9+.-]*:\/\/)([^\s/@:]+)@/gi, "$1<redacted>@");
|
|
6240
|
+
result = result.replace(/\bgh[pousr]_[A-Za-z0-9]{20,}\b/g, "<redacted>");
|
|
6241
|
+
result = result.replace(/\bxoxb-[A-Za-z0-9-]{20,}\b/g, "<redacted>");
|
|
6242
|
+
result = result.replace(/\bnpm_[A-Za-z0-9_]{20,}\b/g, "<redacted>");
|
|
6243
|
+
result = result.replace(/\b[rs]k_(?:live|test)_[A-Za-z0-9_]{20,}\b/g, "<redacted>");
|
|
6244
|
+
result = result.replace(/\bpypi-[A-Za-z0-9_-]{20,}\b/g, "<redacted>");
|
|
6245
|
+
result = result.replace(/\b[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,}\b/g, "<redacted>");
|
|
6246
|
+
result = result.replace(/\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g, "<redacted>");
|
|
6247
|
+
return result;
|
|
6248
|
+
}
|
|
6249
|
+
|
|
6190
6250
|
// src/core/format.ts
|
|
6191
6251
|
function formatBlockedMessage(input) {
|
|
6192
6252
|
const { reason, command: command2, segment } = input;
|
|
@@ -6276,7 +6336,7 @@ function loadBuiltinCommands(disabledCommands) {
|
|
|
6276
6336
|
}
|
|
6277
6337
|
// src/index.ts
|
|
6278
6338
|
var REASON_SAFETY_NET_FAILED_CLOSED = "CC Safety Net failed closed because command analysis failed unexpectedly.";
|
|
6279
|
-
var CCSafetyNetPlugin = async ({ directory }) => {
|
|
6339
|
+
var CCSafetyNetPlugin = async ({ directory, homeDir }) => {
|
|
6280
6340
|
const modes = getCCSafetyNetEnvModes();
|
|
6281
6341
|
return {
|
|
6282
6342
|
config: async (opencodeConfig) => {
|
|
@@ -6308,6 +6368,11 @@ var CCSafetyNetPlugin = async ({ directory }) => {
|
|
|
6308
6368
|
}));
|
|
6309
6369
|
}
|
|
6310
6370
|
if (result) {
|
|
6371
|
+
if (input.sessionID) {
|
|
6372
|
+
writeAuditLog(input.sessionID, command2, result.segment, result.reason, directory, {
|
|
6373
|
+
homeDir
|
|
6374
|
+
});
|
|
6375
|
+
}
|
|
6311
6376
|
const message = formatBlockedMessage({
|
|
6312
6377
|
reason: result.reason,
|
|
6313
6378
|
command: command2,
|