@cybedefend/vibedefend 1.5.0 → 1.7.0
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 +71 -2
- package/dist/api/gateway-request.js +49 -0
- package/dist/api/gateway-request.js.map +1 -0
- package/dist/api/projects.js +67 -0
- package/dist/api/projects.js.map +1 -1
- package/dist/api/repository-link.js +157 -0
- package/dist/api/repository-link.js.map +1 -0
- package/dist/auth/auth-store.js +107 -25
- package/dist/auth/auth-store.js.map +1 -1
- package/dist/auth/auth.js +21 -0
- package/dist/auth/auth.js.map +1 -1
- package/dist/clients/cursor.js +5 -1
- package/dist/clients/cursor.js.map +1 -1
- package/dist/clients/vscode.js +38 -29
- package/dist/clients/vscode.js.map +1 -1
- package/dist/commands/mcp/allow.js +219 -0
- package/dist/commands/mcp/allow.js.map +1 -0
- package/dist/commands/mcp/connect.js +337 -0
- package/dist/commands/mcp/connect.js.map +1 -0
- package/dist/commands/mcp/index.js +170 -0
- package/dist/commands/mcp/index.js.map +1 -0
- package/dist/commands/mcp/proxy.js +320 -0
- package/dist/commands/mcp/proxy.js.map +1 -0
- package/dist/commands/mcp/scan.js +349 -0
- package/dist/commands/mcp/scan.js.map +1 -0
- package/dist/commands/mcp/sync.js +215 -0
- package/dist/commands/mcp/sync.js.map +1 -0
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/diagnostics.js +78 -12
- package/dist/diagnostics.js.map +1 -1
- package/dist/doctor.js +19 -0
- package/dist/doctor.js.map +1 -1
- package/dist/guards-evaluator/analysis-types.js +27 -0
- package/dist/guards-evaluator/analysis-types.js.map +1 -0
- package/dist/guards-evaluator/command-analysis.js +572 -0
- package/dist/guards-evaluator/command-analysis.js.map +1 -0
- package/dist/guards-evaluator/command-view.js +586 -0
- package/dist/guards-evaluator/command-view.js.map +1 -0
- package/dist/guards-evaluator/env.js +11 -0
- package/dist/guards-evaluator/env.js.map +1 -0
- package/dist/guards-evaluator/evaluate.js +91 -41
- package/dist/guards-evaluator/evaluate.js.map +1 -1
- package/dist/guards-evaluator/index.js +2 -1
- package/dist/guards-evaluator/index.js.map +1 -1
- package/dist/guards-evaluator/inline-code.js +259 -0
- package/dist/guards-evaluator/inline-code.js.map +1 -0
- package/dist/guards-evaluator/normalize.js +716 -0
- package/dist/guards-evaluator/normalize.js.map +1 -0
- package/dist/guards-evaluator/preprocess.js +13 -5
- package/dist/guards-evaluator/preprocess.js.map +1 -1
- package/dist/guards-evaluator/redact.js +19 -0
- package/dist/guards-evaluator/redact.js.map +1 -1
- package/dist/guards-evaluator/regex.js +25 -3
- package/dist/guards-evaluator/regex.js.map +1 -1
- package/dist/guards-evaluator/safe-commands.js +155 -0
- package/dist/guards-evaluator/safe-commands.js.map +1 -0
- package/dist/guards-evaluator/triage.js +315 -0
- package/dist/guards-evaluator/triage.js.map +1 -0
- package/dist/hook-runner.js +24679 -5079
- package/dist/hooks/install.js +38 -0
- package/dist/hooks/install.js.map +1 -1
- package/dist/hooks/runtime/config.js +38 -0
- package/dist/hooks/runtime/config.js.map +1 -1
- package/dist/hooks/runtime/emit.js +94 -61
- package/dist/hooks/runtime/emit.js.map +1 -1
- package/dist/hooks/runtime/fetch-rules.js +22 -7
- package/dist/hooks/runtime/fetch-rules.js.map +1 -1
- package/dist/hooks/runtime/gateway-http-error.js +34 -0
- package/dist/hooks/runtime/gateway-http-error.js.map +1 -0
- package/dist/hooks/runtime/guard-rules-cache.js +139 -17
- package/dist/hooks/runtime/guard-rules-cache.js.map +1 -1
- package/dist/hooks/runtime/guard-violations-buffer.js +321 -48
- package/dist/hooks/runtime/guard-violations-buffer.js.map +1 -1
- package/dist/hooks/runtime/judge-cache.js +6 -0
- package/dist/hooks/runtime/judge-cache.js.map +1 -1
- package/dist/hooks/runtime/judge-triage.js +193 -0
- package/dist/hooks/runtime/judge-triage.js.map +1 -0
- package/dist/hooks/runtime/judge.js +7 -1
- package/dist/hooks/runtime/judge.js.map +1 -1
- package/dist/hooks/runtime/pre-compact.js +12 -6
- package/dist/hooks/runtime/pre-compact.js.map +1 -1
- package/dist/hooks/runtime/resolve.js +90 -8
- package/dist/hooks/runtime/resolve.js.map +1 -1
- package/dist/hooks/runtime/retry-policy.js +57 -0
- package/dist/hooks/runtime/retry-policy.js.map +1 -0
- package/dist/hooks/runtime/review-sessions.js +74 -0
- package/dist/hooks/runtime/review-sessions.js.map +1 -0
- package/dist/hooks/runtime/script-resolve.js +695 -0
- package/dist/hooks/runtime/script-resolve.js.map +1 -0
- package/dist/hooks/runtime/session-activation.js +5 -1
- package/dist/hooks/runtime/session-activation.js.map +1 -1
- package/dist/hooks/runtime/session-review.js +163 -45
- package/dist/hooks/runtime/session-review.js.map +1 -1
- package/dist/hooks/runtime/session-start.js +2 -1
- package/dist/hooks/runtime/session-start.js.map +1 -1
- package/dist/hooks/runtime/session-writes.js +232 -0
- package/dist/hooks/runtime/session-writes.js.map +1 -0
- package/dist/hooks/runtime/sniff.js +26 -7
- package/dist/hooks/runtime/sniff.js.map +1 -1
- package/dist/hooks/runtime/target-resolve.js +336 -0
- package/dist/hooks/runtime/target-resolve.js.map +1 -0
- package/dist/hooks/runtime/unmapped-tools.js +93 -0
- package/dist/hooks/runtime/unmapped-tools.js.map +1 -0
- package/dist/hooks/runtime/usage-block.js +4 -0
- package/dist/hooks/runtime/usage-block.js.map +1 -1
- package/dist/hooks/runtime/user-prompt-submit.js +2 -1
- package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
- package/dist/hooks/runtime/wire-text.js +41 -0
- package/dist/hooks/runtime/wire-text.js.map +1 -0
- package/dist/hooks/runtime/work-dir.js +84 -0
- package/dist/hooks/runtime/work-dir.js.map +1 -0
- package/dist/index.js +45 -12
- package/dist/index.js.map +1 -1
- package/dist/install.js +174 -11
- package/dist/install.js.map +1 -1
- package/dist/link.js +74 -48
- package/dist/link.js.map +1 -1
- package/dist/login.js +77 -11
- package/dist/login.js.map +1 -1
- package/dist/mcp/auth/agent-sign-ins.js +174 -0
- package/dist/mcp/auth/agent-sign-ins.js.map +1 -0
- package/dist/mcp/auth/callback.js +200 -0
- package/dist/mcp/auth/callback.js.map +1 -0
- package/dist/mcp/auth/connect.js +392 -0
- package/dist/mcp/auth/connect.js.map +1 -0
- package/dist/mcp/auth/discovery.js +399 -0
- package/dist/mcp/auth/discovery.js.map +1 -0
- package/dist/mcp/auth/oauth.js +326 -0
- package/dist/mcp/auth/oauth.js.map +1 -0
- package/dist/mcp/auth/probe.js +128 -0
- package/dist/mcp/auth/probe.js.map +1 -0
- package/dist/mcp/auth/secrets.js +446 -0
- package/dist/mcp/auth/secrets.js.map +1 -0
- package/dist/mcp/auth/store.js +260 -0
- package/dist/mcp/auth/store.js.map +1 -0
- package/dist/mcp/auth/tokenSource.js +55 -0
- package/dist/mcp/auth/tokenSource.js.map +1 -0
- package/dist/mcp/config/atomicWrite.js +98 -0
- package/dist/mcp/config/atomicWrite.js.map +1 -0
- package/dist/mcp/config/clients.js +171 -0
- package/dist/mcp/config/clients.js.map +1 -0
- package/dist/mcp/config/cybedefend-servers.js +48 -0
- package/dist/mcp/config/cybedefend-servers.js.map +1 -0
- package/dist/mcp/config/discover.js +114 -0
- package/dist/mcp/config/discover.js.map +1 -0
- package/dist/mcp/config/hook.js +123 -0
- package/dist/mcp/config/hook.js.map +1 -0
- package/dist/mcp/config/index.js +11 -0
- package/dist/mcp/config/index.js.map +1 -0
- package/dist/mcp/config/install.js +132 -0
- package/dist/mcp/config/install.js.map +1 -0
- package/dist/mcp/config/installed.js +37 -0
- package/dist/mcp/config/installed.js.map +1 -0
- package/dist/mcp/config/jsonEdit.js +119 -0
- package/dist/mcp/config/jsonEdit.js.map +1 -0
- package/dist/mcp/config/lock.js +102 -0
- package/dist/mcp/config/lock.js.map +1 -0
- package/dist/mcp/config/proxyBinary.js +211 -0
- package/dist/mcp/config/proxyBinary.js.map +1 -0
- package/dist/mcp/config/rewrite.js +388 -0
- package/dist/mcp/config/rewrite.js.map +1 -0
- package/dist/mcp/config/types.js +34 -0
- package/dist/mcp/config/types.js.map +1 -0
- package/dist/mcp/proxy/audit.js +83 -0
- package/dist/mcp/proxy/audit.js.map +1 -0
- package/dist/mcp/proxy/authInterceptor.js +119 -0
- package/dist/mcp/proxy/authInterceptor.js.map +1 -0
- package/dist/mcp/proxy/expand.js +93 -0
- package/dist/mcp/proxy/expand.js.map +1 -0
- package/dist/mcp/proxy/inject.js +412 -0
- package/dist/mcp/proxy/inject.js.map +1 -0
- package/dist/mcp/proxy/intercept.js +63 -0
- package/dist/mcp/proxy/intercept.js.map +1 -0
- package/dist/mcp/proxy/jsonrpc.js +153 -0
- package/dist/mcp/proxy/jsonrpc.js.map +1 -0
- package/dist/mcp/proxy/log.js +109 -0
- package/dist/mcp/proxy/log.js.map +1 -0
- package/dist/mcp/proxy/paths.js +57 -0
- package/dist/mcp/proxy/paths.js.map +1 -0
- package/dist/mcp/proxy/proxy.js +520 -0
- package/dist/mcp/proxy/proxy.js.map +1 -0
- package/dist/mcp/proxy/serverCheckTimer.js +75 -0
- package/dist/mcp/proxy/serverCheckTimer.js.map +1 -0
- package/dist/mcp/proxy/sessionLifecycle.js +29 -0
- package/dist/mcp/proxy/sessionLifecycle.js.map +1 -0
- package/dist/mcp/proxy/spec.js +203 -0
- package/dist/mcp/proxy/spec.js.map +1 -0
- package/dist/mcp/proxy/sse.js +77 -0
- package/dist/mcp/proxy/sse.js.map +1 -0
- package/dist/mcp/proxy/upstream.js +2 -0
- package/dist/mcp/proxy/upstream.js.map +1 -0
- package/dist/mcp/proxy/upstreamHttp.js +499 -0
- package/dist/mcp/proxy/upstreamHttp.js.map +1 -0
- package/dist/mcp/proxy/upstreamStdio.js +188 -0
- package/dist/mcp/proxy/upstreamStdio.js.map +1 -0
- package/dist/mcp/proxy/upstreams.js +143 -0
- package/dist/mcp/proxy/upstreams.js.map +1 -0
- package/dist/mcp/proxy/verifyInterceptor.js +718 -0
- package/dist/mcp/proxy/verifyInterceptor.js.map +1 -0
- package/dist/mcp/proxy/windowsCommand.js +99 -0
- package/dist/mcp/proxy/windowsCommand.js.map +1 -0
- package/dist/mcp/report/buffer.js +351 -0
- package/dist/mcp/report/buffer.js.map +1 -0
- package/dist/mcp/report/coalesce.js +165 -0
- package/dist/mcp/report/coalesce.js.map +1 -0
- package/dist/mcp/report/constants.js +51 -0
- package/dist/mcp/report/constants.js.map +1 -0
- package/dist/mcp/report/project.js +83 -0
- package/dist/mcp/report/project.js.map +1 -0
- package/dist/mcp/report/reporter.js +443 -0
- package/dist/mcp/report/reporter.js.map +1 -0
- package/dist/mcp/report/types.js +3 -0
- package/dist/mcp/report/types.js.map +1 -0
- package/dist/mcp/report/validate.js +179 -0
- package/dist/mcp/report/validate.js.map +1 -0
- package/dist/mcp/scan/args.js +228 -0
- package/dist/mcp/scan/args.js.map +1 -0
- package/dist/mcp/scan/results.js +253 -0
- package/dist/mcp/scan/results.js.map +1 -0
- package/dist/mcp/scan/types.js +40 -0
- package/dist/mcp/scan/types.js.map +1 -0
- package/dist/mcp/verify/api.js +514 -0
- package/dist/mcp/verify/api.js.map +1 -0
- package/dist/mcp/verify/backend.js +143 -0
- package/dist/mcp/verify/backend.js.map +1 -0
- package/dist/mcp/verify/cache.js +200 -0
- package/dist/mcp/verify/cache.js.map +1 -0
- package/dist/mcp/verify/contract-errors.js +198 -0
- package/dist/mcp/verify/contract-errors.js.map +1 -0
- package/dist/mcp/verify/digest.js +91 -0
- package/dist/mcp/verify/digest.js.map +1 -0
- package/dist/mcp/verify/factory.js +56 -0
- package/dist/mcp/verify/factory.js.map +1 -0
- package/dist/mcp/verify/identity.js +286 -0
- package/dist/mcp/verify/identity.js.map +1 -0
- package/dist/mcp/verify/policy-store.js +63 -0
- package/dist/mcp/verify/policy-store.js.map +1 -0
- package/dist/mcp/verify/policy.js +160 -0
- package/dist/mcp/verify/policy.js.map +1 -0
- package/dist/mcp/verify/pool.js +27 -0
- package/dist/mcp/verify/pool.js.map +1 -0
- package/dist/mcp/verify/token-refresh.js +44 -0
- package/dist/mcp/verify/token-refresh.js.map +1 -0
- package/dist/mcp/verify/token.js +67 -0
- package/dist/mcp/verify/token.js.map +1 -0
- package/dist/mcp/verify/verifier.js +1166 -0
- package/dist/mcp/verify/verifier.js.map +1 -0
- package/dist/org-gate.js +1 -1
- package/dist/org-gate.js.map +1 -1
- package/dist/org.js +146 -0
- package/dist/org.js.map +1 -0
- package/dist/project-create.js +129 -0
- package/dist/project-create.js.map +1 -0
- package/dist/prompts.js +21 -0
- package/dist/prompts.js.map +1 -1
- package/dist/re2.wasm +0 -0
- package/dist/status.js +61 -0
- package/dist/status.js.map +1 -1
- package/dist/uninstall.js +49 -4
- package/dist/uninstall.js.map +1 -1
- package/dist/utils.js +62 -6
- package/dist/utils.js.map +1 -1
- package/package.json +10 -3
|
@@ -0,0 +1,443 @@
|
|
|
1
|
+
// ─── The call reporter: what the proxy decided, batched off the hot path ─────
|
|
2
|
+
//
|
|
3
|
+
// `event()` is called from the forwarding loop: it is synchronous, bounded and
|
|
4
|
+
// never throws. Everything else runs on timers that never keep the process
|
|
5
|
+
// alive. The first send goes FIRST_SEND_DELAY_MS after something happened,
|
|
6
|
+
// then at most one send per MIN_SEND_INTERVAL_MS. A send appends its reports
|
|
7
|
+
// to the on-disk buffer first and posts from there, so a backend that is down
|
|
8
|
+
// or refusing loses nothing and the buffer's holds decide when it is asked
|
|
9
|
+
// again: what a flush leaves (refused, timed out, held, or skipped because
|
|
10
|
+
// another proxy held the flush lock) re-arms the one send timer for when it is
|
|
11
|
+
// due, never sooner than MIN_SEND_INTERVAL_MS after the last send.
|
|
12
|
+
//
|
|
13
|
+
// A report carries the server's identity (D2), the inventory snapshot (D8)
|
|
14
|
+
// and the coalesced events (D5), copied field by field: an input object is
|
|
15
|
+
// never spread into a body, because the gateway strips unknown keys rather
|
|
16
|
+
// than refusing them, and what it strips has already left the machine.
|
|
17
|
+
import { readClientInfo } from '../proxy/intercept.js';
|
|
18
|
+
import { EventCoalescer } from './coalesce.js';
|
|
19
|
+
import { FIRST_SEND_DELAY_MS, INVENTORY_REFRESH_MS, MIN_SEND_INTERVAL_MS, REPORT_MAX_EVENTS, REPORT_MAX_TOOL_COUNT, } from './constants.js';
|
|
20
|
+
import { reportProblems } from './validate.js';
|
|
21
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
22
|
+
/** A send that failed before its flush said anything: tried again like a lost lock. */
|
|
23
|
+
const FLUSH_UNKNOWN = { ran: false, retryAt: null };
|
|
24
|
+
export class McpReporter {
|
|
25
|
+
opts;
|
|
26
|
+
now;
|
|
27
|
+
firstDelayMs;
|
|
28
|
+
minIntervalMs;
|
|
29
|
+
refreshMs;
|
|
30
|
+
setTimeoutFn;
|
|
31
|
+
clearTimeoutFn;
|
|
32
|
+
coalescer;
|
|
33
|
+
/** Cleaned like a `clientInfo` name. */
|
|
34
|
+
fallbackClientName;
|
|
35
|
+
client;
|
|
36
|
+
/**
|
|
37
|
+
* Who the agent is is known: a clientInfo arrived, or `initialize` was answered
|
|
38
|
+
* (`start`). Until then the project is not resolved: a send carries none.
|
|
39
|
+
*/
|
|
40
|
+
clientSettled = false;
|
|
41
|
+
listDecided = false;
|
|
42
|
+
lastSnapshotJson;
|
|
43
|
+
lastInventoryAt = 0;
|
|
44
|
+
lastSendAt;
|
|
45
|
+
/** Learnt from the last backend context: the backend and organisation `stop()` appends under. */
|
|
46
|
+
destination;
|
|
47
|
+
projectPromise;
|
|
48
|
+
projectIdKnown;
|
|
49
|
+
/** The one send timer, and when it fires: an earlier need moves it, a later one leaves it. */
|
|
50
|
+
sendTimer;
|
|
51
|
+
sendTimerAt;
|
|
52
|
+
refreshTimer;
|
|
53
|
+
running = false;
|
|
54
|
+
scheduleAfterRun = false;
|
|
55
|
+
stopped = false;
|
|
56
|
+
constructor(opts) {
|
|
57
|
+
this.opts = opts;
|
|
58
|
+
this.now = opts.now ?? Date.now;
|
|
59
|
+
this.firstDelayMs = opts.firstDelayMs ?? FIRST_SEND_DELAY_MS;
|
|
60
|
+
this.minIntervalMs = opts.minIntervalMs ?? MIN_SEND_INTERVAL_MS;
|
|
61
|
+
this.refreshMs = opts.refreshMs ?? INVENTORY_REFRESH_MS;
|
|
62
|
+
this.setTimeoutFn = opts.setTimeoutFn ?? setTimeout;
|
|
63
|
+
this.clearTimeoutFn = opts.clearTimeoutFn ?? clearTimeout;
|
|
64
|
+
this.coalescer = new EventCoalescer({ now: this.now });
|
|
65
|
+
this.fallbackClientName =
|
|
66
|
+
opts.fallbackClientName === undefined
|
|
67
|
+
? undefined
|
|
68
|
+
: readClientInfo({ clientInfo: { name: opts.fallbackClientName } })?.name;
|
|
69
|
+
}
|
|
70
|
+
// ── The hot path ─────────────────────────────────────────────────────────
|
|
71
|
+
event(input) {
|
|
72
|
+
try {
|
|
73
|
+
if (this.stopped)
|
|
74
|
+
return;
|
|
75
|
+
this.coalescer.add(input);
|
|
76
|
+
this.schedule();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// A report is never worth a call.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
noteClient(client) {
|
|
83
|
+
if (client === undefined)
|
|
84
|
+
return;
|
|
85
|
+
try {
|
|
86
|
+
const cleaned = readClientInfo({ clientInfo: client });
|
|
87
|
+
if (cleaned) {
|
|
88
|
+
this.client = cleaned;
|
|
89
|
+
this.clientSettled = true;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Ignored: the fallback name is used.
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
noteListDecided() {
|
|
97
|
+
this.listDecided = true;
|
|
98
|
+
this.scheduleIfInventoryDue();
|
|
99
|
+
}
|
|
100
|
+
noteServerChanged() {
|
|
101
|
+
this.scheduleIfInventoryDue();
|
|
102
|
+
}
|
|
103
|
+
// ── Lifecycle ────────────────────────────────────────────────────────────
|
|
104
|
+
/**
|
|
105
|
+
* After `initialize` was answered, with the clientInfo it carried (none:
|
|
106
|
+
* `undefined`): who the agent is is settled. Flushes what earlier processes
|
|
107
|
+
* left, learns the organization, arms the hourly refresh.
|
|
108
|
+
*/
|
|
109
|
+
start(client) {
|
|
110
|
+
if (this.stopped)
|
|
111
|
+
return;
|
|
112
|
+
this.noteClient(client);
|
|
113
|
+
this.clientSettled = true;
|
|
114
|
+
this.armRefresh();
|
|
115
|
+
void this.flushOnly();
|
|
116
|
+
}
|
|
117
|
+
/** Synchronous: what is pending goes to the buffer, for the next process to send. No network. */
|
|
118
|
+
stop() {
|
|
119
|
+
if (this.stopped)
|
|
120
|
+
return;
|
|
121
|
+
this.stopped = true;
|
|
122
|
+
if (this.sendTimer !== undefined)
|
|
123
|
+
this.clearTimeoutFn(this.sendTimer);
|
|
124
|
+
if (this.refreshTimer !== undefined)
|
|
125
|
+
this.clearTimeoutFn(this.refreshTimer);
|
|
126
|
+
this.sendTimer = undefined;
|
|
127
|
+
this.sendTimerAt = undefined;
|
|
128
|
+
this.refreshTimer = undefined;
|
|
129
|
+
try {
|
|
130
|
+
if (this.destination !== undefined && this.coalescer.hasPending()) {
|
|
131
|
+
this.drainToBuffer(this.destination, false);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Best-effort: the session is ending.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/** One send: the pending reports to the buffer, then the buffer to the backend. Never rejects. */
|
|
139
|
+
async sendNow() {
|
|
140
|
+
this.retryLater(await this.sendOnce());
|
|
141
|
+
}
|
|
142
|
+
/** `ctx: null` when there was no backend context to send with. Never rejects. */
|
|
143
|
+
async sendOnce() {
|
|
144
|
+
let ctx = null;
|
|
145
|
+
try {
|
|
146
|
+
ctx = await this.context();
|
|
147
|
+
if (!ctx)
|
|
148
|
+
return { ctx: null }; // No organization, no send: everything stays in memory, bounded.
|
|
149
|
+
// Resolved once, and only once the agent is known: before that the send
|
|
150
|
+
// carries no project, and the resolution waits for the agent's name.
|
|
151
|
+
if (this.projectIdKnown === undefined && this.clientSettled) {
|
|
152
|
+
this.projectIdKnown = await this.project();
|
|
153
|
+
}
|
|
154
|
+
this.drainToBuffer(ctx, true);
|
|
155
|
+
return { ctx, flushed: await this.flush(ctx) };
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
this.opts.logger.warn('report: a send failed, the reports wait in the buffer', {
|
|
159
|
+
error: error instanceof Error ? error.name : 'unknown',
|
|
160
|
+
});
|
|
161
|
+
return { ctx, flushed: FLUSH_UNKNOWN };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ── Cadence ──────────────────────────────────────────────────────────────
|
|
165
|
+
schedule() {
|
|
166
|
+
if (this.stopped)
|
|
167
|
+
return;
|
|
168
|
+
if (this.running) {
|
|
169
|
+
this.scheduleAfterRun = true;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const nowMs = this.now();
|
|
173
|
+
const sinceLast = this.lastSendAt === undefined ? 0 : this.lastSendAt + this.minIntervalMs - nowMs;
|
|
174
|
+
this.armAt(nowMs + Math.max(this.firstDelayMs, sinceLast));
|
|
175
|
+
}
|
|
176
|
+
/** One timer: an earlier time moves it, a later one leaves it where it is. */
|
|
177
|
+
armAt(at) {
|
|
178
|
+
if (this.stopped)
|
|
179
|
+
return;
|
|
180
|
+
if (this.sendTimer !== undefined && this.sendTimerAt !== undefined && this.sendTimerAt <= at)
|
|
181
|
+
return;
|
|
182
|
+
if (this.sendTimer !== undefined)
|
|
183
|
+
this.clearTimeoutFn(this.sendTimer);
|
|
184
|
+
this.sendTimerAt = at;
|
|
185
|
+
this.sendTimer = this.unref(this.setTimeoutFn(() => void this.tick(), Math.max(0, at - this.now())));
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* After a flush, the one timer for what it left of this backend's records:
|
|
189
|
+
* when its earliest hold ends (a refusal, a timeout, a 401 backoff), at once
|
|
190
|
+
* when something is due already (appended meanwhile), and after the minimum
|
|
191
|
+
* interval when the flush did not run (another proxy held the lock) — never
|
|
192
|
+
* sooner than the minimum interval after the last send.
|
|
193
|
+
*/
|
|
194
|
+
retryLater(outcome) {
|
|
195
|
+
try {
|
|
196
|
+
if (this.stopped || !outcome.ctx || !outcome.flushed)
|
|
197
|
+
return;
|
|
198
|
+
const { ctx, flushed } = outcome;
|
|
199
|
+
const nowMs = this.now();
|
|
200
|
+
let at;
|
|
201
|
+
if (!flushed.ran)
|
|
202
|
+
at = nowMs + this.minIntervalMs;
|
|
203
|
+
else if (flushed.retryAt !== null)
|
|
204
|
+
at = flushed.retryAt;
|
|
205
|
+
else
|
|
206
|
+
at = this.opts.buffer.hasDue(ctx.apiBase, ctx.token) ? nowMs : null;
|
|
207
|
+
if (at === null)
|
|
208
|
+
return;
|
|
209
|
+
const floor = this.lastSendAt === undefined ? at : this.lastSendAt + this.minIntervalMs;
|
|
210
|
+
this.armAt(Math.max(at, floor));
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// Ignored: the next event or the hourly refresh sends again.
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
scheduleIfInventoryDue() {
|
|
217
|
+
try {
|
|
218
|
+
if (this.inventoryDue(this.safeSnapshot()))
|
|
219
|
+
this.schedule();
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
// Ignored: the hourly refresh catches up.
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
async tick() {
|
|
226
|
+
this.sendTimer = undefined;
|
|
227
|
+
this.sendTimerAt = undefined;
|
|
228
|
+
if (this.stopped)
|
|
229
|
+
return;
|
|
230
|
+
if (this.running) {
|
|
231
|
+
// A send is still on its way: this one follows it, at the usual cadence.
|
|
232
|
+
this.scheduleAfterRun = true;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
this.running = true;
|
|
236
|
+
this.scheduleAfterRun = false;
|
|
237
|
+
let outcome = { ctx: null };
|
|
238
|
+
try {
|
|
239
|
+
outcome = await this.sendOnce();
|
|
240
|
+
// No context this time (offline, organization not resolved yet): what
|
|
241
|
+
// is waiting is tried again at the next allowed send, not left for a
|
|
242
|
+
// trigger that may never come.
|
|
243
|
+
if (!outcome.ctx && (this.coalescer.hasPending() || this.inventoryDue(this.safeSnapshot()))) {
|
|
244
|
+
this.scheduleAfterRun = true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
// When the send ENDED: a hold the flush set runs from its own clock, so
|
|
249
|
+
// the next send is never armed a few milliseconds before it is due.
|
|
250
|
+
this.lastSendAt = this.now();
|
|
251
|
+
this.running = false;
|
|
252
|
+
if (this.scheduleAfterRun)
|
|
253
|
+
this.schedule();
|
|
254
|
+
this.retryLater(outcome);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
armRefresh() {
|
|
258
|
+
if (this.stopped)
|
|
259
|
+
return;
|
|
260
|
+
if (this.refreshTimer !== undefined)
|
|
261
|
+
this.clearTimeoutFn(this.refreshTimer);
|
|
262
|
+
this.refreshTimer = this.unref(this.setTimeoutFn(() => {
|
|
263
|
+
this.refreshTimer = undefined;
|
|
264
|
+
this.armRefresh();
|
|
265
|
+
this.schedule();
|
|
266
|
+
}, this.refreshMs));
|
|
267
|
+
}
|
|
268
|
+
inventoryDue(snapshot) {
|
|
269
|
+
if (!this.listDecided || snapshot === null)
|
|
270
|
+
return false;
|
|
271
|
+
if (this.lastSnapshotJson === undefined)
|
|
272
|
+
return true;
|
|
273
|
+
if (JSON.stringify(snapshot) !== this.lastSnapshotJson)
|
|
274
|
+
return true;
|
|
275
|
+
return this.now() - this.lastInventoryAt >= this.refreshMs;
|
|
276
|
+
}
|
|
277
|
+
// ── Building and sending ─────────────────────────────────────────────────
|
|
278
|
+
/**
|
|
279
|
+
* Turns the pending events (and the inventory when it is due) into reports,
|
|
280
|
+
* appended to the buffer under the backend and organisation they are for.
|
|
281
|
+
* Synchronous, no network.
|
|
282
|
+
*/
|
|
283
|
+
drainToBuffer(destination, withInventory) {
|
|
284
|
+
const snapshot = this.safeSnapshot();
|
|
285
|
+
if (snapshot === null)
|
|
286
|
+
return; // Nothing to say about the server yet: the events wait.
|
|
287
|
+
const nowMs = this.now();
|
|
288
|
+
const base = this.body(snapshot, new Date(nowMs).toISOString(), []);
|
|
289
|
+
const baseProblems = reportProblems(base);
|
|
290
|
+
const pending = this.coalescer.pending(Number.MAX_SAFE_INTEGER);
|
|
291
|
+
if (baseProblems.length > 0) {
|
|
292
|
+
// Field names only: never a value.
|
|
293
|
+
this.opts.logger.warn('report: the server part of a report is invalid, nothing is sent', {
|
|
294
|
+
problems: baseProblems,
|
|
295
|
+
});
|
|
296
|
+
this.coalescer.markSent(pending);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const events = [];
|
|
300
|
+
for (const raw of pending) {
|
|
301
|
+
const event = copyEvent(raw);
|
|
302
|
+
if (reportProblems({ ...base, events: [event] }).length > 0) {
|
|
303
|
+
this.opts.logger.warn('report: an event the contract refuses is dropped', {
|
|
304
|
+
kind: String(raw.kind),
|
|
305
|
+
reason: String(raw.reason),
|
|
306
|
+
});
|
|
307
|
+
this.coalescer.markSent([raw]);
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
events.push(event);
|
|
311
|
+
}
|
|
312
|
+
const chunks = [];
|
|
313
|
+
for (let i = 0; i < events.length; i += REPORT_MAX_EVENTS) {
|
|
314
|
+
chunks.push(events.slice(i, i + REPORT_MAX_EVENTS));
|
|
315
|
+
}
|
|
316
|
+
if (chunks.length === 0 && withInventory && this.inventoryDue(snapshot))
|
|
317
|
+
chunks.push([]);
|
|
318
|
+
if (chunks.length === 0)
|
|
319
|
+
return;
|
|
320
|
+
for (const chunk of chunks) {
|
|
321
|
+
this.opts.buffer.append(destination.apiBase, destination.organizationId, { ...base, events: chunk });
|
|
322
|
+
this.coalescer.markSent(chunk);
|
|
323
|
+
}
|
|
324
|
+
this.lastSnapshotJson = JSON.stringify(snapshot);
|
|
325
|
+
this.lastInventoryAt = nowMs;
|
|
326
|
+
this.armRefresh();
|
|
327
|
+
}
|
|
328
|
+
body(snapshot, seenAt, events) {
|
|
329
|
+
const id = this.opts.identity;
|
|
330
|
+
const server = {
|
|
331
|
+
serverKey: id.serverKey,
|
|
332
|
+
kind: id.kind,
|
|
333
|
+
...(id.host !== undefined ? { host: id.host } : {}),
|
|
334
|
+
...(id.package !== undefined
|
|
335
|
+
? {
|
|
336
|
+
package: {
|
|
337
|
+
ecosystem: id.package.ecosystem,
|
|
338
|
+
name: id.package.name,
|
|
339
|
+
...(id.package.version !== undefined ? { version: id.package.version } : {}),
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
: {}),
|
|
343
|
+
...(id.label !== undefined ? { label: id.label } : {}),
|
|
344
|
+
verdict: snapshot.verdict,
|
|
345
|
+
action: snapshot.action,
|
|
346
|
+
blockedBy: snapshot.blockedBy ?? null,
|
|
347
|
+
advisoryIds: [...(snapshot.advisoryIds ?? [])],
|
|
348
|
+
// The gateway refuses a count past its bound with a 400 that would cost the
|
|
349
|
+
// whole report: a server this large is told as the largest it accepts.
|
|
350
|
+
toolCount: Math.min(snapshot.toolCount, REPORT_MAX_TOOL_COUNT),
|
|
351
|
+
flaggedToolCount: Math.min(snapshot.flaggedToolCount, REPORT_MAX_TOOL_COUNT),
|
|
352
|
+
...(snapshot.flaggedTools !== undefined
|
|
353
|
+
? { flaggedTools: snapshot.flaggedTools.map((t) => ({ name: t.name, digest: t.digest })) }
|
|
354
|
+
: {}),
|
|
355
|
+
seenAt,
|
|
356
|
+
};
|
|
357
|
+
const name = this.client?.name ?? this.fallbackClientName ?? 'unknown';
|
|
358
|
+
const version = this.client?.name !== undefined ? this.client.version : undefined;
|
|
359
|
+
const projectId = this.projectIdKnown;
|
|
360
|
+
return {
|
|
361
|
+
vibedefendVersion: this.opts.vibedefendVersion,
|
|
362
|
+
client: version !== undefined ? { name, version } : { name },
|
|
363
|
+
...(typeof projectId === 'string' && UUID.test(projectId) ? { projectId } : {}),
|
|
364
|
+
server,
|
|
365
|
+
events,
|
|
366
|
+
};
|
|
367
|
+
}
|
|
368
|
+
/** Posts the records made for this context's backend, and those only. */
|
|
369
|
+
flush(ctx) {
|
|
370
|
+
return this.opts.buffer.flush(ctx.apiBase, ctx.token, (organizationId, body) => this.opts.post({
|
|
371
|
+
apiBase: ctx.apiBase,
|
|
372
|
+
token: ctx.token,
|
|
373
|
+
mcpName: ctx.mcpName,
|
|
374
|
+
organizationId,
|
|
375
|
+
}, body));
|
|
376
|
+
}
|
|
377
|
+
/** `start()`'s flush: no new report, only what is waiting. */
|
|
378
|
+
async flushOnly() {
|
|
379
|
+
try {
|
|
380
|
+
const ctx = await this.context();
|
|
381
|
+
if (!ctx)
|
|
382
|
+
return;
|
|
383
|
+
// No project lookup here: it is a request of its own, made only once
|
|
384
|
+
// there is a report to file under it.
|
|
385
|
+
this.retryLater({ ctx, flushed: await this.flush(ctx) });
|
|
386
|
+
}
|
|
387
|
+
catch {
|
|
388
|
+
// Best-effort: the next send flushes again.
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
async context() {
|
|
392
|
+
let ctx;
|
|
393
|
+
try {
|
|
394
|
+
ctx = await this.opts.backendContext();
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return null;
|
|
398
|
+
}
|
|
399
|
+
if (!ctx || !ctx.organizationId)
|
|
400
|
+
return null;
|
|
401
|
+
this.destination = { apiBase: ctx.apiBase, organizationId: ctx.organizationId };
|
|
402
|
+
return ctx;
|
|
403
|
+
}
|
|
404
|
+
/** Resolved once per process, for the agent's declared name; never rejects. */
|
|
405
|
+
project() {
|
|
406
|
+
this.projectPromise ??= Promise.resolve()
|
|
407
|
+
.then(() => this.opts.projectId(this.client?.name))
|
|
408
|
+
.catch(() => null);
|
|
409
|
+
return this.projectPromise;
|
|
410
|
+
}
|
|
411
|
+
safeSnapshot() {
|
|
412
|
+
try {
|
|
413
|
+
return this.opts.snapshot();
|
|
414
|
+
}
|
|
415
|
+
catch {
|
|
416
|
+
return null;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
unref(handle) {
|
|
420
|
+
const unref = handle?.unref;
|
|
421
|
+
if (typeof unref === 'function')
|
|
422
|
+
unref.call(handle);
|
|
423
|
+
return handle;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
/** An event as it leaves the machine, field by field. */
|
|
427
|
+
function copyEvent(e) {
|
|
428
|
+
return {
|
|
429
|
+
eventId: e.eventId,
|
|
430
|
+
kind: e.kind,
|
|
431
|
+
reason: e.reason,
|
|
432
|
+
tool: e.tool,
|
|
433
|
+
...(e.toolDigest !== undefined ? { toolDigest: e.toolDigest } : {}),
|
|
434
|
+
...(e.verdict !== undefined ? { verdict: e.verdict } : {}),
|
|
435
|
+
...(e.category !== undefined ? { category: e.category } : {}),
|
|
436
|
+
...(e.severity !== undefined ? { severity: e.severity } : {}),
|
|
437
|
+
...(e.rules !== undefined ? { rules: [...e.rules] } : {}),
|
|
438
|
+
count: e.count,
|
|
439
|
+
firstAt: e.firstAt,
|
|
440
|
+
lastAt: e.lastAt,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
//# sourceMappingURL=reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../../../src/mcp/report/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/mcp/report/types.ts"],"names":[],"mappings":"AAAA,gFAAgF"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// ─── Validation of a report body before it is sent or buffered (contract C4, C5) ──
|
|
2
|
+
//
|
|
3
|
+
// The gateway strips unknown keys and answers 400 on a bound it checks. A body
|
|
4
|
+
// that cannot pass is not worth a request, and one that holds something outside
|
|
5
|
+
// these patterns must not leave the machine. Returns every problem found;
|
|
6
|
+
// an empty list means the body is valid.
|
|
7
|
+
import { HOST_PATTERN, LABEL_PATTERN, NPM_NAME_PATTERN, PACKAGE_VERSION_PATTERN, PYPI_NAME_PATTERN, REPORT_MAX_EVENT_COUNT, REPORT_MAX_EVENTS, REPORT_MAX_TOOL_COUNT, SERVER_KEY_PATTERN, VIBEDEFEND_VERSION_PATTERN, } from './constants.js';
|
|
8
|
+
const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
9
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
10
|
+
const TOOL = /^[A-Za-z0-9._/-]{1,128}$/;
|
|
11
|
+
const DIGEST = /^d1:[0-9a-f]{64}$/;
|
|
12
|
+
const RULE = /^[a-z0-9._-]{1,64}$/;
|
|
13
|
+
const ADVISORY_ID = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
14
|
+
const CATEGORY = /^[a-z0-9_-]{1,64}$/;
|
|
15
|
+
const SEVERITY = /^[a-z]{1,16}$/;
|
|
16
|
+
const ISO_8601 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
17
|
+
const SERVER_KINDS = ['remote', 'package', 'local'];
|
|
18
|
+
const VERDICTS = ['malicious', 'suspicious', 'clean', 'unverified'];
|
|
19
|
+
const ACTIONS = ['allow', 'ask', 'warn', 'block', 'pending'];
|
|
20
|
+
const BLOCKED_BY = ['verdict', 'advisory', 'organization'];
|
|
21
|
+
/** C5: the reasons each kind of event may carry. */
|
|
22
|
+
const REASONS = {
|
|
23
|
+
refused: ['verdict', 'advisory', 'organization', 'declined', 'unanswered'],
|
|
24
|
+
accepted: ['asked'],
|
|
25
|
+
warned: ['warn_mode', 'cannot_ask'],
|
|
26
|
+
injection: ['withheld', 'framed'],
|
|
27
|
+
};
|
|
28
|
+
const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
29
|
+
const isStr = (v, re) => typeof v === 'string' && re.test(v);
|
|
30
|
+
const isTime = (v) => typeof v === 'string' && ISO_8601.test(v) && Number.isFinite(Date.parse(v));
|
|
31
|
+
const isCount = (v, min, max) => typeof v === 'number' && Number.isInteger(v) && v >= min && v <= max;
|
|
32
|
+
export function reportProblems(body) {
|
|
33
|
+
const problems = [];
|
|
34
|
+
const bad = (msg) => {
|
|
35
|
+
problems.push(msg);
|
|
36
|
+
};
|
|
37
|
+
if (!isObj(body))
|
|
38
|
+
return ['body is not an object'];
|
|
39
|
+
if (!isStr(body.vibedefendVersion, VIBEDEFEND_VERSION_PATTERN) ||
|
|
40
|
+
body.vibedefendVersion.length > 32) {
|
|
41
|
+
bad('vibedefendVersion');
|
|
42
|
+
}
|
|
43
|
+
const client = body.client;
|
|
44
|
+
if (!isObj(client))
|
|
45
|
+
bad('client');
|
|
46
|
+
else {
|
|
47
|
+
if (typeof client.name !== 'string' || client.name === '' || client.name.length > 128) {
|
|
48
|
+
bad('client.name');
|
|
49
|
+
}
|
|
50
|
+
if (client.version !== undefined &&
|
|
51
|
+
(typeof client.version !== 'string' || client.version.length > 128)) {
|
|
52
|
+
bad('client.version');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (body.projectId !== undefined && !isStr(body.projectId, UUID))
|
|
56
|
+
bad('projectId');
|
|
57
|
+
const server = body.server;
|
|
58
|
+
if (!isObj(server))
|
|
59
|
+
bad('server');
|
|
60
|
+
else
|
|
61
|
+
checkServer(server, bad);
|
|
62
|
+
const events = body.events;
|
|
63
|
+
if (!Array.isArray(events))
|
|
64
|
+
bad('events');
|
|
65
|
+
else {
|
|
66
|
+
if (events.length > REPORT_MAX_EVENTS)
|
|
67
|
+
bad(`events: more than ${REPORT_MAX_EVENTS}`);
|
|
68
|
+
events.forEach((e, i) => checkEvent(e, i, bad));
|
|
69
|
+
}
|
|
70
|
+
return problems;
|
|
71
|
+
}
|
|
72
|
+
function checkServer(s, bad) {
|
|
73
|
+
if (!isStr(s.serverKey, SERVER_KEY_PATTERN))
|
|
74
|
+
bad('server.serverKey');
|
|
75
|
+
if (typeof s.kind !== 'string' || !SERVER_KINDS.includes(s.kind))
|
|
76
|
+
bad('server.kind');
|
|
77
|
+
if (s.label !== undefined && !isStr(s.label, LABEL_PATTERN))
|
|
78
|
+
bad('server.label');
|
|
79
|
+
if (typeof s.verdict !== 'string' || !VERDICTS.includes(s.verdict))
|
|
80
|
+
bad('server.verdict');
|
|
81
|
+
if (typeof s.action !== 'string' || !ACTIONS.includes(s.action))
|
|
82
|
+
bad('server.action');
|
|
83
|
+
if (s.blockedBy !== null && s.blockedBy !== undefined) {
|
|
84
|
+
if (typeof s.blockedBy !== 'string' || !BLOCKED_BY.includes(s.blockedBy)) {
|
|
85
|
+
bad('server.blockedBy');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (!Array.isArray(s.advisoryIds) || s.advisoryIds.length > 10)
|
|
89
|
+
bad('server.advisoryIds');
|
|
90
|
+
else if (!s.advisoryIds.every((id) => isStr(id, ADVISORY_ID)))
|
|
91
|
+
bad('server.advisoryIds[]');
|
|
92
|
+
if (!isCount(s.toolCount, 0, REPORT_MAX_TOOL_COUNT))
|
|
93
|
+
bad('server.toolCount');
|
|
94
|
+
if (!isCount(s.flaggedToolCount, 0, REPORT_MAX_TOOL_COUNT))
|
|
95
|
+
bad('server.flaggedToolCount');
|
|
96
|
+
if (!isTime(s.seenAt))
|
|
97
|
+
bad('server.seenAt');
|
|
98
|
+
if (s.flaggedTools !== undefined) {
|
|
99
|
+
if (!Array.isArray(s.flaggedTools) || s.flaggedTools.length > 20)
|
|
100
|
+
bad('server.flaggedTools');
|
|
101
|
+
else {
|
|
102
|
+
for (const t of s.flaggedTools) {
|
|
103
|
+
if (!isObj(t) || !isStr(t.name, TOOL) || !isStr(t.digest, DIGEST)) {
|
|
104
|
+
bad('server.flaggedTools[]');
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// kind decides which identity field is required.
|
|
111
|
+
if (s.kind === 'remote') {
|
|
112
|
+
if (!isStr(s.host, HOST_PATTERN))
|
|
113
|
+
bad('server.host (required for remote)');
|
|
114
|
+
}
|
|
115
|
+
else if (s.host !== undefined && !isStr(s.host, HOST_PATTERN)) {
|
|
116
|
+
bad('server.host');
|
|
117
|
+
}
|
|
118
|
+
if (s.kind === 'package') {
|
|
119
|
+
const p = s.package;
|
|
120
|
+
if (!isObj(p))
|
|
121
|
+
bad('server.package (required for package)');
|
|
122
|
+
else {
|
|
123
|
+
if (p.ecosystem !== 'npm' && p.ecosystem !== 'pypi')
|
|
124
|
+
bad('server.package.ecosystem');
|
|
125
|
+
else if (!isStr(p.name, p.ecosystem === 'npm' ? NPM_NAME_PATTERN : PYPI_NAME_PATTERN)) {
|
|
126
|
+
bad('server.package.name');
|
|
127
|
+
}
|
|
128
|
+
if (p.version !== undefined && !isStr(p.version, PACKAGE_VERSION_PATTERN)) {
|
|
129
|
+
bad('server.package.version');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (s.kind === 'local' && !isStr(s.label, LABEL_PATTERN)) {
|
|
134
|
+
bad('server.label (required for local)');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function checkEvent(e, i, bad) {
|
|
138
|
+
const at = `events[${i}]`;
|
|
139
|
+
if (!isObj(e))
|
|
140
|
+
return bad(at);
|
|
141
|
+
if (!isStr(e.eventId, UUID_V4))
|
|
142
|
+
bad(`${at}.eventId`);
|
|
143
|
+
const reasons = typeof e.kind === 'string' ? REASONS[e.kind] : undefined;
|
|
144
|
+
if (!reasons)
|
|
145
|
+
bad(`${at}.kind`);
|
|
146
|
+
else if (typeof e.reason !== 'string' || !reasons.includes(e.reason)) {
|
|
147
|
+
bad(`${at}.reason (${String(e.kind)})`);
|
|
148
|
+
}
|
|
149
|
+
if (!isStr(e.tool, TOOL))
|
|
150
|
+
bad(`${at}.tool`);
|
|
151
|
+
if (e.toolDigest !== undefined && !isStr(e.toolDigest, DIGEST))
|
|
152
|
+
bad(`${at}.toolDigest`);
|
|
153
|
+
if (e.verdict !== undefined && (typeof e.verdict !== 'string' || !VERDICTS.includes(e.verdict))) {
|
|
154
|
+
bad(`${at}.verdict`);
|
|
155
|
+
}
|
|
156
|
+
if (e.category !== undefined && !isStr(e.category, CATEGORY))
|
|
157
|
+
bad(`${at}.category`);
|
|
158
|
+
if (e.severity !== undefined && !isStr(e.severity, SEVERITY))
|
|
159
|
+
bad(`${at}.severity`);
|
|
160
|
+
if (e.rules !== undefined) {
|
|
161
|
+
if (!Array.isArray(e.rules) || e.rules.length > 5 || !e.rules.every((r) => isStr(r, RULE))) {
|
|
162
|
+
bad(`${at}.rules`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (!isCount(e.count, 1, REPORT_MAX_EVENT_COUNT))
|
|
166
|
+
bad(`${at}.count`);
|
|
167
|
+
const firstOk = isTime(e.firstAt);
|
|
168
|
+
const lastOk = isTime(e.lastAt);
|
|
169
|
+
if (!firstOk)
|
|
170
|
+
bad(`${at}.firstAt`);
|
|
171
|
+
if (!lastOk)
|
|
172
|
+
bad(`${at}.lastAt`);
|
|
173
|
+
else if (firstOk && Date.parse(e.lastAt) < Date.parse(e.firstAt)) {
|
|
174
|
+
// An event never ends before it starts, judged on the instants. One defect
|
|
175
|
+
// is named once: an instant that is not an instant is left to its own check.
|
|
176
|
+
bad(`${at}.lastAt (earlier than firstAt)`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=validate.js.map
|