@sentriflow/cli 0.1.7 → 0.1.9
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/index.js +460 -36
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10334,6 +10334,350 @@ function validateRegex(pattern, flags, path, ctx) {
|
|
|
10334
10334
|
}
|
|
10335
10335
|
}
|
|
10336
10336
|
|
|
10337
|
+
// ../core/src/ip/extractor.ts
|
|
10338
|
+
function isValidIPv4(ip) {
|
|
10339
|
+
if (!ip || typeof ip !== "string") return false;
|
|
10340
|
+
const octets = ip.split(".");
|
|
10341
|
+
if (octets.length !== 4) return false;
|
|
10342
|
+
for (const octet of octets) {
|
|
10343
|
+
if (!/^\d+$/.test(octet)) return false;
|
|
10344
|
+
if (octet.length > 1 && octet.startsWith("0")) return false;
|
|
10345
|
+
const num = parseInt(octet, 10);
|
|
10346
|
+
if (isNaN(num) || num < 0 || num > 255) return false;
|
|
10347
|
+
}
|
|
10348
|
+
return true;
|
|
10349
|
+
}
|
|
10350
|
+
function isValidIPv6(ip) {
|
|
10351
|
+
if (!ip || typeof ip !== "string") return false;
|
|
10352
|
+
let addr = ip;
|
|
10353
|
+
const zoneIndex = ip.indexOf("%");
|
|
10354
|
+
if (zoneIndex !== -1) {
|
|
10355
|
+
addr = ip.substring(0, zoneIndex);
|
|
10356
|
+
}
|
|
10357
|
+
if (!addr.includes(":")) return false;
|
|
10358
|
+
if (addr.includes(":::")) return false;
|
|
10359
|
+
const doubleColonCount = (addr.match(/::/g) || []).length;
|
|
10360
|
+
if (doubleColonCount > 1) return false;
|
|
10361
|
+
const parts = addr.split(":");
|
|
10362
|
+
if (doubleColonCount === 1) {
|
|
10363
|
+
const nonEmptyParts = parts.filter((p) => p !== "");
|
|
10364
|
+
for (const part of nonEmptyParts) {
|
|
10365
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(part)) return false;
|
|
10366
|
+
}
|
|
10367
|
+
if (nonEmptyParts.length > 7) return false;
|
|
10368
|
+
} else {
|
|
10369
|
+
if (parts.length !== 8) return false;
|
|
10370
|
+
for (const part of parts) {
|
|
10371
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(part)) return false;
|
|
10372
|
+
}
|
|
10373
|
+
}
|
|
10374
|
+
return true;
|
|
10375
|
+
}
|
|
10376
|
+
function isValidSubnet(subnet) {
|
|
10377
|
+
if (!subnet || typeof subnet !== "string") return false;
|
|
10378
|
+
const slashIndex = subnet.lastIndexOf("/");
|
|
10379
|
+
if (slashIndex === -1) return false;
|
|
10380
|
+
const ip = subnet.substring(0, slashIndex);
|
|
10381
|
+
const prefixStr = subnet.substring(slashIndex + 1);
|
|
10382
|
+
if (!/^\d+$/.test(prefixStr)) return false;
|
|
10383
|
+
const prefix = parseInt(prefixStr, 10);
|
|
10384
|
+
if (isValidIPv4(ip)) {
|
|
10385
|
+
return prefix >= 0 && prefix <= 32;
|
|
10386
|
+
} else if (isValidIPv6(ip)) {
|
|
10387
|
+
return prefix >= 0 && prefix <= 128;
|
|
10388
|
+
}
|
|
10389
|
+
return false;
|
|
10390
|
+
}
|
|
10391
|
+
function normalizeIPv4(ip) {
|
|
10392
|
+
return ip.split(".").map((octet) => parseInt(octet, 10).toString()).join(".");
|
|
10393
|
+
}
|
|
10394
|
+
function normalizeIPv6(ip) {
|
|
10395
|
+
let addr = ip;
|
|
10396
|
+
const zoneIndex = ip.indexOf("%");
|
|
10397
|
+
if (zoneIndex !== -1) {
|
|
10398
|
+
addr = ip.substring(0, zoneIndex);
|
|
10399
|
+
}
|
|
10400
|
+
addr = addr.toLowerCase();
|
|
10401
|
+
if (addr.includes("::")) {
|
|
10402
|
+
const sides = addr.split("::");
|
|
10403
|
+
const left = sides[0] ? sides[0].split(":").filter((p) => p !== "") : [];
|
|
10404
|
+
const right = sides[1] ? sides[1].split(":").filter((p) => p !== "") : [];
|
|
10405
|
+
const zerosNeeded = 8 - left.length - right.length;
|
|
10406
|
+
const expanded = [];
|
|
10407
|
+
for (const part of left) {
|
|
10408
|
+
expanded.push(parseInt(part, 16).toString(16));
|
|
10409
|
+
}
|
|
10410
|
+
for (let i = 0; i < zerosNeeded; i++) {
|
|
10411
|
+
expanded.push("0");
|
|
10412
|
+
}
|
|
10413
|
+
for (const part of right) {
|
|
10414
|
+
expanded.push(parseInt(part, 16).toString(16));
|
|
10415
|
+
}
|
|
10416
|
+
return expanded.join(":");
|
|
10417
|
+
}
|
|
10418
|
+
const parts = addr.split(":");
|
|
10419
|
+
const result = [];
|
|
10420
|
+
for (const part of parts) {
|
|
10421
|
+
if (part !== "") {
|
|
10422
|
+
result.push(parseInt(part, 16).toString(16));
|
|
10423
|
+
}
|
|
10424
|
+
}
|
|
10425
|
+
return result.join(":");
|
|
10426
|
+
}
|
|
10427
|
+
function ipv4ToNumber(ip) {
|
|
10428
|
+
const octets = ip.split(".").map(Number);
|
|
10429
|
+
const o0 = octets[0] ?? 0;
|
|
10430
|
+
const o1 = octets[1] ?? 0;
|
|
10431
|
+
const o2 = octets[2] ?? 0;
|
|
10432
|
+
const o3 = octets[3] ?? 0;
|
|
10433
|
+
return (o0 << 24 >>> 0) + (o1 << 16) + (o2 << 8) + o3;
|
|
10434
|
+
}
|
|
10435
|
+
function compareIPv4(a, b) {
|
|
10436
|
+
const numA = ipv4ToNumber(a);
|
|
10437
|
+
const numB = ipv4ToNumber(b);
|
|
10438
|
+
return numA < numB ? -1 : numA > numB ? 1 : 0;
|
|
10439
|
+
}
|
|
10440
|
+
function expandIPv6(ip) {
|
|
10441
|
+
let addr = ip;
|
|
10442
|
+
const zoneIndex = ip.indexOf("%");
|
|
10443
|
+
if (zoneIndex !== -1) {
|
|
10444
|
+
addr = ip.substring(0, zoneIndex);
|
|
10445
|
+
}
|
|
10446
|
+
const parts = addr.split(":");
|
|
10447
|
+
const result = [];
|
|
10448
|
+
for (let i = 0; i < parts.length; i++) {
|
|
10449
|
+
if (parts[i] === "" && i > 0 && i < parts.length - 1) {
|
|
10450
|
+
const nonEmpty = parts.filter((p) => p !== "").length;
|
|
10451
|
+
const zeros = 8 - nonEmpty;
|
|
10452
|
+
for (let j = 0; j < zeros; j++) {
|
|
10453
|
+
result.push("0");
|
|
10454
|
+
}
|
|
10455
|
+
} else if (parts[i] !== "") {
|
|
10456
|
+
result.push(parts[i] ?? "0");
|
|
10457
|
+
} else if (i === 0 && parts[1] === "") {
|
|
10458
|
+
const nonEmpty = parts.filter((p) => p !== "").length;
|
|
10459
|
+
const zeros = 8 - nonEmpty;
|
|
10460
|
+
for (let j = 0; j < zeros; j++) {
|
|
10461
|
+
result.push("0");
|
|
10462
|
+
}
|
|
10463
|
+
} else if (i === parts.length - 1 && parts[i - 1] === "") {
|
|
10464
|
+
}
|
|
10465
|
+
}
|
|
10466
|
+
while (result.length < 8) {
|
|
10467
|
+
result.push("0");
|
|
10468
|
+
}
|
|
10469
|
+
return result.slice(0, 8);
|
|
10470
|
+
}
|
|
10471
|
+
function ipv6ToBigInt(ip) {
|
|
10472
|
+
const parts = expandIPv6(ip);
|
|
10473
|
+
let result = 0n;
|
|
10474
|
+
for (const part of parts) {
|
|
10475
|
+
result = (result << 16n) + BigInt(parseInt(part, 16) || 0);
|
|
10476
|
+
}
|
|
10477
|
+
return result;
|
|
10478
|
+
}
|
|
10479
|
+
function compareIPv6(a, b) {
|
|
10480
|
+
const bigA = ipv6ToBigInt(a);
|
|
10481
|
+
const bigB = ipv6ToBigInt(b);
|
|
10482
|
+
return bigA < bigB ? -1 : bigA > bigB ? 1 : 0;
|
|
10483
|
+
}
|
|
10484
|
+
function sortIPv4Addresses(ips) {
|
|
10485
|
+
return [...ips].sort(compareIPv4);
|
|
10486
|
+
}
|
|
10487
|
+
function sortIPv6Addresses(ips) {
|
|
10488
|
+
return [...ips].sort(compareIPv6);
|
|
10489
|
+
}
|
|
10490
|
+
function parseSubnet(subnet) {
|
|
10491
|
+
const slashIndex = subnet.lastIndexOf("/");
|
|
10492
|
+
return {
|
|
10493
|
+
network: subnet.substring(0, slashIndex),
|
|
10494
|
+
prefix: parseInt(subnet.substring(slashIndex + 1), 10)
|
|
10495
|
+
};
|
|
10496
|
+
}
|
|
10497
|
+
function sortSubnets(subnets, type) {
|
|
10498
|
+
const compare = type === "ipv4" ? compareIPv4 : compareIPv6;
|
|
10499
|
+
return [...subnets].sort((a, b) => {
|
|
10500
|
+
const subA = parseSubnet(a);
|
|
10501
|
+
const subB = parseSubnet(b);
|
|
10502
|
+
const netCompare = compare(subA.network, subB.network);
|
|
10503
|
+
if (netCompare !== 0) return netCompare;
|
|
10504
|
+
return subA.prefix - subB.prefix;
|
|
10505
|
+
});
|
|
10506
|
+
}
|
|
10507
|
+
function isSubnetMask(ip) {
|
|
10508
|
+
if (!ip.startsWith("255.")) return false;
|
|
10509
|
+
const octets = ip.split(".").map(Number);
|
|
10510
|
+
const num = ((octets[0] ?? 0) << 24) + ((octets[1] ?? 0) << 16) + ((octets[2] ?? 0) << 8) + (octets[3] ?? 0);
|
|
10511
|
+
const inverted = ~num >>> 0;
|
|
10512
|
+
return inverted === 0 || (inverted & inverted + 1) === 0;
|
|
10513
|
+
}
|
|
10514
|
+
function isWildcardMask(ip) {
|
|
10515
|
+
if (!ip.startsWith("0.")) return false;
|
|
10516
|
+
const octets = ip.split(".").map(Number);
|
|
10517
|
+
const num = ((octets[0] ?? 0) << 24) + ((octets[1] ?? 0) << 16) + ((octets[2] ?? 0) << 8) + (octets[3] ?? 0);
|
|
10518
|
+
return num === 0 || (num + 1 & num) === 0;
|
|
10519
|
+
}
|
|
10520
|
+
function maskToCidr(mask) {
|
|
10521
|
+
if (!isSubnetMask(mask)) return -1;
|
|
10522
|
+
const octets = mask.split(".").map(Number);
|
|
10523
|
+
const num = ((octets[0] ?? 0) << 24) + ((octets[1] ?? 0) << 16) + ((octets[2] ?? 0) << 8) + (octets[3] ?? 0);
|
|
10524
|
+
let prefix = 0;
|
|
10525
|
+
let n = num >>> 0;
|
|
10526
|
+
while (n & 2147483648) {
|
|
10527
|
+
prefix++;
|
|
10528
|
+
n = n << 1 >>> 0;
|
|
10529
|
+
}
|
|
10530
|
+
return prefix;
|
|
10531
|
+
}
|
|
10532
|
+
function wildcardToCidr(wildcard) {
|
|
10533
|
+
if (!isWildcardMask(wildcard)) return -1;
|
|
10534
|
+
const octets = wildcard.split(".").map(Number);
|
|
10535
|
+
const num = ((octets[0] ?? 0) << 24) + ((octets[1] ?? 0) << 16) + ((octets[2] ?? 0) << 8) + (octets[3] ?? 0);
|
|
10536
|
+
let prefix = 0;
|
|
10537
|
+
let n = num >>> 0;
|
|
10538
|
+
while (prefix < 32 && !(n & 2147483648)) {
|
|
10539
|
+
prefix++;
|
|
10540
|
+
n = n << 1 >>> 0;
|
|
10541
|
+
}
|
|
10542
|
+
return prefix;
|
|
10543
|
+
}
|
|
10544
|
+
var IPV4_PATTERN = /\b(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\b/g;
|
|
10545
|
+
var IPV4_CIDR_PATTERN = /\b(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\/(?:3[0-2]|[12]?[0-9])\b/g;
|
|
10546
|
+
var IPV4_WITH_MASK_PATTERN = /\b((?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]))\s+(255\.(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]))\b/g;
|
|
10547
|
+
var IPV4_WITH_MASK_KEYWORD_PATTERN = /\b((?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]))\s+mask\s+(255\.(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]))\b/gi;
|
|
10548
|
+
var IPV4_WITH_WILDCARD_PATTERN = /\b((?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]))\s+(0\.(?:(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){2}(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9]))\b/g;
|
|
10549
|
+
var IPV6_PATTERN = /(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|:(?::[0-9a-fA-F]{1,4}){1,7}|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|(?:[0-9a-fA-F]{1,4}:){1,7}:|::/g;
|
|
10550
|
+
var IPV6_CIDR_PATTERN = /(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|:(?::[0-9a-fA-F]{1,4}){1,7}|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?::[0-9a-fA-F]{1,4}){1,6}|(?:[0-9a-fA-F]{1,4}:){1,7}:|::)\/(?:12[0-8]|1[01][0-9]|[1-9]?[0-9])/g;
|
|
10551
|
+
function createEmptyIPSummary() {
|
|
10552
|
+
return {
|
|
10553
|
+
ipv4Addresses: [],
|
|
10554
|
+
ipv6Addresses: [],
|
|
10555
|
+
ipv4Subnets: [],
|
|
10556
|
+
ipv6Subnets: [],
|
|
10557
|
+
counts: {
|
|
10558
|
+
ipv4: 0,
|
|
10559
|
+
ipv6: 0,
|
|
10560
|
+
ipv4Subnets: 0,
|
|
10561
|
+
ipv6Subnets: 0,
|
|
10562
|
+
total: 0
|
|
10563
|
+
}
|
|
10564
|
+
};
|
|
10565
|
+
}
|
|
10566
|
+
function extractIPSummary(content, options = {}) {
|
|
10567
|
+
if (!content || typeof content !== "string") {
|
|
10568
|
+
return createEmptyIPSummary();
|
|
10569
|
+
}
|
|
10570
|
+
const ipv4Set = /* @__PURE__ */ new Set();
|
|
10571
|
+
const ipv6Set = /* @__PURE__ */ new Set();
|
|
10572
|
+
const ipv4SubnetSet = /* @__PURE__ */ new Set();
|
|
10573
|
+
const ipv6SubnetSet = /* @__PURE__ */ new Set();
|
|
10574
|
+
const subnetNetworks = /* @__PURE__ */ new Set();
|
|
10575
|
+
const ipsWithMasks = /* @__PURE__ */ new Set();
|
|
10576
|
+
if (!options.skipSubnets) {
|
|
10577
|
+
const ipv4CidrMatches = content.matchAll(IPV4_CIDR_PATTERN);
|
|
10578
|
+
for (const match of ipv4CidrMatches) {
|
|
10579
|
+
const subnet = match[0];
|
|
10580
|
+
if (isValidSubnet(subnet)) {
|
|
10581
|
+
const { network } = parseSubnet(subnet);
|
|
10582
|
+
const normalizedNetwork = normalizeIPv4(network);
|
|
10583
|
+
ipv4SubnetSet.add(`${normalizedNetwork}/${parseSubnet(subnet).prefix}`);
|
|
10584
|
+
subnetNetworks.add(normalizedNetwork);
|
|
10585
|
+
}
|
|
10586
|
+
}
|
|
10587
|
+
const ipMaskMatches = content.matchAll(IPV4_WITH_MASK_PATTERN);
|
|
10588
|
+
for (const match of ipMaskMatches) {
|
|
10589
|
+
const ip = match[1];
|
|
10590
|
+
const mask = match[2];
|
|
10591
|
+
if (ip && mask && isValidIPv4(ip) && isSubnetMask(mask)) {
|
|
10592
|
+
const normalizedIP = normalizeIPv4(ip);
|
|
10593
|
+
const prefix = maskToCidr(mask);
|
|
10594
|
+
if (prefix >= 0) {
|
|
10595
|
+
ipv4SubnetSet.add(`${normalizedIP}/${prefix}`);
|
|
10596
|
+
ipsWithMasks.add(normalizedIP);
|
|
10597
|
+
}
|
|
10598
|
+
}
|
|
10599
|
+
}
|
|
10600
|
+
const ipMaskKeywordMatches = content.matchAll(IPV4_WITH_MASK_KEYWORD_PATTERN);
|
|
10601
|
+
for (const match of ipMaskKeywordMatches) {
|
|
10602
|
+
const ip = match[1];
|
|
10603
|
+
const mask = match[2];
|
|
10604
|
+
if (ip && mask && isValidIPv4(ip) && isSubnetMask(mask)) {
|
|
10605
|
+
const normalizedIP = normalizeIPv4(ip);
|
|
10606
|
+
const prefix = maskToCidr(mask);
|
|
10607
|
+
if (prefix >= 0) {
|
|
10608
|
+
ipv4SubnetSet.add(`${normalizedIP}/${prefix}`);
|
|
10609
|
+
ipsWithMasks.add(normalizedIP);
|
|
10610
|
+
}
|
|
10611
|
+
}
|
|
10612
|
+
}
|
|
10613
|
+
const ipWildcardMatches = content.matchAll(IPV4_WITH_WILDCARD_PATTERN);
|
|
10614
|
+
for (const match of ipWildcardMatches) {
|
|
10615
|
+
const ip = match[1];
|
|
10616
|
+
const wildcard = match[2];
|
|
10617
|
+
if (ip && wildcard && isValidIPv4(ip) && isWildcardMask(wildcard)) {
|
|
10618
|
+
const normalizedIP = normalizeIPv4(ip);
|
|
10619
|
+
const prefix = wildcardToCidr(wildcard);
|
|
10620
|
+
if (prefix >= 0) {
|
|
10621
|
+
ipv4SubnetSet.add(`${normalizedIP}/${prefix}`);
|
|
10622
|
+
ipsWithMasks.add(normalizedIP);
|
|
10623
|
+
}
|
|
10624
|
+
}
|
|
10625
|
+
}
|
|
10626
|
+
}
|
|
10627
|
+
const ipv4Matches = content.matchAll(IPV4_PATTERN);
|
|
10628
|
+
for (const match of ipv4Matches) {
|
|
10629
|
+
const ip = match[0];
|
|
10630
|
+
if (isValidIPv4(ip)) {
|
|
10631
|
+
const normalized = normalizeIPv4(ip);
|
|
10632
|
+
if (!subnetNetworks.has(normalized) && !ipsWithMasks.has(normalized) && !isSubnetMask(normalized) && !isWildcardMask(normalized)) {
|
|
10633
|
+
ipv4Set.add(normalized);
|
|
10634
|
+
}
|
|
10635
|
+
}
|
|
10636
|
+
}
|
|
10637
|
+
if (!options.skipIPv6) {
|
|
10638
|
+
if (!options.skipSubnets) {
|
|
10639
|
+
const ipv6CidrMatches = content.matchAll(IPV6_CIDR_PATTERN);
|
|
10640
|
+
for (const match of ipv6CidrMatches) {
|
|
10641
|
+
const subnet = match[0];
|
|
10642
|
+
if (isValidSubnet(subnet)) {
|
|
10643
|
+
const { network, prefix } = parseSubnet(subnet);
|
|
10644
|
+
const normalizedNetwork = normalizeIPv6(network);
|
|
10645
|
+
ipv6SubnetSet.add(`${normalizedNetwork}/${prefix}`);
|
|
10646
|
+
subnetNetworks.add(normalizedNetwork);
|
|
10647
|
+
}
|
|
10648
|
+
}
|
|
10649
|
+
}
|
|
10650
|
+
const ipv6Matches = content.matchAll(IPV6_PATTERN);
|
|
10651
|
+
for (const match of ipv6Matches) {
|
|
10652
|
+
const ip = match[0];
|
|
10653
|
+
if (isValidIPv6(ip)) {
|
|
10654
|
+
const normalized = normalizeIPv6(ip);
|
|
10655
|
+
if (!subnetNetworks.has(normalized)) {
|
|
10656
|
+
ipv6Set.add(normalized);
|
|
10657
|
+
}
|
|
10658
|
+
}
|
|
10659
|
+
}
|
|
10660
|
+
}
|
|
10661
|
+
const ipv4Addresses = sortIPv4Addresses([...ipv4Set]);
|
|
10662
|
+
const ipv6Addresses = sortIPv6Addresses([...ipv6Set]);
|
|
10663
|
+
const ipv4Subnets = sortSubnets([...ipv4SubnetSet], "ipv4");
|
|
10664
|
+
const ipv6Subnets = sortSubnets([...ipv6SubnetSet], "ipv6");
|
|
10665
|
+
const counts = {
|
|
10666
|
+
ipv4: ipv4Addresses.length,
|
|
10667
|
+
ipv6: ipv6Addresses.length,
|
|
10668
|
+
ipv4Subnets: ipv4Subnets.length,
|
|
10669
|
+
ipv6Subnets: ipv6Subnets.length,
|
|
10670
|
+
total: ipv4Addresses.length + ipv6Addresses.length + ipv4Subnets.length + ipv6Subnets.length
|
|
10671
|
+
};
|
|
10672
|
+
return {
|
|
10673
|
+
ipv4Addresses,
|
|
10674
|
+
ipv6Addresses,
|
|
10675
|
+
ipv4Subnets,
|
|
10676
|
+
ipv6Subnets,
|
|
10677
|
+
counts
|
|
10678
|
+
};
|
|
10679
|
+
}
|
|
10680
|
+
|
|
10337
10681
|
// index.ts
|
|
10338
10682
|
import { readFile } from "fs/promises";
|
|
10339
10683
|
import { statSync as statSync2 } from "fs";
|
|
@@ -10341,7 +10685,7 @@ import { resolve as resolve4, dirname as dirname2, basename } from "path";
|
|
|
10341
10685
|
|
|
10342
10686
|
// src/sarif.ts
|
|
10343
10687
|
import { relative } from "path";
|
|
10344
|
-
function generateSarif(results, filePath, rules, options = {}) {
|
|
10688
|
+
function generateSarif(results, filePath, rules, options = {}, ipSummary) {
|
|
10345
10689
|
const fileUri = options.relativePaths ? relative(options.baseDir ?? process.cwd(), filePath) : filePath;
|
|
10346
10690
|
const sarifResults = results.map((result) => {
|
|
10347
10691
|
return {
|
|
@@ -10383,16 +10727,18 @@ function generateSarif(results, filePath, rules, options = {}) {
|
|
|
10383
10727
|
kinds: ["superset"]
|
|
10384
10728
|
}));
|
|
10385
10729
|
}
|
|
10386
|
-
|
|
10730
|
+
const hasCvss = secMeta?.cvssScore !== void 0 || secMeta?.cvssVector;
|
|
10731
|
+
const hasTags = rule.metadata.tags && rule.metadata.tags.length > 0;
|
|
10732
|
+
if (hasCvss || hasTags) {
|
|
10387
10733
|
base.properties = {};
|
|
10388
|
-
if (secMeta
|
|
10734
|
+
if (secMeta?.cvssScore !== void 0) {
|
|
10389
10735
|
base.properties["security-severity"] = String(secMeta.cvssScore);
|
|
10390
10736
|
}
|
|
10391
|
-
if (secMeta
|
|
10737
|
+
if (secMeta?.cvssVector) {
|
|
10392
10738
|
base.properties["cvss-vector"] = secMeta.cvssVector;
|
|
10393
10739
|
}
|
|
10394
|
-
if (
|
|
10395
|
-
base.properties.tags =
|
|
10740
|
+
if (hasTags) {
|
|
10741
|
+
base.properties.tags = rule.metadata.tags.map((t) => t.label);
|
|
10396
10742
|
}
|
|
10397
10743
|
}
|
|
10398
10744
|
return base;
|
|
@@ -10408,7 +10754,7 @@ function generateSarif(results, filePath, rules, options = {}) {
|
|
|
10408
10754
|
tool: {
|
|
10409
10755
|
driver: {
|
|
10410
10756
|
name: "Sentriflow",
|
|
10411
|
-
version: "0.1.
|
|
10757
|
+
version: "0.1.9",
|
|
10412
10758
|
informationUri: "https://github.com/sentriflow/sentriflow",
|
|
10413
10759
|
rules: sarifRules,
|
|
10414
10760
|
// SEC-007: Include CWE taxonomy when rules reference it
|
|
@@ -10435,13 +10781,61 @@ function generateSarif(results, filePath, rules, options = {}) {
|
|
|
10435
10781
|
}
|
|
10436
10782
|
]
|
|
10437
10783
|
},
|
|
10438
|
-
results: sarifResults
|
|
10784
|
+
results: sarifResults,
|
|
10785
|
+
// Include IP summary in properties if available
|
|
10786
|
+
...ipSummary && {
|
|
10787
|
+
properties: {
|
|
10788
|
+
ipSummary
|
|
10789
|
+
}
|
|
10790
|
+
}
|
|
10439
10791
|
}
|
|
10440
10792
|
]
|
|
10441
10793
|
};
|
|
10442
10794
|
return JSON.stringify(report, null, 2);
|
|
10443
10795
|
}
|
|
10796
|
+
function aggregateIPSummaries(summaries) {
|
|
10797
|
+
if (summaries.length === 0) return void 0;
|
|
10798
|
+
const ipv4Set = /* @__PURE__ */ new Set();
|
|
10799
|
+
const ipv6Set = /* @__PURE__ */ new Set();
|
|
10800
|
+
const ipv4SubnetSet = /* @__PURE__ */ new Set();
|
|
10801
|
+
const ipv6SubnetSet = /* @__PURE__ */ new Set();
|
|
10802
|
+
for (const summary of summaries) {
|
|
10803
|
+
for (const ip of summary.ipv4Addresses) ipv4Set.add(ip);
|
|
10804
|
+
for (const ip of summary.ipv6Addresses) ipv6Set.add(ip);
|
|
10805
|
+
for (const subnet of summary.ipv4Subnets) ipv4SubnetSet.add(subnet);
|
|
10806
|
+
for (const subnet of summary.ipv6Subnets) ipv6SubnetSet.add(subnet);
|
|
10807
|
+
}
|
|
10808
|
+
const ipv4Addresses = [...ipv4Set].sort((a, b) => {
|
|
10809
|
+
const aParts = a.split(".").map(Number);
|
|
10810
|
+
const bParts = b.split(".").map(Number);
|
|
10811
|
+
for (let i = 0; i < 4; i++) {
|
|
10812
|
+
if ((aParts[i] ?? 0) !== (bParts[i] ?? 0)) {
|
|
10813
|
+
return (aParts[i] ?? 0) - (bParts[i] ?? 0);
|
|
10814
|
+
}
|
|
10815
|
+
}
|
|
10816
|
+
return 0;
|
|
10817
|
+
});
|
|
10818
|
+
const ipv6Addresses = [...ipv6Set].sort();
|
|
10819
|
+
const ipv4Subnets = [...ipv4SubnetSet].sort();
|
|
10820
|
+
const ipv6Subnets = [...ipv6SubnetSet].sort();
|
|
10821
|
+
return {
|
|
10822
|
+
ipv4Addresses,
|
|
10823
|
+
ipv6Addresses,
|
|
10824
|
+
ipv4Subnets,
|
|
10825
|
+
ipv6Subnets,
|
|
10826
|
+
counts: {
|
|
10827
|
+
ipv4: ipv4Addresses.length,
|
|
10828
|
+
ipv6: ipv6Addresses.length,
|
|
10829
|
+
ipv4Subnets: ipv4Subnets.length,
|
|
10830
|
+
ipv6Subnets: ipv6Subnets.length,
|
|
10831
|
+
total: ipv4Addresses.length + ipv6Addresses.length + ipv4Subnets.length + ipv6Subnets.length
|
|
10832
|
+
}
|
|
10833
|
+
};
|
|
10834
|
+
}
|
|
10444
10835
|
function generateMultiFileSarif(fileResults, rules, options = {}) {
|
|
10836
|
+
const aggregatedIpSummary = aggregateIPSummaries(
|
|
10837
|
+
fileResults.map((fr) => fr.ipSummary).filter((s) => !!s)
|
|
10838
|
+
);
|
|
10445
10839
|
const allSarifResults = fileResults.flatMap(({ filePath, results }) => {
|
|
10446
10840
|
const fileUri = options.relativePaths ? relative(options.baseDir ?? process.cwd(), filePath) : filePath;
|
|
10447
10841
|
return results.map((result) => ({
|
|
@@ -10482,16 +10876,18 @@ function generateMultiFileSarif(fileResults, rules, options = {}) {
|
|
|
10482
10876
|
kinds: ["superset"]
|
|
10483
10877
|
}));
|
|
10484
10878
|
}
|
|
10485
|
-
|
|
10879
|
+
const hasCvss = secMeta?.cvssScore !== void 0 || secMeta?.cvssVector;
|
|
10880
|
+
const hasTags = rule.metadata.tags && rule.metadata.tags.length > 0;
|
|
10881
|
+
if (hasCvss || hasTags) {
|
|
10486
10882
|
base.properties = {};
|
|
10487
|
-
if (secMeta
|
|
10883
|
+
if (secMeta?.cvssScore !== void 0) {
|
|
10488
10884
|
base.properties["security-severity"] = String(secMeta.cvssScore);
|
|
10489
10885
|
}
|
|
10490
|
-
if (secMeta
|
|
10886
|
+
if (secMeta?.cvssVector) {
|
|
10491
10887
|
base.properties["cvss-vector"] = secMeta.cvssVector;
|
|
10492
10888
|
}
|
|
10493
|
-
if (
|
|
10494
|
-
base.properties.tags =
|
|
10889
|
+
if (hasTags) {
|
|
10890
|
+
base.properties.tags = rule.metadata.tags.map((t) => t.label);
|
|
10495
10891
|
}
|
|
10496
10892
|
}
|
|
10497
10893
|
return base;
|
|
@@ -10514,7 +10910,7 @@ function generateMultiFileSarif(fileResults, rules, options = {}) {
|
|
|
10514
10910
|
tool: {
|
|
10515
10911
|
driver: {
|
|
10516
10912
|
name: "Sentriflow",
|
|
10517
|
-
version: "0.1.
|
|
10913
|
+
version: "0.1.9",
|
|
10518
10914
|
informationUri: "https://github.com/sentriflow/sentriflow",
|
|
10519
10915
|
rules: sarifRules,
|
|
10520
10916
|
// SEC-007: Include CWE taxonomy when rules reference it
|
|
@@ -10545,7 +10941,13 @@ function generateMultiFileSarif(fileResults, rules, options = {}) {
|
|
|
10545
10941
|
}
|
|
10546
10942
|
]
|
|
10547
10943
|
},
|
|
10548
|
-
results: allSarifResults
|
|
10944
|
+
results: allSarifResults,
|
|
10945
|
+
// Include aggregated IP summary in properties if available
|
|
10946
|
+
...aggregatedIpSummary && {
|
|
10947
|
+
properties: {
|
|
10948
|
+
ipSummary: aggregatedIpSummary
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10549
10951
|
}
|
|
10550
10952
|
]
|
|
10551
10953
|
};
|
|
@@ -13086,9 +13488,12 @@ var cisco_json_rules_default = {
|
|
|
13086
13488
|
description: "Trunk ports should disable DTP (Dynamic Trunking Protocol)",
|
|
13087
13489
|
remediation: "Add 'switchport nonegotiate' to disable DTP on trunk ports",
|
|
13088
13490
|
security: {
|
|
13089
|
-
cwe: ["CWE-319"]
|
|
13090
|
-
|
|
13091
|
-
|
|
13491
|
+
cwe: ["CWE-319"]
|
|
13492
|
+
},
|
|
13493
|
+
tags: [
|
|
13494
|
+
{ type: "security", label: "vlan-hopping" },
|
|
13495
|
+
{ type: "security", label: "network-security" }
|
|
13496
|
+
]
|
|
13092
13497
|
},
|
|
13093
13498
|
check: {
|
|
13094
13499
|
type: "and",
|
|
@@ -13129,9 +13534,12 @@ var cisco_json_rules_default = {
|
|
|
13129
13534
|
description: "VTY lines should have access-class configured for SSH access control",
|
|
13130
13535
|
remediation: "Add 'access-class <acl> in' to restrict VTY access",
|
|
13131
13536
|
security: {
|
|
13132
|
-
cwe: ["CWE-284"]
|
|
13133
|
-
|
|
13134
|
-
|
|
13537
|
+
cwe: ["CWE-284"]
|
|
13538
|
+
},
|
|
13539
|
+
tags: [
|
|
13540
|
+
{ type: "security", label: "access-control" },
|
|
13541
|
+
{ type: "security", label: "remote-access" }
|
|
13542
|
+
]
|
|
13135
13543
|
},
|
|
13136
13544
|
check: {
|
|
13137
13545
|
type: "child_not_exists",
|
|
@@ -13213,9 +13621,12 @@ var juniper_json_rules_default = {
|
|
|
13213
13621
|
description: "SSH should be configured for version 2 only",
|
|
13214
13622
|
remediation: "Configure 'set system services ssh protocol-version v2'",
|
|
13215
13623
|
security: {
|
|
13216
|
-
cwe: ["CWE-327"]
|
|
13217
|
-
|
|
13218
|
-
|
|
13624
|
+
cwe: ["CWE-327"]
|
|
13625
|
+
},
|
|
13626
|
+
tags: [
|
|
13627
|
+
{ type: "security", label: "ssh" },
|
|
13628
|
+
{ type: "security", label: "encryption" }
|
|
13629
|
+
]
|
|
13219
13630
|
},
|
|
13220
13631
|
check: {
|
|
13221
13632
|
type: "and",
|
|
@@ -13246,9 +13657,12 @@ var juniper_json_rules_default = {
|
|
|
13246
13657
|
description: "Telnet service should be disabled",
|
|
13247
13658
|
remediation: "Remove 'set system services telnet' or add 'delete system services telnet'",
|
|
13248
13659
|
security: {
|
|
13249
|
-
cwe: ["CWE-319"]
|
|
13250
|
-
|
|
13251
|
-
|
|
13660
|
+
cwe: ["CWE-319"]
|
|
13661
|
+
},
|
|
13662
|
+
tags: [
|
|
13663
|
+
{ type: "security", label: "telnet" },
|
|
13664
|
+
{ type: "security", label: "cleartext" }
|
|
13665
|
+
]
|
|
13252
13666
|
},
|
|
13253
13667
|
check: {
|
|
13254
13668
|
type: "helper",
|
|
@@ -14459,7 +14873,7 @@ function isStdinRequested(files) {
|
|
|
14459
14873
|
|
|
14460
14874
|
// index.ts
|
|
14461
14875
|
var program = new Command();
|
|
14462
|
-
program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.1.
|
|
14876
|
+
program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.1.9").argument("[files...]", "Path(s) to configuration file(s) (supports multiple files)").option("--ast", "Output the AST instead of rule results").option("-f, --format <format>", "Output format (json, sarif)", "json").option("-q, --quiet", "Only output failures (suppress passed results)").option("-c, --config <path>", "Path to config file (default: auto-detect)").option("--no-config", "Ignore config file").option("-r, --rules <path>", "Additional rules file to load (legacy)").option("-p, --rule-pack <path>", "Rule pack file to load").option(
|
|
14463
14877
|
"--encrypted-pack <path...>",
|
|
14464
14878
|
"SEC-012: Path(s) to encrypted rule pack(s) (.grpx), can specify multiple"
|
|
14465
14879
|
).option(
|
|
@@ -14752,10 +15166,12 @@ Parsing complete: ${allAsts.length} files`);
|
|
|
14752
15166
|
const passed = results2.filter((r) => r.passed).length;
|
|
14753
15167
|
totalFailures += failures;
|
|
14754
15168
|
totalPassed += passed;
|
|
15169
|
+
const fileIpSummary = extractIPSummary(content2);
|
|
14755
15170
|
allFileResults.push({
|
|
14756
15171
|
filePath: filePath2,
|
|
14757
15172
|
results: results2,
|
|
14758
|
-
vendor: { id: vendor2.id, name: vendor2.name }
|
|
15173
|
+
vendor: { id: vendor2.id, name: vendor2.name },
|
|
15174
|
+
ipSummary: fileIpSummary
|
|
14759
15175
|
});
|
|
14760
15176
|
} catch (err) {
|
|
14761
15177
|
const errMsg = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -14787,7 +15203,8 @@ Parsing complete: ${allAsts.length} files`);
|
|
|
14787
15203
|
files: allFileResults.map((fr) => ({
|
|
14788
15204
|
file: fr.filePath,
|
|
14789
15205
|
vendor: fr.vendor,
|
|
14790
|
-
results: fr.results
|
|
15206
|
+
results: fr.results,
|
|
15207
|
+
ipSummary: fr.ipSummary
|
|
14791
15208
|
}))
|
|
14792
15209
|
};
|
|
14793
15210
|
console.log(JSON.stringify(output, null, 2));
|
|
@@ -14863,17 +15280,19 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
14863
15280
|
if (options.quiet) {
|
|
14864
15281
|
results2 = results2.filter((r) => !r.passed);
|
|
14865
15282
|
}
|
|
15283
|
+
const stdinIpSummary = extractIPSummary(content2);
|
|
14866
15284
|
if (options.format === "sarif") {
|
|
14867
15285
|
const sarifOptions = {
|
|
14868
15286
|
relativePaths: options.relativePaths,
|
|
14869
15287
|
baseDir: process.cwd()
|
|
14870
15288
|
};
|
|
14871
|
-
console.log(generateSarif(results2, "<stdin>", stdinRules, sarifOptions));
|
|
15289
|
+
console.log(generateSarif(results2, "<stdin>", stdinRules, sarifOptions, stdinIpSummary));
|
|
14872
15290
|
} else {
|
|
14873
15291
|
const output = {
|
|
14874
15292
|
file: "<stdin>",
|
|
14875
15293
|
vendor: { id: vendor2.id, name: vendor2.name },
|
|
14876
|
-
results: results2
|
|
15294
|
+
results: results2,
|
|
15295
|
+
ipSummary: stdinIpSummary
|
|
14877
15296
|
};
|
|
14878
15297
|
console.log(JSON.stringify(output, null, 2));
|
|
14879
15298
|
}
|
|
@@ -14932,10 +15351,12 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
14932
15351
|
const passed = results2.filter((r) => r.passed).length;
|
|
14933
15352
|
totalFailures += failures;
|
|
14934
15353
|
totalPassed += passed;
|
|
15354
|
+
const fileIpSummary = extractIPSummary(content2);
|
|
14935
15355
|
allFileResults.push({
|
|
14936
15356
|
filePath: filePath2,
|
|
14937
15357
|
results: results2,
|
|
14938
|
-
vendor: { id: vendor2.id, name: vendor2.name }
|
|
15358
|
+
vendor: { id: vendor2.id, name: vendor2.name },
|
|
15359
|
+
ipSummary: fileIpSummary
|
|
14939
15360
|
});
|
|
14940
15361
|
} catch (err) {
|
|
14941
15362
|
const errMsg = err instanceof Error ? err.message : "Unknown error";
|
|
@@ -14962,7 +15383,8 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
14962
15383
|
files: allFileResults.map((fr) => ({
|
|
14963
15384
|
file: fr.filePath,
|
|
14964
15385
|
vendor: fr.vendor,
|
|
14965
|
-
results: fr.results
|
|
15386
|
+
results: fr.results,
|
|
15387
|
+
ipSummary: fr.ipSummary
|
|
14966
15388
|
}))
|
|
14967
15389
|
};
|
|
14968
15390
|
console.log(JSON.stringify(output, null, 2));
|
|
@@ -15047,13 +15469,14 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
15047
15469
|
if (options.quiet) {
|
|
15048
15470
|
results = results.filter((r) => !r.passed);
|
|
15049
15471
|
}
|
|
15472
|
+
const ipSummary = extractIPSummary(content);
|
|
15050
15473
|
if (options.format === "sarif") {
|
|
15051
15474
|
const sarifOptions = {
|
|
15052
15475
|
relativePaths: options.relativePaths,
|
|
15053
15476
|
baseDir: process.cwd()
|
|
15054
15477
|
};
|
|
15055
15478
|
console.log(
|
|
15056
|
-
generateSarif(results, filePath, singleFileRules, sarifOptions)
|
|
15479
|
+
generateSarif(results, filePath, singleFileRules, sarifOptions, ipSummary)
|
|
15057
15480
|
);
|
|
15058
15481
|
} else {
|
|
15059
15482
|
const output = {
|
|
@@ -15061,7 +15484,8 @@ Scan complete: ${allFileResults.length} files, ${totalFailures} failures, ${tota
|
|
|
15061
15484
|
id: vendor.id,
|
|
15062
15485
|
name: vendor.name
|
|
15063
15486
|
},
|
|
15064
|
-
results
|
|
15487
|
+
results,
|
|
15488
|
+
ipSummary
|
|
15065
15489
|
};
|
|
15066
15490
|
console.log(JSON.stringify(output, null, 2));
|
|
15067
15491
|
}
|