@cybedefend/vibedefend 1.1.1 → 1.1.2

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.
@@ -6237,21 +6237,50 @@ function makeViolationsBuffer(opts = {}) {
6237
6237
  const trimmed = line.trim();
6238
6238
  if (!trimmed) continue;
6239
6239
  try {
6240
- records.push(JSON.parse(trimmed));
6240
+ const parsed = JSON.parse(trimmed);
6241
+ if (typeof parsed.projectId === "string" && parsed.projectId) {
6242
+ records.push(parsed);
6243
+ }
6241
6244
  } catch {
6242
6245
  }
6243
6246
  }
6244
- if (records.length === 0) return;
6245
- const BATCH_SIZE = 100;
6246
- try {
6247
- for (let i = 0; i < records.length; i += BATCH_SIZE) {
6248
- const batch = records.slice(i, i + BATCH_SIZE);
6249
- await postFn(ctx, batch);
6250
- }
6247
+ if (records.length === 0) {
6251
6248
  try {
6252
6249
  writeFileSync4(bufferFile, "", "utf8");
6253
6250
  } catch {
6254
6251
  }
6252
+ return;
6253
+ }
6254
+ const groups = /* @__PURE__ */ new Map();
6255
+ for (const r of records) {
6256
+ const arr = groups.get(r.projectId);
6257
+ if (arr) arr.push(r);
6258
+ else groups.set(r.projectId, [r]);
6259
+ }
6260
+ const BATCH_SIZE = 100;
6261
+ const failed = [];
6262
+ for (const [projectId, group] of groups) {
6263
+ let groupFailed = false;
6264
+ for (let i = 0; i < group.length; i += BATCH_SIZE) {
6265
+ const batch = group.slice(i, i + BATCH_SIZE);
6266
+ try {
6267
+ await postFn(ctx, projectId, batch);
6268
+ } catch {
6269
+ groupFailed = true;
6270
+ break;
6271
+ }
6272
+ }
6273
+ if (groupFailed) {
6274
+ failed.push(...group);
6275
+ }
6276
+ }
6277
+ try {
6278
+ if (failed.length === 0) {
6279
+ writeFileSync4(bufferFile, "", "utf8");
6280
+ } else {
6281
+ const serialised = failed.map((r) => JSON.stringify(r)).join("\n") + "\n";
6282
+ writeFileSync4(bufferFile, serialised, "utf8");
6283
+ }
6255
6284
  } catch {
6256
6285
  }
6257
6286
  }
@@ -6598,36 +6627,60 @@ async function runPreToolUseGuard() {
6598
6627
  apiBaseResolved
6599
6628
  });
6600
6629
  const buffer = makeViolationsBuffer();
6601
- process.once("beforeExit", () => {
6602
- buffer.flushViolations(
6603
- { projectId, token, apiBaseResolved },
6604
- async (ctx, batch) => {
6605
- const f = globalThis.fetch;
6606
- const url = `${ctx.apiBaseResolved}/project/${encodeURIComponent(ctx.projectId)}/action-guards/violations`;
6607
- await f(url, {
6608
- method: "POST",
6609
- headers: {
6610
- Authorization: `Bearer ${ctx.token}`,
6611
- "Content-Type": "application/json"
6612
- },
6613
- body: JSON.stringify({ violations: batch }),
6614
- signal: AbortSignal.timeout(5e3)
6615
- });
6616
- }
6617
- ).catch(() => {
6618
- });
6619
- });
6620
- return await runPreToolUseGuardWith({
6630
+ const exitCode = await runPreToolUseGuardWith({
6621
6631
  stdinEnvelope: envelope,
6622
6632
  projectContext,
6623
6633
  rulesOutcome,
6624
6634
  evaluateFn: evaluate,
6625
6635
  bufferViolationFn: buffer.bufferViolation.bind(buffer)
6626
6636
  });
6637
+ try {
6638
+ await buffer.flushViolations(
6639
+ { token, apiBaseResolved },
6640
+ makeGuardViolationsPostFn()
6641
+ );
6642
+ } catch {
6643
+ }
6644
+ return exitCode;
6627
6645
  } catch {
6628
6646
  return 0;
6629
6647
  }
6630
6648
  }
6649
+ function makeGuardViolationsPostFn() {
6650
+ return async (ctx, projectId, batch) => {
6651
+ const url = `${ctx.apiBaseResolved}/project/${encodeURIComponent(projectId)}/action-guards/violations`;
6652
+ const resp = await globalThis.fetch(url, {
6653
+ method: "POST",
6654
+ headers: {
6655
+ Authorization: `Bearer ${ctx.token}`,
6656
+ "Content-Type": "application/json"
6657
+ },
6658
+ body: JSON.stringify({ violations: batch.map(toApiViolation) }),
6659
+ signal: AbortSignal.timeout(2e3)
6660
+ });
6661
+ if (!resp.ok) {
6662
+ throw new Error(
6663
+ `POST /action-guards/violations rejected by gateway: ${resp.status}`
6664
+ );
6665
+ }
6666
+ };
6667
+ }
6668
+ function toApiViolation(r) {
6669
+ const out = {
6670
+ rule_id: r.ruleId,
6671
+ agent: r.agent,
6672
+ tool: r.tool,
6673
+ outcome: r.outcome,
6674
+ // Backend enforces max_length=256 on target_summary — clamp here so a
6675
+ // verbose redactor output never costs us the whole batch.
6676
+ target_summary: (r.targetSummary ?? "").slice(0, 256),
6677
+ target_hash: r.targetHash
6678
+ };
6679
+ if (r.sessionId) {
6680
+ out.session_id = r.sessionId;
6681
+ }
6682
+ return out;
6683
+ }
6631
6684
 
