@cybedefend/vibedefend 1.4.6 → 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/hook-runner.js +386 -142
- 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/usage-block.js +199 -0
- package/dist/hooks/runtime/usage-block.js.map +1 -0
- package/package.json +1 -1
package/dist/hook-runner.js
CHANGED
|
@@ -5808,7 +5808,165 @@ async function resolveProjectIdWithApi(cwd = process.cwd(), deps = {}) {
|
|
|
5808
5808
|
}
|
|
5809
5809
|
}
|
|
5810
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
|
+
|
|
5811
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
|
+
}
|
|
5812
5970
|
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
5813
5971
|
async function fetchBusinessRules(opts) {
|
|
5814
5972
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -5832,7 +5990,7 @@ async function fetchBusinessRules(opts) {
|
|
|
5832
5990
|
} catch {
|
|
5833
5991
|
return null;
|
|
5834
5992
|
}
|
|
5835
|
-
if (!res.
|
|
5993
|
+
if (!await reportUsageSignal(res, opts.onUsageSignal)) return null;
|
|
5836
5994
|
let body;
|
|
5837
5995
|
try {
|
|
5838
5996
|
body = await res.json();
|
|
@@ -5867,7 +6025,7 @@ async function fetchSecurityRules(opts) {
|
|
|
5867
6025
|
} catch {
|
|
5868
6026
|
return null;
|
|
5869
6027
|
}
|
|
5870
|
-
if (!res.
|
|
6028
|
+
if (!await reportUsageSignal(res, opts.onUsageSignal)) return null;
|
|
5871
6029
|
let body;
|
|
5872
6030
|
try {
|
|
5873
6031
|
body = await res.json();
|
|
@@ -5971,6 +6129,7 @@ async function fetchRulesHook(deps = {}) {
|
|
|
5971
6129
|
const fetchRules = deps.fetchRulesFn ?? fetchBusinessRules;
|
|
5972
6130
|
const fetchSecRules = deps.fetchSecurityRulesFn ?? fetchSecurityRules;
|
|
5973
6131
|
const emitFn = deps.emitFn ?? emitPreToolUseContext;
|
|
6132
|
+
const usageNotifier = deps.usageNotifier ?? makeUsageNotifier();
|
|
5974
6133
|
const stdin = await readStdin();
|
|
5975
6134
|
const event = sniff(stdin);
|
|
5976
6135
|
if (!INTERESTED_TOOLS.has(event.toolName)) return;
|
|
@@ -5987,13 +6146,20 @@ async function fetchRulesHook(deps = {}) {
|
|
|
5987
6146
|
const intent = buildIntent(event);
|
|
5988
6147
|
const files = event.filePaths.length > 0 ? event.filePaths : [event.filePath];
|
|
5989
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
|
+
};
|
|
5990
6155
|
const fetchOpts = {
|
|
5991
6156
|
apiBase,
|
|
5992
6157
|
projectId,
|
|
5993
6158
|
token,
|
|
5994
6159
|
files,
|
|
5995
6160
|
intent,
|
|
5996
|
-
topK: 5
|
|
6161
|
+
topK: 5,
|
|
6162
|
+
onUsageSignal
|
|
5997
6163
|
};
|
|
5998
6164
|
const [businessResult, securityResult] = await Promise.all([
|
|
5999
6165
|
fetchRules(fetchOpts),
|
|
@@ -6017,8 +6183,18 @@ async function fetchRulesHook(deps = {}) {
|
|
|
6017
6183
|
)
|
|
6018
6184
|
);
|
|
6019
6185
|
}
|
|
6186
|
+
if (usageNotices.length > 0) {
|
|
6187
|
+
blocks.push(
|
|
6188
|
+
["## CybeDefend VibeDefend usage", ...usageNotices.map((n) => `- ${n}`), ""].join("\n")
|
|
6189
|
+
);
|
|
6190
|
+
}
|
|
6020
6191
|
if (blocks.length > 0) emitFn(blocks.join("\n"), event.client);
|
|
6021
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
|
+
}
|
|
6022
6198
|
function buildIntent(event) {
|
|
6023
6199
|
const limit = parseInt(process.env.CYBEDEFEND_INTENT_BYTES ?? "800", 10);
|
|
6024
6200
|
const snippet = event.newContent.slice(0, Number.isFinite(limit) ? limit : 800).replace(/\s+/g, " ").trim();
|
|
@@ -6099,9 +6275,9 @@ var DIRECT_PROPOSE_DIRECTIVES = {
|
|
|
6099
6275
|
|
|
6100
6276
|
// src/hooks/runtime/self-update-check.ts
|
|
6101
6277
|
import { spawn } from "node:child_process";
|
|
6102
|
-
import { existsSync as
|
|
6103
|
-
import { homedir as
|
|
6104
|
-
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";
|
|
6105
6281
|
|
|
6106
6282
|
// src/hooks/runtime/semver.ts
|
|
6107
6283
|
function parseVersion(input) {
|
|
@@ -6129,7 +6305,7 @@ function isNewer(latest, current) {
|
|
|
6129
6305
|
|
|
6130
6306
|
// src/self-update.ts
|
|
6131
6307
|
import { fileURLToPath } from "node:url";
|
|
6132
|
-
import { existsSync as
|
|
6308
|
+
import { existsSync as existsSync8, realpathSync } from "node:fs";
|
|
6133
6309
|
var GLOBAL_MODES = /* @__PURE__ */ new Set([
|
|
6134
6310
|
"npm-global",
|
|
6135
6311
|
"pnpm-global",
|
|
@@ -6157,7 +6333,7 @@ function detectInstallMode(scriptPath) {
|
|
|
6157
6333
|
}
|
|
6158
6334
|
function resolveScriptPath() {
|
|
6159
6335
|
const fromArgv = process.argv[1];
|
|
6160
|
-
if (fromArgv &&
|
|
6336
|
+
if (fromArgv && existsSync8(fromArgv)) {
|
|
6161
6337
|
try {
|
|
6162
6338
|
return realpathSync(fromArgv);
|
|
6163
6339
|
} catch {
|
|
@@ -6179,9 +6355,9 @@ function isValidRegistryVersion(value) {
|
|
|
6179
6355
|
}
|
|
6180
6356
|
async function selfUpdateCheck(deps) {
|
|
6181
6357
|
const now = deps.now ?? (() => Date.now());
|
|
6182
|
-
const dir = deps.cybeDir ??
|
|
6183
|
-
const throttlePath =
|
|
6184
|
-
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");
|
|
6185
6361
|
const installMode = deps.installMode ?? (deps.runnerSource ? detectInstallMode(deps.runnerSource) : detectInstallMode(resolveScriptPath()));
|
|
6186
6362
|
const priorFailure = takeUpdateLogNotice(logPath);
|
|
6187
6363
|
const throttle = readThrottle(throttlePath);
|
|
@@ -6214,8 +6390,8 @@ function applyOrNotify(deps, installMode, latest, logPath) {
|
|
|
6214
6390
|
}
|
|
6215
6391
|
function readThrottle(path) {
|
|
6216
6392
|
try {
|
|
6217
|
-
if (!
|
|
6218
|
-
const data = JSON.parse(
|
|
6393
|
+
if (!existsSync9(path)) return null;
|
|
6394
|
+
const data = JSON.parse(readFileSync7(path, "utf8"));
|
|
6219
6395
|
if (typeof data.lastCheckedAt !== "number") return null;
|
|
6220
6396
|
return { lastCheckedAt: data.lastCheckedAt };
|
|
6221
6397
|
} catch {
|
|
@@ -6225,16 +6401,16 @@ function readThrottle(path) {
|
|
|
6225
6401
|
function writeThrottle(path, throttle) {
|
|
6226
6402
|
try {
|
|
6227
6403
|
ensureDirFor(path);
|
|
6228
|
-
|
|
6404
|
+
writeFileSync4(path, JSON.stringify(throttle) + "\n");
|
|
6229
6405
|
} catch {
|
|
6230
6406
|
}
|
|
6231
6407
|
}
|
|
6232
6408
|
function takeUpdateLogNotice(logPath) {
|
|
6233
6409
|
try {
|
|
6234
|
-
if (!
|
|
6410
|
+
if (!existsSync9(logPath)) return null;
|
|
6235
6411
|
let parsed = {};
|
|
6236
6412
|
try {
|
|
6237
|
-
parsed = JSON.parse(
|
|
6413
|
+
parsed = JSON.parse(readFileSync7(logPath, "utf8"));
|
|
6238
6414
|
} catch {
|
|
6239
6415
|
parsed = {};
|
|
6240
6416
|
}
|
|
@@ -6309,18 +6485,18 @@ function pmCommand(mode, spec) {
|
|
|
6309
6485
|
|
|
6310
6486
|
// src/hooks/runtime/hook-refresh-trigger.ts
|
|
6311
6487
|
import { spawn as spawn2 } from "node:child_process";
|
|
6312
|
-
import { existsSync as
|
|
6313
|
-
import { homedir as
|
|
6314
|
-
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";
|
|
6315
6491
|
function maybeRefreshStaleWiring(deps) {
|
|
6316
6492
|
try {
|
|
6317
6493
|
const { config, runnerVersion } = deps;
|
|
6318
6494
|
if (!config.runnerSource || config.installedVersion === runnerVersion) {
|
|
6319
6495
|
return false;
|
|
6320
6496
|
}
|
|
6321
|
-
const cliEntry =
|
|
6322
|
-
if (!(deps.exists ??
|
|
6323
|
-
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");
|
|
6324
6500
|
if (readMarkerVersion(markerPath) === runnerVersion) return false;
|
|
6325
6501
|
(deps.spawnRefresh ?? defaultSpawnRefresh)(cliEntry);
|
|
6326
6502
|
writeMarker(markerPath, runnerVersion);
|
|
@@ -6331,8 +6507,8 @@ function maybeRefreshStaleWiring(deps) {
|
|
|
6331
6507
|
}
|
|
6332
6508
|
function readMarkerVersion(path) {
|
|
6333
6509
|
try {
|
|
6334
|
-
if (!
|
|
6335
|
-
const data = JSON.parse(
|
|
6510
|
+
if (!existsSync10(path)) return null;
|
|
6511
|
+
const data = JSON.parse(readFileSync8(path, "utf8"));
|
|
6336
6512
|
return typeof data.version === "string" ? data.version : null;
|
|
6337
6513
|
} catch {
|
|
6338
6514
|
return null;
|
|
@@ -6341,7 +6517,7 @@ function readMarkerVersion(path) {
|
|
|
6341
6517
|
function writeMarker(path, version) {
|
|
6342
6518
|
try {
|
|
6343
6519
|
ensureDirFor(path);
|
|
6344
|
-
|
|
6520
|
+
writeFileSync5(path, JSON.stringify({ version, spawnedAt: Date.now() }) + "\n");
|
|
6345
6521
|
} catch {
|
|
6346
6522
|
}
|
|
6347
6523
|
}
|
|
@@ -6355,18 +6531,18 @@ function defaultSpawnRefresh(cliEntry) {
|
|
|
6355
6531
|
}
|
|
6356
6532
|
|
|
6357
6533
|
// src/version.ts
|
|
6358
|
-
import { readFileSync as
|
|
6534
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
6359
6535
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
6360
|
-
import { dirname as
|
|
6536
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
6361
6537
|
var PKG_NAME = "@cybedefend/vibedefend";
|
|
6362
6538
|
function defaultPkgPath() {
|
|
6363
|
-
return
|
|
6539
|
+
return join8(dirname5(fileURLToPath2(import.meta.url)), "..", "package.json");
|
|
6364
6540
|
}
|
|
6365
6541
|
function readPackageMeta(opts = {}) {
|
|
6366
6542
|
if (typeof opts.bakedVersion === "string" && opts.bakedVersion.length > 0) {
|
|
6367
6543
|
return { name: PKG_NAME, version: opts.bakedVersion };
|
|
6368
6544
|
}
|
|
6369
|
-
const readFile = opts.readFile ?? ((p) =>
|
|
6545
|
+
const readFile = opts.readFile ?? ((p) => readFileSync9(p, "utf8"));
|
|
6370
6546
|
const pkgPath = opts.pkgPath ?? defaultPkgPath();
|
|
6371
6547
|
try {
|
|
6372
6548
|
return JSON.parse(readFile(pkgPath));
|
|
@@ -6375,7 +6551,7 @@ function readPackageMeta(opts = {}) {
|
|
|
6375
6551
|
}
|
|
6376
6552
|
}
|
|
6377
6553
|
var pkg = readPackageMeta({
|
|
6378
|
-
bakedVersion: true ? "1.4.
|
|
6554
|
+
bakedVersion: true ? "1.4.7" : void 0
|
|
6379
6555
|
});
|
|
6380
6556
|
|
|
6381
6557
|
// src/hooks/runtime/session-start.ts
|
|
@@ -6491,7 +6667,7 @@ async function sessionStartHook(deps = {}) {
|
|
|
6491
6667
|
}
|
|
6492
6668
|
|
|
6493
6669
|
// src/hooks/runtime/session-review.ts
|
|
6494
|
-
import { existsSync as
|
|
6670
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
|
|
6495
6671
|
var COUNTED_TOOLS = /* @__PURE__ */ new Set([
|
|
6496
6672
|
"Edit",
|
|
6497
6673
|
"Write",
|
|
@@ -6575,9 +6751,9 @@ function loadTranscript(stdin, readFile) {
|
|
|
6575
6751
|
return out;
|
|
6576
6752
|
}
|
|
6577
6753
|
function defaultReadTranscript(path) {
|
|
6578
|
-
if (!
|
|
6754
|
+
if (!existsSync11(path)) return null;
|
|
6579
6755
|
try {
|
|
6580
|
-
return
|
|
6756
|
+
return readFileSync10(path, "utf8");
|
|
6581
6757
|
} catch {
|
|
6582
6758
|
return null;
|
|
6583
6759
|
}
|
|
@@ -6837,19 +7013,19 @@ async function preCompactHook(deps = {}) {
|
|
|
6837
7013
|
}
|
|
6838
7014
|
|
|
6839
7015
|
// src/hooks/runtime/user-prompt-submit.ts
|
|
6840
|
-
import { existsSync as
|
|
6841
|
-
import { homedir as
|
|
6842
|
-
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";
|
|
6843
7019
|
var SESSIONS_CAP = 100;
|
|
6844
7020
|
function defaultSessionsFile() {
|
|
6845
|
-
const dir = process.env.CYBEDEFEND_BASELINE_DIR ??
|
|
6846
|
-
return
|
|
7021
|
+
const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join9(homedir9(), ".cybedefend");
|
|
7022
|
+
return join9(dir, "user-prompt-seen-sessions.json");
|
|
6847
7023
|
}
|
|
6848
7024
|
function defaultSessionTracker(file = defaultSessionsFile()) {
|
|
6849
7025
|
function load() {
|
|
6850
|
-
if (!
|
|
7026
|
+
if (!existsSync12(file)) return [];
|
|
6851
7027
|
try {
|
|
6852
|
-
const raw =
|
|
7028
|
+
const raw = readFileSync11(file, "utf8");
|
|
6853
7029
|
const data = JSON.parse(raw);
|
|
6854
7030
|
if (!Array.isArray(data.sessions)) return [];
|
|
6855
7031
|
return data.sessions.filter((s) => typeof s === "string");
|
|
@@ -6866,8 +7042,8 @@ function defaultSessionTracker(file = defaultSessionsFile()) {
|
|
|
6866
7042
|
if (existing.includes(sessionId)) return;
|
|
6867
7043
|
const next = [...existing, sessionId].slice(-SESSIONS_CAP);
|
|
6868
7044
|
try {
|
|
6869
|
-
|
|
6870
|
-
|
|
7045
|
+
mkdirSync5(join9(file, ".."), { recursive: true });
|
|
7046
|
+
writeFileSync6(file, JSON.stringify({ sessions: next }), "utf8");
|
|
6871
7047
|
} catch {
|
|
6872
7048
|
}
|
|
6873
7049
|
}
|
|
@@ -7938,10 +8114,10 @@ function nearMiss(req, rule) {
|
|
|
7938
8114
|
}
|
|
7939
8115
|
|
|
7940
8116
|
// src/hooks/runtime/guard-rules-cache.ts
|
|
7941
|
-
import { readFileSync as
|
|
7942
|
-
import { dirname as
|
|
7943
|
-
import { homedir as
|
|
7944
|
-
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";
|
|
7945
8121
|
|
|
7946
8122
|
// node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
7947
8123
|
var external_exports = {};
|
|
@@ -12053,8 +12229,8 @@ function parseRuleFields(raw) {
|
|
|
12053
12229
|
}
|
|
12054
12230
|
|
|
12055
12231
|
// src/hooks/runtime/guard-rules-cache.ts
|
|
12056
|
-
var DEFAULT_CACHE_FILE =
|
|
12057
|
-
|
|
12232
|
+
var DEFAULT_CACHE_FILE = join10(
|
|
12233
|
+
homedir10(),
|
|
12058
12234
|
".cybedefend",
|
|
12059
12235
|
"guards-cache.json"
|
|
12060
12236
|
);
|
|
@@ -12134,14 +12310,26 @@ function makeGuardRulesCache(opts) {
|
|
|
12134
12310
|
const entry = readCache(ctx.projectId);
|
|
12135
12311
|
const ageSec = entry ? (Date.now() - entry.fetchedAt) / 1e3 : Infinity;
|
|
12136
12312
|
if (entry && ageSec < staleSec) {
|
|
12313
|
+
if (entry.blocked) return { kind: "blocked", block: entry.blocked };
|
|
12137
12314
|
return { kind: "rules", rules: entry.rules, llmJudge: entry.llmJudge };
|
|
12138
12315
|
}
|
|
12139
12316
|
try {
|
|
12140
12317
|
const result = await fetchFn({
|
|
12141
12318
|
...ctx,
|
|
12142
|
-
etag: entry?.etag ?? null
|
|
12319
|
+
etag: entry?.blocked ? null : entry?.etag ?? null
|
|
12143
12320
|
});
|
|
12144
|
-
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) {
|
|
12145
12333
|
writeCache({
|
|
12146
12334
|
...entry,
|
|
12147
12335
|
fetchedAt: Date.now()
|
|
@@ -12163,7 +12351,7 @@ function makeGuardRulesCache(opts) {
|
|
|
12163
12351
|
return { kind: "rules", rules: fresh.rules, llmJudge: fresh.llmJudge };
|
|
12164
12352
|
} catch (e) {
|
|
12165
12353
|
const msg = e instanceof Error ? e.message : String(e);
|
|
12166
|
-
if (entry?.rules?.length) {
|
|
12354
|
+
if (entry?.rules?.length && !entry.blocked) {
|
|
12167
12355
|
const ageSec2 = Math.round((Date.now() - entry.fetchedAt) / 1e3);
|
|
12168
12356
|
process.stderr.write(
|
|
12169
12357
|
`[VibeDefend] Action Guards: rule refresh failed (${msg}). Using cached rules (age=${ageSec2}s).
|
|
@@ -12178,9 +12366,9 @@ function makeGuardRulesCache(opts) {
|
|
|
12178
12366
|
}
|
|
12179
12367
|
}
|
|
12180
12368
|
function readCache(projectId) {
|
|
12181
|
-
if (!
|
|
12369
|
+
if (!existsSync13(cacheFile)) return null;
|
|
12182
12370
|
try {
|
|
12183
|
-
const raw =
|
|
12371
|
+
const raw = readFileSync12(cacheFile, "utf8");
|
|
12184
12372
|
const data = JSON.parse(raw);
|
|
12185
12373
|
if (data.projectId !== projectId) return null;
|
|
12186
12374
|
return data;
|
|
@@ -12191,16 +12379,16 @@ function makeGuardRulesCache(opts) {
|
|
|
12191
12379
|
function writeCache(entry) {
|
|
12192
12380
|
try {
|
|
12193
12381
|
ensureDirFor2(cacheFile);
|
|
12194
|
-
|
|
12382
|
+
writeFileSync7(cacheFile, JSON.stringify(entry), "utf8");
|
|
12195
12383
|
} catch {
|
|
12196
12384
|
}
|
|
12197
12385
|
}
|
|
12198
12386
|
return { refreshIfStale };
|
|
12199
12387
|
}
|
|
12200
12388
|
function ensureDirFor2(filePath) {
|
|
12201
|
-
const dir =
|
|
12202
|
-
if (!
|
|
12203
|
-
|
|
12389
|
+
const dir = dirname6(filePath);
|
|
12390
|
+
if (!existsSync13(dir)) {
|
|
12391
|
+
mkdirSync6(dir, { recursive: true });
|
|
12204
12392
|
}
|
|
12205
12393
|
}
|
|
12206
12394
|
var DEFAULT_TIMEOUT_MS2 = 8e3;
|
|
@@ -12223,6 +12411,10 @@ async function fetchEffectiveRulesFromGateway(ctx, opts = {}) {
|
|
|
12223
12411
|
return { rules: [], etag: ctx.etag ?? null, notModified: true };
|
|
12224
12412
|
}
|
|
12225
12413
|
if (!res.ok) {
|
|
12414
|
+
const blocked = await readVibedefendBlock(res);
|
|
12415
|
+
if (blocked) {
|
|
12416
|
+
return { rules: [], etag: null, notModified: false, blocked };
|
|
12417
|
+
}
|
|
12226
12418
|
throw new Error(`HTTP ${res.status} from effective-rules endpoint`);
|
|
12227
12419
|
}
|
|
12228
12420
|
const body = await res.json();
|
|
@@ -12243,6 +12435,9 @@ function parseLlmJudgeHint(raw) {
|
|
|
12243
12435
|
}
|
|
12244
12436
|
|
|
12245
12437
|
// src/hooks/runtime/judge.ts
|
|
12438
|
+
function isJudgeBlocked(res) {
|
|
12439
|
+
return res !== null && "blocked" in res;
|
|
12440
|
+
}
|
|
12246
12441
|
var DEFAULT_TIMEOUT_MS3 = 1500;
|
|
12247
12442
|
async function callJudge(input, opts = {}) {
|
|
12248
12443
|
const f = opts.fetchImpl ?? globalThis.fetch;
|
|
@@ -12266,7 +12461,11 @@ async function callJudge(input, opts = {}) {
|
|
|
12266
12461
|
}),
|
|
12267
12462
|
signal: AbortSignal.timeout(timeoutMs)
|
|
12268
12463
|
});
|
|
12269
|
-
if (!res.ok)
|
|
12464
|
+
if (!res.ok) {
|
|
12465
|
+
const blocked = await readVibedefendBlock(res);
|
|
12466
|
+
return blocked ? { blocked } : null;
|
|
12467
|
+
}
|
|
12468
|
+
const usageWarning = readUsageWarning(res);
|
|
12270
12469
|
const body = await res.json();
|
|
12271
12470
|
const data = body.data ?? body;
|
|
12272
12471
|
const decision = data["decision"];
|
|
@@ -12276,7 +12475,8 @@ async function callJudge(input, opts = {}) {
|
|
|
12276
12475
|
reason: typeof data["reason"] === "string" ? data["reason"] : "",
|
|
12277
12476
|
model: typeof data["model"] === "string" ? data["model"] : "",
|
|
12278
12477
|
quotaRemaining: typeof data["quota_remaining"] === "number" ? data["quota_remaining"] : -1,
|
|
12279
|
-
quotaExceeded: Boolean(data["quota_exceeded"])
|
|
12478
|
+
quotaExceeded: Boolean(data["quota_exceeded"]),
|
|
12479
|
+
...usageWarning ? { usageWarning } : {}
|
|
12280
12480
|
};
|
|
12281
12481
|
} catch {
|
|
12282
12482
|
return null;
|
|
@@ -12321,11 +12521,11 @@ function detectPromptInjection(text) {
|
|
|
12321
12521
|
}
|
|
12322
12522
|
|
|
12323
12523
|
// src/hooks/runtime/judge-cache.ts
|
|
12324
|
-
import { readFileSync as
|
|
12325
|
-
import { dirname as
|
|
12326
|
-
import { homedir as
|
|
12327
|
-
var DEFAULT_JUDGE_CACHE_FILE =
|
|
12328
|
-
|
|
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(),
|
|
12329
12529
|
".cybedefend",
|
|
12330
12530
|
"guards-judge-cache.json"
|
|
12331
12531
|
);
|
|
@@ -12335,9 +12535,9 @@ function makeJudgeCache(opts = {}) {
|
|
|
12335
12535
|
const ttlMs = opts.ttlMs ?? DEFAULT_JUDGE_TTL_MS;
|
|
12336
12536
|
const now = opts.now ?? (() => Date.now());
|
|
12337
12537
|
function readAll() {
|
|
12338
|
-
if (!
|
|
12538
|
+
if (!existsSync14(cacheFile)) return {};
|
|
12339
12539
|
try {
|
|
12340
|
-
return JSON.parse(
|
|
12540
|
+
return JSON.parse(readFileSync13(cacheFile, "utf8"));
|
|
12341
12541
|
} catch {
|
|
12342
12542
|
return {};
|
|
12343
12543
|
}
|
|
@@ -12357,36 +12557,36 @@ function makeJudgeCache(opts = {}) {
|
|
|
12357
12557
|
}
|
|
12358
12558
|
all[key] = { res, at: t };
|
|
12359
12559
|
ensureDirFor3(cacheFile);
|
|
12360
|
-
|
|
12560
|
+
writeFileSync8(cacheFile, JSON.stringify(all), "utf8");
|
|
12361
12561
|
} catch {
|
|
12362
12562
|
}
|
|
12363
12563
|
}
|
|
12364
12564
|
return { get, set };
|
|
12365
12565
|
}
|
|
12366
12566
|
function ensureDirFor3(filePath) {
|
|
12367
|
-
const dir =
|
|
12368
|
-
if (!
|
|
12369
|
-
|
|
12567
|
+
const dir = dirname7(filePath);
|
|
12568
|
+
if (!existsSync14(dir)) {
|
|
12569
|
+
mkdirSync7(dir, { recursive: true });
|
|
12370
12570
|
}
|
|
12371
12571
|
}
|
|
12372
12572
|
|
|
12373
12573
|
// src/hooks/runtime/guard-check.ts
|
|
12374
|
-
import { homedir as
|
|
12375
|
-
import { join as
|
|
12574
|
+
import { homedir as homedir13 } from "node:os";
|
|
12575
|
+
import { join as join13 } from "node:path";
|
|
12376
12576
|
|
|
12377
12577
|
// src/hooks/runtime/guard-violations-buffer.ts
|
|
12378
12578
|
import {
|
|
12379
12579
|
appendFileSync,
|
|
12380
|
-
existsSync as
|
|
12381
|
-
readFileSync as
|
|
12382
|
-
writeFileSync as
|
|
12383
|
-
mkdirSync as
|
|
12580
|
+
existsSync as existsSync15,
|
|
12581
|
+
readFileSync as readFileSync14,
|
|
12582
|
+
writeFileSync as writeFileSync9,
|
|
12583
|
+
mkdirSync as mkdirSync8
|
|
12384
12584
|
} from "node:fs";
|
|
12385
|
-
import { dirname as
|
|
12386
|
-
import { homedir as
|
|
12387
|
-
import { join as
|
|
12388
|
-
var DEFAULT_BUFFER_FILE =
|
|
12389
|
-
|
|
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(),
|
|
12390
12590
|
".cybedefend",
|
|
12391
12591
|
"guards-violations-buffer.jsonl"
|
|
12392
12592
|
);
|
|
@@ -12405,10 +12605,10 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
12405
12605
|
}
|
|
12406
12606
|
}
|
|
12407
12607
|
async function flushViolations2(ctx, postFn) {
|
|
12408
|
-
if (!
|
|
12608
|
+
if (!existsSync15(bufferFile)) return;
|
|
12409
12609
|
let content;
|
|
12410
12610
|
try {
|
|
12411
|
-
content =
|
|
12611
|
+
content = readFileSync14(bufferFile, "utf8").trim();
|
|
12412
12612
|
} catch {
|
|
12413
12613
|
return;
|
|
12414
12614
|
}
|
|
@@ -12427,7 +12627,7 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
12427
12627
|
}
|
|
12428
12628
|
if (records.length === 0) {
|
|
12429
12629
|
try {
|
|
12430
|
-
|
|
12630
|
+
writeFileSync9(bufferFile, "", "utf8");
|
|
12431
12631
|
} catch {
|
|
12432
12632
|
}
|
|
12433
12633
|
return;
|
|
@@ -12457,10 +12657,10 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
12457
12657
|
}
|
|
12458
12658
|
try {
|
|
12459
12659
|
if (failed.length === 0) {
|
|
12460
|
-
|
|
12660
|
+
writeFileSync9(bufferFile, "", "utf8");
|
|
12461
12661
|
} else {
|
|
12462
12662
|
const serialised = failed.map((r) => JSON.stringify(r)).join("\n") + "\n";
|
|
12463
|
-
|
|
12663
|
+
writeFileSync9(bufferFile, serialised, "utf8");
|
|
12464
12664
|
}
|
|
12465
12665
|
} catch {
|
|
12466
12666
|
}
|
|
@@ -12468,9 +12668,9 @@ function makeViolationsBuffer(opts = {}) {
|
|
|
12468
12668
|
return { bufferViolation: bufferViolation2, flushViolations: flushViolations2 };
|
|
12469
12669
|
}
|
|
12470
12670
|
function ensureDirFor4(filePath) {
|
|
12471
|
-
const dir =
|
|
12472
|
-
if (!
|
|
12473
|
-
|
|
12671
|
+
const dir = dirname8(filePath);
|
|
12672
|
+
if (!existsSync15(dir)) {
|
|
12673
|
+
mkdirSync8(dir, { recursive: true });
|
|
12474
12674
|
}
|
|
12475
12675
|
}
|
|
12476
12676
|
var _defaultBuffer = makeViolationsBuffer();
|
|
@@ -12496,11 +12696,11 @@ function appendDebugLog(entry) {
|
|
|
12496
12696
|
try {
|
|
12497
12697
|
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
|
|
12498
12698
|
if (!homeDir) return;
|
|
12499
|
-
const { mkdirSync:
|
|
12500
|
-
const { join:
|
|
12501
|
-
const dir =
|
|
12502
|
-
|
|
12503
|
-
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");
|
|
12504
12704
|
} catch {
|
|
12505
12705
|
}
|
|
12506
12706
|
}
|
|
@@ -12671,6 +12871,28 @@ function stringifyTarget(t) {
|
|
|
12671
12871
|
return `git ${t.subcommand}`;
|
|
12672
12872
|
}
|
|
12673
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
|
+
}
|
|
12674
12896
|
async function runJudgeStep(args) {
|
|
12675
12897
|
const {
|
|
12676
12898
|
judge,
|
|
@@ -12699,30 +12921,30 @@ async function runJudgeStep(args) {
|
|
|
12699
12921
|
const actionHash = hashTarget(targetStr);
|
|
12700
12922
|
const ruleIds = llmRules.map((r) => r.id).sort().join(",");
|
|
12701
12923
|
const cacheKey = `${actionHash}|${ruleIds}|confirm`;
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12707
|
-
|
|
12708
|
-
|
|
12709
|
-
|
|
12710
|
-
|
|
12711
|
-
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
}
|
|
12724
|
-
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" };
|
|
12725
12946
|
}
|
|
12947
|
+
const res = consultation.res;
|
|
12726
12948
|
if (!res) {
|
|
12727
12949
|
return {
|
|
12728
12950
|
engine: "llm",
|
|
@@ -12785,27 +13007,25 @@ async function runCatchJudgeStep(args) {
|
|
|
12785
13007
|
const actionHash = hashTarget(targetStr);
|
|
12786
13008
|
const ruleIds = near.map((r) => r.id).sort();
|
|
12787
13009
|
const cacheKey = `${actionHash}|${ruleIds.join(",")}|catch`;
|
|
12788
|
-
|
|
12789
|
-
|
|
12790
|
-
|
|
12791
|
-
|
|
12792
|
-
|
|
12793
|
-
|
|
12794
|
-
|
|
12795
|
-
|
|
12796
|
-
|
|
12797
|
-
|
|
12798
|
-
|
|
12799
|
-
|
|
12800
|
-
|
|
12801
|
-
|
|
12802
|
-
|
|
12803
|
-
|
|
12804
|
-
|
|
12805
|
-
|
|
12806
|
-
|
|
12807
|
-
if (res) judge.cacheSet(cacheKey, res);
|
|
12808
|
-
}
|
|
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;
|
|
12809
13029
|
if (!res) return void 0;
|
|
12810
13030
|
if (res.quotaExceeded) {
|
|
12811
13031
|
judge.warnOnce(
|
|
@@ -12851,6 +13071,9 @@ function emitUnverifiedBanner(reason) {
|
|
|
12851
13071
|
}
|
|
12852
13072
|
}
|
|
12853
13073
|
}
|
|
13074
|
+
function sessionIdOf2(envelope) {
|
|
13075
|
+
return typeof envelope.session_id === "string" ? envelope.session_id : typeof envelope.sessionId === "string" ? envelope.sessionId : void 0;
|
|
13076
|
+
}
|
|
12854
13077
|
async function runPreToolUseGuardWith(opts) {
|
|
12855
13078
|
const {
|
|
12856
13079
|
stdinEnvelope,
|
|
@@ -12866,11 +13089,19 @@ async function runPreToolUseGuardWith(opts) {
|
|
|
12866
13089
|
);
|
|
12867
13090
|
return 0;
|
|
12868
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
|
+
}
|
|
12869
13100
|
const rules = rulesOutcome.rules;
|
|
12870
13101
|
const caller = sniff(stdinEnvelope);
|
|
12871
13102
|
const partial = mapEnvelopeToGuardRequest(stdinEnvelope, caller.client);
|
|
12872
13103
|
if (partial === null) return 0;
|
|
12873
|
-
const sessionId =
|
|
13104
|
+
const sessionId = sessionIdOf2(stdinEnvelope);
|
|
12874
13105
|
const guardRequest = {
|
|
12875
13106
|
...partial,
|
|
12876
13107
|
projectId: projectContext.projectId,
|
|
@@ -13014,11 +13245,21 @@ function emitAgentMessage(verdict, message) {
|
|
|
13014
13245
|
}
|
|
13015
13246
|
function makeProductionJudgeDeps() {
|
|
13016
13247
|
const cache = makeJudgeCache();
|
|
13017
|
-
const warnDir = process.env.CYBEDEFEND_BASELINE_DIR ??
|
|
13248
|
+
const warnDir = process.env.CYBEDEFEND_BASELINE_DIR ?? join13(homedir13(), ".cybedefend");
|
|
13018
13249
|
const warnTracker = defaultSessionTracker(
|
|
13019
|
-
|
|
13250
|
+
join13(warnDir, "guards-judge-warned-sessions.json")
|
|
13251
|
+
);
|
|
13252
|
+
const blockedTracker = defaultSessionTracker(
|
|
13253
|
+
join13(warnDir, "guards-judge-blocked-sessions.json")
|
|
13020
13254
|
);
|
|
13021
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
|
+
},
|
|
13022
13263
|
callJudge,
|
|
13023
13264
|
cacheGet: (k) => cache.get(k),
|
|
13024
13265
|
cacheSet: (k, r) => cache.set(k, r),
|
|
@@ -13076,7 +13317,10 @@ async function runPreToolUseGuard() {
|
|
|
13076
13317
|
judge: makeProductionJudgeDeps(),
|
|
13077
13318
|
// v1.1 catch — OFF by default; opt in with CYBEDEFEND_JUDGE_CATCH=1 once
|
|
13078
13319
|
// the call-rate gate is validated in the field.
|
|
13079
|
-
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 })
|
|
13080
13324
|
});
|
|
13081
13325
|
try {
|
|
13082
13326
|
await buffer.flushViolations(
|