@datasynx/agentic-ai-cartography 2.9.0 → 2.10.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/dist/{chunk-LRUWWHMQ.js → chunk-ASCA3UFM.js} +148 -2
- package/dist/chunk-ASCA3UFM.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.cjs +153 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.js +150 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp-bin.js +1 -1
- package/llms-full.txt +1 -0
- package/package.json +1 -1
- package/server.json +2 -2
- package/dist/chunk-LRUWWHMQ.js.map +0 -1
package/dist/cli.js
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -36,6 +36,7 @@ __export(src_exports, {
|
|
|
36
36
|
AuthorizationError: () => AuthorizationError,
|
|
37
37
|
CLIENTS: () => CLIENTS,
|
|
38
38
|
CONFIDENCE: () => CONFIDENCE,
|
|
39
|
+
CORRELATION_CONFIDENCE: () => CORRELATION_CONFIDENCE,
|
|
39
40
|
CartographyDB: () => CartographyDB,
|
|
40
41
|
ComplianceReportSchema: () => ComplianceReportSchema,
|
|
41
42
|
ComplianceRuleSchema: () => ComplianceRuleSchema,
|
|
@@ -122,6 +123,7 @@ __export(src_exports, {
|
|
|
122
123
|
computeIdentity: () => computeIdentity,
|
|
123
124
|
connectionsScanner: () => connectionsScanner,
|
|
124
125
|
contentHash: () => contentHash,
|
|
126
|
+
correlateTopology: () => correlateTopology,
|
|
125
127
|
createBashTool: () => createBashTool,
|
|
126
128
|
createCartographyTools: () => createCartographyTools,
|
|
127
129
|
createClaudeProvider: () => createClaudeProvider,
|
|
@@ -169,6 +171,7 @@ __export(src_exports, {
|
|
|
169
171
|
exportJGF: () => exportJGF,
|
|
170
172
|
exportJSON: () => exportJSON,
|
|
171
173
|
extractListeningPorts: () => extractListeningPorts,
|
|
174
|
+
extractSignals: () => extractSignals,
|
|
172
175
|
filterBySeverity: () => filterBySeverity,
|
|
173
176
|
findAnonViolations: () => findAnonViolations,
|
|
174
177
|
formatComplianceText: () => formatComplianceText,
|
|
@@ -6134,9 +6137,146 @@ async function resolveNlQuery(db, sessionId, search, raw, opts) {
|
|
|
6134
6137
|
return executeNlQuery(db, sessionId, search, parseNlQuery(raw), opts);
|
|
6135
6138
|
}
|
|
6136
6139
|
|
|
6140
|
+
// src/correlation/signals.ts
|
|
6141
|
+
var IPV4 = /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g;
|
|
6142
|
+
var DNS = /\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,8}[a-z]{2,24}\b/gi;
|
|
6143
|
+
var ENDPOINT = /\b((?:[a-z0-9][a-z0-9.-]{0,253})):(\d{1,5})\b/gi;
|
|
6144
|
+
function isPrivateIp(ip) {
|
|
6145
|
+
return /^(?:10\.|127\.|169\.254\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.)/.test(ip);
|
|
6146
|
+
}
|
|
6147
|
+
function sources(node) {
|
|
6148
|
+
const out = [node.id, node.name];
|
|
6149
|
+
const meta = node.metadata;
|
|
6150
|
+
if (meta) {
|
|
6151
|
+
for (const k of ["host", "hostname", "ip", "address", "dns", "dnsName", "endpoint", "fqdn", "publicIp", "privateIp", "url"]) {
|
|
6152
|
+
const v = meta[k];
|
|
6153
|
+
if (typeof v === "string") out.push(v);
|
|
6154
|
+
}
|
|
6155
|
+
}
|
|
6156
|
+
return out;
|
|
6157
|
+
}
|
|
6158
|
+
var KNOWN_PROVIDERS = /* @__PURE__ */ new Set(["aws", "gcp", "azure", "k8s", "kubernetes", "localhost", "docker"]);
|
|
6159
|
+
function parseProvider(id) {
|
|
6160
|
+
const seg = id.split(":");
|
|
6161
|
+
if (seg.length >= 3 && KNOWN_PROVIDERS.has(seg[1])) return seg[1];
|
|
6162
|
+
return void 0;
|
|
6163
|
+
}
|
|
6164
|
+
function extractSignals(node) {
|
|
6165
|
+
const text = sources(node).join(" \n ");
|
|
6166
|
+
const publicIps = /* @__PURE__ */ new Set();
|
|
6167
|
+
const privateIps = /* @__PURE__ */ new Set();
|
|
6168
|
+
const dnsNames = /* @__PURE__ */ new Set();
|
|
6169
|
+
const endpoints = /* @__PURE__ */ new Set();
|
|
6170
|
+
for (const m of text.matchAll(IPV4)) (isPrivateIp(m[0]) ? privateIps : publicIps).add(m[0]);
|
|
6171
|
+
for (const m of text.matchAll(DNS)) dnsNames.add(m[0].toLowerCase());
|
|
6172
|
+
for (const m of text.matchAll(ENDPOINT)) endpoints.add(`${m[1].toLowerCase()}:${m[2]}`);
|
|
6173
|
+
const provider = parseProvider(node.id);
|
|
6174
|
+
const sortUniq = (s) => [...s].sort();
|
|
6175
|
+
return {
|
|
6176
|
+
...provider ? { provider } : {},
|
|
6177
|
+
endpoints: sortUniq(endpoints),
|
|
6178
|
+
publicIps: sortUniq(publicIps),
|
|
6179
|
+
privateIps: sortUniq(privateIps),
|
|
6180
|
+
// The DNS regex requires an alphabetic TLD, so pure IPv4s never land here.
|
|
6181
|
+
dnsNames: sortUniq(dnsNames)
|
|
6182
|
+
};
|
|
6183
|
+
}
|
|
6184
|
+
|
|
6185
|
+
// src/correlation/correlate.ts
|
|
6186
|
+
var CORRELATION_CONFIDENCE = {
|
|
6187
|
+
"global-identity": 1,
|
|
6188
|
+
"dns-name": 0.95,
|
|
6189
|
+
"public-ip": 0.9,
|
|
6190
|
+
"endpoint": 0.85,
|
|
6191
|
+
"private-ip": 0.5
|
|
6192
|
+
};
|
|
6193
|
+
var MERGE_THRESHOLD = 0.85;
|
|
6194
|
+
var DSU = class {
|
|
6195
|
+
parent;
|
|
6196
|
+
constructor(n) {
|
|
6197
|
+
this.parent = Array.from({ length: n }, (_, i) => i);
|
|
6198
|
+
}
|
|
6199
|
+
find(x) {
|
|
6200
|
+
while (this.parent[x] !== x) {
|
|
6201
|
+
this.parent[x] = this.parent[this.parent[x]];
|
|
6202
|
+
x = this.parent[x];
|
|
6203
|
+
}
|
|
6204
|
+
return x;
|
|
6205
|
+
}
|
|
6206
|
+
union(a, b) {
|
|
6207
|
+
const ra = this.find(a), rb = this.find(b);
|
|
6208
|
+
if (ra !== rb) this.parent[Math.max(ra, rb)] = Math.min(ra, rb);
|
|
6209
|
+
}
|
|
6210
|
+
};
|
|
6211
|
+
function correlateTopology(nodes, _edges = []) {
|
|
6212
|
+
const n = nodes.length;
|
|
6213
|
+
const signals = nodes.map(extractSignals);
|
|
6214
|
+
const dsu = new DSU(n);
|
|
6215
|
+
const correlations = [];
|
|
6216
|
+
const merged = /* @__PURE__ */ new Set();
|
|
6217
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
6218
|
+
const add = (sig, val, i) => {
|
|
6219
|
+
const k = `${sig}|${val}`;
|
|
6220
|
+
const arr = buckets.get(k);
|
|
6221
|
+
if (arr) arr.push(i);
|
|
6222
|
+
else buckets.set(k, [i]);
|
|
6223
|
+
};
|
|
6224
|
+
nodes.forEach((node, i) => {
|
|
6225
|
+
if (node.globalId) add("global-identity", node.globalId, i);
|
|
6226
|
+
for (const d of signals[i].dnsNames) add("dns-name", d, i);
|
|
6227
|
+
for (const ip of signals[i].publicIps) add("public-ip", ip, i);
|
|
6228
|
+
for (const e of signals[i].endpoints) add("endpoint", e, i);
|
|
6229
|
+
});
|
|
6230
|
+
const order = ["global-identity", "dns-name", "public-ip", "endpoint"];
|
|
6231
|
+
for (const sig of order) {
|
|
6232
|
+
const conf = CORRELATION_CONFIDENCE[sig];
|
|
6233
|
+
const keys = [...buckets.keys()].filter((k) => k.startsWith(`${sig}|`)).sort();
|
|
6234
|
+
for (const key of keys) {
|
|
6235
|
+
const members = buckets.get(key).slice().sort((x, y) => nodes[x].id.localeCompare(nodes[y].id));
|
|
6236
|
+
const value = key.slice(sig.length + 1);
|
|
6237
|
+
for (let j = 1; j < members.length; j++) {
|
|
6238
|
+
const a = members[0], b = members[j];
|
|
6239
|
+
if (conf < MERGE_THRESHOLD) continue;
|
|
6240
|
+
dsu.union(a, b);
|
|
6241
|
+
merged.add(a);
|
|
6242
|
+
merged.add(b);
|
|
6243
|
+
const [s, t] = [nodes[a].id, nodes[b].id].sort();
|
|
6244
|
+
correlations.push({ sourceId: s, targetId: t, relationship: "same_as", signal: sig, confidence: conf, evidence: `[${sig}] shared ${value}` });
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
6249
|
+
for (let i = 0; i < n; i++) {
|
|
6250
|
+
const r = dsu.find(i);
|
|
6251
|
+
const arr = clusters.get(r);
|
|
6252
|
+
if (arr) arr.push(i);
|
|
6253
|
+
else clusters.set(r, [i]);
|
|
6254
|
+
}
|
|
6255
|
+
const canonical = [];
|
|
6256
|
+
let crossCloud = 0;
|
|
6257
|
+
for (const idxs of clusters.values()) {
|
|
6258
|
+
const memberNodes = idxs.map((i) => nodes[i]);
|
|
6259
|
+
const members = memberNodes.map((m) => m.id).sort();
|
|
6260
|
+
const providers = [...new Set(idxs.map((i) => signals[i].provider).filter((p) => !!p))].sort();
|
|
6261
|
+
const rep = memberNodes.reduce((a, b) => a.id < b.id ? a : b);
|
|
6262
|
+
const memberIds = new Set(members);
|
|
6263
|
+
const internal = correlations.filter((c) => memberIds.has(c.sourceId) && memberIds.has(c.targetId));
|
|
6264
|
+
const confidence = internal.length ? Math.min(...internal.map((c) => c.confidence)) : 1;
|
|
6265
|
+
if (providers.length > 1) crossCloud += 1;
|
|
6266
|
+
canonical.push({ id: rep.id, type: rep.type, name: rep.name, members, providers, confidence });
|
|
6267
|
+
}
|
|
6268
|
+
canonical.sort((a, b) => a.id.localeCompare(b.id));
|
|
6269
|
+
correlations.sort((a, b) => b.confidence - a.confidence || a.sourceId.localeCompare(b.sourceId) || a.targetId.localeCompare(b.targetId));
|
|
6270
|
+
return {
|
|
6271
|
+
canonical,
|
|
6272
|
+
correlations,
|
|
6273
|
+
summary: { rawNodes: n, canonicalNodes: canonical.length, collapsed: n - canonical.length, crossCloud }
|
|
6274
|
+
};
|
|
6275
|
+
}
|
|
6276
|
+
|
|
6137
6277
|
// src/mcp/server.ts
|
|
6138
6278
|
var SERVER_NAME = "cartography";
|
|
6139
|
-
var SERVER_VERSION = "2.
|
|
6279
|
+
var SERVER_VERSION = "2.10.0";
|
|
6140
6280
|
var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
|
|
6141
6281
|
var DATA_TYPES = NODE_TYPE_GROUPS.data;
|
|
6142
6282
|
var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
|
|
@@ -6296,6 +6436,15 @@ function createMcpServer(opts = {}) {
|
|
|
6296
6436
|
return json(db.getGraphSummary(sid));
|
|
6297
6437
|
}
|
|
6298
6438
|
);
|
|
6439
|
+
server.registerTool(
|
|
6440
|
+
"correlate_topology",
|
|
6441
|
+
{ title: "Correlate multi-cloud topology", description: "Collapse the same logical resource discovered across clouds/on-prem (by global identity or a shared DNS name / public IP / endpoint) into canonical entities with confidence-scored same_as links. Read-only, non-destructive (5.1).", inputSchema: {}, annotations: readOnly },
|
|
6442
|
+
() => {
|
|
6443
|
+
const sid = resolveSession();
|
|
6444
|
+
if (!sid) return json({ error: "No discovery session found." });
|
|
6445
|
+
return json(correlateTopology(db.getNodes(sid), db.getEdges(sid)));
|
|
6446
|
+
}
|
|
6447
|
+
);
|
|
6299
6448
|
server.registerTool(
|
|
6300
6449
|
"get_cost_summary",
|
|
6301
6450
|
{ title: "Get cost summary", description: "FinOps rollup: cost by domain and owner, currency/period-bucketed (3.3).", inputSchema: {}, annotations: readOnly },
|
|
@@ -12220,6 +12369,7 @@ function checkClaudePrerequisites() {
|
|
|
12220
12369
|
AuthorizationError,
|
|
12221
12370
|
CLIENTS,
|
|
12222
12371
|
CONFIDENCE,
|
|
12372
|
+
CORRELATION_CONFIDENCE,
|
|
12223
12373
|
CartographyDB,
|
|
12224
12374
|
ComplianceReportSchema,
|
|
12225
12375
|
ComplianceRuleSchema,
|
|
@@ -12306,6 +12456,7 @@ function checkClaudePrerequisites() {
|
|
|
12306
12456
|
computeIdentity,
|
|
12307
12457
|
connectionsScanner,
|
|
12308
12458
|
contentHash,
|
|
12459
|
+
correlateTopology,
|
|
12309
12460
|
createBashTool,
|
|
12310
12461
|
createCartographyTools,
|
|
12311
12462
|
createClaudeProvider,
|
|
@@ -12353,6 +12504,7 @@ function checkClaudePrerequisites() {
|
|
|
12353
12504
|
exportJGF,
|
|
12354
12505
|
exportJSON,
|
|
12355
12506
|
extractListeningPorts,
|
|
12507
|
+
extractSignals,
|
|
12356
12508
|
filterBySeverity,
|
|
12357
12509
|
findAnonViolations,
|
|
12358
12510
|
formatComplianceText,
|