6632
6685
  // src/hooks/runtime/index.ts
6633
6686
  async function main() {
@@ -3,8 +3,7 @@
3
3
  *
4
4
  * Violations (deny/warn outcomes) are appended as JSON lines to a local
5
5
  * buffer file (`~/.cybedefend/guards-violations-buffer.jsonl`) and flushed
6
- * to the gateway on demand (typically on hook process exit via
7
- * `process.on('beforeExit')`).
6
+ * to the gateway on demand by the hook before it returns.
8
7
  *
9
8
  * Why JSONL + append semantics?
10
9
  * - Append to a file is atomic at the OS level for small writes.
@@ -13,6 +12,17 @@
13
12
  * - The hook NEVER blocks on the audit write — `bufferViolation` is
14
13
  * synchronous (sync appendFileSync with minimal data).
15
14
  *
15
+ * Per-project semantics:
16
+ * - Each buffered record carries its own `projectId`. When the buffer is
17
+ * flushed, records are grouped by `projectId` and each group is POSTed
18
+ * to its own project's endpoint. This is required because a developer
19
+ * may switch projects between hook invocations, and the user's Logto
20
+ * token (same across all their projects) can authorise calls to any
21
+ * project the Permify policy allows.
22
+ * - On partial failure (e.g. permission denied for one project, success
23
+ * for another) only the failed groups are kept in the buffer; the
24
+ * successful ones are purged.
25
+ *
16
26
  * Violation semantics:
17
27
  * - `deny` → outcome = 'denied'
18
28
  * - `warn` → outcome = 'warned'
@@ -53,7 +63,7 @@ export function makeViolationsBuffer(opts = {}) {
53
63
  content = readFileSync(bufferFile, 'utf8').trim();
54
64
  }
55
65
  catch {
56
- return; // can't read — skip
66
+ return;
57
67
  }
58
68
  if (!content)
59
69
  return;
@@ -63,30 +73,70 @@ export function makeViolationsBuffer(opts = {}) {
63
73
  if (!trimmed)
64
74
  continue;
65
75
  try {
66
- records.push(JSON.parse(trimmed));
76
+ const parsed = JSON.parse(trimmed);
77
+ // Drop records that pre-date the projectId field or are malformed
78
+ // beyond recovery — keeping them would block the flush forever
79
+ // since they cannot be addressed to a project.
80
+ if (typeof parsed.projectId === 'string' && parsed.projectId) {
81
+ records.push(parsed);
82
+ }
67
83
  }
68
84
  catch {
69
- // Corrupt line — skip it
85
+ // Corrupt line — drop it
70
86
  }
71
87
  }
72
- if (records.length === 0)
88
+ if (records.length === 0) {
89
+ // Nothing salvageable — clear the file so corrupt content doesn't
90
+ // grow unboundedly.
91
+ try {
92
+ writeFileSync(bufferFile, '', 'utf8');
93
+ }
94
+ catch {
95
+ // Best-effort
96
+ }
73
97
  return;
98
+ }
99
+ // Group by projectId, preserving original insertion order within each group.
100
+ const groups = new Map();
101
+ for (const r of records) {
102
+ const arr = groups.get(r.projectId);
103
+ if (arr)
104
+ arr.push(r);
105
+ else
106
+ groups.set(r.projectId, [r]);
107
+ }
74
108
  const BATCH_SIZE = 100;
75
- try {
76
- for (let i = 0; i < records.length; i += BATCH_SIZE) {
77
- const batch = records.slice(i, i + BATCH_SIZE);
78
- await postFn(ctx, batch);
109
+ const failed = [];
110
+ for (const [projectId, group] of groups) {
111
+ let groupFailed = false;
112
+ for (let i = 0; i < group.length; i += BATCH_SIZE) {
113
+ const batch = group.slice(i, i + BATCH_SIZE);
114
+ try {
115
+ await postFn(ctx, projectId, batch);
116
+ }
117
+ catch {
118
+ groupFailed = true;
119
+ break;
120
+ }
79
121
  }
80
- // Truncate on success
81
- try {
122
+ if (groupFailed) {
123
+ failed.push(...group);
124
+ }
125
+ }
126
+ // Rewrite the file with only the failed groups (or wipe it on full success).
127
+ try {
128
+ if (failed.length === 0) {
82
129
  writeFileSync(bufferFile, '', 'utf8');
83
130
  }
84
- catch {
85
- // Best-effort truncate
131
+ else {
132
+ const serialised = failed.map((r) => JSON.stringify(r)).join('\n') + '\n';
133
+ writeFileSync(bufferFile, serialised, 'utf8');
86
134
  }
87
135
  }
88
136
  catch {
89
- // POST failed preserve buffer for next flush attempt
137
+ // Best-effortif we can't rewrite, the worst case is that successful
138
+ // groups are re-flushed on the next attempt (the API push is idempotent
139
+ // at the (project_id, rule_id, target_hash, session_id) level).
90
140
  }
91
141
  }
92
142
  return { bufferViolation, flushViolations };
@@ -1 +1 @@
1
- {"version":3,"file":"guard-violations-buffer.js","sourceRoot":"","sources":["../../../src/hooks/runtime/guard-violations-buffer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guard-violations-buffer.js","sourceRoot":"","sources":["../../../src/hooks/runtime/guard-violations-buffer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cybedefend/vibedefend",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "VibeDefend — one-shot installer for CybeDefend's MCP server and Claude Code / Cursor / Codex / Windsurf hooks (for AI coding agents).",
5
5
  "type": "module",
6
6
  "bin": {