@cybedefend/vibedefend 1.4.5 → 1.4.7
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/auth/auth.js +15 -2
- package/dist/auth/auth.js.map +1 -1
- package/dist/clients/claude-code.js +81 -30
- package/dist/clients/claude-code.js.map +1 -1
- package/dist/clients/codex.js +42 -15
- package/dist/clients/codex.js.map +1 -1
- package/dist/clients/cursor.js +8 -14
- package/dist/clients/cursor.js.map +1 -1
- package/dist/clients/mcp-url.js +42 -0
- package/dist/clients/mcp-url.js.map +1 -0
- package/dist/clients/unwire.js +1 -1
- package/dist/clients/unwire.js.map +1 -1
- package/dist/clients/vscode.js +9 -15
- package/dist/clients/vscode.js.map +1 -1
- package/dist/clients/windsurf.js +9 -14
- package/dist/clients/windsurf.js.map +1 -1
- package/dist/diagnostics.js +4 -2
- package/dist/diagnostics.js.map +1 -1
- package/dist/doctor.js +12 -2
- package/dist/doctor.js.map +1 -1
- package/dist/hook-runner.js +382 -136
- package/dist/hooks/install.js +2 -1
- package/dist/hooks/install.js.map +1 -1
- package/dist/hooks/runtime/api.js +29 -2
- package/dist/hooks/runtime/api.js.map +1 -1
- package/dist/hooks/runtime/fetch-rules.js +27 -1
- package/dist/hooks/runtime/fetch-rules.js.map +1 -1
- package/dist/hooks/runtime/guard-rules-cache.js +25 -3
- package/dist/hooks/runtime/guard-rules-cache.js.map +1 -1
- package/dist/hooks/runtime/judge.js +10 -2
- package/dist/hooks/runtime/judge.js.map +1 -1
- package/dist/hooks/runtime/resolve.js +12 -5
- package/dist/hooks/runtime/resolve.js.map +1 -1
- package/dist/hooks/runtime/usage-block.js +199 -0
- package/dist/hooks/runtime/usage-block.js.map +1 -0
- package/dist/install.js +95 -22
- package/dist/install.js.map +1 -1
- package/dist/json-file.js +115 -0
- package/dist/json-file.js.map +1 -0
- package/dist/link.js +5 -2
- package/dist/link.js.map +1 -1
- package/dist/login.js +12 -4
- package/dist/login.js.map +1 -1
- package/dist/refresh-hooks.js +1 -1
- package/dist/refresh-hooks.js.map +1 -1
- package/dist/status.js +5 -1
- package/dist/status.js.map +1 -1
- package/dist/utils.js +1 -20
- package/dist/utils.js.map +1 -1
- package/package.json +4 -2
package/dist/hook-runner.js
CHANGED
|
@@ -4962,7 +4962,7 @@ function extractToolInputShape(input, client) {
|
|
|
4962
4962
|
// src/hooks/runtime/resolve.ts
|
|
4963
4963
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
4964
4964
|
import { createHash } from "node:crypto";
|
|
4965
|
-
import { existsSync as existsSync6, readFileSync as
|
|
4965
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
|
|
4966
4966
|
|
|
4967
4967
|
// src/auth/auth-store.ts
|
|
4968
4968
|
import { execFileSync } from "node:child_process";
|
|
@@ -5307,11 +5307,11 @@ async function resolveProjectByRepository(apiBase, token, q, deps = {}) {
|
|
|
5307
5307
|
}
|
|
5308
5308
|
|
|
5309
5309
|
// src/hooks/runtime/repo-link-cache.ts
|
|
5310
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as
|
|
5310
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5311
5311
|
import { dirname as dirname2 } from "node:path";
|
|
5312
5312
|
|
|
5313
5313
|
// src/utils.ts
|
|
5314
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2
|
|
5314
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "node:fs";
|
|
5315
5315
|
import { dirname, join as join2 } from "node:path";
|
|
5316
5316
|
import { homedir as homedir3, platform } from "node:os";
|
|
5317
5317
|
|
|
@@ -5436,7 +5436,7 @@ function readRepoLinkCache(deps = {}) {
|
|
|
5436
5436
|
const p = pathOf(deps);
|
|
5437
5437
|
if (!existsSync3(p)) return {};
|
|
5438
5438
|
try {
|
|
5439
|
-
const raw =
|
|
5439
|
+
const raw = readFileSync2(p, "utf8");
|
|
5440
5440
|
if (!raw.trim()) return {};
|
|
5441
5441
|
const parsed = JSON.parse(raw);
|
|
5442
5442
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -5484,13 +5484,13 @@ function writeCachedRepoLink(key, entry, deps = {}) {
|
|
|
5484
5484
|
const p = pathOf(deps);
|
|
5485
5485
|
const dir = dirname2(p);
|
|
5486
5486
|
if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
|
|
5487
|
-
|
|
5487
|
+
writeFileSync2(p, JSON.stringify(map, null, 2) + "\n");
|
|
5488
5488
|
} catch {
|
|
5489
5489
|
}
|
|
5490
5490
|
}
|
|
5491
5491
|
|
|
5492
5492
|
// src/hooks/runtime/config.ts
|
|
5493
|
-
import { existsSync as existsSync4, readFileSync as
|
|
5493
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
|
|
5494
5494
|
import { homedir as homedir4 } from "node:os";
|
|
5495
5495
|
import { join as join3 } from "node:path";
|
|
5496
5496
|
function runtimeConfigPath() {
|
|
@@ -5500,7 +5500,7 @@ function loadRuntimeConfig(path) {
|
|
|
5500
5500
|
const p = path ?? runtimeConfigPath();
|
|
5501
5501
|
if (!existsSync4(p)) return null;
|
|
5502
5502
|
try {
|
|
5503
|
-
const raw =
|
|
5503
|
+
const raw = readFileSync3(p, "utf8");
|
|
5504
5504
|
return JSON.parse(raw);
|
|
5505
5505
|
} catch {
|
|
5506
5506
|
return null;
|
|
@@ -5522,7 +5522,7 @@ async function readStdinJson() {
|
|
|
5522
5522
|
}
|
|
5523
5523
|
|
|
5524
5524
|
// src/hooks/runtime/trusted-api-base.ts
|
|
5525
|
-
import { existsSync as existsSync5, readFileSync as
|
|
5525
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
5526
5526
|
import { homedir as homedir5 } from "node:os";
|
|
5527
5527
|
import { join as join4 } from "node:path";
|
|
5528
5528
|
|
|
@@ -5571,7 +5571,7 @@ function normalizeOrigin(candidate) {
|
|
|
5571
5571
|
function readUserRegionApiBases(path) {
|
|
5572
5572
|
try {
|
|
5573
5573
|
if (!existsSync5(path)) return [];
|
|
5574
|
-
const parsed = JSON.parse(
|
|
5574
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
5575
5575
|
if (!Array.isArray(parsed)) return [];
|
|
5576
5576
|
return parsed.map(
|
|
5577
5577
|
(entry) => entry && typeof entry === "object" ? entry.apiBase : null
|
|
@@ -5640,7 +5640,7 @@ function resolveProjectId(cwd = process.cwd(), deps = {}) {
|
|
|
5640
5640
|
const configPath = `${cwd}/.cybedefend/config.json`;
|
|
5641
5641
|
if (existsSync6(configPath)) {
|
|
5642
5642
|
try {
|
|
5643
|
-
const raw =
|
|
5643
|
+
const raw = readFileSync5(configPath, "utf8");
|
|
5644
5644
|
const cfg = JSON.parse(raw);
|
|
5645
5645
|
return {
|
|
5646
5646
|
projectId: cfg.projectId ?? null,
|
|
@@ -5661,7 +5661,7 @@ function resolveToken(mcpName, opts = {}) {
|
|
|
5661
5661
|
const platform2 = opts.platform ?? process.platform;
|
|
5662
5662
|
const readKeychain = opts.readKeychain ?? readMacOSKeychain2;
|
|
5663
5663
|
if (platform2 === "darwin") {
|
|
5664
|
-
return resolveTokenMacOS(mcpName, readKeychain) ?? resolveTokenCodexMacOS(mcpName, opts.mcpUrl, readKeychain);
|
|
5664
|
+
return resolveTokenMacOS(mcpName, opts.mcpUrl, readKeychain) ?? resolveTokenCodexMacOS(mcpName, opts.mcpUrl, readKeychain);
|
|
5665
5665
|
}
|
|
5666
5666
|
if (platform2 === "linux") return resolveTokenLinux(mcpName);
|
|
5667
5667
|
return null;
|
|
@@ -5692,16 +5692,18 @@ function readMacOSKeychain2(service, account) {
|
|
|
5692
5692
|
return null;
|
|
5693
5693
|
}
|
|
5694
5694
|
}
|
|
5695
|
-
function resolveTokenMacOS(mcpName, readKeychain) {
|
|
5695
|
+
function resolveTokenMacOS(mcpName, mcpUrl, readKeychain) {
|
|
5696
5696
|
const blob = readKeychain("Claude Code-credentials");
|
|
5697
5697
|
if (!blob) return null;
|
|
5698
5698
|
try {
|
|
5699
5699
|
const data = JSON.parse(blob);
|
|
5700
5700
|
const oauth = data.mcpOAuth ?? {};
|
|
5701
5701
|
for (const entry of Object.values(oauth)) {
|
|
5702
|
-
if (entry?.serverName
|
|
5703
|
-
|
|
5702
|
+
if (entry?.serverName !== mcpName || !entry.accessToken) continue;
|
|
5703
|
+
if (mcpUrl && typeof entry.serverUrl === "string" && entry.serverUrl !== mcpUrl) {
|
|
5704
|
+
continue;
|
|
5704
5705
|
}
|
|
5706
|
+
return entry.accessToken;
|
|
5705
5707
|
}
|
|
5706
5708
|
} catch {
|
|
5707
5709
|
return blob || null;
|
|
@@ -5806,7 +5808,165 @@ async function resolveProjectIdWithApi(cwd = process.cwd(), deps = {}) {
|
|
|
5806
5808
|
}
|
|
5807
5809
|
}
|
|
5808
5810
|
|
|
5811
|
+
// src/hooks/runtime/usage-block.ts
|
|
5812
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
|
|
5813
|
+
import { homedir as homedir6 } from "node:os";
|
|
5814
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
5815
|
+
var BLOCKED_CODE = "VIBEDEFEND_BLOCKED";
|
|
5816
|
+
var USAGE_WARNING_HEADER = "x-vibedefend-usage-warning";
|
|
5817
|
+
var UNITS = ["injection", "judgment", "diff_scan"];
|
|
5818
|
+
var REASONS = [
|
|
5819
|
+
"free_tier_exhausted",
|
|
5820
|
+
"cap_reached",
|
|
5821
|
+
"unpaid",
|
|
5822
|
+
"product_inactive"
|
|
5823
|
+
];
|
|
5824
|
+
var WARNINGS = [
|
|
5825
|
+
"approaching_limit",
|
|
5826
|
+
"limit_reached"
|
|
5827
|
+
];
|
|
5828
|
+
function pick(allowed, value) {
|
|
5829
|
+
return allowed.find((v) => v === value);
|
|
5830
|
+
}
|
|
5831
|
+
async function readVibedefendBlock(res) {
|
|
5832
|
+
if (res.status !== 402) return null;
|
|
5833
|
+
let body;
|
|
5834
|
+
try {
|
|
5835
|
+
body = await res.json();
|
|
5836
|
+
} catch {
|
|
5837
|
+
return null;
|
|
5838
|
+
}
|
|
5839
|
+
if (!body || typeof body !== "object") return null;
|
|
5840
|
+
const root = body;
|
|
5841
|
+
const data = root.data && typeof root.data === "object" ? root.data : root;
|
|
5842
|
+
const source = data.code === BLOCKED_CODE ? data : root;
|
|
5843
|
+
if (source.code !== BLOCKED_CODE) return null;
|
|
5844
|
+
return {
|
|
5845
|
+
reason: pick(REASONS, source.reason) ?? "unknown",
|
|
5846
|
+
unit: pick(UNITS, source.unit) ?? "unknown"
|
|
5847
|
+
};
|
|
5848
|
+
}
|
|
5849
|
+
function readUsageWarning(res) {
|
|
5850
|
+
let raw;
|
|
5851
|
+
try {
|
|
5852
|
+
raw = res.headers?.get?.(USAGE_WARNING_HEADER);
|
|
5853
|
+
} catch {
|
|
5854
|
+
return null;
|
|
5855
|
+
}
|
|
5856
|
+
if (typeof raw !== "string") return null;
|
|
5857
|
+
const parts = raw.trim().split(":");
|
|
5858
|
+
if (parts.length !== 2) return null;
|
|
5859
|
+
const unit = pick(UNITS, parts[0]);
|
|
5860
|
+
const warning = pick(WARNINGS, parts[1]);
|
|
5861
|
+
if (!unit || !warning) return null;
|
|
5862
|
+
return { unit, warning };
|
|
5863
|
+
}
|
|
5864
|
+
var UNIT_LABEL = {
|
|
5865
|
+
injection: "Security rules injection",
|
|
5866
|
+
judgment: "Action Guards judgments",
|
|
5867
|
+
diff_scan: "Diff scans",
|
|
5868
|
+
unknown: "Part of VibeDefend"
|
|
5869
|
+
};
|
|
5870
|
+
var UNIT_EFFECT = {
|
|
5871
|
+
injection: "no security or business rules are added to your edits",
|
|
5872
|
+
judgment: "Action Guards are off for this session",
|
|
5873
|
+
diff_scan: "no diff scan can be started",
|
|
5874
|
+
unknown: "part of VibeDefend is paused"
|
|
5875
|
+
};
|
|
5876
|
+
function blockSentence(block) {
|
|
5877
|
+
const label = UNIT_LABEL[block.unit];
|
|
5878
|
+
const effect = UNIT_EFFECT[block.unit];
|
|
5879
|
+
switch (block.reason) {
|
|
5880
|
+
case "free_tier_exhausted":
|
|
5881
|
+
return `VibeDefend paused: ${label} used up this month's free tier, so ${effect}. An administrator of your organisation can add a card in the VibeDefend usage page to continue.`;
|
|
5882
|
+
case "cap_reached":
|
|
5883
|
+
return `VibeDefend paused: ${label} reached the monthly spending cap of your organisation, so ${effect}. An administrator can raise the cap in the VibeDefend usage page.`;
|
|
5884
|
+
case "unpaid":
|
|
5885
|
+
return "VibeDefend paused: your organisation's last VibeDefend invoice is unpaid, so rules, Action Guards and diff scans are off. An administrator can update the card in the VibeDefend usage page.";
|
|
5886
|
+
case "product_inactive":
|
|
5887
|
+
return "VibeDefend is not active for your organisation, so rules, Action Guards and diff scans are off. An administrator can activate it in the CybeDefend dashboard.";
|
|
5888
|
+
default:
|
|
5889
|
+
return `VibeDefend paused: ${label} is blocked for your organisation, so ${effect}. An administrator can check the VibeDefend usage page.`;
|
|
5890
|
+
}
|
|
5891
|
+
}
|
|
5892
|
+
function warningSentence(w) {
|
|
5893
|
+
const label = UNIT_LABEL[w.unit];
|
|
5894
|
+
if (w.warning === "approaching_limit") {
|
|
5895
|
+
return `VibeDefend: ${label} has used 80 % of this month's allowance of your organisation. An administrator can follow it in the VibeDefend usage page.`;
|
|
5896
|
+
}
|
|
5897
|
+
return `VibeDefend: ${label} has reached this month's limit of your organisation; the next requests will be refused. An administrator can add a card or raise the cap in the VibeDefend usage page.`;
|
|
5898
|
+
}
|
|
5899
|
+
var ORG_WIDE_REASONS = /* @__PURE__ */ new Set([
|
|
5900
|
+
"unpaid",
|
|
5901
|
+
"product_inactive"
|
|
5902
|
+
]);
|
|
5903
|
+
function blockKey(block) {
|
|
5904
|
+
return ORG_WIDE_REASONS.has(block.reason) ? `block:${block.reason}` : `block:${block.reason}:${block.unit}`;
|
|
5905
|
+
}
|
|
5906
|
+
var NOTICES_CAP = 300;
|
|
5907
|
+
function defaultUsageNoticesFile() {
|
|
5908
|
+
const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join5(homedir6(), ".cybedefend");
|
|
5909
|
+
return join5(dir, "usage-notices-sessions.json");
|
|
5910
|
+
}
|
|
5911
|
+
function makeUsageNotifier(opts = {}) {
|
|
5912
|
+
const file = opts.file ?? defaultUsageNoticesFile();
|
|
5913
|
+
const write = opts.write ?? ((line) => {
|
|
5914
|
+
try {
|
|
5915
|
+
process.stderr.write(line);
|
|
5916
|
+
} catch {
|
|
5917
|
+
}
|
|
5918
|
+
});
|
|
5919
|
+
function load() {
|
|
5920
|
+
try {
|
|
5921
|
+
if (!existsSync7(file)) return [];
|
|
5922
|
+
const data = JSON.parse(readFileSync6(file, "utf8"));
|
|
5923
|
+
return Array.isArray(data.seen) ? data.seen.filter((s) => typeof s === "string") : [];
|
|
5924
|
+
} catch {
|
|
5925
|
+
return [];
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5928
|
+
function once(sessionId, key, sentence) {
|
|
5929
|
+
if (sessionId) {
|
|
5930
|
+
const marker = `${sessionId}|${key}`;
|
|
5931
|
+
const seen = load();
|
|
5932
|
+
if (seen.includes(marker)) return null;
|
|
5933
|
+
try {
|
|
5934
|
+
mkdirSync4(dirname3(file), { recursive: true });
|
|
5935
|
+
writeFileSync3(
|
|
5936
|
+
file,
|
|
5937
|
+
JSON.stringify({ seen: [...seen, marker].slice(-NOTICES_CAP) }),
|
|
5938
|
+
"utf8"
|
|
5939
|
+
);
|
|
5940
|
+
} catch {
|
|
5941
|
+
}
|
|
5942
|
+
}
|
|
5943
|
+
write(`[VibeDefend] ${sentence}
|
|
5944
|
+
`);
|
|
5945
|
+
return sentence;
|
|
5946
|
+
}
|
|
5947
|
+
return {
|
|
5948
|
+
notifyBlock: (sessionId, block) => once(sessionId, blockKey(block), blockSentence(block)),
|
|
5949
|
+
notifyWarning: (sessionId, w) => once(sessionId, `warning:${w.unit}:${w.warning}`, warningSentence(w))
|
|
5950
|
+
};
|
|
5951
|
+
}
|
|
5952
|
+
|
|
5809
5953
|
// src/hooks/runtime/api.ts
|
|
5954
|
+
async function reportUsageSignal(res, listener) {
|
|
5955
|
+
const emit2 = (signal) => {
|
|
5956
|
+
try {
|
|
5957
|
+
listener?.(signal);
|
|
5958
|
+
} catch {
|
|
5959
|
+
}
|
|
5960
|
+
};
|
|
5961
|
+
if (!res.ok) {
|
|
5962
|
+
const block = await readVibedefendBlock(res);
|
|
5963
|
+
if (block) emit2({ kind: "blocked", block });
|
|
5964
|
+
return false;
|
|
5965
|
+
}
|
|
5966
|
+
const warning = readUsageWarning(res);
|
|
5967
|
+
if (warning) emit2({ kind: "warning", warning });
|
|
5968
|
+
return true;
|
|
5969
|
+
}
|
|
5810
5970
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
5811
5971
|
async function fetchBusinessRules(opts) {
|
|
5812
5972
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -5830,7 +5990,7 @@ async function fetchBusinessRules(opts) {
|
|
|
5830
5990
|
} catch {
|
|
5831
5991
|
return null;
|
|
5832
5992
|
}
|
|
5833
|
-
if (!res.
|
|
5993
|
+
if (!await reportUsageSignal(res, opts.onUsageSignal)) return null;
|
|
5834
5994
|
let body;
|
|
5835
5995
|
try {
|
|
5836
5996
|
body = await res.json();
|
|
@@ -5865,7 +6025,7 @@ async function fetchSecurityRules(opts) {
|
|
|
5865
6025
|
} catch {
|
|
5866
6026
|
return null;
|
|
5867
6027
|
}
|
|
5868
|
-
if (!res.
|
|
6028
|
+
if (!await reportUsageSignal(res, opts.onUsageSignal)) return null;
|
|
5869
6029
|
let body;
|
|
5870
6030
|
try {
|
|
5871
6031
|
body = await res.json();
|
|
@@ -5969,6 +6129,7 @@ async function fetchRulesHook(deps = {}) {
|
|
|
5969
6129
|
const fetchRules = deps.fetchRulesFn ?? fetchBusinessRules;
|
|
5970
6130
|
const fetchSecRules = deps.fetchSecurityRulesFn ?? fetchSecurityRules;
|
|
5971
6131
|
const emitFn = deps.emitFn ?? emitPreToolUseContext;
|
|
6132
|
+
const usageNotifier = deps.usageNotifier ?? makeUsageNotifier();
|
|
5972
6133
|
const stdin = await readStdin();
|
|
5973
6134
|
const event = sniff(stdin);
|
|
5974
6135
|
if (!INTERESTED_TOOLS.has(event.toolName)) return;
|
|
@@ -5985,13 +6146,20 @@ async function fetchRulesHook(deps = {}) {
|
|
|
5985
6146
|
const intent = buildIntent(event);
|
|
5986
6147
|
const files = event.filePaths.length > 0 ? event.filePaths : [event.filePath];
|
|
5987
6148
|
const fileLabel = files.join(", ");
|
|
6149
|
+
const sessionId = sessionIdOf(stdin);
|
|
6150
|
+
const usageNotices = [];
|
|
6151
|
+
const onUsageSignal = (signal) => {
|
|
6152
|
+
const sentence = signal.kind === "blocked" ? usageNotifier.notifyBlock(sessionId, signal.block) : usageNotifier.notifyWarning(sessionId, signal.warning);
|
|
6153
|
+
if (sentence) usageNotices.push(sentence);
|
|
6154
|
+
};
|
|
5988
6155
|
const fetchOpts = {
|
|
5989
6156
|
apiBase,
|
|
5990
6157
|
projectId,
|
|
5991
6158
|
token,
|
|
5992
6159
|
files,
|
|
5993
6160
|
intent,
|
|
5994
|
-
topK: 5
|
|
6161
|
+
topK: 5,
|
|
6162
|
+
onUsageSignal
|
|
5995
6163
|
};
|
|
5996
6164
|
const [businessResult, securityResult] = await Promise.all([
|
|
5997
6165
|
fetchRules(fetchOpts),
|
|
@@ -6015,8 +6183,18 @@ async function fetchRulesHook(deps = {}) {
|
|
|
6015
6183
|
)
|
|
6016
6184
|
);
|
|
6017
6185
|
}
|
|
6186
|
+
if (usageNotices.length > 0) {
|
|
6187
|
+
blocks.push(
|
|
6188
|
+
["## CybeDefend VibeDefend usage", ...usageNotices.map((n) => `- ${n}`), ""].join("\n")
|
|
6189
|
+
);
|
|
6190
|
+
}
|
|
6018
6191
|
if (blocks.length > 0) emitFn(blocks.join("\n"), event.client);
|
|
6019
6192
|
}
|
|
6193
|
+
function sessionIdOf(stdin) {
|
|
6194
|
+
if (typeof stdin.session_id === "string" && stdin.session_id) return stdin.session_id;
|
|
6195
|
+
if (typeof stdin.sessionId === "string" && stdin.sessionId) return stdin.sessionId;
|
|
6196
|
+
return void 0;
|
|
6197
|
+
}
|
|
6020
6198
|
function buildIntent(event) {
|
|
6021
6199
|
const limit = parseInt(process.env.CYBEDEFEND_INTENT_BYTES ?? "800", 10);
|
|
6022
6200
|
const snippet = event.newContent.slice(0, Number.isFinite(limit) ? limit : 800).replace(/\s+/g, " ").trim();
|
|
@@ -6097,9 +6275,9 @@ var DIRECT_PROPOSE_DIRECTIVES = {
|
|
|
6097
6275
|
|
|
6098
6276
|
// src/hooks/runtime/self-update-check.ts
|
|
6099
6277
|
import { spawn } from "node:child_process";
|
|
6100
|
-
import { existsSync as
|
|
6101
|
-
import { homedir as
|
|
6102
|
-
import { join as
|
|
6278
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
6279
|
+
import { homedir as homedir7 } from "node:os";
|
|
6280
|
+
import { join as join6 } from "node:path";
|
|
6103
6281
|
|
|
6104
6282
|
// src/hooks/runtime/semver.ts
|
|
6105
6283
|
function parseVersion(input) {
|
|
@@ -6127,7 +6305,7 @@ function isNewer(latest, current) {
|
|
|
6127
6305
|
|
|
6128
6306
|
// src/self-update.ts
|
|
6129
6307
|
import { fileURLToPath } from "node:url";
|
|
6130
|
-
import { existsSync as
|
|
6308
|
+
import { existsSync as existsSync8, realpathSync } from "node:fs";
|
|
6131
6309
|
var GLOBAL_MODES = /* @__PURE__ */ new Set([
|
|
6132
6310
|
"npm-global",
|
|
6133
6311
|
"pnpm-global",
|
|
@@ -6155,7 +6333,7 @@ function detectInstallMode(scriptPath) {
|
|
|
6155
6333
|
}
|
|
6156
6334
|
function resolveScriptPath() {
|
|
6157
6335
|
const fromArgv = process.argv[1];
|
|
6158
|
-
if (fromArgv &&
|
|
6336
|
+
if (fromArgv && existsSync8(fromArgv)) {
|
|
6159
6337
|
try {
|
|
6160
6338
|
return realpathSync(fromArgv);
|
|
6161
6339
|
} catch {
|
|
@@ -6177,9 +6355,9 @@ function isValidRegistryVersion(value) {
|
|
|
6177
6355
|
}
|
|
6178
6356
|
async function selfUpdateCheck(deps) {
|
|
6179
6357
|
const now = deps.now ?? (() => Date.now());
|
|
6180
|
-
const dir = deps.cybeDir ??
|
|
6181
|
-
const throttlePath =
|
|
6182
|
-
const logPath =
|
|
6358
|
+
const dir = deps.cybeDir ?? join6(process.env.CYBEDEFEND_HOME ?? homedir7(), ".cybedefend");
|
|
6359
|
+
const throttlePath = join6(dir, "update-check.json");
|
|
6360
|
+
const logPath = join6(dir, "last-update.log");
|
|
6183
6361
|
const installMode = deps.installMode ?? (deps.runnerSource ? detectInstallMode(deps.runnerSource) : detectInstallMode(resolveScriptPath()));
|
|
6184
6362
|
const priorFailure = takeUpdateLogNotice(logPath);
|
|
6185
6363
|
const throttle = readThrottle(throttlePath);
|
|
@@ -6212,7 +6390,7 @@ function applyOrNotify(deps, installMode, latest, logPath) {
|
|
|
6212
6390
|
}
|
|
6213
6391
|
function readThrottle(path) {
|
|
6214
6392
|
try {
|
|
6215
|
-
if (!
|
|
6393
|
+
if (!existsSync9(path)) return null;
|
|
6216
6394
|
const data = JSON.parse(readFileSync7(path, "utf8"));
|
|
6217
6395
|
if (typeof data.lastCheckedAt !== "number") return null;
|
|
6218
6396
|
return { lastCheckedAt: data.lastCheckedAt };
|
|
@@ -6229,7 +6407,7 @@ function writeThrottle(path, throttle) {
|
|
|
6229
6407
|
}
|
|
6230
6408
|
function takeUpdateLogNotice(logPath) {
|
|
6231
6409
|
try {
|
|
6232
|
-
if (!
|
|
6410
|
+
if (!existsSync9(logPath)) return null;
|
|
6233
6411
|
let parsed = {};
|
|
6234
6412
|
try {
|
|
6235
6413
|
parsed = JSON.parse(readFileSync7(logPath, "utf8"));
|
|
@@ -6307,18 +6485,18 @@ function pmCommand(mode, spec) {
|
|
|
6307
6485
|
|
|
6308
6486
|
// src/hooks/runtime/hook-refresh-trigger.ts
|
|
6309
6487
|
import { spawn as spawn2 } from "node:child_process";
|
|
6310
|
-
import { existsSync as
|
|
6311
|
-
import { homedir as
|
|
6312
|
-
import { dirname as
|
|
6488
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
6489
|
+
import { homedir as homedir8 } from "node:os";
|
|
6490
|
+
import { dirname as dirname4, join as join7 } from "node:path";
|
|
6313
6491
|
function maybeRefreshStaleWiring(deps) {
|
|
6314
6492
|
try {
|
|
6315
6493
|
const { config, runnerVersion } = deps;
|
|
6316
6494
|
if (!config.runnerSource || config.installedVersion === runnerVersion) {
|
|
6317
6495
|
return false;
|
|
6318
6496
|
}
|
|
6319
|
-
const cliEntry =
|
|
6320
|
-
if (!(deps.exists ??
|
|
6321
|
-
const markerPath = deps.markerPath ??
|
|
6497
|
+
const cliEntry = join7(dirname4(dirname4(config.runnerSource)), "bin", "vibedefend.js");
|
|
6498
|
+
if (!(deps.exists ?? existsSync10)(cliEntry)) return false;
|
|
6499
|
+
const markerPath = deps.markerPath ?? join7(process.env.CYBEDEFEND_HOME ?? homedir8(), ".cybedefend", "hook-refresh.json");
|
|
6322
6500
|
if (readMarkerVersion(markerPath) === runnerVersion) return false;
|
|
6323
6501
|
(deps.spawnRefresh ?? defaultSpawnRefresh)(cliEntry);
|
|
6324
6502
|
writeMarker(markerPath, runnerVersion);
|
|
@@ -6329,7 +6507,7 @@ function maybeRefreshStaleWiring(deps) {
|
|
|
6329
6507
|
}
|
|
6330
6508
|
function readMarkerVersion(path) {
|
|
6331
6509
|
try {
|
|
6332
|
-
if (!
|
|
6510
|
+
if (!existsSync10(path)) return null;
|
|
6333
6511
|
const data = JSON.parse(readFileSync8(path, "utf8"));
|
|
6334
6512
|
return typeof data.version === "string" ? data.version : null;
|
|
6335
6513
|
} catch {
|
|
@@ -6355,10 +6533,10 @@ function defaultSpawnRefresh(cliEntry) {
|
|
|
6355
6533
|
// src/version.ts
|
|
6356
6534
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
6357
6535
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6358
|
-
import { dirname as
|
|
6536
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
6359
6537
|
var PKG_NAME = "@cybedefend/vibedefend";
|
|
6360
6538
|
function defaultPkgPath() {
|
|
6361
|
-
return
|
|
6539
|
+
return join8(dirname5(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
6362
6540
|
}
|
|
6363
6541
|
function readPackageMeta(opts = {}) {
|
|
6364
6542
|
if (typeof opts.bakedVersion === "string" && opts.bakedVersion.length > 0) {
|
|
@@ -6373,7 +6551,7 @@ function readPackageMeta(opts = {}) {
|
|
|
6373
6551
|
}
|
|
6374
6552
|
}
|
|
6375
6553
|
var pkg = readPackageMeta({
|
|
6376
|
-
bakedVersion: true ? "1.4.
|
|
6554
|
+
bakedVersion: true ? "1.4.7" : void 0
|
|
6377
6555
|
});
|
|
6378
6556
|
|
|
6379
6557
|
// src/hooks/runtime/session-start.ts
|
|
@@ -6489,7 +6667,7 @@ async function sessionStartHook(deps = {}) {
|
|
|
6489
6667
|
}
|
|
6490
6668
|
|
|
6491
6669
|
// src/hooks/runtime/session-review.ts
|
|
6492
|
-
import { existsSync as
|
|
6670
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
|
|
6493
6671
|
var COUNTED_TOOLS = /* @__PURE__ */ new Set([
|
|
6494
6672
|
"Edit",
|
|
6495
6673
|
"Write",
|
|
@@ -6573,7 +6751,7 @@ function loadTranscript(stdin, readFile) {
|
|
|
6573
6751
|
return out;
|
|
6574
6752
|
}
|
|
6575
6753
|
function defaultReadTranscript(path) {
|
|
6576
|
-
if (!
|
|
6754
|
+
if (!existsSync11(path)) return null;
|
|
6577
6755
|
try {
|
|
6578
6756
|
return readFileSync10(path, "utf8");
|
|
6579
6757
|
} catch {
|
|
@@ -6835,17 +7013,17 @@ async function preCompactHook(deps = {}) {
|
|
|
6835
7013
|
}
|
|
6836
7014
|
|
|
6837
7015
|
// src/hooks/runtime/user-prompt-submit.ts
|
|
6838
|
-
import { existsSync as
|
|
6839
|
-
import { homedir as
|
|
6840
|
-
import { join as
|
|
7016
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync5, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
|
|
7017
|
+
import { homedir as homedir9 } from "node:os";
|
|
7018
|
+
import { join as join9 } from "node:path";
|
|
6841
7019
|
var SESSIONS_CAP = 100;
|
|
6842
7020
|
function defaultSessionsFile() {
|
|
6843
|
-
const dir = process.env.CYBEDEFEND_BASELINE_DIR ??
|
|
6844
|
-
return
|
|
7021
|
+
const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join9(homedir9(), ".cybedefend");
|
|
7022
|
+
return join9(dir, "user-prompt-seen-sessions.json");
|
|
6845
7023
|
}
|
|
6846
7024
|
function defaultSessionTracker(file = defaultSessionsFile()) {
|
|
6847
7025
|
function load() {
|
|
6848
|
-
if (!
|
|
7026
|
+
if (!existsSync12(file)) return [];
|
|
6849
7027
|
try {
|
|
6850
7028
|
const raw = readFileSync11(file, "utf8");
|
|
6851
7029
|
const data = JSON.parse(raw);
|
|
@@ -6864,7 +7042,7 @@ function defaultSessionTracker(file = defaultSessionsFile()) {
|
|
|
6864
7042
|
if (existing.includes(sessionId)) return;
|
|
6865
7043
|
const next = [...existing, sessionId].slice(-SESSIONS_CAP);
|
|
6866
7044
|
try {
|
|
6867
|
-
|
|
7045
|
+
mkdirSync5(join9(file, ".."), { recursive: true });
|
|
6868
7046
|
writeFileSync6(file, JSON.stringify({ sessions: next }), "utf8");
|
|
6869
7047
|
} catch {
|
|
6870
7048
|
}
|
|
@@ -7936,10 +8114,10 @@ function nearMiss(req, rule) {
|
|
|
7936
8114
|
}
|
|
7937
8115
|
|
|
7938
8116
|
// src/hooks/runtime/guard-rules-cache.ts
|
|
7939
|
-
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as
|
|
7940
|
-
import { dirname as
|
|
7941
|
-
import { homedir as
|
|
7942
|
-
import { join as
|
|
8117
|
+
import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "node:fs";
|
|
8118
|
+
import { dirname as dirname6 } from "node:path";
|
|
8119
|
+
import { homedir as homedir10 } from "node:os";
|
|
8120
|
+
import { join as join10 } from "node:path";
|
|
7943
8121
|
|
|
7944
8122
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
7945
8123
|
var external_exports = {};
|
|
@@ -12051,8 +12229,8 @@ function parseRuleFields(raw) {
|
|
|
12051
12229
|
}
|
|
12052
12230
|
|
|
12053
12231
|
// src/hooks/runtime/guard-rules-cache.ts
|
|
12054
|
-
var DEFAULT_CACHE_FILE =
|
|
12055
|
-
|
|
12232
|
+
var DEFAULT_CACHE_FILE = join10(
|
|
12233
|
+
homedir10(),
|
|
12056
12234
|
".cybedefend",
|
|
12057
12235
|
"guards-cache.json"
|
|
12058
12236
|
);
|
|
@@ -12132,14 +12310,26 @@ function makeGuardRulesCache(opts) {
|
|
|
12132
12310
|
const entry = readCache(ctx.projectId);
|
|
12133
12311
|
const ageSec = entry ? (Date.now() - entry.fetchedAt) / 1e3 : Infinity;
|
|
12134
12312
|
if (entry && ageSec < staleSec) {
|
|
12313
|
+
if (entry.blocked) return { kind: "blocked", block: entry.blocked };
|
|
12135
12314
|
return { kind: "rules", rules: entry.rules, llmJudge: entry.llmJudge };
|
|
12136
12315
|
}
|
|
12137
12316
|
try {
|
|
12138
12317
|
const result = await fetchFn({
|
|
12139
12318
|
...ctx,
|
|
12140
|
-
etag: entry?.etag ?? null
|
|
12319
|
+
etag: entry?.blocked ? null : entry?.etag ?? null
|
|
12141
12320
|
});
|
|
12142
|
-
if (result.
|
|
12321
|
+
if (result.blocked) {
|
|
12322
|
+
writeCache({
|
|
12323
|
+
projectId: ctx.projectId,
|
|
12324
|
+
rules: [],
|
|
12325
|
+
etag: null,
|
|
12326
|
+
fetchedAt: Date.now(),
|
|
12327
|
+
llmJudge: null,
|
|
12328
|
+
blocked: result.blocked
|
|
12329
|
+
});
|
|
12330
|
+
return { kind: "blocked", block: result.blocked };
|
|
12331
|
+
}
|
|
12332
|
+
if (result.notModified && entry && !entry.blocked) {
|
|
12143
12333
|
writeCache({
|
|
12144
12334
|
...entry,
|
|
12145
12335
|
fetchedAt: Date.now()
|
|
@@ -12161,7 +12351,7 @@ function makeGuardRulesCache(opts) {
|
|
|
12161
12351
|
return { kind: "rules", rules: fresh.rules, llmJudge: fresh.llmJudge };
|
|
12162
12352
|
} catch (e) {
|
|
12163
12353
|
const msg = e instanceof Error ? e.message : String(e);
|
|
12164
|
-
if (entry?.rules?.length) {
|
|
12354
|
+
if (entry?.rules?.length && !entry.blocked) {
|
|
12165
12355
|
const ageSec2 = Math.round((Date.now() - entry.fetchedAt) / 1e3);
|
|
12166
12356
|
process.stderr.write(
|
|
12167
12357
|
`[VibeDefend] Action Guards: rule refresh failed (${msg}). Using cached rules (age=${ageSec2}s).
|
|
@@ -12176,7 +12366,7 @@ function makeGuardRulesCache(opts) {
|
|
|
12176
12366
|
}
|
|
12177
12367
|
}
|
|
12178
12368
|
function readCache(projectId) {
|
|
12179
|
-
if (!
|
|
12369
|
+
if (!existsSync13(cacheFile)) return null;
|
|
12180
12370
|
try {
|
|
12181
12371
|
const raw = readFileSync12(cacheFile, "utf8");
|
|
12182
12372
|
const data = JSON.parse(raw);
|
|
@@ -12196,9 +12386,9 @@ function makeGuardRulesCache(opts) {
|
|
|
12196
12386
|
return { refreshIfStale };
|
|
12197
12387
|
}
|
|
12198
12388
|
function ensureDirFor2(filePath) {
|
|
12199
|
-
const dir =
|
|
12200
|
-
if (!
|
|
12201
|
-
|
|
12389
|
+
const dir = dirname6(filePath);
|
|
12390
|
+
if (!existsSync13(dir)) {
|
|
12391
|
+
mkdirSync6(dir, { recursive: true });
|
|
12202
12392
|
}
|
|
12203
12393
|
}
|
|
12204
12394
|
var DEFAULT_TIMEOUT_MS2 = 8e3;
|
|
@@ -12221,6 +12411,10 @@ async function fetchEffectiveRulesFromGateway(ctx, opts = {}) {
|
|
|
12221
12411
|
return { rules: [], etag: ctx.etag ?? null, notModified: true };
|
|
12222
12412
|
}
|
|
12223
12413
|
if (!res.ok) {
|
|
12414
|
+
const blocked = await readVibedefendBlock(res);
|
|
12415
|
+
if (blocked) {
|
|
12416
|
+
return { rules: [], etag: null, notModified: false, blocked };
|
|
12417
|
+
}
|
|
12224
12418
|
throw new Error(`HTTP ${res.status} from effective-rules endpoint`);
|
|
12225
12419
|
}
|
|
12226
12420
|
const body = await res.json();
|
|
@@ -12241,6 +12435,9 @@ function parseLlmJudgeHint(raw) {
|
|
|
12241
12435
|
}
|
|
12242
12436
|
|
|
12243
12437
|
// src/hooks/runtime/judge.ts
|
|
12438
|
+
function isJudgeBlocked(res) {
|
|
12439
|
+
return res !== null && "blocked" in res;
|
|
12440
|
+
}
|
|
12244
12441
|
var DEFAULT_TIMEOUT_MS3 = 1500;
|
|
12245
12442
|
async function callJudge(input, opts = {}) {
|
|
12246
12443
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -12264,7 +12461,11 @@ async function callJudge(input, opts = {}) {
|
|
|
12264
12461
|
}),
|
|
12265
12462
|
signal: AbortSignal.timeout(timeoutMs)
|
|
12266
12463
|
});
|
|
12267
|
-
if (!res.ok)
|
|
12464
|
+
if (!res.ok) {
|
|
12465
|
+
const blocked = await readVibedefendBlock(res);
|
|
12466
|
+
return blocked ? { blocked } : null;
|
|
12467
|
+
}
|
|
12468
|
+
const usageWarning = readUsageWarning(res);
|
|
12268
12469
|
const body = await res.json();
|
|
12269
12470
|
const data = body.data ?? body;
|
|
12270
12471
|
const decision = data["decision"];
|
|
@@ -12274,7 +12475,8 @@ async function callJudge(input, opts = {}) {
|
|
|
12274
12475
|
reason: typeof data["reason"] === "string" ? data["reason"] : "",
|
|
12275
12476
|
model: typeof data["model"] === "string" ? data["model"] : "",
|
|
12276
12477
|
quotaRemaining: typeof data["quota_remaining"] === "number" ? data["quota_remaining"] : -1,
|
|
12277
|
-
quotaExceeded: Boolean(data["quota_exceeded"])
|
|
12478
|
+
quotaExceeded: Boolean(data["quota_exceeded"]),
|
|
12479
|
+
...usageWarning ? { usageWarning } : {}
|
|
12278
12480
|
};
|
|
12279
12481
|
} catch {
|
|
12280
12482
|
return null;
|
|
@@ -12319,11 +12521,11 @@ function detectPromptInjection(text) {
|
|
|
12319
12521
|
}
|
|
12320
12522
|
|
|
12321
12523
|
// src/hooks/runtime/judge-cache.ts
|
|
12322
|
-
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as
|
|
12323
|
-
import { dirname as
|
|
12324
|
-
import { homedir as
|
|
12325
|
-
var DEFAULT_JUDGE_CACHE_FILE =
|
|
12326
|
-
|
|
12524
|
+
import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync14, mkdirSync as mkdirSync7 } from "node:fs";
|
|
12525
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
12526
|
+
import { homedir as homedir11 } from "node:os";
|
|
12527
|
+
var DEFAULT_JUDGE_CACHE_FILE = join11(
|
|
12528
|
+
homedir11(),
|
|
12327
12529
|
".cybedefend",
|
|
12328
12530
|
"guards-judge-cache.json"
|
|
12329
12531
|
);
|
|
@@ -12333,7 +12535,7 @@ function makeJudgeCache(opts = {}) {
|
|
|
12333
12535
|
const ttlMs = opts.ttlMs ?? DEFAULT_JUDGE_TTL_MS;
|
|
12334
12536
|
const now = opts.now ?? (() => Date.now());
|
|
12335
12537
|
function readAll() {
|
|
12336
|
-
if (!
|
|
12538
|
+
if (!existsSync14(cacheFile)) return {};
|
|
12337
12539
|
try {
|
|
12338
12540
|
return JSON.parse(readFileSync13(cacheFile, "utf8"));
|
|
12339
12541
|
} catch {
|
|
@@ -12362,29 +12564,29 @@ function makeJudgeCache(opts = {}) {
|
|
|
12362
12564
|
return { get, set };
|
|
12363
12565
|
}
|
|
12364
12566
|
function ensureDirFor3(filePath) {
|
|
12365
|
-
const dir =
|
|
12366
|
-
if (!
|
|
12367
|
-
|
|
12567
|
+
const dir = dirname7(filePath);
|
|
12568
|
+
if (!existsSync14(dir)) {
|
|
12569
|
+
mkdirSync7(dir, { recursive: true });
|
|
12368
12570
|
}
|
|
12369
12571
|
}
|
|
12370
12572
|
|
|
12371
12573
|
// src/hooks/runtime/guard-check.ts
|
|
12372
|
-
import { homedir as
|
|
12373
|
-
import { join as
|
|
12574
|
+
import { homedir as homedir13 } from "node:os";
|
|
12575
|
+
import { join as join13 } from "node:path";
|
|
12374
12576
|
|
|
12375
12577
|
// src/hooks/runtime/guard-violations-buffer.ts
|
|
12376
12578
|
import {
|
|
12377
12579
|
appendFileSync,
|
|
12378
|
-
existsSync as
|
|
12580
|
+
existsSync as existsSync15,
|
|
12379
12581
|
readFileSync as readFileSync14,
|
|
12380
12582
|
writeFileSync as writeFileSync9,
|
|
12381
|
-
mkdirSync as
|
|
12583
|
+
mkdirSync as mkdirSync8
|
|
12382
12584
|
} from "node:fs";
|
|
12383
|
-
import { dirname as
|
|
12384
|
-
import { homedir as
|
|
12385
|
-
import { join as
|
|
12386
|
-
var DEFAULT_BUFFER_FILE =
|
|
12387
|
-
|
|
12585
|
+
import { dirname as dirname8 } from "node:path";
|
|
12586
|
+
import { homedir as homedir12 } from "node:os";
|
|
12587
|
+
import { join as join12 } from "node:path";
|
|
12588
|
+
var DEFAULT_BUFFER_FILE = join12(
|
|
12589
|
+
homedir12(),
|
|
12388
12590
|
".cybedefend",
|
|
12389
12591
|
"guards-violations-buffer.jsonl"
|
|
12390
12592
|
);
|
|
@@ -12403,7 +12605,7 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
12403
12605
|
}
|
|
12404
12606
|
}
|
|
12405
12607
|
async function flushViolations2(ctx, postFn) {
|
|
12406
|
-
if (!
|
|
12608
|
+
if (!existsSync15(bufferFile)) return;
|
|
12407
12609
|
let content;
|
|
12408
12610
|
try {
|
|
12409
12611
|
content = readFileSync14(bufferFile, "utf8").trim();
|
|
@@ -12466,9 +12668,9 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
12466
12668
|
return { bufferViolation: bufferViolation2, flushViolations: flushViolations2 };
|
|
12467
12669
|
}
|
|
12468
12670
|
function ensureDirFor4(filePath) {
|
|
12469
|
-
const dir =
|
|
12470
|
-
if (!
|
|
12471
|
-
|
|
12671
|
+
const dir = dirname8(filePath);
|
|
12672
|
+
if (!existsSync15(dir)) {
|
|
12673
|
+
mkdirSync8(dir, { recursive: true });
|
|
12472
12674
|
}
|
|
12473
12675
|
}
|
|
12474
12676
|
var _defaultBuffer = makeViolationsBuffer();
|
|
@@ -12494,11 +12696,11 @@ function appendDebugLog(entry) {
|
|
|
12494
12696
|
try {
|
|
12495
12697
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12496
12698
|
if (!homeDir) return;
|
|
12497
|
-
const { mkdirSync:
|
|
12498
|
-
const { join:
|
|
12499
|
-
const dir =
|
|
12500
|
-
|
|
12501
|
-
appendFileSync2(
|
|
12699
|
+
const { mkdirSync: mkdirSync9, appendFileSync: appendFileSync2 } = __require("node:fs");
|
|
12700
|
+
const { join: join14 } = __require("node:path");
|
|
12701
|
+
const dir = join14(homeDir, ".cybedefend");
|
|
12702
|
+
mkdirSync9(dir, { recursive: true });
|
|
12703
|
+
appendFileSync2(join14(dir, "hook-debug.log"), entry + "\n", "utf8");
|
|
12502
12704
|
} catch {
|
|
12503
12705
|
}
|
|
12504
12706
|
}
|
|
@@ -12669,6 +12871,28 @@ function stringifyTarget(t) {
|
|
|
12669
12871
|
return `git ${t.subcommand}`;
|
|
12670
12872
|
}
|
|
12671
12873
|
}
|
|
12874
|
+
async function consultJudge(judge, sessionId, cacheKey, input) {
|
|
12875
|
+
const cached = judge.cacheGet(cacheKey);
|
|
12876
|
+
if (cached) return { kind: "result", res: cached };
|
|
12877
|
+
if (judge.usage?.isBlocked(sessionId)) return { kind: "blocked" };
|
|
12878
|
+
const answer = await judge.callJudge(input);
|
|
12879
|
+
if (isJudgeBlocked(answer)) {
|
|
12880
|
+
if (judge.usage) {
|
|
12881
|
+
judge.usage.markBlocked(sessionId);
|
|
12882
|
+
judge.usage.notifier.notifyBlock(sessionId, answer.blocked);
|
|
12883
|
+
} else {
|
|
12884
|
+
judge.warnOnce(sessionId, blockSentence(answer.blocked));
|
|
12885
|
+
}
|
|
12886
|
+
return { kind: "blocked" };
|
|
12887
|
+
}
|
|
12888
|
+
if (!answer) return { kind: "result", res: null };
|
|
12889
|
+
const { usageWarning, ...res } = answer;
|
|
12890
|
+
if (usageWarning) {
|
|
12891
|
+
judge.usage?.notifier.notifyWarning(sessionId, usageWarning);
|
|
12892
|
+
}
|
|
12893
|
+
judge.cacheSet(cacheKey, res);
|
|
12894
|
+
return { kind: "result", res };
|
|
12895
|
+
}
|
|
12672
12896
|
async function runJudgeStep(args) {
|
|
12673
12897
|
const {
|
|
12674
12898
|
judge,
|
|
@@ -12697,30 +12921,30 @@ async function runJudgeStep(args) {
|
|
|
12697
12921
|
const actionHash = hashTarget(targetStr);
|
|
12698
12922
|
const ruleIds = llmRules.map((r) => r.id).sort().join(",");
|
|
12699
12923
|
const cacheKey = `${actionHash}|${ruleIds}|confirm`;
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
}
|
|
12722
|
-
if (res) judge.cacheSet(cacheKey, res);
|
|
12924
|
+
const consultation = await consultJudge(judge, sessionId, cacheKey, {
|
|
12925
|
+
apiBaseResolved: projectContext.apiBaseResolved,
|
|
12926
|
+
token: projectContext.token,
|
|
12927
|
+
projectId: request.projectId,
|
|
12928
|
+
tool: request.tool,
|
|
12929
|
+
agent: request.agent,
|
|
12930
|
+
sessionId,
|
|
12931
|
+
phase: "confirm",
|
|
12932
|
+
// Judge over the FULL (schema-bounded 2048) redacted action — the default
|
|
12933
|
+
// redactTarget cap of 256 (right for the DB-stored violation summary) would
|
|
12934
|
+
// truncate a long dangerous action and risk an overturn on a partial view.
|
|
12935
|
+
actionSummary: redactTarget(targetStr, 2048),
|
|
12936
|
+
actionHash,
|
|
12937
|
+
rules: llmRules.map((r) => ({
|
|
12938
|
+
id: r.id,
|
|
12939
|
+
name: r.name ?? r.id,
|
|
12940
|
+
description: r.description ?? "",
|
|
12941
|
+
severity: r.severity
|
|
12942
|
+
}))
|
|
12943
|
+
});
|
|
12944
|
+
if (consultation.kind === "blocked") {
|
|
12945
|
+
return { engine: "llm", outcome: "quota_exceeded" };
|
|
12723
12946
|
}
|
|
12947
|
+
const res = consultation.res;
|
|
12724
12948
|
if (!res) {
|
|
12725
12949
|
return {
|
|
12726
12950
|
engine: "llm",
|
|
@@ -12783,27 +13007,25 @@ async function runCatchJudgeStep(args) {
|
|
|
12783
13007
|
const actionHash = hashTarget(targetStr);
|
|
12784
13008
|
const ruleIds = near.map((r) => r.id).sort();
|
|
12785
13009
|
const cacheKey = `${actionHash}|${ruleIds.join(",")}|catch`;
|
|
12786
|
-
|
|
12787
|
-
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
if (res) judge.cacheSet(cacheKey, res);
|
|
12806
|
-
}
|
|
13010
|
+
const consultation = await consultJudge(judge, sessionId, cacheKey, {
|
|
13011
|
+
apiBaseResolved: projectContext.apiBaseResolved,
|
|
13012
|
+
token: projectContext.token,
|
|
13013
|
+
projectId: request.projectId,
|
|
13014
|
+
tool: request.tool,
|
|
13015
|
+
agent: request.agent,
|
|
13016
|
+
sessionId,
|
|
13017
|
+
phase: "catch",
|
|
13018
|
+
actionSummary: redactTarget(targetStr, 2048),
|
|
13019
|
+
actionHash,
|
|
13020
|
+
rules: near.map((r) => ({
|
|
13021
|
+
id: r.id,
|
|
13022
|
+
name: r.name ?? r.id,
|
|
13023
|
+
description: r.description ?? "",
|
|
13024
|
+
severity: r.severity
|
|
13025
|
+
}))
|
|
13026
|
+
});
|
|
13027
|
+
if (consultation.kind === "blocked") return void 0;
|
|
13028
|
+
const res = consultation.res;
|
|
12807
13029
|
if (!res) return void 0;
|
|
12808
13030
|
if (res.quotaExceeded) {
|
|
12809
13031
|
judge.warnOnce(
|
|
@@ -12849,6 +13071,9 @@ function emitUnverifiedBanner(reason) {
|
|
|
12849
13071
|
}
|
|
12850
13072
|
}
|
|
12851
13073
|
}
|
|
13074
|
+
function sessionIdOf2(envelope) {
|
|
13075
|
+
return typeof envelope.session_id === "string" ? envelope.session_id : typeof envelope.sessionId === "string" ? envelope.sessionId : void 0;
|
|
13076
|
+
}
|
|
12852
13077
|
async function runPreToolUseGuardWith(opts) {
|
|
12853
13078
|
const {
|
|
12854
13079
|
stdinEnvelope,
|
|
@@ -12864,11 +13089,19 @@ async function runPreToolUseGuardWith(opts) {
|
|
|
12864
13089
|
);
|
|
12865
13090
|
return 0;
|
|
12866
13091
|
}
|
|
13092
|
+
if (rulesOutcome.kind === "blocked") {
|
|
13093
|
+
const sentence = opts.usageNotifier?.notifyBlock(
|
|
13094
|
+
sessionIdOf2(stdinEnvelope),
|
|
13095
|
+
rulesOutcome.block
|
|
13096
|
+
);
|
|
13097
|
+
if (sentence) emitAgentMessage("warn", sentence);
|
|
13098
|
+
return 0;
|
|
13099
|
+
}
|
|
12867
13100
|
const rules = rulesOutcome.rules;
|
|
12868
13101
|
const caller = sniff(stdinEnvelope);
|
|
12869
13102
|
const partial = mapEnvelopeToGuardRequest(stdinEnvelope, caller.client);
|
|
12870
13103
|
if (partial === null) return 0;
|
|
12871
|
-
const sessionId =
|
|
13104
|
+
const sessionId = sessionIdOf2(stdinEnvelope);
|
|
12872
13105
|
const guardRequest = {
|
|
12873
13106
|
...partial,
|
|
12874
13107
|
projectId: projectContext.projectId,
|
|
@@ -13012,11 +13245,21 @@ function emitAgentMessage(verdict, message) {
|
|
|
13012
13245
|
}
|
|
13013
13246
|
function makeProductionJudgeDeps() {
|
|
13014
13247
|
const cache = makeJudgeCache();
|
|
13015
|
-
const warnDir = process.env.CYBEDEFEND_BASELINE_DIR ??
|
|
13248
|
+
const warnDir = process.env.CYBEDEFEND_BASELINE_DIR ?? join13(homedir13(), ".cybedefend");
|
|
13016
13249
|
const warnTracker = defaultSessionTracker(
|
|
13017
|
-
|
|
13250
|
+
join13(warnDir, "guards-judge-warned-sessions.json")
|
|
13251
|
+
);
|
|
13252
|
+
const blockedTracker = defaultSessionTracker(
|
|
13253
|
+
join13(warnDir, "guards-judge-blocked-sessions.json")
|
|
13018
13254
|
);
|
|
13019
13255
|
return {
|
|
13256
|
+
usage: {
|
|
13257
|
+
notifier: makeUsageNotifier(),
|
|
13258
|
+
isBlocked: (sessionId) => sessionId ? blockedTracker.hasSeen(sessionId) : false,
|
|
13259
|
+
markBlocked: (sessionId) => {
|
|
13260
|
+
if (sessionId) void blockedTracker.markSeen(sessionId);
|
|
13261
|
+
}
|
|
13262
|
+
},
|
|
13020
13263
|
callJudge,
|
|
13021
13264
|
cacheGet: (k) => cache.get(k),
|
|
13022
13265
|
cacheSet: (k, r) => cache.set(k, r),
|
|
@@ -13074,7 +13317,10 @@ async function runPreToolUseGuard() {
|
|
|
13074
13317
|
judge: makeProductionJudgeDeps(),
|
|
13075
13318
|
// v1.1 catch — OFF by default; opt in with CYBEDEFEND_JUDGE_CATCH=1 once
|
|
13076
13319
|
// the call-rate gate is validated in the field.
|
|
13077
|
-
judgeCatch: process.env.CYBEDEFEND_JUDGE_CATCH === "1"
|
|
13320
|
+
judgeCatch: process.env.CYBEDEFEND_JUDGE_CATCH === "1",
|
|
13321
|
+
// The refusal sentence goes out through the agent-message channels
|
|
13322
|
+
// (stderr included), so this notifier itself writes nothing.
|
|
13323
|
+
usageNotifier: makeUsageNotifier({ write: () => void 0 })
|
|
13078
13324
|
});
|
|
13079
13325
|
try {
|
|
13080
13326
|
await buffer.flushViolations(
|