@cybedefend/vibedefend 1.4.1 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -1
- package/dist/clients/claude-code.js +4 -1
- package/dist/clients/claude-code.js.map +1 -1
- package/dist/clients/codex.js +4 -2
- package/dist/clients/codex.js.map +1 -1
- package/dist/custom-regions.js +68 -0
- package/dist/custom-regions.js.map +1 -1
- package/dist/guards-evaluator/preprocess.js +114 -3
- package/dist/guards-evaluator/preprocess.js.map +1 -1
- package/dist/hook-runner.js +5446 -591
- package/dist/hooks/runtime/emit.js +25 -12
- package/dist/hooks/runtime/emit.js.map +1 -1
- package/dist/hooks/runtime/fetch-rules.js +38 -12
- package/dist/hooks/runtime/fetch-rules.js.map +1 -1
- package/dist/hooks/runtime/guard-rules-cache.js +109 -28
- package/dist/hooks/runtime/guard-rules-cache.js.map +1 -1
- package/dist/hooks/runtime/resolve.js +20 -2
- package/dist/hooks/runtime/resolve.js.map +1 -1
- package/dist/hooks/runtime/rule-schema.js +113 -0
- package/dist/hooks/runtime/rule-schema.js.map +1 -0
- package/dist/hooks/runtime/self-update-check.js +36 -2
- package/dist/hooks/runtime/self-update-check.js.map +1 -1
- package/dist/hooks/runtime/session-start.js +5 -1
- package/dist/hooks/runtime/session-start.js.map +1 -1
- package/dist/hooks/runtime/shell-writes.js +311 -0
- package/dist/hooks/runtime/shell-writes.js.map +1 -0
- package/dist/hooks/runtime/sniff.js +84 -5
- package/dist/hooks/runtime/sniff.js.map +1 -1
- package/dist/hooks/runtime/trusted-api-base.js +194 -0
- package/dist/hooks/runtime/trusted-api-base.js.map +1 -0
- package/dist/link.js +29 -3
- package/dist/link.js.map +1 -1
- package/package.json +16 -9
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which clients need the `hookSpecificOutput.additionalContext` envelope for
|
|
3
|
+
* a given event. Everything not listed here keeps its plain-markdown (or
|
|
4
|
+
* Cursor's own) shape, which is the contract that already works for it.
|
|
5
|
+
*
|
|
6
|
+
* SessionStart deliberately does NOT list claude-code: its plain stdout is
|
|
7
|
+
* delivered by contract today and works — leave it alone (BACK-173 §2).
|
|
8
|
+
*/
|
|
9
|
+
const ENVELOPE_CLIENTS = {
|
|
10
|
+
SessionStart: new Set(['codex']),
|
|
11
|
+
PreToolUse: new Set(['claude-code', 'codex']),
|
|
12
|
+
};
|
|
1
13
|
export function emit(body, client) {
|
|
2
14
|
if (!body)
|
|
3
15
|
return;
|
|
@@ -15,29 +27,30 @@ export function emit(body, client) {
|
|
|
15
27
|
process.stdout.write('\n');
|
|
16
28
|
}
|
|
17
29
|
/**
|
|
18
|
-
*
|
|
30
|
+
* Model-context emission for a given hook event.
|
|
19
31
|
*
|
|
20
|
-
* For
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
32
|
+
* For the clients listed in `ENVELOPE_CLIENTS[hookEventName]`: writes ONE
|
|
33
|
+
* JSON object `{ hookSpecificOutput: { hookEventName, additionalContext } }`
|
|
34
|
+
* so the body lands in the model's context. Callers MUST call this once per
|
|
35
|
+
* hook run — two JSON objects concatenated on stdout are not valid JSON and
|
|
36
|
+
* the agent drops both (see fetch-rules, which accumulates the business and
|
|
37
|
+
* security blocks before emitting).
|
|
24
38
|
*
|
|
25
39
|
* For every other client: delegates to `emit`, which preserves Cursor's
|
|
26
|
-
* permission-allow envelope and
|
|
27
|
-
*
|
|
28
|
-
* SessionStart equivalents.
|
|
40
|
+
* permission-allow envelope and the plain-markdown shape that already works
|
|
41
|
+
* for the remaining client/event pairs.
|
|
29
42
|
*/
|
|
30
|
-
export function emitContext(body, client) {
|
|
43
|
+
export function emitContext(body, client, hookEventName) {
|
|
31
44
|
if (!body)
|
|
32
45
|
return;
|
|
33
|
-
if (client
|
|
46
|
+
if (ENVELOPE_CLIENTS[hookEventName].has(client)) {
|
|
34
47
|
const payload = JSON.stringify({
|
|
35
48
|
hookSpecificOutput: {
|
|
36
|
-
hookEventName
|
|
49
|
+
hookEventName,
|
|
37
50
|
additionalContext: body,
|
|
38
51
|
},
|
|
39
52
|
});
|
|
40
|
-
process.stdout.write(payload);
|
|
53
|
+
process.stdout.write(payload + '\n');
|
|
41
54
|
return;
|
|
42
55
|
}
|
|
43
56
|
emit(body, client);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../../../src/hooks/runtime/emit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../../../src/hooks/runtime/emit.ts"],"names":[],"mappings":"AA0CA;;;;;;;GAOG;AACH,MAAM,gBAAgB,GAAoD;IACxE,YAAY,EAAE,IAAI,GAAG,CAAW,CAAC,OAAO,CAAC,CAAC;IAC1C,UAAU,EAAE,IAAI,GAAG,CAAW,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;CACxD,CAAC;AAEF,MAAM,UAAU,IAAI,CAAC,IAAY,EAAE,MAAgB;IACjD,IAAI,CAAC,IAAI;QAAE,OAAO;IAElB,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;QACxB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC7B,UAAU,EAAE,OAAO;YACnB,aAAa,EAAE,IAAI;SACpB,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9B,OAAO;IACT,CAAC;IAED,qCAAqC;IACrC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,MAAgB,EAChB,aAA+B;IAE/B,IAAI,CAAC,IAAI;QAAE,OAAO;IAElB,IAAI,gBAAgB,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;YAC7B,kBAAkB,EAAE;gBAClB,aAAa;gBACb,iBAAiB,EAAE,IAAI;aACxB;SACF,CAAC,CAAC;QACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;QACrC,OAAO;IACT,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,QAAQ,CACtB,IAAY,EACZ,MAAgB,EAChB,MAAe;IAEf,IAAI,CAAC,IAAI;QAAE,OAAO;IAElB,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;QAC7B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,IAAI,CAAC,SAAS,CAAC;YACb,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI;SAChD,CAAC,CACH,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACrB,CAAC"}
|
|
@@ -1,22 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* PreToolUse hook — fetch business rules before
|
|
2
|
+
* PreToolUse hook — fetch business rules before a file is written.
|
|
3
3
|
*
|
|
4
4
|
* Triggered by every agent on every tool call (Cursor and Claude Code
|
|
5
|
-
* honour matchers
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* honour matchers; VS Code ignores matchers, so we self-filter; Windsurf
|
|
6
|
+
* has its own pre_write_code event; Codex uses apply_patch). We early-exit
|
|
7
|
+
* unless the tool name is in our interested set.
|
|
8
|
+
*
|
|
9
|
+
* `Bash` is in that set (BACK-173 follow-up): a shell command that writes
|
|
10
|
+
* files — `cat > f <<EOF`, `sed -i`, `tee`, `cp`… — fetches the rules for
|
|
11
|
+
* every file it targets, exactly like the edit tools. Without it an agent
|
|
12
|
+
* writing through the shell bypassed rules injection entirely, which is the
|
|
13
|
+
* common path in practice. A read-only shell command has no targets and
|
|
14
|
+
* exits silently (see `sniff` + `shell-writes`).
|
|
9
15
|
*/
|
|
10
16
|
import { sniff } from './sniff.js';
|
|
11
17
|
import { resolveProjectIdWithApi, resolveToken, } from './resolve.js';
|
|
12
18
|
import { fetchBusinessRules, fetchSecurityRules } from './api.js';
|
|
13
|
-
import {
|
|
19
|
+
import { emitContext } from './emit.js';
|
|
14
20
|
import { loadRuntimeConfig, readStdinJson } from './config.js';
|
|
21
|
+
/**
|
|
22
|
+
* Production emitter: the rules body must reach the MODEL, and on Claude
|
|
23
|
+
* Code / Codex a PreToolUse hook's plain stdout never does (BACK-173) — it
|
|
24
|
+
* has to travel in a `hookSpecificOutput.additionalContext` envelope whose
|
|
25
|
+
* `hookEventName` names THIS hook.
|
|
26
|
+
*/
|
|
27
|
+
function emitPreToolUseContext(body, client) {
|
|
28
|
+
emitContext(body, client, 'PreToolUse');
|
|
29
|
+
}
|
|
15
30
|
const INTERESTED_TOOLS = new Set([
|
|
16
31
|
'Edit',
|
|
17
32
|
'Write',
|
|
18
33
|
'MultiEdit',
|
|
19
34
|
'apply_patch',
|
|
35
|
+
'Bash',
|
|
20
36
|
]);
|
|
21
37
|
export async function fetchRulesHook(deps = {}) {
|
|
22
38
|
const readStdin = deps.readStdin ?? readStdinJson;
|
|
@@ -25,7 +41,7 @@ export async function fetchRulesHook(deps = {}) {
|
|
|
25
41
|
const resolveTok = deps.resolveTokenFn ?? resolveToken;
|
|
26
42
|
const fetchRules = deps.fetchRulesFn ?? fetchBusinessRules;
|
|
27
43
|
const fetchSecRules = deps.fetchSecurityRulesFn ?? fetchSecurityRules;
|
|
28
|
-
const emitFn = deps.emitFn ??
|
|
44
|
+
const emitFn = deps.emitFn ?? emitPreToolUseContext;
|
|
29
45
|
const stdin = await readStdin();
|
|
30
46
|
const event = sniff(stdin);
|
|
31
47
|
// Self-filter: ignore tool calls we don't care about. Crucial for
|
|
@@ -48,11 +64,15 @@ export async function fetchRulesHook(deps = {}) {
|
|
|
48
64
|
if (!token)
|
|
49
65
|
return;
|
|
50
66
|
const intent = buildIntent(event);
|
|
67
|
+
// Edit tools write one file; a shell command may write several — fetch
|
|
68
|
+
// for all of them and label the callout with the whole list.
|
|
69
|
+
const files = event.filePaths.length > 0 ? event.filePaths : [event.filePath];
|
|
70
|
+
const fileLabel = files.join(', ');
|
|
51
71
|
const fetchOpts = {
|
|
52
72
|
apiBase,
|
|
53
73
|
projectId,
|
|
54
74
|
token,
|
|
55
|
-
files
|
|
75
|
+
files,
|
|
56
76
|
intent,
|
|
57
77
|
topK: 5,
|
|
58
78
|
};
|
|
@@ -62,17 +82,23 @@ export async function fetchRulesHook(deps = {}) {
|
|
|
62
82
|
fetchRules(fetchOpts),
|
|
63
83
|
fetchSecRules(fetchOpts),
|
|
64
84
|
]);
|
|
85
|
+
// Accumulate, then emit ONCE. On Claude Code / Codex the emitter writes a
|
|
86
|
+
// JSON envelope; two envelopes concatenated on stdout are not JSON and the
|
|
87
|
+
// agent drops both — so both blocks ride in a single body (BACK-173 §1).
|
|
88
|
+
const blocks = [];
|
|
65
89
|
if (businessResult) {
|
|
66
|
-
const body = composeBody(businessResult.markdown, businessResult.total,
|
|
90
|
+
const body = composeBody(businessResult.markdown, businessResult.total, fileLabel);
|
|
67
91
|
if (body)
|
|
68
|
-
|
|
92
|
+
blocks.push(body);
|
|
69
93
|
}
|
|
70
94
|
if (securityResult &&
|
|
71
95
|
securityResult.total > 0 &&
|
|
72
96
|
securityResult.markdown &&
|
|
73
97
|
securityResult.markdown.length > 0) {
|
|
74
|
-
|
|
98
|
+
blocks.push(composeSecurityBody(securityResult.markdown, securityResult.total, fileLabel));
|
|
75
99
|
}
|
|
100
|
+
if (blocks.length > 0)
|
|
101
|
+
emitFn(blocks.join('\n'), event.client);
|
|
76
102
|
}
|
|
77
103
|
/** Build the retrieval `intent` string from the event content. */
|
|
78
104
|
function buildIntent(event) {
|
|
@@ -104,7 +130,7 @@ function visibleCalloutTemplate(kind, total, filePath) {
|
|
|
104
130
|
return [
|
|
105
131
|
'---',
|
|
106
132
|
'',
|
|
107
|
-
`**🛡️ REQUIRED USER-VISIBLE MARKER** — you MUST include this exact block as the FIRST paragraph of your next assistant reply, BEFORE the
|
|
133
|
+
`**🛡️ REQUIRED USER-VISIBLE MARKER** — you MUST include this exact block as the FIRST paragraph of your next assistant reply, BEFORE the tool call that writes the file. Do not paraphrase, do not skip — without it, the user cannot see CybeDefend is influencing the change.`,
|
|
108
134
|
'',
|
|
109
135
|
`> 🛡️ **CybeDefend ${kind} Rules** — ${total} rule${total === 1 ? '' : 's'} for \`${filePath}\``,
|
|
110
136
|
`> - **<rule-title>** [<severity> · <category>] — applies because <one-sentence reason>`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-rules.js","sourceRoot":"","sources":["../../../src/hooks/runtime/fetch-rules.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"fetch-rules.js","sourceRoot":"","sources":["../../../src/hooks/runtime/fetch-rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -29,6 +29,8 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
|
29
29
|
import { dirname } from 'node:path';
|
|
30
30
|
import { homedir } from 'node:os';
|
|
31
31
|
import { join } from 'node:path';
|
|
32
|
+
import { validateRule } from '../../guards-evaluator/index.js';
|
|
33
|
+
import { parseRuleFields } from './rule-schema.js';
|
|
32
34
|
export const DEFAULT_CACHE_FILE = join(homedir(), '.cybedefend', 'guards-cache.json');
|
|
33
35
|
export const DEFAULT_STALE_SEC = 60;
|
|
34
36
|
// ─── Category-id → evaluator category string ─────────────────────────────────
|
|
@@ -39,6 +41,27 @@ const CATEGORY_ID_MAP = {
|
|
|
39
41
|
'gc-git': 'git',
|
|
40
42
|
'gc-process': 'process',
|
|
41
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Narrow an unknown value to `string[]`, or `undefined`.
|
|
46
|
+
*
|
|
47
|
+
* The matcher fields are fed to `matchAnyGlob`, which iterates them. A
|
|
48
|
+
* non-array value (a bare string is the classic one) used to survive the
|
|
49
|
+
* transform untouched, throw inside the evaluator, and trip `evaluate()`'s
|
|
50
|
+
* fail-CLOSED catch — so ONE malformed rule denied every subsequent tool call
|
|
51
|
+
* for that project. Non-string entries are filtered out; an array left empty
|
|
52
|
+
* collapses to `undefined` so `validateRule` sees "matcher absent" rather than
|
|
53
|
+
* "matcher present but useless".
|
|
54
|
+
*/
|
|
55
|
+
function toStringArray(value) {
|
|
56
|
+
if (!Array.isArray(value))
|
|
57
|
+
return undefined;
|
|
58
|
+
const strings = value.filter((v) => typeof v === 'string');
|
|
59
|
+
return strings.length > 0 ? strings : undefined;
|
|
60
|
+
}
|
|
61
|
+
/** Narrow an unknown value to a non-empty `string`, or `undefined`. */
|
|
62
|
+
function toStringField(value) {
|
|
63
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
64
|
+
}
|
|
42
65
|
/**
|
|
43
66
|
* Transform a raw snake_case rule object returned by the gateway API
|
|
44
67
|
* into the camelCase `Rule` shape expected by the bundled guards-evaluator.
|
|
@@ -49,48 +72,103 @@ const CATEGORY_ID_MAP = {
|
|
|
49
72
|
* matcher field resolve to `undefined`, so nothing ever matches and the verdict
|
|
50
73
|
* is always `allow`.
|
|
51
74
|
*
|
|
75
|
+
* Every field consumed by the evaluator is TYPE-NARROWED here rather than
|
|
76
|
+
* blind-cast: rule content is authored by org members, so the payload is
|
|
77
|
+
* semi-trusted, not a constant. `validateRule` (below) then decides whether
|
|
78
|
+
* the resulting rule is coherent enough to enforce.
|
|
79
|
+
*
|
|
52
80
|
* Mirrors mcp-service's `snakeToCamelRule` in
|
|
53
81
|
* `mcp-service/src/modules/gateway-client/gateway-client.service.ts`.
|
|
54
82
|
*/
|
|
55
83
|
export function snakeToCamelRule(raw) {
|
|
56
|
-
const rawCategoryId = raw['category_id'];
|
|
84
|
+
const rawCategoryId = toStringField(raw['category_id']);
|
|
57
85
|
const category = rawCategoryId
|
|
58
86
|
? (CATEGORY_ID_MAP[rawCategoryId] ?? rawCategoryId)
|
|
59
87
|
: raw['category'];
|
|
88
|
+
// Every matcher/metadata field goes through the zod schema, which drops
|
|
89
|
+
// anything of the wrong type instead of casting it blindly. A non-array glob
|
|
90
|
+
// used to reach matchAnyGlob, throw, and trip evaluate()'s fail-closed catch
|
|
91
|
+
// — bricking every tool call for the project until the 60s cache expired.
|
|
92
|
+
const fields = parseRuleFields(raw);
|
|
93
|
+
// The identity fields stay permissive on purpose: the gateway legitimately
|
|
94
|
+
// sends values this CLI does not enumerate, and validateRule() is the
|
|
95
|
+
// authority on those semantics (it mirrors the server-side Python
|
|
96
|
+
// matcher_validation).
|
|
60
97
|
return {
|
|
61
98
|
id: raw['id'],
|
|
62
99
|
category: category,
|
|
63
100
|
...(rawCategoryId ? { categoryId: rawCategoryId } : {}),
|
|
64
|
-
builtinKey: (raw['builtin_key'] ?? raw['builtinKey']),
|
|
65
|
-
name: raw['name'],
|
|
66
|
-
description: raw['description'],
|
|
67
101
|
severity: raw['severity'],
|
|
68
102
|
action: raw['action'],
|
|
69
103
|
subcategory: raw['subcategory'],
|
|
70
|
-
|
|
71
|
-
version: raw['version'],
|
|
72
|
-
tags: raw['tags'],
|
|
73
|
-
isBuiltin: (raw['is_builtin'] ?? raw['isBuiltin']),
|
|
74
|
-
toolMatch: (raw['tool_match'] ?? raw['toolMatch']),
|
|
75
|
-
filePathGlobs: (raw['file_path_globs'] ?? raw['filePathGlobs']),
|
|
76
|
-
filePathExcludes: (raw['file_path_excludes'] ?? raw['filePathExcludes']),
|
|
77
|
-
commandRegex: (raw['command_regex'] ?? raw['commandRegex']),
|
|
78
|
-
commandArgGlobs: (raw['command_arg_globs'] ?? raw['commandArgGlobs']),
|
|
79
|
-
envNameGlobs: (raw['env_name_globs'] ?? raw['envNameGlobs']),
|
|
80
|
-
httpHostGlobs: (raw['http_host_globs'] ?? raw['httpHostGlobs']),
|
|
81
|
-
httpHostExcludes: (raw['http_host_excludes'] ?? raw['httpHostExcludes']),
|
|
82
|
-
httpMethod: (raw['http_method'] ?? raw['httpMethod']),
|
|
83
|
-
httpPathGlobs: (raw['http_path_globs'] ?? raw['httpPathGlobs']),
|
|
84
|
-
gitRemoteGlobs: (raw['git_remote_globs'] ?? raw['gitRemoteGlobs']),
|
|
85
|
-
gitBranchGlobs: (raw['git_branch_globs'] ?? raw['gitBranchGlobs']),
|
|
86
|
-
gitSubcommand: (raw['git_subcommand'] ?? raw['gitSubcommand']),
|
|
87
|
-
denyMessage: (raw['deny_message'] ?? raw['denyMessage']),
|
|
88
|
-
warnMessage: (raw['warn_message'] ?? raw['warnMessage']),
|
|
89
|
-
defaultAction: (raw['default_action'] ?? raw['defaultAction']),
|
|
90
|
-
llmJudgeEnabled: (raw['llm_judge_enabled'] ?? raw['llmJudgeEnabled']),
|
|
91
|
-
catchEligible: (raw['catch_eligible'] ?? raw['catchEligible']),
|
|
104
|
+
...fields,
|
|
92
105
|
};
|
|
93
106
|
}
|
|
107
|
+
/** How many dropped rule ids we name in the stderr warning. */
|
|
108
|
+
const MAX_REPORTED_DROPS = 5;
|
|
109
|
+
/**
|
|
110
|
+
* Transform the raw effective-rules payload and keep only the rules that pass
|
|
111
|
+
* `validateRule` — the SAME checks the gateway's Python `matcher_validation`
|
|
112
|
+
* applies at write time. The function existed, was exported… and was never
|
|
113
|
+
* called on this path, so a malformed rule went straight into `evaluate()`.
|
|
114
|
+
*
|
|
115
|
+
* Dropping is the right failure mode here. The alternative was not "enforce it
|
|
116
|
+
* anyway": a rule the evaluator cannot read either matches nothing (dead) or
|
|
117
|
+
* throws and turns `evaluate()`'s fail-closed catch into a project-wide outage
|
|
118
|
+
* that survives 60s of disk cache. Drops are reported on stderr so a schema
|
|
119
|
+
* drift between gateway and CLI is visible immediately instead of silently
|
|
120
|
+
* shrinking the ruleset.
|
|
121
|
+
*/
|
|
122
|
+
export function transformAndValidateRules(raw) {
|
|
123
|
+
if (!Array.isArray(raw))
|
|
124
|
+
return { rules: [], dropped: [] };
|
|
125
|
+
const rules = [];
|
|
126
|
+
const dropped = [];
|
|
127
|
+
raw.forEach((entry, index) => {
|
|
128
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry)) {
|
|
129
|
+
dropped.push({
|
|
130
|
+
id: `#${index}`,
|
|
131
|
+
errors: ['rule entry is not a JSON object'],
|
|
132
|
+
});
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
let rule;
|
|
136
|
+
try {
|
|
137
|
+
rule = snakeToCamelRule(entry);
|
|
138
|
+
}
|
|
139
|
+
catch (e) {
|
|
140
|
+
dropped.push({
|
|
141
|
+
id: `#${index}`,
|
|
142
|
+
errors: [e instanceof Error ? e.message : 'transform failed'],
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const verdict = validateRule(rule);
|
|
147
|
+
if (verdict.ok)
|
|
148
|
+
rules.push(rule);
|
|
149
|
+
else {
|
|
150
|
+
dropped.push({
|
|
151
|
+
id: typeof rule.id === 'string' && rule.id ? rule.id : `#${index}`,
|
|
152
|
+
errors: verdict.errors,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return { rules, dropped };
|
|
157
|
+
}
|
|
158
|
+
/** One-line stderr report for the rules we refused to enforce. */
|
|
159
|
+
function reportDroppedRules(dropped) {
|
|
160
|
+
if (dropped.length === 0)
|
|
161
|
+
return;
|
|
162
|
+
const named = dropped
|
|
163
|
+
.slice(0, MAX_REPORTED_DROPS)
|
|
164
|
+
.map((d) => `${d.id} (${d.errors[0] ?? 'invalid'})`)
|
|
165
|
+
.join(', ');
|
|
166
|
+
const more = dropped.length > MAX_REPORTED_DROPS
|
|
167
|
+
? `, +${dropped.length - MAX_REPORTED_DROPS} more`
|
|
168
|
+
: '';
|
|
169
|
+
process.stderr.write(`[VibeDefend] Action Guards: dropped ${dropped.length} invalid rule(s) ` +
|
|
170
|
+
`from the effective-rules payload: ${named}${more}.\n`);
|
|
171
|
+
}
|
|
94
172
|
/**
|
|
95
173
|
* Create an injectable guard rules cache (testable via custom cacheFile and fetchFn).
|
|
96
174
|
*/
|
|
@@ -119,8 +197,11 @@ export function makeGuardRulesCache(opts) {
|
|
|
119
197
|
});
|
|
120
198
|
return { kind: 'rules', rules: entry.rules, llmJudge: entry.llmJudge };
|
|
121
199
|
}
|
|
122
|
-
// 200 —
|
|
123
|
-
|
|
200
|
+
// 200 — transform snake_case → camelCase, then keep only the rules that
|
|
201
|
+
// pass the same validation the gateway applies at write time. An invalid
|
|
202
|
+
// rule is dropped (and reported) instead of reaching `evaluate()`.
|
|
203
|
+
const { rules: transformedRules, dropped } = transformAndValidateRules(result.rules);
|
|
204
|
+
reportDroppedRules(dropped);
|
|
124
205
|
const fresh = {
|
|
125
206
|
projectId: ctx.projectId,
|
|
126
207
|
rules: transformedRules,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guard-rules-cache.js","sourceRoot":"","sources":["../../../src/hooks/runtime/guard-rules-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"guard-rules-cache.js","sourceRoot":"","sources":["../../../src/hooks/runtime/guard-rules-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -21,7 +21,25 @@ import { getOriginRemote, parseRemoteUrl } from '../../git.js';
|
|
|
21
21
|
import { resolveProjectByRepository, } from '../../api/projects.js';
|
|
22
22
|
import { getCachedRepoLink, writeCachedRepoLink, } from './repo-link-cache.js';
|
|
23
23
|
import { loadRuntimeConfig } from './config.js';
|
|
24
|
-
|
|
24
|
+
import { sanitizeRepoApiBase, } from './trusted-api-base.js';
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the project id (and any API-base override) for the current repo.
|
|
27
|
+
*
|
|
28
|
+
* SECURITY. `.cybedefend/config.json` is committed inside the repository, so
|
|
29
|
+
* everything it says is attacker-controlled once you clone someone else's
|
|
30
|
+
* code. Its `apiBase` therefore goes through {@link sanitizeRepoApiBase}: it
|
|
31
|
+
* survives only when it points at an origin the USER already trusts (a
|
|
32
|
+
* built-in region, the region they installed against, or a user-global custom
|
|
33
|
+
* region). Anything else is dropped with a stderr warning and the caller falls
|
|
34
|
+
* back to the configured region — otherwise merely opening a hostile repo sent
|
|
35
|
+
* every `Authorization: Bearer <token>` hook call to the attacker's host and
|
|
36
|
+
* let that host serve a permissive Action Guard ruleset.
|
|
37
|
+
*
|
|
38
|
+
* This is the single chokepoint: `guard-check`, `fetch-rules`, `session-start`
|
|
39
|
+
* and `user-prompt-submit` all take their `apiBase` override from here (either
|
|
40
|
+
* directly or via {@link resolveProjectIdWithApi}).
|
|
41
|
+
*/
|
|
42
|
+
export function resolveProjectId(cwd = process.cwd(), deps = {}) {
|
|
25
43
|
const envProjectId = process.env.CYBEDEFEND_PROJECT_ID;
|
|
26
44
|
if (envProjectId && envProjectId.length > 0) {
|
|
27
45
|
return { projectId: envProjectId, apiBase: null };
|
|
@@ -33,7 +51,7 @@ export function resolveProjectId(cwd = process.cwd()) {
|
|
|
33
51
|
const cfg = JSON.parse(raw);
|
|
34
52
|
return {
|
|
35
53
|
projectId: cfg.projectId ?? null,
|
|
36
|
-
apiBase: cfg.apiBase
|
|
54
|
+
apiBase: sanitizeRepoApiBase(cfg.apiBase, deps.trust),
|
|
37
55
|
};
|
|
38
56
|
}
|
|
39
57
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/hooks/runtime/resolve.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EACL,0BAA0B,GAE3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,GAEpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../../src/hooks/runtime/resolve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Shape sanitisation for the Action Guard rule payload served by the gateway.
|
|
4
|
+
*
|
|
5
|
+
* The payload is semi-trusted: authored by org members through the dashboard,
|
|
6
|
+
* so neither attacker-controlled nor a constant. Before this schema every
|
|
7
|
+
* field was a blind `as` cast, so a wrong type flowed unchecked into the
|
|
8
|
+
* evaluator. The concrete failure that motivated it: a glob field that was not
|
|
9
|
+
* an array threw inside `matchAnyGlob`, tripping `evaluate()`'s fail-closed
|
|
10
|
+
* catch and bricking every tool call for the project until the 60s disk cache
|
|
11
|
+
* expired.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately LENIENT, not strict. Enum-like fields (`action`, `severity`,
|
|
14
|
+
* `category`, `subcategory`) are passed through as free strings here because
|
|
15
|
+
* the gateway legitimately sends values this CLI does not enumerate, and
|
|
16
|
+
* `validateRule()` (src/guards-evaluator/validation.ts) is the authority on
|
|
17
|
+
* those semantics — it mirrors the server-side Python matcher_validation. This
|
|
18
|
+
* schema's job is narrower and complementary: guarantee that every field has
|
|
19
|
+
* the TYPE the rest of the code assumes, dropping anything that does not
|
|
20
|
+
* instead of letting it reach the evaluator.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* A string array with non-string entries filtered out, or undefined when the
|
|
24
|
+
* input is absent or is not an array at all.
|
|
25
|
+
*
|
|
26
|
+
* Filtering rather than dropping the whole field is deliberate: a glob list is
|
|
27
|
+
* a matcher, so discarding it entirely would silently stop a `deny` rule from
|
|
28
|
+
* matching. Keeping the well-formed entries preserves the rule's intent.
|
|
29
|
+
*/
|
|
30
|
+
const globs = z
|
|
31
|
+
.preprocess((v) => (Array.isArray(v) ? v.filter((e) => typeof e === 'string') : v), z.array(z.string()).optional())
|
|
32
|
+
.catch(undefined);
|
|
33
|
+
/** A string, or undefined when the input is absent or of the wrong type. */
|
|
34
|
+
const str = z.string().optional().catch(undefined);
|
|
35
|
+
/** A boolean, or undefined when the input is absent or of the wrong type. */
|
|
36
|
+
const bool = z.boolean().optional().catch(undefined);
|
|
37
|
+
/** A number, or undefined when the input is absent or of the wrong type. */
|
|
38
|
+
const num = z.number().optional().catch(undefined);
|
|
39
|
+
/**
|
|
40
|
+
* Sanitises the matcher and metadata fields of a rule.
|
|
41
|
+
*
|
|
42
|
+
* Every member uses `.catch(undefined)`, so a malformed field is dropped rather
|
|
43
|
+
* than throwing — one bad field must never take the whole rule set down. The
|
|
44
|
+
* identity fields (`id`, `category`, `subcategory`, `action`, `severity`) are
|
|
45
|
+
* intentionally absent: they are assembled by `snakeToCamelRule` and judged by
|
|
46
|
+
* `validateRule`.
|
|
47
|
+
*/
|
|
48
|
+
export const RuleFieldsSchema = z.object({
|
|
49
|
+
builtinKey: str,
|
|
50
|
+
name: str,
|
|
51
|
+
description: str,
|
|
52
|
+
active: bool,
|
|
53
|
+
version: num,
|
|
54
|
+
tags: globs,
|
|
55
|
+
isBuiltin: bool,
|
|
56
|
+
toolMatch: globs,
|
|
57
|
+
filePathGlobs: globs,
|
|
58
|
+
filePathExcludes: globs,
|
|
59
|
+
commandRegex: str,
|
|
60
|
+
commandArgGlobs: globs,
|
|
61
|
+
envNameGlobs: globs,
|
|
62
|
+
httpHostGlobs: globs,
|
|
63
|
+
httpHostExcludes: globs,
|
|
64
|
+
httpMethod: globs,
|
|
65
|
+
httpPathGlobs: globs,
|
|
66
|
+
gitRemoteGlobs: globs,
|
|
67
|
+
gitBranchGlobs: globs,
|
|
68
|
+
gitSubcommand: globs,
|
|
69
|
+
denyMessage: str,
|
|
70
|
+
warnMessage: str,
|
|
71
|
+
defaultAction: z.enum(['deny', 'warn']).optional().catch(undefined),
|
|
72
|
+
llmJudgeEnabled: bool,
|
|
73
|
+
catchEligible: bool,
|
|
74
|
+
});
|
|
75
|
+
/** Reads a field the gateway may spell in either snake_case or camelCase. */
|
|
76
|
+
function dual(raw, snake, camel) {
|
|
77
|
+
return raw[snake] ?? raw[camel];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Runs {@link RuleFieldsSchema} over a raw gateway rule, accepting either
|
|
81
|
+
* spelling of every dual-spelled key. Never throws: malformed fields come back
|
|
82
|
+
* as `undefined`.
|
|
83
|
+
*/
|
|
84
|
+
export function parseRuleFields(raw) {
|
|
85
|
+
return RuleFieldsSchema.parse({
|
|
86
|
+
builtinKey: dual(raw, 'builtin_key', 'builtinKey'),
|
|
87
|
+
name: raw['name'],
|
|
88
|
+
description: raw['description'],
|
|
89
|
+
active: raw['active'],
|
|
90
|
+
version: raw['version'],
|
|
91
|
+
tags: raw['tags'],
|
|
92
|
+
isBuiltin: dual(raw, 'is_builtin', 'isBuiltin'),
|
|
93
|
+
toolMatch: dual(raw, 'tool_match', 'toolMatch'),
|
|
94
|
+
filePathGlobs: dual(raw, 'file_path_globs', 'filePathGlobs'),
|
|
95
|
+
filePathExcludes: dual(raw, 'file_path_excludes', 'filePathExcludes'),
|
|
96
|
+
commandRegex: dual(raw, 'command_regex', 'commandRegex'),
|
|
97
|
+
commandArgGlobs: dual(raw, 'command_arg_globs', 'commandArgGlobs'),
|
|
98
|
+
envNameGlobs: dual(raw, 'env_name_globs', 'envNameGlobs'),
|
|
99
|
+
httpHostGlobs: dual(raw, 'http_host_globs', 'httpHostGlobs'),
|
|
100
|
+
httpHostExcludes: dual(raw, 'http_host_excludes', 'httpHostExcludes'),
|
|
101
|
+
httpMethod: dual(raw, 'http_method', 'httpMethod'),
|
|
102
|
+
httpPathGlobs: dual(raw, 'http_path_globs', 'httpPathGlobs'),
|
|
103
|
+
gitRemoteGlobs: dual(raw, 'git_remote_globs', 'gitRemoteGlobs'),
|
|
104
|
+
gitBranchGlobs: dual(raw, 'git_branch_globs', 'gitBranchGlobs'),
|
|
105
|
+
gitSubcommand: dual(raw, 'git_subcommand', 'gitSubcommand'),
|
|
106
|
+
denyMessage: dual(raw, 'deny_message', 'denyMessage'),
|
|
107
|
+
warnMessage: dual(raw, 'warn_message', 'warnMessage'),
|
|
108
|
+
defaultAction: dual(raw, 'default_action', 'defaultAction'),
|
|
109
|
+
llmJudgeEnabled: dual(raw, 'llm_judge_enabled', 'llmJudgeEnabled'),
|
|
110
|
+
catchEligible: dual(raw, 'catch_eligible', 'catchEligible'),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=rule-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rule-schema.js","sourceRoot":"","sources":["../../../src/hooks/runtime/rule-schema.ts"],"names":[],"mappings":"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"}
|