Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
"ecmaVersion": 2020
},
"ignorePatterns": [
"node_modules/",
Expand Down
9 changes: 4 additions & 5 deletions bannerGenerator.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/usr/bin/env node

const figlet = require('figlet');
const chalk = require('chalk');
const gradient = require('gradient-string');
const boxen = require('boxen').default; // <-- fix here

Expand All @@ -13,10 +12,10 @@ const colored = gradient.rainbow(banner);

// Wrap in a box
const boxed = boxen(colored, {
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'magenta'
padding: 1,
margin: 1,
borderStyle: 'round',
borderColor: 'magenta'
});

// Print to console
Expand Down
10 changes: 10 additions & 0 deletions config_example.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,15 @@ module.exports = {
// login: process.env.GITHUB_LOGIN || 'myusername',
// organization: process.env.GITHUB_ORG || false
}
},
saas: {
// Set to true to automatically sync scan metadata to the hosted SaaS platform
enabled: process.env.KHULNASOFT_SAAS_SYNC === 'true' || false,
// API key used to authenticate with the hosted SaaS platform
api_key: process.env.KHULNASOFT_SAAS_KEY || '',
// The cloud account identifier on the SaaS platform (leave empty for auto-detection)
account_id: process.env.KHULNASOFT_SAAS_ACCOUNT_ID || '',
// Override default SaaS API endpoint URL (defaults to https://cloud.khulnasoft.com/api/v1/sync)
url: process.env.KHULNASOFT_SAAS_URL || 'https://cloud.khulnasoft.com/api/v1/sync'
}
};
23 changes: 16 additions & 7 deletions engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,19 @@ var engine = function(cloudConfig, settings) {
if (skip) {
skippedPlugins.push(pluginId);
} else {
plugin.apis.forEach(function(api) {
if (apiCalls.indexOf(api) === -1) apiCalls.push(api);
});
// add the remediation api calls also for data to be collected
if (settings.remediate && settings.remediate.includes(pluginId)){
plugin.apis_remediate.forEach(function(api) {
if (plugin.apis && Array.isArray(plugin.apis)) {
plugin.apis.forEach(function(api) {
if (apiCalls.indexOf(api) === -1) apiCalls.push(api);
});
}
// add the remediation api calls also for data to be collected
if (settings.remediate && settings.remediate.includes(pluginId)){
if (plugin.apis_remediate && Array.isArray(plugin.apis_remediate)) {
plugin.apis_remediate.forEach(function(api) {
if (apiCalls.indexOf(api) === -1) apiCalls.push(api);
});
}
}
}
});

Expand Down Expand Up @@ -169,7 +173,12 @@ var engine = function(cloudConfig, settings) {
api_calls: apiCalls,
paginate: settings.skip_paginate,
govcloud: settings.govcloud,
china: settings.china
china: settings.china,
identifier: settings.identifier,
integration: settings.integration,
lastScanId: settings.lastScanId,
scanTriggeredFromEventsFlow: settings.scanTriggeredFromEventsFlow,
previousCollection: settings.previousCollection
}, function(err, collection) {
if (err || !collection || !Object.keys(collection).length) return console.log(`ERROR: Unable to obtain API metadata: ${err || 'No data returned'}`);
outputHandler.writeCollection(collection, settings.cloud);
Expand Down
3,357 changes: 1,682 additions & 1,675 deletions exports.js

Large diffs are not rendered by default.

285 changes: 146 additions & 139 deletions helpers/azure/api.js

Large diffs are not rendered by default.

73 changes: 42 additions & 31 deletions helpers/azure/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,12 +119,12 @@ function findOpenPorts(ngs, protocols, service, location, results, checkAllPorts
` port ` + ports + ` open to ` + sourceFilter;
if (strings.indexOf(string) === -1) strings.push(string);
} else {
for (let portRange of securityRule.properties['destinationPortRanges']){
for (let portRange of securityRule.properties['destinationPortRanges']) {
if (portRange.toString().indexOf("-") > -1) {
portRange = portRange.split("-");
let startPort = portRange[0];
let endPort = portRange[1];
if (parseInt(startPort) <= port && parseInt(endPort) >= port){
if (parseInt(startPort) <= port && parseInt(endPort) >= port) {
var string = `Security Rule "` + securityRule['name'] + `": ` + (protocol === '*' ? `All protocols` : protocol.toUpperCase()) +
` port ` + ports + ` open to ` + sourceFilter;
strings.push(string);
Expand All @@ -151,7 +151,7 @@ function findOpenPorts(ngs, protocols, service, location, results, checkAllPorts
for (const key in protocols) {
strings.push(`${key.toUpperCase()}:${protocols[key]}`);
}
if (strings.length){
if (strings.length) {
addResult(results, 0,
`Security group:( ${sGroups.name}) does not have ${strings.join(', ')} open *`,
location, resource);
Expand Down Expand Up @@ -197,7 +197,7 @@ function checkPolicyAssignment(policyAssignments, param, text, results, location
if (policyAssignment.parameters && Object.keys(policyAssignment.parameters).length) {
policyAssignmentStatus = (policyAssignment.parameters && policyAssignment.parameters[param] && policyAssignment.parameters[param].value) || defualtPolicyAssignments[param] || '';
} else {
policyAssignmentStatus = defualtPolicyAssignments[param]
policyAssignmentStatus = defualtPolicyAssignments[param]
}

if (!policyAssignmentStatus || !policyAssignmentStatus.length) {
Expand Down Expand Up @@ -335,7 +335,7 @@ function checkServerConfigs(servers, cache, source, location, results, serverTyp
return;
}

servers.data.forEach(function(server) {
servers.data.forEach(function (server) {
const configurations = shared.addSource(cache, source,
['configurations', 'listByServer', location, server.id]);

Expand Down Expand Up @@ -370,7 +370,7 @@ function checkFlexibleServerConfigs(servers, cache, source, location, results, s
return;
}

servers.data.forEach(function(server) {
servers.data.forEach(function (server) {
const configurations = shared.addSource(cache, source,
['flexibleServersConfigurations', 'listByPostgresServer', location, server.id]);

Expand All @@ -391,7 +391,7 @@ function checkFlexibleServerConfigs(servers, cache, source, location, results, s
});
}

function checkMicrosoftDefender(pricings, serviceName, serviceDisplayName, results, location ) {
function checkMicrosoftDefender(pricings, serviceName, serviceDisplayName, results, location) {

let pricingData = pricings.data.find((pricing) => pricing.name.toLowerCase() === serviceName);
if (pricingData) {
Expand All @@ -418,7 +418,7 @@ function processCall(config, method, body, baseUrl, resource, callback) {
auth.call(params, callback);
}

function remediateOpenPortsHelper( putCall, pluginName, protocols, ports, config, cache, settings, resource, remediation_file, baseUrl, method, actions, errors, callback) {
function remediateOpenPortsHelper(putCall, pluginName, protocols, ports, config, cache, settings, resource, remediation_file, baseUrl, method, actions, errors, callback) {
var params = {
properties: {
securityRules: []
Expand All @@ -431,15 +431,15 @@ function remediateOpenPortsHelper( putCall, pluginName, protocols, ports, config
async.each(protocols, function (protocol, ocb) {
async.eachOf(ports, function (port, p, cb) {
//var protocol = protocols[p];
remediateOpenPorts(putCall, pluginName, protocol, port, config, cache, settings, resource, remediation_file, baseUrl, method, params,function (error, action) {
remediateOpenPorts(putCall, pluginName, protocol, port, config, cache, settings, resource, remediation_file, baseUrl, method, params, function (error, action) {
if (error && (error.length || Object.keys(error).length)) {
errors.push(error);
} else if (action && (action.length || Object.keys(action).length)) {
actions.push(action);
}
cb();
});
}, function() {
}, function () {
ocb();
});
}, function () {
Expand Down Expand Up @@ -468,7 +468,7 @@ function remediateOpenPortsHelper( putCall, pluginName, protocols, ports, config
}

function remediatePlugin(config, method, body, baseUrl, resource, remediation_file, putCall, pluginName, callback) {
processCall(config, method, body, baseUrl, resource, function(err) {
processCall(config, method, body, baseUrl, resource, function (err) {
if (err) {
remediation_file['remediate']['actions'][pluginName]['error'] = err;
return callback(err, null);
Expand All @@ -486,7 +486,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
var sgName;
if (resource && resource.length) {
var sgNameArr = resource.split('/');
sgName = sgNameArr[sgNameArr.length -1];
sgName = sgNameArr[sgNameArr.length - 1];
config.region = settings.regions[resource];
params.location = config.region;
} else {
Expand Down Expand Up @@ -519,7 +519,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
let portRange = portToCheck.split("-");
let startPort = portRange[0];
let endPort = portRange[1];
if (parseInt(startPort) <= port && parseInt(endPort) >= port && protocolToCheck && (protocolToCheck === protocol || protocol === '*')) {
if (parseInt(startPort) <= port && parseInt(endPort) >= port && protocolToCheck && (protocolToCheck === protocol || protocol === '*')) {
if (passingPermission) {
passingPermission = false;
}
Expand All @@ -530,7 +530,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
}
} else if (portToCheck &&
portToCheck.toString().indexOf(port.toString()) > -1) {
if (portToCheck <= port && portToCheck >= port && protocolToCheck && (protocolToCheck === protocol || protocol === '*' )) {
if (portToCheck <= port && portToCheck >= port && protocolToCheck && (protocolToCheck === protocol || protocol === '*')) {
if (passingPermission) {
passingPermission = false;
}
Expand Down Expand Up @@ -562,10 +562,10 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
passingSecurityRules.push(rule);
}
});
if (passingSecurityRules.length){
for ( var i in passingSecurityRules){
if (passingSecurityRules.length) {
for (var i in passingSecurityRules) {
var foundPass = params.properties.securityRules.findIndex(rule => rule.name === passingSecurityRules[i].name);
if (foundPass === -1){
if (foundPass === -1) {
params.properties.securityRules.push(passingSecurityRules[i]);
}
}
Expand All @@ -584,7 +584,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
var publicIpv6Strings = ['/0', '::/0', '::'];

// changed this to an async function to avoid the callback already called error(was forEach loop before)
async.each(failingPermissions,function(failingPermission, fpCb) {
async.each(failingPermissions, function (failingPermission, fpCb) {
var spliced = false;
var openIpRange = false;
var openIpv6Range = false;
Expand All @@ -599,7 +599,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
// I had to parse > stringify because it was using the final state instead of the current state of failingPermission.properties
remediation_file['pre_remediate']['actions'][pluginName][resource].push(JSON.parse(JSON.stringify(failingPermission.properties)));

if ( failingPermission.properties.sourceAddressPrefixes ) {
if (failingPermission.properties.sourceAddressPrefixes) {
sourceAddressArr = failingPermission.properties.sourceAddressPrefixes;
}
if (failingPermission.properties.sourceAddressPrefix) sourceAddressArr.push(failingPermission.properties.sourceAddressPrefix);
Expand All @@ -621,7 +621,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
} else if ((!settings.input || !settings.input[inputKey]) && (failingRulePortIndex[failingPermission.name]) && !spliced) {
spliced = true;
failingPermission.properties['destinationPortRanges'].splice([failingRulePortIndex[failingPermission.name]], 1);
if ( failingPermission.properties['destinationPortRanges'].length === 0 ){
if (failingPermission.properties['destinationPortRanges'].length === 0) {
sourceAddressArr = [];
}
// this is if there is no input and the failing port is not an array,we will remove the public
Expand Down Expand Up @@ -669,22 +669,22 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
if (sourceAddressArr && sourceAddressArr.length && sourceAddressArr.length === 1) {
failingPermission.properties.sourceAddressPrefix = sourceAddressArr.join(', ');
if (failingPermission.properties.sourceAddressPrefixes) delete failingPermission.properties.sourceAddressPrefixes;
if (found > -1){
if (found > -1) {
// remove the previous one and add the latest one with new adjustments
params.properties.securityRules.splice(found, 1);
}
params.properties.securityRules.push(failingPermission);
} else if (sourceAddressArr && sourceAddressArr.length && sourceAddressArr.length > 1) {
failingPermission.properties.sourceAddressPrefixes = sourceAddressArr
if (failingPermission.properties.sourceAddressPrefix) delete failingPermission.properties.sourceAddressPrefix;
if (found > -1){
if (found > -1) {
// remove the previous one and add the latest one with new adjustments.
params.properties.securityRules.splice(found, 1);
}
params.properties.securityRules.push(failingPermission);
} else {
// we are here means there are no source ips at all,remove the rule from the params. It will be deleted.
if (found > -1){
if (found > -1) {
params.properties.securityRules.splice(found, 1);
}
}
Expand Down Expand Up @@ -714,7 +714,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,
'action': 'ADDED'
});

} else if (openIpRange && localIpExists && settings.input && settings.input[ipv4InputKey]){
} else if (openIpRange && localIpExists && settings.input && settings.input[ipv4InputKey]) {
remediation_file['remediate']['actions'][pluginName][resource]['steps'].push({
'inboundRule': settings.input[ipv4InputKey],
'action': 'Already Exists'
Expand All @@ -730,7 +730,7 @@ function remediateOpenPorts(putCall, pluginName, protocol, port, config, cache,

actions.push(params);
fpCb();
}, function(err) {
}, function (err) {
if (errors && errors.length) {
cb(errors.join(', '));
} else if (err) {
Expand Down Expand Up @@ -764,18 +764,18 @@ function checkSecurityGroup(securityGroups) {
for (const rule of sortedRules) {
if (rule.properties.direction === "Inbound" && openPrefix.includes(rule.properties.sourceAddressPrefix)) {
if (rule.properties.access === "Deny") {
return {exposed: false};
return { exposed: false };
}
if (rule.properties.access === "Allow") {
return {exposed: true, nsg: rule.nsgName};
return { exposed: true, nsg: rule.nsgName };
}
}
}

return {exposed: true};
return { exposed: true };
}

function checkNetworkExposure(cache, source, networkInterfaces, securityGroups, location, results, attachedResources, resource) {
function checkNetworkExposure(cache, source, networkInterfaces, securityGroups, location, results, attachedResources, resource) {
let exposedPath = '';

const isFunctionApp = resource && resource.kind &&
Expand Down Expand Up @@ -855,6 +855,16 @@ function checkNetworkExposure(cache, source, networkInterfaces, securityGroups,

return exposedPath;
}

function isOpenCidrRange(cidr) {
if (!cidr || typeof cidr !== 'string') return false;

const trimmed = cidr.trim();
return trimmed === '0.0.0.0/0' ||
trimmed === '::/0' ||
trimmed === '0.0.0.0';
}

module.exports = {
addResult: addResult,
findOpenPorts: findOpenPorts,
Expand All @@ -867,8 +877,9 @@ module.exports = {
remediateOpenPorts: remediateOpenPorts,
remediateOpenPortsHelper: remediateOpenPortsHelper,
checkMicrosoftDefender: checkMicrosoftDefender,
checkFlexibleServerConfigs:checkFlexibleServerConfigs,
checkNetworkExposure: checkNetworkExposure
checkFlexibleServerConfigs: checkFlexibleServerConfigs,
checkNetworkExposure: checkNetworkExposure,
isOpenCidrRange: isOpenCidrRange

};

Loading
Loading