Skip to content

Commit

Permalink
syncing with saas
Browse files Browse the repository at this point in the history
  • Loading branch information
alphadev4 committed Jan 1, 2025
1 parent 7358797 commit 45d3007
Show file tree
Hide file tree
Showing 23 changed files with 488 additions and 126 deletions.
4 changes: 3 additions & 1 deletion exports.js
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ module.exports = {
'lambdaDeadLetterQueue' : require(__dirname + '/plugins/aws/lambda/lambdaDeadLetterQueue.js'),
'lambdaEnhancedMonitoring' : require(__dirname + '/plugins/aws/lambda/lambdaEnhancedMonitoring.js'),
'lambdaUniqueExecutionRole' : require(__dirname + '/plugins/aws/lambda/lambdaUniqueExecutionRole.js'),
'lambdaNetworkExposure' : require(__dirname + '/plugins/aws/lambda/lambdaNetworkExposure.js'),

'webServerPublicAccess' : require(__dirname + '/plugins/aws/mwaa/webServerPublicAccess.js'),
'environmentAdminPrivileges' : require(__dirname + '/plugins/aws/mwaa/environmentAdminPrivileges.js'),
Expand Down Expand Up @@ -1003,6 +1004,7 @@ module.exports = {
'disableFTPDeployments' : require(__dirname + '/plugins/azure/appservice/disableFTPDeployments.js'),
'accessControlAllowCredential' : require(__dirname + '/plugins/azure/appservice/accessControlAllowCredential.js'),
'appServiceDiagnosticLogs' : require(__dirname + '/plugins/azure/appservice/appServiceDiagnosticLogs.js'),
'functionAppNetworkExposure' : require(__dirname + '/plugins/azure/appservice/functionAppNetworkExposure.js'),

'rbacEnabled' : require(__dirname + '/plugins/azure/kubernetesservice/rbacEnabled.js'),
'aksManagedIdentity' : require(__dirname + '/plugins/azure/kubernetesservice/aksManagedIdentity.js'),
Expand Down Expand Up @@ -1608,8 +1610,8 @@ module.exports = {
'cloudFunctionLabelsAdded' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionLabelsAdded.js'),
'cloudFunctionOldRuntime' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionOldRuntime.js'),
'functionAllUsersPolicy' : require(__dirname + '/plugins/google/cloudfunctions/functionAllUsersPolicy.js'),

'serverlessVPCAccess' : require(__dirname + '/plugins/google/cloudfunctions/serverlessVPCAccess.js'),
'cloudFunctionNetworkExposure' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionNetworkExposure.js'),

'computeAllowedExternalIPs' : require(__dirname + '/plugins/google/cloudresourcemanager/computeAllowedExternalIPs.js'),
'disableAutomaticIAMGrants' : require(__dirname + '/plugins/google/cloudresourcemanager/disableAutomaticIAMGrants.js'),
Expand Down
2 changes: 1 addition & 1 deletion helpers/asl/asl-1.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ var parse = function(obj, path, region, cloud, accountId, resourceId) {
return parse(obj[localPath], path);
} else return ['not set'];
} else if (!Array.isArray(obj) && path && path.length) {
if (obj[path]) return [obj[path]];
if (obj[path] || typeof obj[path] === 'boolean') return [obj[path]];
else {
if (cloud==='aws' && path.startsWith('arn:aws')) {
const template_string = path;
Expand Down
73 changes: 35 additions & 38 deletions helpers/aws/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,6 @@ var getAttachedELBs = function(cache, source, region, resourceId, lbField, lbAt
return elbs;
};


var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs, region, results, resource) {
var internetExposed = '';
var isSubnetPrivate = false;
Expand All @@ -1249,41 +1248,41 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
if (resource.functionUrlConfig && resource.functionUrlConfig.data) {
if (resource.functionUrlConfig.data.AuthType === 'NONE') {
internetExposed += 'public function URL';
} else if (resource.functionUrlConfig.data.AuthType === 'AWS_IAM' &&
resource.functionPolicy && resource.functionPolicy.data) {
} else if (resource.functionUrlConfig.data.AuthType === 'AWS_IAM' &&
resource.functionPolicy && resource.functionPolicy.data) {
let authConfig = resource.functionPolicy.data;
if (authConfig.Policy) {
let statements = normalizePolicyDocument(authConfig.Policy);

if (statements) {
let hasDenyAll = false;
let hasPublicAllow = false;
let hasRestrictiveConditions = false;

for (let statement of statements) {
// Check for explicit deny statements first
if (statement.Effect === 'Deny') {
// Check if there's a deny for all principals
if ((!statement.Condition || Object.keys(statement.Condition).length === 0) &&
if ((!statement.Condition || Object.keys(statement.Condition).length === 0) &&
globalPrincipal(statement.Principal)) {
hasDenyAll = true;
break;
}

// Check for deny with IP restrictions
if (statement.Condition &&
(statement.Condition['NotIpAddress'] ||
statement.Condition['IpAddress'])) {
if (statement.Condition &&
(statement.Condition['NotIpAddress'] ||
statement.Condition['IpAddress'])) {
hasRestrictiveConditions = true;
}
} else if (statement.Effect === 'Allow') {
// Skip if the statement doesn't include relevant Lambda actions
if (!statement.Action ||
(!Array.isArray(statement.Action) ?
if (!statement.Action ||
(!Array.isArray(statement.Action) ?
!statement.Action.includes('lambda:InvokeFunctionUrl') :
!statement.Action.some(action =>
action === '*' ||
action === 'lambda:*' ||
!statement.Action.some(action =>
action === '*' ||
action === 'lambda:*' ||
action === 'lambda:InvokeFunctionUrl'
))) {
continue;
Expand All @@ -1303,17 +1302,17 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
'aws:PrincipalArn',
'aws:SourceAccount'
];
const hasRestriction = restrictiveConditions.some(condition =>
Object.keys(statement.Condition).some(key =>

const hasRestriction = restrictiveConditions.some(condition =>
Object.keys(statement.Condition).some(key =>
key.toLowerCase().includes(condition.toLowerCase())
)
);

if (hasRestriction) {
hasRestrictiveConditions = true;
} else if (statement.Condition['StringEquals'] &&
statement.Condition['StringEquals']['lambda:FunctionUrlAuthType'] === 'NONE') {
statement.Condition['StringEquals']['lambda:FunctionUrlAuthType'] === 'NONE') {
hasPublicAllow = true;
}
}
Expand All @@ -1323,8 +1322,8 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs

// Only mark as exposed if we have a public allow and no restrictions
if (hasPublicAllow && !hasDenyAll && !hasRestrictiveConditions) {
internetExposed += internetExposed.length ?
', function URL with global IAM access' :
internetExposed += internetExposed.length ?
', function URL with global IAM access' :
'function URL with global IAM access';
}
}
Expand Down Expand Up @@ -1352,19 +1351,19 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
['apigateway', 'getIntegration', region, api.id]);

if (!getIntegration || getIntegration.err || !Object.keys(getIntegration).length) continue;

for (let apiResource of Object.values(getIntegration)) {
// Check if any integration points to this Lambda function
let lambdaIntegrations = Object.values(apiResource).filter(integration => {
return integration && integration.data && (integration.data.type === 'AWS' || integration.data.type === 'AWS_PROXY') &&
integration.data.uri &&
return integration && integration.data && (integration.data.type === 'AWS' || integration.data.type === 'AWS_PROXY') &&
integration.data.uri &&
integration.data.uri.includes(resource.functionArn);
});

if (lambdaIntegrations.length) {
internetExposed += internetExposed.length ? `, API Gateway ${api.name}` : `API Gateway ${api.name}`;
}
}
}
}
}
}
Expand All @@ -1375,7 +1374,7 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
}

if (!resource.functionArn) {
// Scenario 1: check if resource is in a private subnet
// Scenario 1: check if resource is in a private subnet
let subnetRouteTableMap, privateSubnets;
var describeSubnets = helpers.addSource(cache, source,
['ec2', 'describeSubnets', region]);
Expand Down Expand Up @@ -1500,15 +1499,13 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
if (elbs && elbs.length) {
if (!describeSecurityGroups || !describeSecurityGroups.data) {
describeSecurityGroups = helpers.addSource(cache, source,
['ec2', 'describeSecurityGroups', region]);
['ec2', 'describeSecurityGroups', region]);
}

elbs.forEach(lb => {
let isLBPublic = false;
if (lb.Scheme && lb.Scheme.toLowerCase() === 'internet-facing') {
if (lb.SecurityGroups && lb.SecurityGroups.length) {
var describeSecurityGroups = helpers.addSource(cache, source,
['ec2', 'describeSecurityGroups', region]);
if (describeSecurityGroups &&
!describeSecurityGroups.err && describeSecurityGroups.data && describeSecurityGroups.data.length) {
let elbSGs = describeSecurityGroups.data.filter(sg => lb.SecurityGroups.includes(sg.GroupId));
Expand All @@ -1533,7 +1530,7 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs

let getLambdaTargetELBs = function(cache, source, region) {
let lambdaELBMap = {};

var describeLoadBalancersv2 = helpers.addSource(cache, source,
['elbv2', 'describeLoadBalancers', region]);

Expand All @@ -1545,18 +1542,18 @@ let getLambdaTargetELBs = function(cache, source, region) {
var describeTargetGroups = helpers.addSource(cache, source,
['elbv2', 'describeTargetGroups', region, lb.DNSName]);

if (!describeTargetGroups || describeTargetGroups.err || !describeTargetGroups.data ||
if (!describeTargetGroups || describeTargetGroups.err || !describeTargetGroups.data ||
!describeTargetGroups.data.TargetGroups) return;

describeTargetGroups.data.TargetGroups.forEach(tg => {
var describeTargetHealth = helpers.addSource(cache, source,
['elbv2', 'describeTargetHealth', region, tg.TargetGroupArn]);

if (!describeTargetHealth || describeTargetHealth.err || !describeTargetHealth.data ||
if (!describeTargetHealth || describeTargetHealth.err || !describeTargetHealth.data ||
!describeTargetHealth.data.TargetHealthDescriptions) return;

describeTargetHealth.data.TargetHealthDescriptions.forEach(target => {
if (target.Target && target.Target.Id &&
if (target.Target && target.Target.Id &&
target.Target.Id.startsWith('arn:aws:lambda')) {
if (!lambdaELBMap[target.Target.Id]) {
lambdaELBMap[target.Target.Id] = [];
Expand All @@ -1567,21 +1564,21 @@ let getLambdaTargetELBs = function(cache, source, region) {
targetGroupArn: tg.TargetGroupArn,
targets: [target.Target]
});

// Check if there's an active listener for this target group
let hasListener = false;
var describeListeners = helpers.addSource(cache, source,
['elbv2', 'describeListeners', region, lb.DNSName]);
if (describeListeners && describeListeners.data &&

if (describeListeners && describeListeners.data &&
describeListeners.data.Listeners) {
hasListener = describeListeners.data.Listeners.some(listener =>
listener.DefaultActions.some(action =>
action.TargetGroupArn === tg.TargetGroupArn
)
);
}

if (hasListener) {
lambdaELBMap[target.Target.Id].push(lb);
}
Expand Down
68 changes: 55 additions & 13 deletions helpers/azure/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -775,21 +775,29 @@ function checkSecurityGroup(securityGroups) {
return {exposed: true};
}

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

if (securityGroups && securityGroups.length) {
// Scenario 1: check if security group allow all inbound traffic
let exposedSG = checkSecurityGroup(securityGroups);
if (exposedSG && exposedSG.exposed) {
if (exposedSG.nsg) {
return `nsg ${exposedSG.nsg}`
} else {
return '';
const isFunctionApp = resource && resource.kind &&
resource.kind.toLowerCase().includes('functionapp');

if (!isFunctionApp) {
if (securityGroups && securityGroups.length) {
// Scenario 1: check if security group allow all inbound traffic
let exposedSG = checkSecurityGroup(securityGroups);
if (exposedSG && exposedSG.exposed) {
if (exposedSG.nsg) {
return `nsg ${exposedSG.nsg}`
} else {
return '';
}
}
}
}


const { applicationGateways, lbNames, frontDoors } = attachedResources;

if (lbNames && lbNames.length) {
const loadBalancers = shared.addSource(cache, source,
['loadBalancers', 'listAll', location]);
Expand All @@ -802,18 +810,51 @@ function checkNetworkExposure(cache, source, networkInterfaces, securityGroups,
if (lb.frontendIPConfigurations && lb.frontendIPConfigurations.length) {
isPublic = lb.frontendIPConfigurations.some(ipConfig => ipConfig.properties
&& ipConfig.properties.publicIPAddress && ipConfig.properties.publicIPAddress.id);
if (isPublic && ((lb.nboundNatRules && nboundNatRules.length) || (lb.loadBalancingRules && lb.loadBalancingRules.length))) {
exposedPath += `lb ${lb.name}`;
break;
if (isPublic && ((lb.inboundNatRules && inboundNatRules.length) || (lb.loadBalancingRules && lb.loadBalancingRules.length))) {
exposedPath += exposedPath.length ? `, lb ${lb.name}` : `lb ${lb.name}`;
}
}
}
}
}
}


if (applicationGateways && applicationGateways.length) {
for (const ag of applicationGateways) {
if (ag.frontendIPConfigurations && ag.frontendIPConfigurations.some(config => config.publicIPAddress && config.publicIPAddress.id)) {
exposedPath += exposedPath.length ? `, ag ${ag.name}` : `ag ${ag.name}`;
}
}
}

if (frontDoors && frontDoors.length) {
for (const fd of frontDoors) {
if (!fd.associatedWafPolicies || !fd.associatedWafPolicies.length) {
exposedPath += exposedPath.length ? `, fd ${fd.name}` : `fd ${fd.name}`;
continue;
}

// Check WAF policies
let hasSecureWaf = false;
for (const policy of fd.associatedWafPolicies) {
if (policy.policySettings &&
policy.policySettings.enabledState === 'Enabled' &&
policy.policySettings.mode === 'Prevention') {
hasSecureWaf = true;
break;
}
}

if (!hasSecureWaf) {
exposedPath += exposedPath.length ? `, fd ${fd.name}` : `fd ${fd.name}`;
}
}
}


return exposedPath;
}

module.exports = {
addResult: addResult,
findOpenPorts: findOpenPorts,
Expand All @@ -830,3 +871,4 @@ module.exports = {
checkNetworkExposure: checkNetworkExposure

};

Loading

0 comments on commit 45d3007

Please sign in to comment.