@sentriflow/cli 0.3.2 → 0.4.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/index.js +141 -601
- package/package.json +54 -54
package/dist/index.js
CHANGED
|
@@ -4738,6 +4738,7 @@ __export(helpers_exports, {
|
|
|
4738
4738
|
cumulus: () => cumulus_exports,
|
|
4739
4739
|
equalsIgnoreCase: () => equalsIgnoreCase,
|
|
4740
4740
|
extreme: () => extreme_exports,
|
|
4741
|
+
findParentSection: () => findParentSection,
|
|
4741
4742
|
fortinet: () => fortinet_exports,
|
|
4742
4743
|
getAllVendorModules: () => getAllVendorModules,
|
|
4743
4744
|
getChildCommand: () => getChildCommand,
|
|
@@ -4953,6 +4954,20 @@ var isShutdown = (node) => {
|
|
|
4953
4954
|
return id === "shutdown" || id === "disable";
|
|
4954
4955
|
});
|
|
4955
4956
|
};
|
|
4957
|
+
var findParentSection = (ast, targetNode) => {
|
|
4958
|
+
for (const node of ast) {
|
|
4959
|
+
if (node.type === "section") {
|
|
4960
|
+
if (node.children.some(
|
|
4961
|
+
(child) => child.loc.startLine === targetNode.loc.startLine
|
|
4962
|
+
)) {
|
|
4963
|
+
return node;
|
|
4964
|
+
}
|
|
4965
|
+
const found = findParentSection(node.children, targetNode);
|
|
4966
|
+
if (found) return found;
|
|
4967
|
+
}
|
|
4968
|
+
}
|
|
4969
|
+
return void 0;
|
|
4970
|
+
};
|
|
4956
4971
|
var isInterfaceDefinition = (node) => {
|
|
4957
4972
|
if (!node?.id) return false;
|
|
4958
4973
|
const id = node.id.toLowerCase();
|
|
@@ -6109,6 +6124,7 @@ var getDescription = (node) => {
|
|
|
6109
6124
|
// ../core/src/helpers/cisco/index.ts
|
|
6110
6125
|
var cisco_exports = {};
|
|
6111
6126
|
__export(cisco_exports, {
|
|
6127
|
+
findParentSection: () => findParentSection,
|
|
6112
6128
|
getBgpNeighbors: () => getBgpNeighbors,
|
|
6113
6129
|
getChildCommand: () => getChildCommand,
|
|
6114
6130
|
getChildCommands: () => getChildCommands,
|
|
@@ -6143,6 +6159,7 @@ __export(cisco_exports, {
|
|
|
6143
6159
|
isEndpointPort: () => isEndpointPort,
|
|
6144
6160
|
isExternalFacing: () => isExternalFacing,
|
|
6145
6161
|
isLikelyTrunk: () => isLikelyTrunk,
|
|
6162
|
+
isLineConfigPassword: () => isLineConfigPassword,
|
|
6146
6163
|
isLoopbackInterface: () => isLoopbackInterface,
|
|
6147
6164
|
isPhoneOrAP: () => isPhoneOrAP,
|
|
6148
6165
|
isPhysicalPort: () => isPhysicalPort,
|
|
@@ -6240,6 +6257,12 @@ var hasWeakUsernamePassword = (node) => {
|
|
|
6240
6257
|
}
|
|
6241
6258
|
return false;
|
|
6242
6259
|
};
|
|
6260
|
+
var isLineConfigPassword = (ast, node) => {
|
|
6261
|
+
const parent = findParentSection(ast, node);
|
|
6262
|
+
if (!parent) return false;
|
|
6263
|
+
const parentId = parent.id.toLowerCase();
|
|
6264
|
+
return parentId.startsWith("line vty") || parentId.startsWith("line console") || parentId.startsWith("line aux");
|
|
6265
|
+
};
|
|
6243
6266
|
var getSshVersion = (node) => {
|
|
6244
6267
|
if (includesIgnoreCase(node.rawText, "ip ssh version")) {
|
|
6245
6268
|
const match = node.params.find((p) => p === "1" || p === "2");
|
|
@@ -10010,16 +10033,16 @@ var hasFloodProtection = (zppNode) => {
|
|
|
10010
10033
|
const udp = findStanza6(flood, "udp");
|
|
10011
10034
|
const icmp = findStanza6(flood, "icmp");
|
|
10012
10035
|
const otherIp = findStanza6(flood, "other-ip");
|
|
10013
|
-
const
|
|
10036
|
+
const isEnabled5 = (stanza) => {
|
|
10014
10037
|
if (!stanza) return false;
|
|
10015
10038
|
const enableCmd = getChildCommand(stanza, "enable");
|
|
10016
10039
|
return enableCmd?.id.toLowerCase().includes("yes") ?? false;
|
|
10017
10040
|
};
|
|
10018
10041
|
return {
|
|
10019
|
-
hasSyn:
|
|
10020
|
-
hasUdp:
|
|
10021
|
-
hasIcmp:
|
|
10022
|
-
hasOtherIp:
|
|
10042
|
+
hasSyn: isEnabled5(tcpSyn),
|
|
10043
|
+
hasUdp: isEnabled5(udp),
|
|
10044
|
+
hasIcmp: isEnabled5(icmp),
|
|
10045
|
+
hasOtherIp: isEnabled5(otherIp)
|
|
10023
10046
|
};
|
|
10024
10047
|
};
|
|
10025
10048
|
var hasReconProtection = (zppNode) => {
|
|
@@ -10702,8 +10725,10 @@ var GRX2_EXTENDED_FLAG = 1;
|
|
|
10702
10725
|
var GRX2_PORTABLE_FLAG = 2;
|
|
10703
10726
|
var GRX2_ALGORITHM_AES_256_GCM = 1;
|
|
10704
10727
|
var GRX2_KDF_PBKDF2 = 1;
|
|
10705
|
-
var
|
|
10706
|
-
var
|
|
10728
|
+
var SENTRIFLOW_HOME = join(homedir(), ".sentriflow");
|
|
10729
|
+
var DEFAULT_PACKS_DIRECTORY = join(SENTRIFLOW_HOME, "packs");
|
|
10730
|
+
var DEFAULT_RULES_DIRECTORY = join(SENTRIFLOW_HOME, "rules");
|
|
10731
|
+
var CACHE_DIRECTORY = join(SENTRIFLOW_HOME, "cache");
|
|
10707
10732
|
|
|
10708
10733
|
// ../core/src/grx2-loader/MachineId.ts
|
|
10709
10734
|
var import_node_machine_id = __toESM(require_dist(), 1);
|
|
@@ -10865,7 +10890,6 @@ function parseExtendedHeader(data) {
|
|
|
10865
10890
|
}
|
|
10866
10891
|
async function loadExtendedPack(filePath, licenseKey, machineId2, debug) {
|
|
10867
10892
|
debug?.(`[GRX2Loader] Loading pack: ${filePath}`);
|
|
10868
|
-
debug?.(`[GRX2Loader] License key length: ${licenseKey.length}, first 20 chars: ${licenseKey.substring(0, 20)}...`);
|
|
10869
10893
|
debug?.(`[GRX2Loader] Machine ID: "${machineId2}" (length: ${machineId2.length})`);
|
|
10870
10894
|
const data = await readFile(filePath);
|
|
10871
10895
|
debug?.(`[GRX2Loader] Pack file size: ${data.length} bytes`);
|
|
@@ -12500,7 +12524,7 @@ function generateSarif(results, filePath, rules, options = {}, ipSummary) {
|
|
|
12500
12524
|
tool: {
|
|
12501
12525
|
driver: {
|
|
12502
12526
|
name: "Sentriflow",
|
|
12503
|
-
version: "0.
|
|
12527
|
+
version: "0.4.0",
|
|
12504
12528
|
informationUri: "https://github.com/sentriflow/sentriflow",
|
|
12505
12529
|
rules: sarifRules,
|
|
12506
12530
|
// SEC-007: Include CWE taxonomy when rules reference it
|
|
@@ -12660,7 +12684,7 @@ function generateMultiFileSarif(fileResults, rules, options = {}) {
|
|
|
12660
12684
|
tool: {
|
|
12661
12685
|
driver: {
|
|
12662
12686
|
name: "Sentriflow",
|
|
12663
|
-
version: "0.
|
|
12687
|
+
version: "0.4.0",
|
|
12664
12688
|
informationUri: "https://github.com/sentriflow/sentriflow",
|
|
12665
12689
|
rules: sarifRules,
|
|
12666
12690
|
// SEC-007: Include CWE taxonomy when rules reference it
|
|
@@ -12874,10 +12898,10 @@ var InterfaceDescriptionRequired = {
|
|
|
12874
12898
|
loc: node.loc
|
|
12875
12899
|
};
|
|
12876
12900
|
}
|
|
12877
|
-
const
|
|
12901
|
+
const hasDescription5 = node.children.some(
|
|
12878
12902
|
(child) => startsWithIgnoreCase(child.id, "description")
|
|
12879
12903
|
);
|
|
12880
|
-
if (!
|
|
12904
|
+
if (!hasDescription5) {
|
|
12881
12905
|
return {
|
|
12882
12906
|
passed: false,
|
|
12883
12907
|
message: `Interface "${node.params.slice(1).join(" ")}" is missing a description.`,
|
|
@@ -12902,59 +12926,6 @@ var allCommonRules = [
|
|
|
12902
12926
|
InterfaceDescriptionRequired
|
|
12903
12927
|
];
|
|
12904
12928
|
|
|
12905
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/common/validation.ts
|
|
12906
|
-
var equalsIgnoreCase2 = (a, b) => a != null && b != null && a.toLowerCase() === b.toLowerCase();
|
|
12907
|
-
var includesIgnoreCase2 = (haystack, needle) => haystack != null && needle != null && haystack.toLowerCase().includes(needle.toLowerCase());
|
|
12908
|
-
var startsWithIgnoreCase2 = (str, prefix) => str != null && prefix != null && str.toLowerCase().startsWith(prefix.toLowerCase());
|
|
12909
|
-
|
|
12910
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/common/helpers.ts
|
|
12911
|
-
var hasChildCommand5 = (node, prefix) => {
|
|
12912
|
-
if (!node?.children || !prefix) return false;
|
|
12913
|
-
return node.children.some(
|
|
12914
|
-
(child) => child?.id?.toLowerCase().startsWith(prefix.toLowerCase()) ?? false
|
|
12915
|
-
);
|
|
12916
|
-
};
|
|
12917
|
-
var getChildCommand5 = (node, prefix) => {
|
|
12918
|
-
if (!node?.children || !prefix) return void 0;
|
|
12919
|
-
return node.children.find(
|
|
12920
|
-
(child) => child?.id?.toLowerCase().startsWith(prefix.toLowerCase()) ?? false
|
|
12921
|
-
);
|
|
12922
|
-
};
|
|
12923
|
-
var isShutdown6 = (node) => {
|
|
12924
|
-
if (!node?.children) return false;
|
|
12925
|
-
return node.children.some((child) => {
|
|
12926
|
-
const id = child?.id?.toLowerCase().trim();
|
|
12927
|
-
return id === "shutdown" || id === "disable";
|
|
12928
|
-
});
|
|
12929
|
-
};
|
|
12930
|
-
|
|
12931
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/cisco/helpers.ts
|
|
12932
|
-
var isShutdown7 = (node) => {
|
|
12933
|
-
if (!node?.children) return false;
|
|
12934
|
-
return node.children.some((child) => {
|
|
12935
|
-
return child?.id && equalsIgnoreCase2(child.id.trim(), "shutdown");
|
|
12936
|
-
});
|
|
12937
|
-
};
|
|
12938
|
-
var isPhysicalPort4 = (interfaceName) => {
|
|
12939
|
-
return !includesIgnoreCase2(interfaceName, "loopback") && !includesIgnoreCase2(interfaceName, "null") && !includesIgnoreCase2(interfaceName, "vlan") && !includesIgnoreCase2(interfaceName, "tunnel") && !includesIgnoreCase2(interfaceName, "port-channel") && !includesIgnoreCase2(interfaceName, "bvi") && !includesIgnoreCase2(interfaceName, "nve");
|
|
12940
|
-
};
|
|
12941
|
-
var isTrunkPort4 = (node) => {
|
|
12942
|
-
return hasChildCommand5(node, "switchport mode trunk");
|
|
12943
|
-
};
|
|
12944
|
-
var isTrunkToNonCisco2 = (node) => {
|
|
12945
|
-
const desc = getChildCommand5(node, "description");
|
|
12946
|
-
if (desc) {
|
|
12947
|
-
const descText = desc.rawText;
|
|
12948
|
-
if (includesIgnoreCase2(descText, "server:") || includesIgnoreCase2(descText, "storage:") || includesIgnoreCase2(descText, "esx") || includesIgnoreCase2(descText, "vmware") || includesIgnoreCase2(descText, "hyperv") || includesIgnoreCase2(descText, "hyper-v") || includesIgnoreCase2(descText, "linux") || includesIgnoreCase2(descText, "appliance") || includesIgnoreCase2(descText, "firewall") || includesIgnoreCase2(descText, "loadbalancer") || includesIgnoreCase2(descText, "lb:") || includesIgnoreCase2(descText, "nas:") || includesIgnoreCase2(descText, "san:")) {
|
|
12949
|
-
return true;
|
|
12950
|
-
}
|
|
12951
|
-
if (includesIgnoreCase2(descText, "uplink:") || includesIgnoreCase2(descText, "downlink:") || includesIgnoreCase2(descText, "isl:") || includesIgnoreCase2(descText, "po-member:")) {
|
|
12952
|
-
return false;
|
|
12953
|
-
}
|
|
12954
|
-
}
|
|
12955
|
-
return false;
|
|
12956
|
-
};
|
|
12957
|
-
|
|
12958
12929
|
// ../rules-default/src/cisco/ios-rules.ts
|
|
12959
12930
|
var TrunkNoDTP = {
|
|
12960
12931
|
id: "NET-TRUNK-001",
|
|
@@ -12968,16 +12939,16 @@ var TrunkNoDTP = {
|
|
|
12968
12939
|
remediation: 'Add "switchport nonegotiate" to disable DTP on trunk ports connected to non-Cisco devices.'
|
|
12969
12940
|
},
|
|
12970
12941
|
check: (node) => {
|
|
12971
|
-
if (!
|
|
12942
|
+
if (!isPhysicalPort(node.id) || isShutdown3(node)) {
|
|
12972
12943
|
return { passed: true, message: "Not applicable.", ruleId: "NET-TRUNK-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
12973
12944
|
}
|
|
12974
|
-
if (!
|
|
12945
|
+
if (!isTrunkPort2(node)) {
|
|
12975
12946
|
return { passed: true, message: "Not a trunk port.", ruleId: "NET-TRUNK-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
12976
12947
|
}
|
|
12977
|
-
if (!
|
|
12948
|
+
if (!isTrunkToNonCisco(node)) {
|
|
12978
12949
|
return { passed: true, message: "Trunk to Cisco device - DTP acceptable.", ruleId: "NET-TRUNK-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
12979
12950
|
}
|
|
12980
|
-
if (!
|
|
12951
|
+
if (!hasChildCommand(node, "switchport nonegotiate")) {
|
|
12981
12952
|
return {
|
|
12982
12953
|
passed: false,
|
|
12983
12954
|
message: `Trunk port "${node.params.slice(1).join(" ")}" connected to non-Cisco device needs "switchport nonegotiate".`,
|
|
@@ -13002,11 +12973,11 @@ var AccessExplicitMode = {
|
|
|
13002
12973
|
remediation: 'Add "switchport mode access" to explicitly configure access mode.'
|
|
13003
12974
|
},
|
|
13004
12975
|
check: (node) => {
|
|
13005
|
-
if (!
|
|
12976
|
+
if (!isPhysicalPort(node.id) || isShutdown3(node)) {
|
|
13006
12977
|
return { passed: true, message: "Not applicable.", ruleId: "NET-ACCESS-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13007
12978
|
}
|
|
13008
|
-
const hasAccessVlan =
|
|
13009
|
-
const hasExplicitMode =
|
|
12979
|
+
const hasAccessVlan = hasChildCommand(node, "switchport access vlan");
|
|
12980
|
+
const hasExplicitMode = hasChildCommand(node, "switchport mode");
|
|
13010
12981
|
if (hasAccessVlan && !hasExplicitMode) {
|
|
13011
12982
|
return {
|
|
13012
12983
|
passed: false,
|
|
@@ -13066,15 +13037,26 @@ var CiscoNoPlaintextPasswords = {
|
|
|
13066
13037
|
level: "error",
|
|
13067
13038
|
obu: "Security",
|
|
13068
13039
|
owner: "SecOps",
|
|
13069
|
-
remediation: 'Use "secret" instead of "password", or
|
|
13040
|
+
remediation: 'Use "secret" instead of "password", or encrypt with type 7/8/9.'
|
|
13070
13041
|
},
|
|
13071
|
-
check: (node) => {
|
|
13042
|
+
check: (node, context) => {
|
|
13072
13043
|
const params = node.params;
|
|
13073
|
-
const nodeId = node.id;
|
|
13074
|
-
if (
|
|
13044
|
+
const nodeId = node.id.toLowerCase();
|
|
13045
|
+
if (nodeId.includes("encryption") || nodeId.includes("service")) {
|
|
13075
13046
|
return {
|
|
13076
13047
|
passed: true,
|
|
13077
|
-
message: "Global password configuration
|
|
13048
|
+
message: "Global password configuration.",
|
|
13049
|
+
ruleId: "NET-SEC-001",
|
|
13050
|
+
nodeId: node.id,
|
|
13051
|
+
level: "info",
|
|
13052
|
+
loc: node.loc
|
|
13053
|
+
};
|
|
13054
|
+
}
|
|
13055
|
+
const ast = context.getAst?.();
|
|
13056
|
+
if (ast && isLineConfigPassword(ast, node)) {
|
|
13057
|
+
return {
|
|
13058
|
+
passed: true,
|
|
13059
|
+
message: "Line password - use AAA authentication for security (line passwords cannot be encrypted).",
|
|
13078
13060
|
ruleId: "NET-SEC-001",
|
|
13079
13061
|
nodeId: node.id,
|
|
13080
13062
|
level: "info",
|
|
@@ -13083,20 +13065,10 @@ var CiscoNoPlaintextPasswords = {
|
|
|
13083
13065
|
}
|
|
13084
13066
|
if (params.length >= 2) {
|
|
13085
13067
|
const typeOrValue = params[1];
|
|
13086
|
-
if (
|
|
13087
|
-
return {
|
|
13088
|
-
passed: false,
|
|
13089
|
-
message: 'Possible plaintext password detected. Use encryption type 7 or "secret" command.',
|
|
13090
|
-
ruleId: "NET-SEC-001",
|
|
13091
|
-
nodeId: node.id,
|
|
13092
|
-
level: "error",
|
|
13093
|
-
loc: node.loc
|
|
13094
|
-
};
|
|
13095
|
-
}
|
|
13096
|
-
if (typeOrValue === "7" || typeOrValue === "5" || typeOrValue === "8" || typeOrValue === "9") {
|
|
13068
|
+
if (typeOrValue && ["5", "7", "8", "9"].includes(typeOrValue)) {
|
|
13097
13069
|
return {
|
|
13098
13070
|
passed: true,
|
|
13099
|
-
message:
|
|
13071
|
+
message: `Password is encrypted (type ${typeOrValue}).`,
|
|
13100
13072
|
ruleId: "NET-SEC-001",
|
|
13101
13073
|
nodeId: node.id,
|
|
13102
13074
|
level: "info",
|
|
@@ -13106,17 +13078,17 @@ var CiscoNoPlaintextPasswords = {
|
|
|
13106
13078
|
if (typeOrValue === "0") {
|
|
13107
13079
|
return {
|
|
13108
13080
|
passed: false,
|
|
13109
|
-
message:
|
|
13081
|
+
message: 'Plaintext password detected (type 0). Use "secret" or encrypt with type 7.',
|
|
13110
13082
|
ruleId: "NET-SEC-001",
|
|
13111
13083
|
nodeId: node.id,
|
|
13112
13084
|
level: "error",
|
|
13113
13085
|
loc: node.loc
|
|
13114
13086
|
};
|
|
13115
13087
|
}
|
|
13116
|
-
if (!/^\d+$/.test(typeOrValue)) {
|
|
13088
|
+
if (typeOrValue && !/^\d+$/.test(typeOrValue)) {
|
|
13117
13089
|
return {
|
|
13118
13090
|
passed: false,
|
|
13119
|
-
message: '
|
|
13091
|
+
message: 'Plaintext password detected. Use "secret" or encrypt with type 7.',
|
|
13120
13092
|
ruleId: "NET-SEC-001",
|
|
13121
13093
|
nodeId: node.id,
|
|
13122
13094
|
level: "error",
|
|
@@ -13144,38 +13116,6 @@ var allCiscoRules = [
|
|
|
13144
13116
|
EnableSecretStrong
|
|
13145
13117
|
];
|
|
13146
13118
|
|
|
13147
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/juniper/helpers.ts
|
|
13148
|
-
var findStanza8 = (node, stanzaName) => {
|
|
13149
|
-
if (!node?.children) return void 0;
|
|
13150
|
-
return node.children.find(
|
|
13151
|
-
(child) => child?.id && equalsIgnoreCase2(child.id, stanzaName)
|
|
13152
|
-
);
|
|
13153
|
-
};
|
|
13154
|
-
var findStanzas7 = (node, pattern) => {
|
|
13155
|
-
if (!node?.children) return [];
|
|
13156
|
-
return node.children.filter((child) => child?.id && pattern.test(child.id));
|
|
13157
|
-
};
|
|
13158
|
-
var isFilterTermDrop2 = (termNode) => {
|
|
13159
|
-
if (!termNode?.children) return false;
|
|
13160
|
-
for (const child of termNode.children) {
|
|
13161
|
-
const id = child?.id?.trim();
|
|
13162
|
-
if (!id) continue;
|
|
13163
|
-
if (equalsIgnoreCase2(id, "then discard") || equalsIgnoreCase2(id, "then discard;") || equalsIgnoreCase2(id, "then reject") || equalsIgnoreCase2(id, "then reject;")) {
|
|
13164
|
-
return true;
|
|
13165
|
-
}
|
|
13166
|
-
}
|
|
13167
|
-
const thenStanza = findStanza8(termNode, "then");
|
|
13168
|
-
if (!thenStanza?.children) return false;
|
|
13169
|
-
for (const child of thenStanza.children) {
|
|
13170
|
-
const id = child?.id?.trim();
|
|
13171
|
-
if (!id) continue;
|
|
13172
|
-
if (equalsIgnoreCase2(id, "discard") || equalsIgnoreCase2(id, "discard;") || equalsIgnoreCase2(id, "reject") || equalsIgnoreCase2(id, "reject;")) {
|
|
13173
|
-
return true;
|
|
13174
|
-
}
|
|
13175
|
-
}
|
|
13176
|
-
return false;
|
|
13177
|
-
};
|
|
13178
|
-
|
|
13179
13119
|
// ../rules-default/src/juniper/junos-rules.ts
|
|
13180
13120
|
var RootAuthRequired = {
|
|
13181
13121
|
id: "JUN-SYS-001",
|
|
@@ -13189,7 +13129,7 @@ var RootAuthRequired = {
|
|
|
13189
13129
|
remediation: 'Configure "root-authentication" under system stanza with encrypted-password or ssh-rsa.'
|
|
13190
13130
|
},
|
|
13191
13131
|
check: (node) => {
|
|
13192
|
-
const rootAuth =
|
|
13132
|
+
const rootAuth = findStanza4(node, "root-authentication");
|
|
13193
13133
|
if (!rootAuth) {
|
|
13194
13134
|
return {
|
|
13195
13135
|
passed: false,
|
|
@@ -13200,8 +13140,8 @@ var RootAuthRequired = {
|
|
|
13200
13140
|
loc: node.loc
|
|
13201
13141
|
};
|
|
13202
13142
|
}
|
|
13203
|
-
const hasPassword =
|
|
13204
|
-
const hasSshKey =
|
|
13143
|
+
const hasPassword = hasChildCommand(rootAuth, "encrypted-password");
|
|
13144
|
+
const hasSshKey = hasChildCommand(rootAuth, "ssh-rsa") || hasChildCommand(rootAuth, "ssh-ecdsa");
|
|
13205
13145
|
if (!hasPassword && !hasSshKey) {
|
|
13206
13146
|
return {
|
|
13207
13147
|
passed: false,
|
|
@@ -13234,7 +13174,7 @@ var JunosBgpRouterId = {
|
|
|
13234
13174
|
remediation: 'Configure "router-id" under routing-options stanza.'
|
|
13235
13175
|
},
|
|
13236
13176
|
check: (node) => {
|
|
13237
|
-
const hasRouterId =
|
|
13177
|
+
const hasRouterId = hasChildCommand(node, "router-id");
|
|
13238
13178
|
if (!hasRouterId) {
|
|
13239
13179
|
return {
|
|
13240
13180
|
passed: false,
|
|
@@ -13285,7 +13225,7 @@ var JunosFirewallDefaultDeny = {
|
|
|
13285
13225
|
}
|
|
13286
13226
|
const issues = [];
|
|
13287
13227
|
for (const filter of filters) {
|
|
13288
|
-
const terms =
|
|
13228
|
+
const terms = findStanzas3(filter, /^term/i);
|
|
13289
13229
|
if (terms.length === 0) {
|
|
13290
13230
|
continue;
|
|
13291
13231
|
}
|
|
@@ -13293,7 +13233,7 @@ var JunosFirewallDefaultDeny = {
|
|
|
13293
13233
|
if (!lastTerm) {
|
|
13294
13234
|
continue;
|
|
13295
13235
|
}
|
|
13296
|
-
if (!
|
|
13236
|
+
if (!isFilterTermDrop(lastTerm)) {
|
|
13297
13237
|
issues.push(`Filter "${filter.id}" does not end with a deny term.`);
|
|
13298
13238
|
}
|
|
13299
13239
|
}
|
|
@@ -13326,69 +13266,6 @@ var allJuniperRules = [
|
|
|
13326
13266
|
JunosFirewallDefaultDeny
|
|
13327
13267
|
];
|
|
13328
13268
|
|
|
13329
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/aruba/helpers.ts
|
|
13330
|
-
var getInterfaceName4 = (node) => {
|
|
13331
|
-
if (!node?.id) return void 0;
|
|
13332
|
-
const match = node.id.match(/interface\s+(.+)/i);
|
|
13333
|
-
const ifName = match?.[1]?.trim();
|
|
13334
|
-
return ifName && ifName.length > 0 ? ifName : void 0;
|
|
13335
|
-
};
|
|
13336
|
-
var isAosCxPhysicalPort2 = (interfaceName) => {
|
|
13337
|
-
return /^\d+\/\d+\/\d+$/.test(interfaceName.trim());
|
|
13338
|
-
};
|
|
13339
|
-
var isAosCxLag2 = (interfaceName) => {
|
|
13340
|
-
return /^lag\s*\d+$/i.test(interfaceName.trim());
|
|
13341
|
-
};
|
|
13342
|
-
var isAosCxTrunk2 = (node) => {
|
|
13343
|
-
return hasChildCommand5(node, "vlan trunk");
|
|
13344
|
-
};
|
|
13345
|
-
var getAosCxTrunkAllowed2 = (node) => {
|
|
13346
|
-
const cmd = getChildCommand5(node, "vlan trunk allowed");
|
|
13347
|
-
if (!cmd) return [];
|
|
13348
|
-
const match = cmd.id.match(/vlan\s+trunk\s+allowed\s+([\d,]+)/i);
|
|
13349
|
-
const vlanList = match?.[1];
|
|
13350
|
-
if (!vlanList) return [];
|
|
13351
|
-
return vlanList.split(",").map((v) => parseInt(v.trim(), 10)).filter((v) => !isNaN(v));
|
|
13352
|
-
};
|
|
13353
|
-
var getAosSwitchVlanName2 = (node) => {
|
|
13354
|
-
const cmd = getChildCommand5(node, "name");
|
|
13355
|
-
if (!cmd) return void 0;
|
|
13356
|
-
const match = cmd.id.match(/name\s+["']?([^"']+)["']?/i);
|
|
13357
|
-
const name = match?.[1];
|
|
13358
|
-
return name?.trim();
|
|
13359
|
-
};
|
|
13360
|
-
var getWlanEncryption2 = (node) => {
|
|
13361
|
-
const cmd = getChildCommand5(node, "opmode");
|
|
13362
|
-
if (!cmd) return null;
|
|
13363
|
-
const match = cmd.id.match(/opmode\s+(\S+)/i);
|
|
13364
|
-
const mode = match?.[1];
|
|
13365
|
-
return mode ? mode.toLowerCase() : null;
|
|
13366
|
-
};
|
|
13367
|
-
var hasSecureEncryption2 = (node) => {
|
|
13368
|
-
const opmode = getWlanEncryption2(node);
|
|
13369
|
-
if (!opmode) return false;
|
|
13370
|
-
return opmode.includes("wpa2") || opmode.includes("wpa3") || opmode.includes("aes");
|
|
13371
|
-
};
|
|
13372
|
-
var isOpenSsid2 = (node) => {
|
|
13373
|
-
const opmode = getWlanEncryption2(node);
|
|
13374
|
-
return opmode === "opensystem" || opmode === "open";
|
|
13375
|
-
};
|
|
13376
|
-
var getRadiusHost2 = (node) => {
|
|
13377
|
-
const cmd = getChildCommand5(node, "host");
|
|
13378
|
-
if (!cmd) return void 0;
|
|
13379
|
-
const match = cmd.id.match(/host\s+(\S+)/i);
|
|
13380
|
-
const host = match?.[1];
|
|
13381
|
-
return host?.trim();
|
|
13382
|
-
};
|
|
13383
|
-
var extractProfileName2 = (nodeId) => {
|
|
13384
|
-
const match = nodeId.match(/(?:ssid-profile|virtual-ap|profile|server-group|ap-group|arm-profile)\s+["']?([^"'\n]+)["']?$/i);
|
|
13385
|
-
const profile = match?.[1];
|
|
13386
|
-
return profile ? profile.trim() : void 0;
|
|
13387
|
-
};
|
|
13388
|
-
var hasDescription5 = (node) => {
|
|
13389
|
-
return hasChildCommand5(node, "description");
|
|
13390
|
-
};
|
|
13391
|
-
|
|
13392
13269
|
// ../rules-default/src/aruba/common-rules.ts
|
|
13393
13270
|
var SshEnabled = {
|
|
13394
13271
|
id: "ARU-SEC-001",
|
|
@@ -13476,17 +13353,17 @@ var AosCxInterfaceDescription = {
|
|
|
13476
13353
|
remediation: "Add a description to physical interfaces for documentation."
|
|
13477
13354
|
},
|
|
13478
13355
|
check: (node) => {
|
|
13479
|
-
const ifName =
|
|
13356
|
+
const ifName = getInterfaceName(node);
|
|
13480
13357
|
if (!ifName) {
|
|
13481
13358
|
return { passed: true, message: "Not an interface.", ruleId: "AOSCX-IF-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13482
13359
|
}
|
|
13483
|
-
if (!
|
|
13360
|
+
if (!isAosCxPhysicalPort(ifName) && !isAosCxLag(ifName)) {
|
|
13484
13361
|
return { passed: true, message: "Not a physical interface.", ruleId: "AOSCX-IF-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13485
13362
|
}
|
|
13486
|
-
if (
|
|
13363
|
+
if (isShutdown(node)) {
|
|
13487
13364
|
return { passed: true, message: "Interface is shutdown.", ruleId: "AOSCX-IF-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13488
13365
|
}
|
|
13489
|
-
if (!
|
|
13366
|
+
if (!hasDescription(node)) {
|
|
13490
13367
|
return {
|
|
13491
13368
|
passed: false,
|
|
13492
13369
|
message: `Interface ${ifName} missing description.`,
|
|
@@ -13518,17 +13395,17 @@ var AosCxTrunkAllowedVlans = {
|
|
|
13518
13395
|
remediation: 'Configure "vlan trunk allowed <vlans>" on trunk interfaces.'
|
|
13519
13396
|
},
|
|
13520
13397
|
check: (node) => {
|
|
13521
|
-
const ifName =
|
|
13398
|
+
const ifName = getInterfaceName(node);
|
|
13522
13399
|
if (!ifName) {
|
|
13523
13400
|
return { passed: true, message: "Not an interface.", ruleId: "AOSCX-L2-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13524
13401
|
}
|
|
13525
|
-
if (!
|
|
13402
|
+
if (!isAosCxPhysicalPort(ifName) && !isAosCxLag(ifName)) {
|
|
13526
13403
|
return { passed: true, message: "Not a switchport interface.", ruleId: "AOSCX-L2-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13527
13404
|
}
|
|
13528
|
-
if (!
|
|
13405
|
+
if (!isAosCxTrunk(node)) {
|
|
13529
13406
|
return { passed: true, message: "Not a trunk port.", ruleId: "AOSCX-L2-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13530
13407
|
}
|
|
13531
|
-
const allowedVlans =
|
|
13408
|
+
const allowedVlans = getAosCxTrunkAllowed(node);
|
|
13532
13409
|
if (allowedVlans.length === 0) {
|
|
13533
13410
|
return {
|
|
13534
13411
|
passed: false,
|
|
@@ -13578,7 +13455,7 @@ var AosSwitchVlanName = {
|
|
|
13578
13455
|
if (vlanId === null || isDefaultVlan(vlanId)) {
|
|
13579
13456
|
return { passed: true, message: "Default VLAN 1.", ruleId: "AOSSW-L2-001", nodeId: node.id, level: "info", loc: node.loc };
|
|
13580
13457
|
}
|
|
13581
|
-
const vlanName =
|
|
13458
|
+
const vlanName = getAosSwitchVlanName(node);
|
|
13582
13459
|
if (!vlanName) {
|
|
13583
13460
|
return {
|
|
13584
13461
|
passed: false,
|
|
@@ -13652,8 +13529,8 @@ var WlcSsidEncryption = {
|
|
|
13652
13529
|
remediation: 'Configure "opmode wpa2-aes" or "opmode wpa3-sae-aes" for secure encryption.'
|
|
13653
13530
|
},
|
|
13654
13531
|
check: (node) => {
|
|
13655
|
-
const profileName =
|
|
13656
|
-
const opmode =
|
|
13532
|
+
const profileName = extractProfileName(node.id);
|
|
13533
|
+
const opmode = getWlanEncryption(node);
|
|
13657
13534
|
if (!opmode) {
|
|
13658
13535
|
return {
|
|
13659
13536
|
passed: false,
|
|
@@ -13664,7 +13541,7 @@ var WlcSsidEncryption = {
|
|
|
13664
13541
|
loc: node.loc
|
|
13665
13542
|
};
|
|
13666
13543
|
}
|
|
13667
|
-
if (
|
|
13544
|
+
if (isOpenSsid(node)) {
|
|
13668
13545
|
return {
|
|
13669
13546
|
passed: false,
|
|
13670
13547
|
message: `SSID profile "${profileName}" is open/unencrypted. Use WPA2 or WPA3.`,
|
|
@@ -13674,7 +13551,7 @@ var WlcSsidEncryption = {
|
|
|
13674
13551
|
loc: node.loc
|
|
13675
13552
|
};
|
|
13676
13553
|
}
|
|
13677
|
-
if (!
|
|
13554
|
+
if (!hasSecureEncryption(node)) {
|
|
13678
13555
|
return {
|
|
13679
13556
|
passed: false,
|
|
13680
13557
|
message: `SSID profile "${profileName}" uses weak encryption (${opmode}). Use WPA2 or WPA3.`,
|
|
@@ -13706,8 +13583,8 @@ var WlcRadiusHost = {
|
|
|
13706
13583
|
remediation: 'Configure "host <ip-address>" for the RADIUS server.'
|
|
13707
13584
|
},
|
|
13708
13585
|
check: (node) => {
|
|
13709
|
-
const profileName =
|
|
13710
|
-
const host =
|
|
13586
|
+
const profileName = extractProfileName(node.id);
|
|
13587
|
+
const host = getRadiusHost(node);
|
|
13711
13588
|
if (!host) {
|
|
13712
13589
|
return {
|
|
13713
13590
|
passed: false,
|
|
@@ -13755,34 +13632,6 @@ function getRulesByArubaVendor(vendorId) {
|
|
|
13755
13632
|
}
|
|
13756
13633
|
}
|
|
13757
13634
|
|
|
13758
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/paloalto/helpers.ts
|
|
13759
|
-
var findStanza9 = (node, stanzaName) => {
|
|
13760
|
-
if (!node?.children) return void 0;
|
|
13761
|
-
return node.children.find(
|
|
13762
|
-
(child) => child?.id?.toLowerCase() === stanzaName.toLowerCase()
|
|
13763
|
-
);
|
|
13764
|
-
};
|
|
13765
|
-
var hasLogging3 = (ruleNode) => {
|
|
13766
|
-
const logStart = hasChildCommand5(ruleNode, "log-start");
|
|
13767
|
-
const logEnd = hasChildCommand5(ruleNode, "log-end");
|
|
13768
|
-
return { logStart, logEnd };
|
|
13769
|
-
};
|
|
13770
|
-
var isRuleDisabled2 = (ruleNode) => {
|
|
13771
|
-
const disabled = getChildCommand5(ruleNode, "disabled");
|
|
13772
|
-
if (!disabled) return false;
|
|
13773
|
-
return disabled.id.toLowerCase().includes("yes") || disabled.id.toLowerCase().includes("true");
|
|
13774
|
-
};
|
|
13775
|
-
var getSecurityRules2 = (rulebaseNode) => {
|
|
13776
|
-
const security = findStanza9(rulebaseNode, "security");
|
|
13777
|
-
if (!security) return [];
|
|
13778
|
-
const rules = findStanza9(security, "rules");
|
|
13779
|
-
if (!rules?.children) return [];
|
|
13780
|
-
return rules.children;
|
|
13781
|
-
};
|
|
13782
|
-
var hasZoneProtectionProfile2 = (zoneNode) => {
|
|
13783
|
-
return hasChildCommand5(zoneNode, "zone-protection-profile");
|
|
13784
|
-
};
|
|
13785
|
-
|
|
13786
13635
|
// ../rules-default/src/paloalto/panos-rules.ts
|
|
13787
13636
|
var HostnameRequired = {
|
|
13788
13637
|
id: "PAN-SYS-001",
|
|
@@ -13796,7 +13645,7 @@ var HostnameRequired = {
|
|
|
13796
13645
|
remediation: "Configure hostname under deviceconfig > system."
|
|
13797
13646
|
},
|
|
13798
13647
|
check: (node) => {
|
|
13799
|
-
const system =
|
|
13648
|
+
const system = findStanza6(node, "system");
|
|
13800
13649
|
if (!system) {
|
|
13801
13650
|
return {
|
|
13802
13651
|
passed: false,
|
|
@@ -13807,7 +13656,7 @@ var HostnameRequired = {
|
|
|
13807
13656
|
loc: node.loc
|
|
13808
13657
|
};
|
|
13809
13658
|
}
|
|
13810
|
-
const hasHostname =
|
|
13659
|
+
const hasHostname = hasChildCommand(system, "hostname");
|
|
13811
13660
|
if (!hasHostname) {
|
|
13812
13661
|
return {
|
|
13813
13662
|
passed: false,
|
|
@@ -13840,7 +13689,7 @@ var SecurityRuleLogging = {
|
|
|
13840
13689
|
remediation: "Enable log-end (and optionally log-start) on all security rules."
|
|
13841
13690
|
},
|
|
13842
13691
|
check: (node) => {
|
|
13843
|
-
const rules =
|
|
13692
|
+
const rules = getSecurityRules(node);
|
|
13844
13693
|
if (rules.length === 0) {
|
|
13845
13694
|
return {
|
|
13846
13695
|
passed: true,
|
|
@@ -13853,8 +13702,8 @@ var SecurityRuleLogging = {
|
|
|
13853
13702
|
}
|
|
13854
13703
|
const issues = [];
|
|
13855
13704
|
for (const rule of rules) {
|
|
13856
|
-
if (
|
|
13857
|
-
const logging =
|
|
13705
|
+
if (isRuleDisabled(rule)) continue;
|
|
13706
|
+
const logging = hasLogging2(rule);
|
|
13858
13707
|
if (!logging.logEnd) {
|
|
13859
13708
|
issues.push(`Rule "${rule.id}" does not have log-end enabled.`);
|
|
13860
13709
|
}
|
|
@@ -13893,7 +13742,7 @@ var ZoneProtectionRequired = {
|
|
|
13893
13742
|
check: (node) => {
|
|
13894
13743
|
const issues = [];
|
|
13895
13744
|
for (const zone of node.children) {
|
|
13896
|
-
if (!
|
|
13745
|
+
if (!hasZoneProtectionProfile(zone)) {
|
|
13897
13746
|
issues.push(`Zone "${zone.id}" does not have a Zone Protection Profile applied.`);
|
|
13898
13747
|
}
|
|
13899
13748
|
}
|
|
@@ -13941,34 +13790,6 @@ function getRulesByPaloAltoVendor() {
|
|
|
13941
13790
|
return [...allCommonRules, ...allPaloAltoRules];
|
|
13942
13791
|
}
|
|
13943
13792
|
|
|
13944
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/arista/helpers.ts
|
|
13945
|
-
var checkMlagRequirements2 = (mlagNode) => {
|
|
13946
|
-
return {
|
|
13947
|
-
hasDomainId: hasChildCommand5(mlagNode, "domain-id"),
|
|
13948
|
-
hasPeerLink: hasChildCommand5(mlagNode, "peer-link"),
|
|
13949
|
-
hasPeerAddress: hasChildCommand5(mlagNode, "peer-address"),
|
|
13950
|
-
hasLocalInterface: hasChildCommand5(mlagNode, "local-interface")
|
|
13951
|
-
};
|
|
13952
|
-
};
|
|
13953
|
-
var isShutdown8 = (interfaceNode) => {
|
|
13954
|
-
if (!interfaceNode?.children) return false;
|
|
13955
|
-
const hasShutdown = interfaceNode.children.some(
|
|
13956
|
-
(child) => child?.id && equalsIgnoreCase2(child.id, "shutdown")
|
|
13957
|
-
);
|
|
13958
|
-
const hasNoShutdown = interfaceNode.children.some(
|
|
13959
|
-
(child) => child?.id && equalsIgnoreCase2(child.id, "no shutdown")
|
|
13960
|
-
);
|
|
13961
|
-
return hasShutdown && !hasNoShutdown;
|
|
13962
|
-
};
|
|
13963
|
-
var getInterfaceDescription2 = (interfaceNode) => {
|
|
13964
|
-
if (!interfaceNode?.children) return void 0;
|
|
13965
|
-
const descCmd = interfaceNode.children.find(
|
|
13966
|
-
(child) => child?.id && startsWithIgnoreCase2(child.id, "description ")
|
|
13967
|
-
);
|
|
13968
|
-
if (!descCmd) return void 0;
|
|
13969
|
-
return descCmd.id.replace(/^description\s+/i, "").trim();
|
|
13970
|
-
};
|
|
13971
|
-
|
|
13972
13793
|
// ../rules-default/src/arista/eos-rules.ts
|
|
13973
13794
|
var HostnameRequired2 = {
|
|
13974
13795
|
id: "ARI-SYS-001",
|
|
@@ -14017,7 +13838,7 @@ var MlagConfigComplete = {
|
|
|
14017
13838
|
remediation: "MLAG configuration requires: domain-id, peer-link, peer-address, and local-interface."
|
|
14018
13839
|
},
|
|
14019
13840
|
check: (node, context) => {
|
|
14020
|
-
const requirements =
|
|
13841
|
+
const requirements = checkMlagRequirements(node);
|
|
14021
13842
|
const issues = [];
|
|
14022
13843
|
if (!requirements.hasDomainId) {
|
|
14023
13844
|
issues.push("domain-id");
|
|
@@ -14063,7 +13884,7 @@ var InterfaceDescription = {
|
|
|
14063
13884
|
remediation: "Add description to interface: description <text>"
|
|
14064
13885
|
},
|
|
14065
13886
|
check: (node, context) => {
|
|
14066
|
-
if (
|
|
13887
|
+
if (isShutdown2(node)) {
|
|
14067
13888
|
return {
|
|
14068
13889
|
passed: true,
|
|
14069
13890
|
message: "Interface is shutdown, description not required.",
|
|
@@ -14073,7 +13894,7 @@ var InterfaceDescription = {
|
|
|
14073
13894
|
loc: node.loc
|
|
14074
13895
|
};
|
|
14075
13896
|
}
|
|
14076
|
-
const description =
|
|
13897
|
+
const description = getInterfaceDescription(node);
|
|
14077
13898
|
if (!description) {
|
|
14078
13899
|
return {
|
|
14079
13900
|
passed: false,
|
|
@@ -14108,37 +13929,6 @@ function getRulesByAristaVendor() {
|
|
|
14108
13929
|
return [...allCommonRules, ...allAristaRules];
|
|
14109
13930
|
}
|
|
14110
13931
|
|
|
14111
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/vyos/helpers.ts
|
|
14112
|
-
var isDisabled4 = (node) => {
|
|
14113
|
-
if (!node?.children) return false;
|
|
14114
|
-
return node.children.some((child) => child?.id?.toLowerCase().trim() === "disable");
|
|
14115
|
-
};
|
|
14116
|
-
var findStanzasByPrefix3 = (node, prefix) => {
|
|
14117
|
-
if (!node?.children) return [];
|
|
14118
|
-
return node.children.filter(
|
|
14119
|
-
(child) => child?.id?.toLowerCase().startsWith(prefix.toLowerCase())
|
|
14120
|
-
);
|
|
14121
|
-
};
|
|
14122
|
-
var getEthernetInterfaces2 = (interfacesNode) => {
|
|
14123
|
-
if (!interfacesNode?.children) return [];
|
|
14124
|
-
return interfacesNode.children.filter(
|
|
14125
|
-
(child) => child?.id?.toLowerCase().startsWith("ethernet eth")
|
|
14126
|
-
);
|
|
14127
|
-
};
|
|
14128
|
-
var getFirewallDefaultAction2 = (rulesetNode) => {
|
|
14129
|
-
if (!rulesetNode?.children) return void 0;
|
|
14130
|
-
for (const child of rulesetNode.children) {
|
|
14131
|
-
const id = child?.id?.toLowerCase().trim();
|
|
14132
|
-
if (!id) continue;
|
|
14133
|
-
if (id.startsWith("default-action")) {
|
|
14134
|
-
if (id.includes("drop")) return "drop";
|
|
14135
|
-
if (id.includes("accept")) return "accept";
|
|
14136
|
-
if (id.includes("reject")) return "reject";
|
|
14137
|
-
}
|
|
14138
|
-
}
|
|
14139
|
-
return void 0;
|
|
14140
|
-
};
|
|
14141
|
-
|
|
14142
13932
|
// ../rules-default/src/vyos/vyos-rules.ts
|
|
14143
13933
|
var VyosHostnameRequired = {
|
|
14144
13934
|
id: "VYOS-SYS-001",
|
|
@@ -14152,7 +13942,7 @@ var VyosHostnameRequired = {
|
|
|
14152
13942
|
remediation: 'Configure "host-name" under system stanza.'
|
|
14153
13943
|
},
|
|
14154
13944
|
check: (node) => {
|
|
14155
|
-
const hasHostname =
|
|
13945
|
+
const hasHostname = hasChildCommand(node, "host-name");
|
|
14156
13946
|
if (!hasHostname) {
|
|
14157
13947
|
return {
|
|
14158
13948
|
passed: false,
|
|
@@ -14221,12 +14011,12 @@ var VyosInterfaceDescription = {
|
|
|
14221
14011
|
},
|
|
14222
14012
|
check: (node) => {
|
|
14223
14013
|
const issues = [];
|
|
14224
|
-
const ethernetInterfaces =
|
|
14014
|
+
const ethernetInterfaces = getEthernetInterfaces(node);
|
|
14225
14015
|
for (const iface of ethernetInterfaces) {
|
|
14226
|
-
if (
|
|
14016
|
+
if (isDisabled2(iface)) {
|
|
14227
14017
|
continue;
|
|
14228
14018
|
}
|
|
14229
|
-
const hasDesc =
|
|
14019
|
+
const hasDesc = hasChildCommand(iface, "description");
|
|
14230
14020
|
if (!hasDesc) {
|
|
14231
14021
|
const ifaceName = iface.id.split(/\s+/).pop() || iface.id;
|
|
14232
14022
|
issues.push(`Interface "${ifaceName}" missing description.`);
|
|
@@ -14264,7 +14054,7 @@ var VyosFirewallDefaultAction = {
|
|
|
14264
14054
|
remediation: 'Set "default-action drop" or "default-action reject" for each firewall ruleset.'
|
|
14265
14055
|
},
|
|
14266
14056
|
check: (node) => {
|
|
14267
|
-
const rulesets =
|
|
14057
|
+
const rulesets = findStanzasByPrefix2(node, "name");
|
|
14268
14058
|
if (rulesets.length === 0) {
|
|
14269
14059
|
return {
|
|
14270
14060
|
passed: true,
|
|
@@ -14277,7 +14067,7 @@ var VyosFirewallDefaultAction = {
|
|
|
14277
14067
|
}
|
|
14278
14068
|
const issues = [];
|
|
14279
14069
|
for (const ruleset of rulesets) {
|
|
14280
|
-
const defaultAction =
|
|
14070
|
+
const defaultAction = getFirewallDefaultAction(ruleset);
|
|
14281
14071
|
if (!defaultAction) {
|
|
14282
14072
|
const rulesetName = ruleset.id.split(/\s+/)[1] || ruleset.id;
|
|
14283
14073
|
issues.push(`Firewall ruleset "${rulesetName}" has no default-action configured.`);
|
|
@@ -14319,65 +14109,6 @@ function getRulesByVyosVendor() {
|
|
|
14319
14109
|
return [...allCommonRules, ...allVyosRules];
|
|
14320
14110
|
}
|
|
14321
14111
|
|
|
14322
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/fortinet/helpers.ts
|
|
14323
|
-
var getEditEntries2 = (configSection) => {
|
|
14324
|
-
if (!configSection?.children) return [];
|
|
14325
|
-
return configSection.children.filter(
|
|
14326
|
-
(child) => child?.id?.toLowerCase().startsWith("edit ")
|
|
14327
|
-
);
|
|
14328
|
-
};
|
|
14329
|
-
var getEditEntryName2 = (editEntry) => {
|
|
14330
|
-
const match = editEntry.id.match(/^edit\s+["']?([^"']+)["']?$/i);
|
|
14331
|
-
const entryName = match?.[1];
|
|
14332
|
-
return entryName ?? editEntry.id;
|
|
14333
|
-
};
|
|
14334
|
-
var getSetValue2 = (node, paramName) => {
|
|
14335
|
-
if (!node?.children) return void 0;
|
|
14336
|
-
const normalizedParam = paramName.toLowerCase();
|
|
14337
|
-
for (const child of node.children) {
|
|
14338
|
-
const childId = child?.id?.toLowerCase();
|
|
14339
|
-
if (!childId) continue;
|
|
14340
|
-
const match = childId.match(new RegExp(`^set\\s+${normalizedParam}\\s+(.+)$`, "i"));
|
|
14341
|
-
const value = match?.[1];
|
|
14342
|
-
if (value) {
|
|
14343
|
-
return value.replace(/^["']|["']$/g, "").trim();
|
|
14344
|
-
}
|
|
14345
|
-
}
|
|
14346
|
-
return void 0;
|
|
14347
|
-
};
|
|
14348
|
-
var isPolicyDisabled2 = (policyNode) => {
|
|
14349
|
-
const status = getSetValue2(policyNode, "status");
|
|
14350
|
-
return status?.toLowerCase() === "disable";
|
|
14351
|
-
};
|
|
14352
|
-
var hasLogging4 = (policyNode) => {
|
|
14353
|
-
const logtraffic = getSetValue2(policyNode, "logtraffic");
|
|
14354
|
-
const logtrafficStart = getSetValue2(policyNode, "logtraffic-start");
|
|
14355
|
-
return {
|
|
14356
|
-
logtraffic,
|
|
14357
|
-
logtrafficStart: logtrafficStart?.toLowerCase() === "enable"
|
|
14358
|
-
};
|
|
14359
|
-
};
|
|
14360
|
-
var getAdminProfile2 = (adminNode) => {
|
|
14361
|
-
return getSetValue2(adminNode, "accprofile");
|
|
14362
|
-
};
|
|
14363
|
-
var isSuperAdmin2 = (adminNode) => {
|
|
14364
|
-
const profile = getAdminProfile2(adminNode);
|
|
14365
|
-
return profile?.toLowerCase() === "super_admin";
|
|
14366
|
-
};
|
|
14367
|
-
var getAdminTrustedHosts2 = (adminNode) => {
|
|
14368
|
-
const trustedHosts = [];
|
|
14369
|
-
for (let i = 1; i <= 10; i++) {
|
|
14370
|
-
const host = getSetValue2(adminNode, `trusthost${i}`);
|
|
14371
|
-
if (host && host !== "0.0.0.0 0.0.0.0") {
|
|
14372
|
-
trustedHosts.push(host);
|
|
14373
|
-
}
|
|
14374
|
-
}
|
|
14375
|
-
return trustedHosts;
|
|
14376
|
-
};
|
|
14377
|
-
var hasAdminTrustedHosts2 = (adminNode) => {
|
|
14378
|
-
return getAdminTrustedHosts2(adminNode).length > 0;
|
|
14379
|
-
};
|
|
14380
|
-
|
|
14381
14112
|
// ../rules-default/src/fortinet/fortigate-rules.ts
|
|
14382
14113
|
var HostnameRequired3 = {
|
|
14383
14114
|
id: "FGT-SYS-001",
|
|
@@ -14391,7 +14122,7 @@ var HostnameRequired3 = {
|
|
|
14391
14122
|
remediation: 'Configure hostname under "config system global" using "set hostname <name>".'
|
|
14392
14123
|
},
|
|
14393
14124
|
check: (node) => {
|
|
14394
|
-
const hostname =
|
|
14125
|
+
const hostname = getSetValue(node, "hostname");
|
|
14395
14126
|
if (!hostname) {
|
|
14396
14127
|
return {
|
|
14397
14128
|
passed: false,
|
|
@@ -14424,7 +14155,7 @@ var AdminTrustedHostRequired = {
|
|
|
14424
14155
|
remediation: 'Configure trusted hosts for each admin user using "set trusthost1", "set trusthost2", etc.'
|
|
14425
14156
|
},
|
|
14426
14157
|
check: (node) => {
|
|
14427
|
-
const admins =
|
|
14158
|
+
const admins = getEditEntries(node);
|
|
14428
14159
|
if (admins.length === 0) {
|
|
14429
14160
|
return {
|
|
14430
14161
|
passed: true,
|
|
@@ -14437,11 +14168,11 @@ var AdminTrustedHostRequired = {
|
|
|
14437
14168
|
}
|
|
14438
14169
|
const issues = [];
|
|
14439
14170
|
for (const admin of admins) {
|
|
14440
|
-
const adminName =
|
|
14441
|
-
if (
|
|
14171
|
+
const adminName = getEditEntryName(admin);
|
|
14172
|
+
if (hasAdminTrustedHosts(admin)) {
|
|
14442
14173
|
continue;
|
|
14443
14174
|
}
|
|
14444
|
-
if (
|
|
14175
|
+
if (isSuperAdmin(admin)) {
|
|
14445
14176
|
issues.push(`Super admin "${adminName}" has no trusted host restrictions. This is a critical security issue.`);
|
|
14446
14177
|
} else {
|
|
14447
14178
|
issues.push(`Admin "${adminName}" has no trusted host restrictions.`);
|
|
@@ -14479,7 +14210,7 @@ var PolicyLoggingRequired = {
|
|
|
14479
14210
|
remediation: 'Enable logging on all firewall policies using "set logtraffic all" or "set logtraffic utm".'
|
|
14480
14211
|
},
|
|
14481
14212
|
check: (node) => {
|
|
14482
|
-
const policies =
|
|
14213
|
+
const policies = getEditEntries(node);
|
|
14483
14214
|
if (policies.length === 0) {
|
|
14484
14215
|
return {
|
|
14485
14216
|
passed: true,
|
|
@@ -14492,10 +14223,10 @@ var PolicyLoggingRequired = {
|
|
|
14492
14223
|
}
|
|
14493
14224
|
const issues = [];
|
|
14494
14225
|
for (const policy of policies) {
|
|
14495
|
-
if (
|
|
14496
|
-
const logging =
|
|
14226
|
+
if (isPolicyDisabled(policy)) continue;
|
|
14227
|
+
const logging = hasLogging(policy);
|
|
14497
14228
|
if (!logging.logtraffic || isFeatureDisabled(logging.logtraffic)) {
|
|
14498
|
-
const policyId =
|
|
14229
|
+
const policyId = getEditEntryName(policy);
|
|
14499
14230
|
issues.push(`Policy ${policyId} does not have logging enabled.`);
|
|
14500
14231
|
}
|
|
14501
14232
|
}
|
|
@@ -14533,44 +14264,6 @@ function getRulesByFortinetVendor() {
|
|
|
14533
14264
|
return [...allCommonRules, ...allFortinetRules];
|
|
14534
14265
|
}
|
|
14535
14266
|
|
|
14536
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/extreme/helpers.ts
|
|
14537
|
-
var getExosVlanName2 = (node) => {
|
|
14538
|
-
const match = node.id.match(/^(?:create|configure)\s+vlan\s+["']?(\w+)["']?/i);
|
|
14539
|
-
const vlanName = match?.[1];
|
|
14540
|
-
return vlanName?.trim();
|
|
14541
|
-
};
|
|
14542
|
-
var isVossVlanCreate2 = (node) => {
|
|
14543
|
-
return /^vlan\s+create\s+\d+/i.test(node.id);
|
|
14544
|
-
};
|
|
14545
|
-
var getVossVlanId2 = (node) => {
|
|
14546
|
-
const match = node.id.match(/^vlan\s+(?:create|members|i-sid)\s+(\d+)/i);
|
|
14547
|
-
const vlanId = match?.[1];
|
|
14548
|
-
return vlanId ? parseInt(vlanId, 10) : void 0;
|
|
14549
|
-
};
|
|
14550
|
-
var isVossGigabitEthernet2 = (node) => {
|
|
14551
|
-
return /^interface\s+GigabitEthernet\s+\d+\/\d+/i.test(node.id);
|
|
14552
|
-
};
|
|
14553
|
-
var isVossShutdown2 = (node) => {
|
|
14554
|
-
if (!node?.children) return false;
|
|
14555
|
-
const hasShutdown = node.children.some(
|
|
14556
|
-
(child) => child?.id?.toLowerCase() === "shutdown"
|
|
14557
|
-
);
|
|
14558
|
-
const hasNoShutdown = node.children.some(
|
|
14559
|
-
(child) => child?.id?.toLowerCase() === "no shutdown"
|
|
14560
|
-
);
|
|
14561
|
-
return hasShutdown && !hasNoShutdown;
|
|
14562
|
-
};
|
|
14563
|
-
var getVossDefaultVlan2 = (node) => {
|
|
14564
|
-
if (!node?.children) return void 0;
|
|
14565
|
-
const defaultVlan = node.children.find(
|
|
14566
|
-
(child) => child?.id && /^default-vlan-id\s+\d+/i.test(child.id)
|
|
14567
|
-
);
|
|
14568
|
-
if (!defaultVlan?.id) return void 0;
|
|
14569
|
-
const match = defaultVlan.id.match(/default-vlan-id\s+(\d+)/i);
|
|
14570
|
-
const vlanId = match?.[1];
|
|
14571
|
-
return vlanId ? parseInt(vlanId, 10) : void 0;
|
|
14572
|
-
};
|
|
14573
|
-
|
|
14574
14267
|
// ../rules-default/src/extreme/exos-rules.ts
|
|
14575
14268
|
var ExosSysnameRequired = {
|
|
14576
14269
|
id: "EXOS-SYS-001",
|
|
@@ -14650,7 +14343,7 @@ var ExosVlanNaming = {
|
|
|
14650
14343
|
remediation: 'Use descriptive VLAN names: create vlan "<meaningful-name>" tag <id>'
|
|
14651
14344
|
},
|
|
14652
14345
|
check: (node, context) => {
|
|
14653
|
-
const vlanName =
|
|
14346
|
+
const vlanName = getExosVlanName(node);
|
|
14654
14347
|
if (!vlanName) {
|
|
14655
14348
|
return {
|
|
14656
14349
|
passed: false,
|
|
@@ -14741,7 +14434,7 @@ var VossVlanIsidRequired = {
|
|
|
14741
14434
|
remediation: "Configure I-SID for VLAN: vlan i-sid <vlan-id> <isid>"
|
|
14742
14435
|
},
|
|
14743
14436
|
check: (node, context) => {
|
|
14744
|
-
if (!
|
|
14437
|
+
if (!isVossVlanCreate(node)) {
|
|
14745
14438
|
return {
|
|
14746
14439
|
passed: true,
|
|
14747
14440
|
message: "Not a VLAN create command.",
|
|
@@ -14751,7 +14444,7 @@ var VossVlanIsidRequired = {
|
|
|
14751
14444
|
loc: node.loc
|
|
14752
14445
|
};
|
|
14753
14446
|
}
|
|
14754
|
-
const vlanId =
|
|
14447
|
+
const vlanId = getVossVlanId(node);
|
|
14755
14448
|
if (!vlanId) {
|
|
14756
14449
|
return {
|
|
14757
14450
|
passed: false,
|
|
@@ -14794,7 +14487,7 @@ var VossInterfaceDefaultVlan = {
|
|
|
14794
14487
|
remediation: "Configure default VLAN: default-vlan-id <vlan-id>"
|
|
14795
14488
|
},
|
|
14796
14489
|
check: (node, context) => {
|
|
14797
|
-
if (!
|
|
14490
|
+
if (!isVossGigabitEthernet(node)) {
|
|
14798
14491
|
return {
|
|
14799
14492
|
passed: true,
|
|
14800
14493
|
message: "Not a GigabitEthernet interface.",
|
|
@@ -14804,7 +14497,7 @@ var VossInterfaceDefaultVlan = {
|
|
|
14804
14497
|
loc: node.loc
|
|
14805
14498
|
};
|
|
14806
14499
|
}
|
|
14807
|
-
if (
|
|
14500
|
+
if (isVossShutdown(node)) {
|
|
14808
14501
|
return {
|
|
14809
14502
|
passed: true,
|
|
14810
14503
|
message: "Interface is shutdown.",
|
|
@@ -14814,7 +14507,7 @@ var VossInterfaceDefaultVlan = {
|
|
|
14814
14507
|
loc: node.loc
|
|
14815
14508
|
};
|
|
14816
14509
|
}
|
|
14817
|
-
const defaultVlan =
|
|
14510
|
+
const defaultVlan = getVossDefaultVlan(node);
|
|
14818
14511
|
if (!defaultVlan) {
|
|
14819
14512
|
return {
|
|
14820
14513
|
passed: false,
|
|
@@ -14866,22 +14559,6 @@ function getRulesByExtremeVendor(vendorId) {
|
|
|
14866
14559
|
}
|
|
14867
14560
|
}
|
|
14868
14561
|
|
|
14869
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/huawei/helpers.ts
|
|
14870
|
-
var isEnabled4 = (node) => {
|
|
14871
|
-
if (!node?.children) return false;
|
|
14872
|
-
return node.children.some((child) => {
|
|
14873
|
-
const rawText = child?.rawText?.toLowerCase().trim();
|
|
14874
|
-
return rawText === "undo shutdown";
|
|
14875
|
-
});
|
|
14876
|
-
};
|
|
14877
|
-
var isPhysicalPort5 = (interfaceName) => {
|
|
14878
|
-
const name = interfaceName.toLowerCase();
|
|
14879
|
-
return !name.includes("vlanif") && !name.includes("loopback") && !name.includes("null") && !name.includes("tunnel") && !name.includes("eth-trunk") && !name.includes("nve") && !name.includes("vbdif");
|
|
14880
|
-
};
|
|
14881
|
-
var hasDescription6 = (node) => {
|
|
14882
|
-
return hasChildCommand5(node, "description");
|
|
14883
|
-
};
|
|
14884
|
-
|
|
14885
14562
|
// ../rules-default/src/huawei/vrp-rules.ts
|
|
14886
14563
|
var SysnameRequired = {
|
|
14887
14564
|
id: "HUAWEI-SYS-001",
|
|
@@ -14929,7 +14606,7 @@ var InterfaceDescriptionRequired2 = {
|
|
|
14929
14606
|
},
|
|
14930
14607
|
check: (node) => {
|
|
14931
14608
|
const interfaceName = node.id.replace(/^interface\s+/i, "").trim();
|
|
14932
|
-
if (!
|
|
14609
|
+
if (!isPhysicalPort2(interfaceName)) {
|
|
14933
14610
|
return {
|
|
14934
14611
|
passed: true,
|
|
14935
14612
|
message: "Non-physical interface, description optional.",
|
|
@@ -14939,7 +14616,7 @@ var InterfaceDescriptionRequired2 = {
|
|
|
14939
14616
|
loc: node.loc
|
|
14940
14617
|
};
|
|
14941
14618
|
}
|
|
14942
|
-
if (!
|
|
14619
|
+
if (!isEnabled(node)) {
|
|
14943
14620
|
return {
|
|
14944
14621
|
passed: true,
|
|
14945
14622
|
message: "Interface is shutdown, description optional.",
|
|
@@ -14949,7 +14626,7 @@ var InterfaceDescriptionRequired2 = {
|
|
|
14949
14626
|
loc: node.loc
|
|
14950
14627
|
};
|
|
14951
14628
|
}
|
|
14952
|
-
if (!
|
|
14629
|
+
if (!hasDescription3(node)) {
|
|
14953
14630
|
return {
|
|
14954
14631
|
passed: false,
|
|
14955
14632
|
message: `Interface ${interfaceName} is enabled but has no description.`,
|
|
@@ -15033,55 +14710,6 @@ function getRulesByHuaweiVendor() {
|
|
|
15033
14710
|
return [...allCommonRules, ...allHuaweiRules];
|
|
15034
14711
|
}
|
|
15035
14712
|
|
|
15036
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/mikrotik/helpers.ts
|
|
15037
|
-
var parseProperty2 = (commandStr, propertyName) => {
|
|
15038
|
-
const regex = new RegExp(`\\b${propertyName}=(?:"([^"]+)"|'([^']+)'|(\\S+))`, "i");
|
|
15039
|
-
const match = commandStr.match(regex);
|
|
15040
|
-
if (match) {
|
|
15041
|
-
return match[1] || match[2] || match[3];
|
|
15042
|
-
}
|
|
15043
|
-
return void 0;
|
|
15044
|
-
};
|
|
15045
|
-
var getFirewallChain2 = (nodeOrCommand) => {
|
|
15046
|
-
const str = typeof nodeOrCommand === "string" ? nodeOrCommand : nodeOrCommand.id;
|
|
15047
|
-
return parseProperty2(str, "chain");
|
|
15048
|
-
};
|
|
15049
|
-
var getFirewallAction2 = (nodeOrCommand) => {
|
|
15050
|
-
const str = typeof nodeOrCommand === "string" ? nodeOrCommand : nodeOrCommand.id;
|
|
15051
|
-
return parseProperty2(str, "action");
|
|
15052
|
-
};
|
|
15053
|
-
var getName2 = (nodeOrCommand) => {
|
|
15054
|
-
const str = typeof nodeOrCommand === "string" ? nodeOrCommand : nodeOrCommand.id;
|
|
15055
|
-
return parseProperty2(str, "name");
|
|
15056
|
-
};
|
|
15057
|
-
var isAddCommand2 = (nodeOrCommand) => {
|
|
15058
|
-
const str = typeof nodeOrCommand === "string" ? nodeOrCommand : nodeOrCommand.id;
|
|
15059
|
-
return /^add\s+/i.test(str.trim());
|
|
15060
|
-
};
|
|
15061
|
-
var isSetCommand2 = (nodeOrCommand) => {
|
|
15062
|
-
const str = typeof nodeOrCommand === "string" ? nodeOrCommand : nodeOrCommand.id;
|
|
15063
|
-
return /^set\s+/i.test(str.trim());
|
|
15064
|
-
};
|
|
15065
|
-
var getAddCommands2 = (node) => {
|
|
15066
|
-
if (!node?.children) return [];
|
|
15067
|
-
return node.children.filter((child) => isAddCommand2(child));
|
|
15068
|
-
};
|
|
15069
|
-
var isServiceDisabled2 = (nodeOrCommand) => {
|
|
15070
|
-
const str = typeof nodeOrCommand === "string" ? nodeOrCommand : nodeOrCommand.id;
|
|
15071
|
-
const disabled = parseProperty2(str, "disabled");
|
|
15072
|
-
return disabled?.toLowerCase() === "yes";
|
|
15073
|
-
};
|
|
15074
|
-
var getSystemIdentity2 = (node) => {
|
|
15075
|
-
if (!node?.children) return void 0;
|
|
15076
|
-
for (const child of node.children) {
|
|
15077
|
-
if (isSetCommand2(child)) {
|
|
15078
|
-
const name = getName2(child);
|
|
15079
|
-
if (name) return name;
|
|
15080
|
-
}
|
|
15081
|
-
}
|
|
15082
|
-
return void 0;
|
|
15083
|
-
};
|
|
15084
|
-
|
|
15085
14713
|
// ../rules-default/src/mikrotik/routeros-rules.ts
|
|
15086
14714
|
var MikrotikSystemIdentity = {
|
|
15087
14715
|
id: "MIK-SYS-001",
|
|
@@ -15095,7 +14723,7 @@ var MikrotikSystemIdentity = {
|
|
|
15095
14723
|
remediation: "Configure system identity: /system identity set name=MyRouter"
|
|
15096
14724
|
},
|
|
15097
14725
|
check: (node) => {
|
|
15098
|
-
const identity =
|
|
14726
|
+
const identity = getSystemIdentity(node);
|
|
15099
14727
|
if (!identity || equalsIgnoreCase(identity, "mikrotik") || equalsIgnoreCase(identity, "routerboard")) {
|
|
15100
14728
|
return {
|
|
15101
14729
|
passed: false,
|
|
@@ -15133,8 +14761,8 @@ var MikrotikDisableUnusedServices = {
|
|
|
15133
14761
|
for (const child of node.children) {
|
|
15134
14762
|
const childId = child.id.toLowerCase();
|
|
15135
14763
|
for (const service of dangerousServices) {
|
|
15136
|
-
if (childId.includes(service) && !
|
|
15137
|
-
const disabled =
|
|
14764
|
+
if (childId.includes(service) && !isServiceDisabled(child)) {
|
|
14765
|
+
const disabled = parseProperty(child.id, "disabled");
|
|
15138
14766
|
if (!disabled || !equalsIgnoreCase(disabled, "yes")) {
|
|
15139
14767
|
issues.push(`Service '${service}' is enabled. Consider disabling it.`);
|
|
15140
14768
|
}
|
|
@@ -15173,11 +14801,11 @@ var MikrotikInputChainDrop = {
|
|
|
15173
14801
|
remediation: "Add drop rule for input chain: add chain=input action=drop"
|
|
15174
14802
|
},
|
|
15175
14803
|
check: (node) => {
|
|
15176
|
-
const addCommands =
|
|
14804
|
+
const addCommands = getAddCommands(node);
|
|
15177
14805
|
let hasInputDrop = false;
|
|
15178
14806
|
for (const cmd of addCommands) {
|
|
15179
|
-
const chain =
|
|
15180
|
-
const action =
|
|
14807
|
+
const chain = getFirewallChain(cmd);
|
|
14808
|
+
const action = getFirewallAction(cmd);
|
|
15181
14809
|
if (chain && equalsIgnoreCase(chain, "input") && action && equalsIgnoreCase(action, "drop")) {
|
|
15182
14810
|
hasInputDrop = true;
|
|
15183
14811
|
break;
|
|
@@ -15214,60 +14842,6 @@ function getRulesByMikroTikVendor() {
|
|
|
15214
14842
|
return [...allCommonRules, ...allMikroTikRules];
|
|
15215
14843
|
}
|
|
15216
14844
|
|
|
15217
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/nokia/helpers.ts
|
|
15218
|
-
var isAdminStateDisabled2 = (node) => {
|
|
15219
|
-
if (!node?.children) return false;
|
|
15220
|
-
const directCheck = node.children.some((child) => {
|
|
15221
|
-
const rawText = child?.rawText?.toLowerCase().trim();
|
|
15222
|
-
return rawText === "admin-state disable";
|
|
15223
|
-
});
|
|
15224
|
-
if (directCheck) return true;
|
|
15225
|
-
return node?.rawText?.toLowerCase().includes("admin-state disable") ?? false;
|
|
15226
|
-
};
|
|
15227
|
-
var isPhysicalPort6 = (portName) => {
|
|
15228
|
-
const name = portName.toLowerCase();
|
|
15229
|
-
return /^\d+\/\d+\/\d+/.test(name);
|
|
15230
|
-
};
|
|
15231
|
-
var hasDescription7 = (node) => {
|
|
15232
|
-
return hasChildCommand5(node, "description");
|
|
15233
|
-
};
|
|
15234
|
-
var getDescription4 = (node) => {
|
|
15235
|
-
const descCmd = getChildCommand5(node, "description");
|
|
15236
|
-
if (descCmd?.rawText) {
|
|
15237
|
-
const match = descCmd.rawText.match(/description\s+"([^"]+)"|description\s+(\S+)/i);
|
|
15238
|
-
if (match) {
|
|
15239
|
-
return match[1] || match[2];
|
|
15240
|
-
}
|
|
15241
|
-
}
|
|
15242
|
-
return void 0;
|
|
15243
|
-
};
|
|
15244
|
-
var getSystemName2 = (node) => {
|
|
15245
|
-
if (!node?.children) return void 0;
|
|
15246
|
-
const nameCmd = node.children.find((child) => {
|
|
15247
|
-
return child?.id?.toLowerCase().startsWith("name");
|
|
15248
|
-
});
|
|
15249
|
-
if (nameCmd?.rawText) {
|
|
15250
|
-
const match = nameCmd.rawText.match(/name\s+"([^"]+)"/i);
|
|
15251
|
-
if (match) {
|
|
15252
|
-
return match[1];
|
|
15253
|
-
}
|
|
15254
|
-
}
|
|
15255
|
-
return void 0;
|
|
15256
|
-
};
|
|
15257
|
-
var hasBgpRouterId3 = (node) => {
|
|
15258
|
-
return hasChildCommand5(node, "router-id");
|
|
15259
|
-
};
|
|
15260
|
-
var getBgpRouterId2 = (node) => {
|
|
15261
|
-
const routerIdCmd = getChildCommand5(node, "router-id");
|
|
15262
|
-
if (routerIdCmd?.rawText) {
|
|
15263
|
-
const match = routerIdCmd.rawText.match(/router-id\s+([\d.]+)/i);
|
|
15264
|
-
if (match) {
|
|
15265
|
-
return match[1];
|
|
15266
|
-
}
|
|
15267
|
-
}
|
|
15268
|
-
return void 0;
|
|
15269
|
-
};
|
|
15270
|
-
|
|
15271
14845
|
// ../rules-default/src/nokia/sros-rules.ts
|
|
15272
14846
|
var SystemNameRequired = {
|
|
15273
14847
|
id: "NOKIA-SYS-001",
|
|
@@ -15281,7 +14855,7 @@ var SystemNameRequired = {
|
|
|
15281
14855
|
remediation: 'Configure system name using: system > name "<hostname>"'
|
|
15282
14856
|
},
|
|
15283
14857
|
check: (node) => {
|
|
15284
|
-
const name =
|
|
14858
|
+
const name = getSystemName(node);
|
|
15285
14859
|
if (!name || name.length === 0) {
|
|
15286
14860
|
return {
|
|
15287
14861
|
passed: false,
|
|
@@ -15315,7 +14889,7 @@ var PortDescriptionRequired = {
|
|
|
15315
14889
|
},
|
|
15316
14890
|
check: (node) => {
|
|
15317
14891
|
const portName = node.id.replace(/^port\s+/i, "").trim();
|
|
15318
|
-
if (!
|
|
14892
|
+
if (!isPhysicalPort3(portName)) {
|
|
15319
14893
|
return {
|
|
15320
14894
|
passed: true,
|
|
15321
14895
|
message: "Not a physical port, description optional.",
|
|
@@ -15325,7 +14899,7 @@ var PortDescriptionRequired = {
|
|
|
15325
14899
|
loc: node.loc
|
|
15326
14900
|
};
|
|
15327
14901
|
}
|
|
15328
|
-
if (
|
|
14902
|
+
if (isAdminStateDisabled(node)) {
|
|
15329
14903
|
return {
|
|
15330
14904
|
passed: true,
|
|
15331
14905
|
message: "Port is disabled, description optional.",
|
|
@@ -15335,7 +14909,7 @@ var PortDescriptionRequired = {
|
|
|
15335
14909
|
loc: node.loc
|
|
15336
14910
|
};
|
|
15337
14911
|
}
|
|
15338
|
-
if (!
|
|
14912
|
+
if (!hasDescription4(node)) {
|
|
15339
14913
|
return {
|
|
15340
14914
|
passed: false,
|
|
15341
14915
|
message: `Port ${portName} is enabled but has no description.`,
|
|
@@ -15347,7 +14921,7 @@ var PortDescriptionRequired = {
|
|
|
15347
14921
|
}
|
|
15348
14922
|
return {
|
|
15349
14923
|
passed: true,
|
|
15350
|
-
message: `Port ${portName} has description: ${
|
|
14924
|
+
message: `Port ${portName} has description: ${getDescription3(node)}`,
|
|
15351
14925
|
ruleId: "NOKIA-PORT-001",
|
|
15352
14926
|
nodeId: node.id,
|
|
15353
14927
|
level: "info",
|
|
@@ -15367,7 +14941,7 @@ var BgpRouterIdRequired = {
|
|
|
15367
14941
|
remediation: "Configure BGP router-id: bgp > router-id <ip-address>"
|
|
15368
14942
|
},
|
|
15369
14943
|
check: (node) => {
|
|
15370
|
-
if (
|
|
14944
|
+
if (isAdminStateDisabled(node)) {
|
|
15371
14945
|
return {
|
|
15372
14946
|
passed: true,
|
|
15373
14947
|
message: "BGP is disabled.",
|
|
@@ -15377,7 +14951,7 @@ var BgpRouterIdRequired = {
|
|
|
15377
14951
|
loc: node.loc
|
|
15378
14952
|
};
|
|
15379
14953
|
}
|
|
15380
|
-
if (!
|
|
14954
|
+
if (!hasBgpRouterId2(node)) {
|
|
15381
14955
|
return {
|
|
15382
14956
|
passed: false,
|
|
15383
14957
|
message: "BGP router-id is not configured. Configure a stable router-id.",
|
|
@@ -15389,7 +14963,7 @@ var BgpRouterIdRequired = {
|
|
|
15389
14963
|
}
|
|
15390
14964
|
return {
|
|
15391
14965
|
passed: true,
|
|
15392
|
-
message: `BGP router-id is configured: ${
|
|
14966
|
+
message: `BGP router-id is configured: ${getBgpRouterId(node)}`,
|
|
15393
14967
|
ruleId: "NOKIA-BGP-001",
|
|
15394
14968
|
nodeId: node.id,
|
|
15395
14969
|
level: "info",
|
|
@@ -15411,40 +14985,6 @@ function getRulesByNokiaVendor() {
|
|
|
15411
14985
|
return [...allCommonRules, ...allNokiaRules];
|
|
15412
14986
|
}
|
|
15413
14987
|
|
|
15414
|
-
// ../../node_modules/.bun/@sentriflow+core@0.2.1+1fb4c65d43e298b9/node_modules/@sentriflow/core/src/helpers/cumulus/helpers.ts
|
|
15415
|
-
var isSwitchPort2 = (interfaceName) => {
|
|
15416
|
-
const name = interfaceName.toLowerCase();
|
|
15417
|
-
return /swp\d+/.test(name);
|
|
15418
|
-
};
|
|
15419
|
-
var isBridgeInterface4 = (interfaceName) => {
|
|
15420
|
-
const name = interfaceName.toLowerCase();
|
|
15421
|
-
return name.includes("bridge") || name === "br_default" || /^br\d+$/.test(name);
|
|
15422
|
-
};
|
|
15423
|
-
var isVlanAwareBridge2 = (node) => {
|
|
15424
|
-
return node.children.some(
|
|
15425
|
-
(child) => child.id.toLowerCase().includes("bridge-vlan-aware") && child.id.toLowerCase().includes("yes")
|
|
15426
|
-
);
|
|
15427
|
-
};
|
|
15428
|
-
var getInterfaceName6 = (node) => {
|
|
15429
|
-
const parts = node.id.split(/\s+/);
|
|
15430
|
-
return parts[1] || node.id;
|
|
15431
|
-
};
|
|
15432
|
-
var hasDescription8 = (node) => {
|
|
15433
|
-
return node.children.some(
|
|
15434
|
-
(child) => child.id.toLowerCase().startsWith("alias ")
|
|
15435
|
-
);
|
|
15436
|
-
};
|
|
15437
|
-
var hasBridgeVids2 = (node) => {
|
|
15438
|
-
return node.children.some(
|
|
15439
|
-
(child) => child.id.toLowerCase().startsWith("bridge-vids ")
|
|
15440
|
-
);
|
|
15441
|
-
};
|
|
15442
|
-
var hasBgpRouterId4 = (node) => {
|
|
15443
|
-
return node.children.some(
|
|
15444
|
-
(child) => child.id.toLowerCase().startsWith("bgp router-id ")
|
|
15445
|
-
);
|
|
15446
|
-
};
|
|
15447
|
-
|
|
15448
14988
|
// ../rules-default/src/cumulus/cumulus-rules.ts
|
|
15449
14989
|
var CumulusInterfaceDescription = {
|
|
15450
14990
|
id: "CUM-IF-001",
|
|
@@ -15458,8 +14998,8 @@ var CumulusInterfaceDescription = {
|
|
|
15458
14998
|
remediation: 'Add "alias <description>" under the interface stanza.'
|
|
15459
14999
|
},
|
|
15460
15000
|
check: (node) => {
|
|
15461
|
-
const ifaceName =
|
|
15462
|
-
if (!
|
|
15001
|
+
const ifaceName = getInterfaceName2(node);
|
|
15002
|
+
if (!isSwitchPort(ifaceName)) {
|
|
15463
15003
|
return {
|
|
15464
15004
|
passed: true,
|
|
15465
15005
|
message: "Not a switch port interface.",
|
|
@@ -15469,7 +15009,7 @@ var CumulusInterfaceDescription = {
|
|
|
15469
15009
|
loc: node.loc
|
|
15470
15010
|
};
|
|
15471
15011
|
}
|
|
15472
|
-
if (!
|
|
15012
|
+
if (!hasDescription2(node)) {
|
|
15473
15013
|
return {
|
|
15474
15014
|
passed: false,
|
|
15475
15015
|
message: `Switch port "${ifaceName}" missing description (alias).`,
|
|
@@ -15501,8 +15041,8 @@ var CumulusBridgeVlans = {
|
|
|
15501
15041
|
remediation: 'Add "bridge-vids <vlan-ids>" to define allowed VLANs on the bridge.'
|
|
15502
15042
|
},
|
|
15503
15043
|
check: (node) => {
|
|
15504
|
-
const ifaceName =
|
|
15505
|
-
if (!
|
|
15044
|
+
const ifaceName = getInterfaceName2(node);
|
|
15045
|
+
if (!isBridgeInterface(ifaceName)) {
|
|
15506
15046
|
return {
|
|
15507
15047
|
passed: true,
|
|
15508
15048
|
message: "Not a bridge interface.",
|
|
@@ -15512,7 +15052,7 @@ var CumulusBridgeVlans = {
|
|
|
15512
15052
|
loc: node.loc
|
|
15513
15053
|
};
|
|
15514
15054
|
}
|
|
15515
|
-
if (!
|
|
15055
|
+
if (!isVlanAwareBridge(node)) {
|
|
15516
15056
|
return {
|
|
15517
15057
|
passed: true,
|
|
15518
15058
|
message: "Not a VLAN-aware bridge.",
|
|
@@ -15522,7 +15062,7 @@ var CumulusBridgeVlans = {
|
|
|
15522
15062
|
loc: node.loc
|
|
15523
15063
|
};
|
|
15524
15064
|
}
|
|
15525
|
-
if (!
|
|
15065
|
+
if (!hasBridgeVids(node)) {
|
|
15526
15066
|
return {
|
|
15527
15067
|
passed: false,
|
|
15528
15068
|
message: `VLAN-aware bridge "${ifaceName}" has no VLANs (bridge-vids) configured.`,
|
|
@@ -15554,7 +15094,7 @@ var CumulusBgpRouterId = {
|
|
|
15554
15094
|
remediation: 'Add "bgp router-id <ip>" to explicitly set router ID.'
|
|
15555
15095
|
},
|
|
15556
15096
|
check: (node) => {
|
|
15557
|
-
if (!
|
|
15097
|
+
if (!hasBgpRouterId(node)) {
|
|
15558
15098
|
return {
|
|
15559
15099
|
passed: false,
|
|
15560
15100
|
message: "BGP missing explicit router-id configuration.",
|
|
@@ -17142,7 +16682,7 @@ function enrichResultsWithRuleMetadata(results, rules) {
|
|
|
17142
16682
|
});
|
|
17143
16683
|
}
|
|
17144
16684
|
var program = new Command();
|
|
17145
|
-
program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.
|
|
16685
|
+
program.name("sentriflow").description("SentriFlow Network Configuration Validator").version("0.4.0").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(
|
|
17146
16686
|
"--pack <path...>",
|
|
17147
16687
|
"Path(s) to rule pack(s) (auto-detects format: .grx2, .grpx, or unencrypted)"
|
|
17148
16688
|
).option(
|