cc-safety-net 1.0.3 → 1.0.5

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 CHANGED
@@ -226,6 +226,8 @@ Install CC Safety Net into your Kimi Code config:
226
226
  npx -y cc-safety-net hook install --kimi-code
227
227
  ```
228
228
 
229
+ Optional: run `npx skill add kenryu42/cc-safety-net` to add the `/cc-safety-net` skill for configuring custom rules.
230
+
229
231
  ---
230
232
 
231
233
 
@@ -6429,7 +6429,7 @@ var CLAUDE_CODE_TOOL_NAME = "Bash";
6429
6429
  var GEMINI_CLI_HOOK_EVENT = "BeforeTool";
6430
6430
  var GEMINI_CLI_TOOL_NAME = "run_shell_command";
6431
6431
  var KIMI_CODE_HOOK_EVENT = "PreToolUse";
6432
- var KIMI_CODE_TOOL_NAME = "Shell";
6432
+ var KIMI_CODE_TOOL_NAME = "Bash";
6433
6433
 
6434
6434
  // src/bin/hook/claude-code.ts
6435
6435
  async function runClaudeCodeHook() {
@@ -7531,7 +7531,7 @@ function detectGeminiCLI(extensionsListOutput) {
7531
7531
  };
7532
7532
  }
7533
7533
  function _getKimiConfigPath(homeDir) {
7534
- return join10(process.env.KIMI_SHARE_DIR || join10(homeDir, ".kimi"), "config.toml");
7534
+ return join10(process.env.KIMI_CODE_HOME || join10(homeDir, ".kimi-code"), "config.toml");
7535
7535
  }
7536
7536
  function detectKimiCode(homeDir) {
7537
7537
  const configPath = _getKimiConfigPath(homeDir);
@@ -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.3";
7930
+ var CURRENT_VERSION = "1.0.5";
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.3";
9439
+ var version = "1.0.5";
9440
9440
  var INDENT = " ";
9441
9441
  var PROGRAM_NAME = "cc-safety-net";
9442
9442
  function formatOptionFlags(option) {
@@ -9635,11 +9635,11 @@ function removeArrayRangeItem(content, item) {
9635
9635
  var KIMI_HOOK_COMMAND = "npx -y cc-safety-net hook --kimi-code";
9636
9636
  var KIMI_HOOK_BLOCK = `[[hooks]]
9637
9637
  event = "PreToolUse"
9638
- matcher = "Shell"
9638
+ matcher = "Bash"
9639
9639
  command = "${KIMI_HOOK_COMMAND}"`;
9640
- var KIMI_INLINE_HOOK = `{ event = "PreToolUse", matcher = "Shell", command = "${KIMI_HOOK_COMMAND}" }`;
9640
+ var KIMI_INLINE_HOOK = `{ event = "PreToolUse", matcher = "Bash", command = "${KIMI_HOOK_COMMAND}" }`;
9641
9641
  function getKimiConfigPath(homeDir) {
9642
- return join12(process.env.KIMI_SHARE_DIR ?? join12(homeDir, ".kimi"), "config.toml");
9642
+ return join12(process.env.KIMI_CODE_HOME ?? join12(homeDir, ".kimi-code"), "config.toml");
9643
9643
  }
9644
9644
  function removeTopLevelEmptyHooksArray(content) {
9645
9645
  const result = content.split(`
@@ -3,4 +3,4 @@ export declare const CLAUDE_CODE_TOOL_NAME = "Bash";
3
3
  export declare const GEMINI_CLI_HOOK_EVENT = "BeforeTool";
4
4
  export declare const GEMINI_CLI_TOOL_NAME = "run_shell_command";
5
5
  export declare const KIMI_CODE_HOOK_EVENT = "PreToolUse";
6
- export declare const KIMI_CODE_TOOL_NAME = "Shell";
6
+ export declare const KIMI_CODE_TOOL_NAME = "Bash";
package/dist/index.d.ts CHANGED
@@ -1,2 +1,15 @@
1
- import type { Plugin } from '@opencode-ai/plugin';
2
- export declare const CCSafetyNetPlugin: Plugin;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-safety-net",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "A coding agent CLI hook - block destructive git and filesystem commands before execution",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",