chron-mcp 0.1.14 → 0.1.17
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 +305 -0
- package/dashboards/logscale/README.md +121 -0
- package/dashboards/sentinel/README.md +123 -0
- package/dashboards/sentinel/ai-tool-usage.kql +7 -0
- package/dashboards/sentinel/daily-active-developers.kql +6 -0
- package/dashboards/sentinel/events-by-type.kql +5 -0
- package/dashboards/sentinel/secrets-alert.kql +7 -0
- package/dashboards/sentinel/secrets-detected.kql +7 -0
- package/dashboards/sentinel/sessions-by-developer.kql +6 -0
- package/dashboards/splunk/README.md +138 -0
- package/dashboards/splunk/ai-tool-usage.spl +3 -0
- package/dashboards/splunk/daily-active-developers.spl +2 -0
- package/dashboards/splunk/events-by-type.spl +2 -0
- package/dashboards/splunk/secrets-alert.spl +3 -0
- package/dashboards/splunk/secrets-detected.spl +4 -0
- package/dashboards/splunk/sessions-by-developer.spl +3 -0
- package/dist/cli/index.js +300 -3
- package/dist/index.js +180 -38
- package/package.json +1 -1
- package/skills/chron/SKILL.md +8 -1
- package/skills/chron.skill.md +7 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Chron → Splunk
|
|
2
|
+
|
|
3
|
+
Stream AI session telemetry from developer machines into Splunk via HTTP Event Collector (HEC). Works with Splunk Enterprise, Splunk Cloud, and a local Docker instance for testing.
|
|
4
|
+
|
|
5
|
+
## Quickest way to see it working — Splunk Docker
|
|
6
|
+
|
|
7
|
+
The fastest path to visually verify the integration with no accounts or licenses:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
docker run -d \
|
|
11
|
+
--name splunk-chron \
|
|
12
|
+
--platform linux/amd64 \
|
|
13
|
+
-p 8000:8000 \
|
|
14
|
+
-p 8088:8088 \
|
|
15
|
+
-e SPLUNK_START_ARGS=--accept-license \
|
|
16
|
+
-e SPLUNK_PASSWORD=Admin1234! \
|
|
17
|
+
-e SPLUNK_HEC_TOKEN=chron-test-token \
|
|
18
|
+
splunk/splunk:latest
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
> **Apple Silicon (M1/M2/M3):** The `--platform linux/amd64` flag is required — the Splunk image has no ARM64 manifest. Docker Desktop uses Rosetta 2 to emulate it automatically.
|
|
22
|
+
|
|
23
|
+
Wait ~2 minutes for Splunk to start, then:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
chron connect splunk
|
|
27
|
+
# URL: https://localhost:8088 ← HTTPS (Splunk 9.x uses SSL by default)
|
|
28
|
+
# Token: chron-test-token
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
> Certificate verification is automatically skipped for localhost URLs — no extra flags needed.
|
|
32
|
+
|
|
33
|
+
Add to your MCP config and use your AI tool. Open [http://localhost:8000](http://localhost:8000) (admin / Admin1234!) and search:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
sourcetype="chron:event"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Events appear in real time as you have AI conversations.
|
|
40
|
+
|
|
41
|
+
> Note: The default Docker image auto-creates a HEC token with the value you set in `SPLUNK_HEC_TOKEN`. If events aren't arriving, verify HEC is enabled under **Settings → Data Inputs → HTTP Event Collector**.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Production setup — Splunk Enterprise or Cloud
|
|
46
|
+
|
|
47
|
+
### Step 1 — Enable HTTP Event Collector
|
|
48
|
+
|
|
49
|
+
**Splunk Enterprise:** Settings → Data Inputs → HTTP Event Collector → Global Settings → Enable
|
|
50
|
+
|
|
51
|
+
**Splunk Cloud:** Settings → Data Inputs → HTTP Event Collector → New Token
|
|
52
|
+
|
|
53
|
+
### Step 2 — Create a HEC token
|
|
54
|
+
|
|
55
|
+
1. Settings → Data Inputs → HTTP Event Collector → New Token
|
|
56
|
+
2. Name: `chron-ingest`
|
|
57
|
+
3. Source type: `chron:event` (create new if prompted)
|
|
58
|
+
4. Index: `main` (or a dedicated `chron` index)
|
|
59
|
+
5. Copy the token value
|
|
60
|
+
|
|
61
|
+
### Step 3 — Connect from the developer machine
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
chron connect splunk
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Enter the HEC base URL and token when prompted. The command sends a test event and confirms before saving.
|
|
68
|
+
|
|
69
|
+
### Step 4 — Add env vars to your MCP client
|
|
70
|
+
|
|
71
|
+
The `connect` command prints the exact block to paste. For Claude Code, add to the `chron` entry in `~/.claude.json`:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"mcpServers": {
|
|
76
|
+
"chron": {
|
|
77
|
+
"type": "stdio",
|
|
78
|
+
"command": "node",
|
|
79
|
+
"args": ["/path/to/dist/index.js"],
|
|
80
|
+
"env": {
|
|
81
|
+
"CHRON_SPLUNK_URL": "https://your-splunk-host:8088",
|
|
82
|
+
"CHRON_SPLUNK_TOKEN": "<your-hec-token>"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Step 5 — Run SPL queries
|
|
90
|
+
|
|
91
|
+
In Splunk → Search & Reporting, run queries from this directory:
|
|
92
|
+
|
|
93
|
+
| File | Use |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `events-by-type.spl` | Time-series — event volume by type |
|
|
96
|
+
| `sessions-by-developer.spl` | Table — sessions per host and AI tool |
|
|
97
|
+
| `secrets-detected.spl` | Table — credential exposures for SOC review |
|
|
98
|
+
| `ai-tool-usage.spl` | Summary — tool adoption breakdown |
|
|
99
|
+
| `daily-active-developers.spl` | Time-series — distinct active machines per day |
|
|
100
|
+
| `secrets-alert.spl` | Alert search — credential exposures (last 1h) |
|
|
101
|
+
|
|
102
|
+
### Step 6 — Set up the credential alert (recommended)
|
|
103
|
+
|
|
104
|
+
1. Search & Reporting → paste `secrets-alert.spl` → Save As → Alert
|
|
105
|
+
2. Schedule: Run every hour
|
|
106
|
+
3. Trigger: Number of results is greater than 0
|
|
107
|
+
4. Action: Send email or create a notable event
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Event schema
|
|
112
|
+
|
|
113
|
+
All events arrive with `sourcetype=chron:event` and these fields:
|
|
114
|
+
|
|
115
|
+
| Field | Present on |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `host` | All events: developer's machine hostname |
|
|
118
|
+
| `source` | All events: `chron-mcp` |
|
|
119
|
+
| `event_type` | All: `session_started`, `message_logged`, `secret_detected` |
|
|
120
|
+
| `session_id_prefix` | All events |
|
|
121
|
+
| `ai_tool` | All events: `claude`, `cursor`, `windsurf`, etc. |
|
|
122
|
+
| `os` | All events: `darwin`, `linux`, `win32` |
|
|
123
|
+
| `chron_version` | All events |
|
|
124
|
+
| `role` | `message_logged` only: `user` or `assistant` |
|
|
125
|
+
| `detection_type` | `secret_detected` only: `api_key`, `aws_key`, `jwt`, etc. |
|
|
126
|
+
| `masked_value` | `secret_detected` only: e.g. `AKIA****F3K2` |
|
|
127
|
+
|
|
128
|
+
**What is never transmitted:** message content, actual secret values, file paths, or any user-typed text.
|
|
129
|
+
|
|
130
|
+
## Env vars
|
|
131
|
+
|
|
132
|
+
| Env var | Description |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `CHRON_SPLUNK_URL` | Splunk HEC base URL, e.g. `https://localhost:8088` or `https://your-host:8088` |
|
|
135
|
+
| `CHRON_SPLUNK_TOKEN` | HEC ingest token |
|
|
136
|
+
| `CHRON_SPLUNK_INSECURE` | Set to `1` to skip TLS certificate verification (local Docker with self-signed cert) |
|
|
137
|
+
|
|
138
|
+
`CHRON_SPLUNK_URL` and `CHRON_SPLUNK_TOKEN` must both be set for events to flow. If unset, the integration is silently disabled.
|
package/dist/cli/index.js
CHANGED
|
@@ -17031,7 +17031,7 @@ var require_package = __commonJS({
|
|
|
17031
17031
|
"package.json"(exports2, module2) {
|
|
17032
17032
|
module2.exports = {
|
|
17033
17033
|
name: "chron-mcp",
|
|
17034
|
-
version: "0.1.
|
|
17034
|
+
version: "0.1.17",
|
|
17035
17035
|
mcpName: "io.github.sirinivask/chron",
|
|
17036
17036
|
description: "Audit-grade timestamped logs for every AI conversation",
|
|
17037
17037
|
repository: {
|
|
@@ -17122,6 +17122,24 @@ function saveConfig(data) {
|
|
|
17122
17122
|
(0, import_fs2.mkdirSync)(dir, { recursive: true });
|
|
17123
17123
|
(0, import_fs2.writeFileSync)(configPath(), JSON.stringify(data, null, 2), "utf8");
|
|
17124
17124
|
}
|
|
17125
|
+
function patchClaudeJson(vars) {
|
|
17126
|
+
const path = (0, import_path3.join)((0, import_os3.homedir)(), ".claude.json");
|
|
17127
|
+
if (!(0, import_fs2.existsSync)(path))
|
|
17128
|
+
return false;
|
|
17129
|
+
try {
|
|
17130
|
+
const raw = (0, import_fs2.readFileSync)(path, "utf8");
|
|
17131
|
+
const doc = JSON.parse(raw);
|
|
17132
|
+
const servers = doc.mcpServers ?? {};
|
|
17133
|
+
const chron = servers.chron ?? {};
|
|
17134
|
+
chron.env = { ...chron.env ?? {}, ...vars };
|
|
17135
|
+
servers.chron = chron;
|
|
17136
|
+
doc.mcpServers = servers;
|
|
17137
|
+
(0, import_fs2.writeFileSync)(path, JSON.stringify(doc, null, 2), "utf8");
|
|
17138
|
+
return true;
|
|
17139
|
+
} catch {
|
|
17140
|
+
return false;
|
|
17141
|
+
}
|
|
17142
|
+
}
|
|
17125
17143
|
async function connectCrowdStrike() {
|
|
17126
17144
|
process.stdout.write(`
|
|
17127
17145
|
${BOLD5}Connect Chron \u2192 CrowdStrike LogScale${RESET5}
|
|
@@ -17230,6 +17248,276 @@ ${DIM4}Sending test event...${RESET5} `);
|
|
|
17230
17248
|
`);
|
|
17231
17249
|
process.stdout.write(`${DIM4}Connection saved to ~/.chron/config.json${RESET5}
|
|
17232
17250
|
|
|
17251
|
+
`);
|
|
17252
|
+
}
|
|
17253
|
+
function isLocalhost(url) {
|
|
17254
|
+
return url.includes("localhost") || url.includes("127.0.0.1") || url.includes("::1");
|
|
17255
|
+
}
|
|
17256
|
+
async function connectSplunk() {
|
|
17257
|
+
process.stdout.write(`
|
|
17258
|
+
${BOLD5}Connect Chron \u2192 Splunk${RESET5}
|
|
17259
|
+
|
|
17260
|
+
`);
|
|
17261
|
+
process.stdout.write(`${DIM4}Chron will send session telemetry to your Splunk instance via HTTP Event
|
|
17262
|
+
`);
|
|
17263
|
+
process.stdout.write(`Collector (HEC). Message content is never transmitted.${RESET5}
|
|
17264
|
+
|
|
17265
|
+
`);
|
|
17266
|
+
process.stdout.write(`${DIM4}Works with Splunk Enterprise, Splunk Cloud, and local Docker instances.
|
|
17267
|
+
`);
|
|
17268
|
+
process.stdout.write(`See dashboards/splunk/README.md for setup steps.${RESET5}
|
|
17269
|
+
|
|
17270
|
+
`);
|
|
17271
|
+
process.stdout.write(`${DIM4}Note: Splunk 9.x Docker uses HTTPS on port 8088 with a self-signed cert.
|
|
17272
|
+
`);
|
|
17273
|
+
process.stdout.write(`Use https://localhost:8088 \u2014 certificate verification is skipped for localhost.${RESET5}
|
|
17274
|
+
|
|
17275
|
+
`);
|
|
17276
|
+
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
17277
|
+
let url, token;
|
|
17278
|
+
try {
|
|
17279
|
+
url = (await prompt(rl, ` Splunk HEC base URL ${DIM4}(e.g. https://localhost:8088 or https://your.splunkcloud.com:8088)${RESET5}
|
|
17280
|
+
${CYAN4}>${RESET5} `)).trim();
|
|
17281
|
+
token = (await prompt(rl, `
|
|
17282
|
+
HEC token
|
|
17283
|
+
${CYAN4}>${RESET5} `)).trim();
|
|
17284
|
+
} finally {
|
|
17285
|
+
rl.close();
|
|
17286
|
+
}
|
|
17287
|
+
if (!url.startsWith("http")) {
|
|
17288
|
+
process.stdout.write(`
|
|
17289
|
+
${RED}URL must start with http:// or https://${RESET5}
|
|
17290
|
+
`);
|
|
17291
|
+
process.exit(1);
|
|
17292
|
+
}
|
|
17293
|
+
if (!token) {
|
|
17294
|
+
process.stdout.write(`
|
|
17295
|
+
${RED}HEC token cannot be empty${RESET5}
|
|
17296
|
+
`);
|
|
17297
|
+
process.exit(1);
|
|
17298
|
+
}
|
|
17299
|
+
if (isLocalhost(url)) {
|
|
17300
|
+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
17301
|
+
}
|
|
17302
|
+
process.stdout.write(`
|
|
17303
|
+
${DIM4}Sending test event...${RESET5} `);
|
|
17304
|
+
const hecUrl = `${url.replace(/\/$/, "")}/services/collector/event`;
|
|
17305
|
+
const testPayload = JSON.stringify({
|
|
17306
|
+
time: Date.now() / 1e3,
|
|
17307
|
+
host: require("os").hostname(),
|
|
17308
|
+
source: "chron-mcp",
|
|
17309
|
+
sourcetype: "chron:event",
|
|
17310
|
+
event: {
|
|
17311
|
+
event_type: "connection_test",
|
|
17312
|
+
chron_version: require_package().version,
|
|
17313
|
+
os: process.platform,
|
|
17314
|
+
message: "chron connect splunk \u2014 test event"
|
|
17315
|
+
}
|
|
17316
|
+
});
|
|
17317
|
+
try {
|
|
17318
|
+
const res = await fetch(hecUrl, {
|
|
17319
|
+
method: "POST",
|
|
17320
|
+
headers: {
|
|
17321
|
+
"Authorization": `Splunk ${token}`,
|
|
17322
|
+
"Content-Type": "application/json"
|
|
17323
|
+
},
|
|
17324
|
+
body: testPayload
|
|
17325
|
+
});
|
|
17326
|
+
if (!res.ok) {
|
|
17327
|
+
const err = await res.text();
|
|
17328
|
+
process.stdout.write(`${RED}failed (HTTP ${res.status})${RESET5}
|
|
17329
|
+
`);
|
|
17330
|
+
process.stderr.write(` ${RED}${err}${RESET5}
|
|
17331
|
+
|
|
17332
|
+
`);
|
|
17333
|
+
process.exit(1);
|
|
17334
|
+
}
|
|
17335
|
+
process.stdout.write(`${GREEN}OK${RESET5}
|
|
17336
|
+
|
|
17337
|
+
`);
|
|
17338
|
+
} catch (e) {
|
|
17339
|
+
process.stdout.write(`${RED}failed${RESET5}
|
|
17340
|
+
`);
|
|
17341
|
+
process.stderr.write(` ${RED}Error: ${e.message}${RESET5}
|
|
17342
|
+
|
|
17343
|
+
`);
|
|
17344
|
+
process.exit(1);
|
|
17345
|
+
}
|
|
17346
|
+
const config = loadConfig();
|
|
17347
|
+
config.splunk = { url, token, insecure: isLocalhost(url), connected_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
17348
|
+
saveConfig(config);
|
|
17349
|
+
const splunkVars = { CHRON_SPLUNK_URL: url, CHRON_SPLUNK_TOKEN: token };
|
|
17350
|
+
if (isLocalhost(url))
|
|
17351
|
+
splunkVars.CHRON_SPLUNK_INSECURE = "1";
|
|
17352
|
+
patchClaudeJson(splunkVars);
|
|
17353
|
+
process.stdout.write(`${GREEN}${BOLD5}Connected!${RESET5} Splunk is receiving chron events.
|
|
17354
|
+
|
|
17355
|
+
`);
|
|
17356
|
+
process.stdout.write(`${DIM4}Config saved to ~/.chron/config.json \u2014 events will flow immediately in all running sessions.${RESET5}
|
|
17357
|
+
|
|
17358
|
+
`);
|
|
17359
|
+
}
|
|
17360
|
+
async function connectSentinel() {
|
|
17361
|
+
process.stdout.write(`
|
|
17362
|
+
${BOLD5}Connect Chron \u2192 Microsoft Sentinel${RESET5}
|
|
17363
|
+
|
|
17364
|
+
`);
|
|
17365
|
+
process.stdout.write(`${DIM4}Chron will send session telemetry to your Sentinel workspace via the
|
|
17366
|
+
`);
|
|
17367
|
+
process.stdout.write(`Azure Monitor Logs Ingestion API. Message content is never transmitted.${RESET5}
|
|
17368
|
+
|
|
17369
|
+
`);
|
|
17370
|
+
process.stdout.write(`${DIM4}Prerequisites: Azure App Registration, Data Collection Endpoint (DCE),
|
|
17371
|
+
`);
|
|
17372
|
+
process.stdout.write(`and a Data Collection Rule (DCR) pointing to a ChronEvents_CL custom table.
|
|
17373
|
+
`);
|
|
17374
|
+
process.stdout.write(`See dashboards/sentinel/README.md for full setup steps.${RESET5}
|
|
17375
|
+
|
|
17376
|
+
`);
|
|
17377
|
+
const rl = (0, import_readline.createInterface)({ input: process.stdin, output: process.stdout });
|
|
17378
|
+
let tenantId, clientId, clientSecret, dce, dcrId, stream;
|
|
17379
|
+
try {
|
|
17380
|
+
tenantId = (await prompt(rl, ` Azure Tenant ID ${DIM4}(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)${RESET5}
|
|
17381
|
+
${CYAN4}>${RESET5} `)).trim();
|
|
17382
|
+
clientId = (await prompt(rl, `
|
|
17383
|
+
App Registration Client ID
|
|
17384
|
+
${CYAN4}>${RESET5} `)).trim();
|
|
17385
|
+
clientSecret = (await prompt(rl, `
|
|
17386
|
+
Client Secret
|
|
17387
|
+
${CYAN4}>${RESET5} `)).trim();
|
|
17388
|
+
dce = (await prompt(rl, `
|
|
17389
|
+
Data Collection Endpoint URL ${DIM4}(https://...)${RESET5}
|
|
17390
|
+
${CYAN4}>${RESET5} `)).trim();
|
|
17391
|
+
dcrId = (await prompt(rl, `
|
|
17392
|
+
DCR Immutable ID ${DIM4}(dcr-xxxx)${RESET5}
|
|
17393
|
+
${CYAN4}>${RESET5} `)).trim();
|
|
17394
|
+
stream = (await prompt(rl, `
|
|
17395
|
+
Stream name ${DIM4}(press Enter for Custom-ChronEvents_CL)${RESET5}
|
|
17396
|
+
${CYAN4}>${RESET5} `)).trim() || "Custom-ChronEvents_CL";
|
|
17397
|
+
} finally {
|
|
17398
|
+
rl.close();
|
|
17399
|
+
}
|
|
17400
|
+
if (!tenantId || !clientId || !clientSecret) {
|
|
17401
|
+
process.stdout.write(`
|
|
17402
|
+
${RED}Tenant ID, Client ID, and Client Secret are all required.${RESET5}
|
|
17403
|
+
`);
|
|
17404
|
+
process.exit(1);
|
|
17405
|
+
}
|
|
17406
|
+
if (!dce.startsWith("https://")) {
|
|
17407
|
+
process.stdout.write(`
|
|
17408
|
+
${RED}DCE URL must start with https://${RESET5}
|
|
17409
|
+
`);
|
|
17410
|
+
process.exit(1);
|
|
17411
|
+
}
|
|
17412
|
+
if (dcrId && !dcrId.startsWith("dcr-")) {
|
|
17413
|
+
process.stdout.write(`
|
|
17414
|
+
${YELLOW3}Warning: DCR Immutable ID usually starts with "dcr-"${RESET5}
|
|
17415
|
+
`);
|
|
17416
|
+
}
|
|
17417
|
+
process.stdout.write(`
|
|
17418
|
+
${DIM4}Authenticating with Azure AD...${RESET5} `);
|
|
17419
|
+
let accessToken;
|
|
17420
|
+
try {
|
|
17421
|
+
const tokenRes = await fetch(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, {
|
|
17422
|
+
method: "POST",
|
|
17423
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
17424
|
+
body: new URLSearchParams({
|
|
17425
|
+
grant_type: "client_credentials",
|
|
17426
|
+
client_id: clientId,
|
|
17427
|
+
client_secret: clientSecret,
|
|
17428
|
+
scope: "https://monitor.azure.com/.default"
|
|
17429
|
+
}).toString()
|
|
17430
|
+
});
|
|
17431
|
+
if (!tokenRes.ok) {
|
|
17432
|
+
const err = await tokenRes.text();
|
|
17433
|
+
process.stdout.write(`${RED}failed (HTTP ${tokenRes.status})${RESET5}
|
|
17434
|
+
`);
|
|
17435
|
+
process.stderr.write(` ${RED}${err}${RESET5}
|
|
17436
|
+
|
|
17437
|
+
`);
|
|
17438
|
+
process.exit(1);
|
|
17439
|
+
}
|
|
17440
|
+
const tokenData = await tokenRes.json();
|
|
17441
|
+
accessToken = tokenData.access_token;
|
|
17442
|
+
process.stdout.write(`${GREEN}OK${RESET5}
|
|
17443
|
+
`);
|
|
17444
|
+
} catch (e) {
|
|
17445
|
+
process.stdout.write(`${RED}failed${RESET5}
|
|
17446
|
+
`);
|
|
17447
|
+
process.stderr.write(` ${RED}Error: ${e.message}${RESET5}
|
|
17448
|
+
|
|
17449
|
+
`);
|
|
17450
|
+
process.exit(1);
|
|
17451
|
+
}
|
|
17452
|
+
process.stdout.write(`${DIM4}Sending test event...${RESET5} `);
|
|
17453
|
+
const ingestUrl = `${dce}/dataCollectionRules/${dcrId}/streams/${stream}?api-version=2023-01-01`;
|
|
17454
|
+
const testRecord = {
|
|
17455
|
+
TimeGenerated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17456
|
+
EventType: "connection_test",
|
|
17457
|
+
SessionIdPrefix: "test",
|
|
17458
|
+
AiTool: "",
|
|
17459
|
+
OS: process.platform,
|
|
17460
|
+
ChronVersion: require_package().version,
|
|
17461
|
+
Computer: require("os").hostname(),
|
|
17462
|
+
Role: "",
|
|
17463
|
+
DetectionType: "",
|
|
17464
|
+
MaskedValue: ""
|
|
17465
|
+
};
|
|
17466
|
+
try {
|
|
17467
|
+
const res = await fetch(ingestUrl, {
|
|
17468
|
+
method: "POST",
|
|
17469
|
+
headers: {
|
|
17470
|
+
"Authorization": `Bearer ${accessToken}`,
|
|
17471
|
+
"Content-Type": "application/json"
|
|
17472
|
+
},
|
|
17473
|
+
body: JSON.stringify([testRecord])
|
|
17474
|
+
});
|
|
17475
|
+
if (!res.ok) {
|
|
17476
|
+
const err = await res.text();
|
|
17477
|
+
process.stdout.write(`${RED}failed (HTTP ${res.status})${RESET5}
|
|
17478
|
+
`);
|
|
17479
|
+
process.stderr.write(` ${RED}${err}${RESET5}
|
|
17480
|
+
|
|
17481
|
+
`);
|
|
17482
|
+
process.exit(1);
|
|
17483
|
+
}
|
|
17484
|
+
process.stdout.write(`${GREEN}OK${RESET5}
|
|
17485
|
+
|
|
17486
|
+
`);
|
|
17487
|
+
} catch (e) {
|
|
17488
|
+
process.stdout.write(`${RED}failed${RESET5}
|
|
17489
|
+
`);
|
|
17490
|
+
process.stderr.write(` ${RED}Error: ${e.message}${RESET5}
|
|
17491
|
+
|
|
17492
|
+
`);
|
|
17493
|
+
process.exit(1);
|
|
17494
|
+
}
|
|
17495
|
+
const config = loadConfig();
|
|
17496
|
+
config.sentinel = { dce, dcrId, stream, tenantId, clientId, connected_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
17497
|
+
saveConfig(config);
|
|
17498
|
+
process.stdout.write(`${GREEN}${BOLD5}Connected!${RESET5} Test event sent to Sentinel workspace.
|
|
17499
|
+
|
|
17500
|
+
`);
|
|
17501
|
+
process.stdout.write(`${BOLD5}Add these env vars to your MCP client config:${RESET5}
|
|
17502
|
+
|
|
17503
|
+
`);
|
|
17504
|
+
process.stdout.write(` ${CYAN4}CHRON_SENTINEL_TENANT_ID${RESET5} ${tenantId}
|
|
17505
|
+
`);
|
|
17506
|
+
process.stdout.write(` ${CYAN4}CHRON_SENTINEL_CLIENT_ID${RESET5} ${clientId}
|
|
17507
|
+
`);
|
|
17508
|
+
process.stdout.write(` ${CYAN4}CHRON_SENTINEL_CLIENT_SECRET${RESET5} ${DIM4}<your-client-secret>${RESET5}
|
|
17509
|
+
`);
|
|
17510
|
+
process.stdout.write(` ${CYAN4}CHRON_SENTINEL_DCE${RESET5} ${dce}
|
|
17511
|
+
`);
|
|
17512
|
+
process.stdout.write(` ${CYAN4}CHRON_SENTINEL_DCR_ID${RESET5} ${dcrId}
|
|
17513
|
+
`);
|
|
17514
|
+
process.stdout.write(` ${CYAN4}CHRON_SENTINEL_STREAM${RESET5} ${stream}
|
|
17515
|
+
|
|
17516
|
+
`);
|
|
17517
|
+
process.stdout.write(`${DIM4}For Claude Code \u2014 add to the "env" block of the chron entry in ~/.claude.json.
|
|
17518
|
+
`);
|
|
17519
|
+
process.stdout.write(`Connection saved to ~/.chron/config.json${RESET5}
|
|
17520
|
+
|
|
17233
17521
|
`);
|
|
17234
17522
|
}
|
|
17235
17523
|
async function runConnect(args2) {
|
|
@@ -17238,6 +17526,12 @@ async function runConnect(args2) {
|
|
|
17238
17526
|
case "crowdstrike":
|
|
17239
17527
|
await connectCrowdStrike();
|
|
17240
17528
|
break;
|
|
17529
|
+
case "sentinel":
|
|
17530
|
+
await connectSentinel();
|
|
17531
|
+
break;
|
|
17532
|
+
case "splunk":
|
|
17533
|
+
await connectSplunk();
|
|
17534
|
+
break;
|
|
17241
17535
|
default: {
|
|
17242
17536
|
const name = subcommand ? `Unknown integration: ${subcommand}
|
|
17243
17537
|
|
|
@@ -17247,13 +17541,15 @@ async function runConnect(args2) {
|
|
|
17247
17541
|
|
|
17248
17542
|
Integrations:
|
|
17249
17543
|
crowdstrike Connect to CrowdStrike LogScale (direct ingest)
|
|
17544
|
+
sentinel Connect to Microsoft Sentinel (Azure Monitor Logs Ingestion API)
|
|
17545
|
+
splunk Connect to Splunk via HTTP Event Collector (HEC)
|
|
17250
17546
|
`
|
|
17251
17547
|
);
|
|
17252
17548
|
process.exit(subcommand ? 1 : 0);
|
|
17253
17549
|
}
|
|
17254
17550
|
}
|
|
17255
17551
|
}
|
|
17256
|
-
var import_readline, import_os3, import_path3, import_fs2, RESET5, BOLD5, DIM4, CYAN4, GREEN, RED;
|
|
17552
|
+
var import_readline, import_os3, import_path3, import_fs2, RESET5, BOLD5, DIM4, CYAN4, GREEN, RED, YELLOW3;
|
|
17257
17553
|
var init_connect = __esm({
|
|
17258
17554
|
"src/cli/connect.ts"() {
|
|
17259
17555
|
"use strict";
|
|
@@ -17267,6 +17563,7 @@ var init_connect = __esm({
|
|
|
17267
17563
|
CYAN4 = "\x1B[36m";
|
|
17268
17564
|
GREEN = "\x1B[32m";
|
|
17269
17565
|
RED = "\x1B[31m";
|
|
17566
|
+
YELLOW3 = "\x1B[33m";
|
|
17270
17567
|
}
|
|
17271
17568
|
});
|
|
17272
17569
|
|
|
@@ -17317,7 +17614,7 @@ Commands:
|
|
|
17317
17614
|
export Export a session as markdown
|
|
17318
17615
|
secrets List detected secrets across sessions
|
|
17319
17616
|
settings View current configuration
|
|
17320
|
-
connect Connect to a SIEM integration (crowdstrike)
|
|
17617
|
+
connect Connect to a SIEM integration (crowdstrike, sentinel, splunk)
|
|
17321
17618
|
|
|
17322
17619
|
Options (history):
|
|
17323
17620
|
--limit=<n> Max sessions to show (default: 20)
|