Skip to content

Commit 5370f01

Browse files
Merge pull request #2119 from aquasecurity/bundles_sync/01_01/pluginsPR
syncing with saas
2 parents 7358797 + 3e1811b commit 5370f01

26 files changed

Lines changed: 496 additions & 133 deletions

exports.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@ module.exports = {
514514
'lambdaDeadLetterQueue' : require(__dirname + '/plugins/aws/lambda/lambdaDeadLetterQueue.js'),
515515
'lambdaEnhancedMonitoring' : require(__dirname + '/plugins/aws/lambda/lambdaEnhancedMonitoring.js'),
516516
'lambdaUniqueExecutionRole' : require(__dirname + '/plugins/aws/lambda/lambdaUniqueExecutionRole.js'),
517+
'lambdaNetworkExposure' : require(__dirname + '/plugins/aws/lambda/lambdaNetworkExposure.js'),
517518

518519
'webServerPublicAccess' : require(__dirname + '/plugins/aws/mwaa/webServerPublicAccess.js'),
519520
'environmentAdminPrivileges' : require(__dirname + '/plugins/aws/mwaa/environmentAdminPrivileges.js'),
@@ -1003,6 +1004,7 @@ module.exports = {
10031004
'disableFTPDeployments' : require(__dirname + '/plugins/azure/appservice/disableFTPDeployments.js'),
10041005
'accessControlAllowCredential' : require(__dirname + '/plugins/azure/appservice/accessControlAllowCredential.js'),
10051006
'appServiceDiagnosticLogs' : require(__dirname + '/plugins/azure/appservice/appServiceDiagnosticLogs.js'),
1007+
'functionAppNetworkExposure' : require(__dirname + '/plugins/azure/appservice/functionAppNetworkExposure.js'),
10061008

10071009
'rbacEnabled' : require(__dirname + '/plugins/azure/kubernetesservice/rbacEnabled.js'),
10081010
'aksManagedIdentity' : require(__dirname + '/plugins/azure/kubernetesservice/aksManagedIdentity.js'),
@@ -1608,8 +1610,8 @@ module.exports = {
16081610
'cloudFunctionLabelsAdded' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionLabelsAdded.js'),
16091611
'cloudFunctionOldRuntime' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionOldRuntime.js'),
16101612
'functionAllUsersPolicy' : require(__dirname + '/plugins/google/cloudfunctions/functionAllUsersPolicy.js'),
1611-
16121613
'serverlessVPCAccess' : require(__dirname + '/plugins/google/cloudfunctions/serverlessVPCAccess.js'),
1614+
'cloudFunctionNetworkExposure' : require(__dirname + '/plugins/google/cloudfunctions/cloudFunctionNetworkExposure.js'),
16131615

16141616
'computeAllowedExternalIPs' : require(__dirname + '/plugins/google/cloudresourcemanager/computeAllowedExternalIPs.js'),
16151617
'disableAutomaticIAMGrants' : require(__dirname + '/plugins/google/cloudresourcemanager/disableAutomaticIAMGrants.js'),

helpers/asl/asl-1.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var parse = function(obj, path, region, cloud, accountId, resourceId) {
2424
return parse(obj[localPath], path);
2525
} else return ['not set'];
2626
} else if (!Array.isArray(obj) && path && path.length) {
27-
if (obj[path]) return [obj[path]];
27+
if (obj[path] || typeof obj[path] === 'boolean') return [obj[path]];
2828
else {
2929
if (cloud==='aws' && path.startsWith('arn:aws')) {
3030
const template_string = path;

helpers/aws/functions.js

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,7 +1239,6 @@ var getAttachedELBs = function(cache, source, region, resourceId, lbField, lbAt
12391239
return elbs;
12401240
};
12411241

1242-
12431242
var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs, region, results, resource) {
12441243
var internetExposed = '';
12451244
var isSubnetPrivate = false;
@@ -1249,41 +1248,41 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
12491248
if (resource.functionUrlConfig && resource.functionUrlConfig.data) {
12501249
if (resource.functionUrlConfig.data.AuthType === 'NONE') {
12511250
internetExposed += 'public function URL';
1252-
} else if (resource.functionUrlConfig.data.AuthType === 'AWS_IAM' &&
1253-
resource.functionPolicy && resource.functionPolicy.data) {
1251+
} else if (resource.functionUrlConfig.data.AuthType === 'AWS_IAM' &&
1252+
resource.functionPolicy && resource.functionPolicy.data) {
12541253
let authConfig = resource.functionPolicy.data;
12551254
if (authConfig.Policy) {
12561255
let statements = normalizePolicyDocument(authConfig.Policy);
1257-
1256+
12581257
if (statements) {
12591258
let hasDenyAll = false;
12601259
let hasPublicAllow = false;
12611260
let hasRestrictiveConditions = false;
1262-
1261+
12631262
for (let statement of statements) {
12641263
// Check for explicit deny statements first
12651264
if (statement.Effect === 'Deny') {
12661265
// Check if there's a deny for all principals
1267-
if ((!statement.Condition || Object.keys(statement.Condition).length === 0) &&
1266+
if ((!statement.Condition || Object.keys(statement.Condition).length === 0) &&
12681267
globalPrincipal(statement.Principal)) {
12691268
hasDenyAll = true;
12701269
break;
12711270
}
1272-
1271+
12731272
// Check for deny with IP restrictions
1274-
if (statement.Condition &&
1275-
(statement.Condition['NotIpAddress'] ||
1276-
statement.Condition['IpAddress'])) {
1273+
if (statement.Condition &&
1274+
(statement.Condition['NotIpAddress'] ||
1275+
statement.Condition['IpAddress'])) {
12771276
hasRestrictiveConditions = true;
12781277
}
12791278
} else if (statement.Effect === 'Allow') {
12801279
// Skip if the statement doesn't include relevant Lambda actions
1281-
if (!statement.Action ||
1282-
(!Array.isArray(statement.Action) ?
1280+
if (!statement.Action ||
1281+
(!Array.isArray(statement.Action) ?
12831282
!statement.Action.includes('lambda:InvokeFunctionUrl') :
1284-
!statement.Action.some(action =>
1285-
action === '*' ||
1286-
action === 'lambda:*' ||
1283+
!statement.Action.some(action =>
1284+
action === '*' ||
1285+
action === 'lambda:*' ||
12871286
action === 'lambda:InvokeFunctionUrl'
12881287
))) {
12891288
continue;
@@ -1303,17 +1302,17 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
13031302
'aws:PrincipalArn',
13041303
'aws:SourceAccount'
13051304
];
1306-
1307-
const hasRestriction = restrictiveConditions.some(condition =>
1308-
Object.keys(statement.Condition).some(key =>
1305+
1306+
const hasRestriction = restrictiveConditions.some(condition =>
1307+
Object.keys(statement.Condition).some(key =>
13091308
key.toLowerCase().includes(condition.toLowerCase())
13101309
)
13111310
);
1312-
1311+
13131312
if (hasRestriction) {
13141313
hasRestrictiveConditions = true;
13151314
} else if (statement.Condition['StringEquals'] &&
1316-
statement.Condition['StringEquals']['lambda:FunctionUrlAuthType'] === 'NONE') {
1315+
statement.Condition['StringEquals']['lambda:FunctionUrlAuthType'] === 'NONE') {
13171316
hasPublicAllow = true;
13181317
}
13191318
}
@@ -1323,8 +1322,8 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
13231322

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

13541353
if (!getIntegration || getIntegration.err || !Object.keys(getIntegration).length) continue;
1355-
1354+
13561355
for (let apiResource of Object.values(getIntegration)) {
13571356
// Check if any integration points to this Lambda function
13581357
let lambdaIntegrations = Object.values(apiResource).filter(integration => {
1359-
return integration && integration.data && (integration.data.type === 'AWS' || integration.data.type === 'AWS_PROXY') &&
1360-
integration.data.uri &&
1358+
return integration && integration.data && (integration.data.type === 'AWS' || integration.data.type === 'AWS_PROXY') &&
1359+
integration.data.uri &&
13611360
integration.data.uri.includes(resource.functionArn);
13621361
});
13631362

13641363
if (lambdaIntegrations.length) {
13651364
internetExposed += internetExposed.length ? `, API Gateway ${api.name}` : `API Gateway ${api.name}`;
13661365
}
1367-
}
1366+
}
13681367
}
13691368
}
13701369
}
@@ -1375,7 +1374,7 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
13751374
}
13761375

13771376
if (!resource.functionArn) {
1378-
// Scenario 1: check if resource is in a private subnet
1377+
// Scenario 1: check if resource is in a private subnet
13791378
let subnetRouteTableMap, privateSubnets;
13801379
var describeSubnets = helpers.addSource(cache, source,
13811380
['ec2', 'describeSubnets', region]);
@@ -1500,15 +1499,13 @@ var checkNetworkExposure = function(cache, source, subnets, securityGroups, elbs
15001499
if (elbs && elbs.length) {
15011500
if (!describeSecurityGroups || !describeSecurityGroups.data) {
15021501
describeSecurityGroups = helpers.addSource(cache, source,
1503-
['ec2', 'describeSecurityGroups', region]);
1502+
['ec2', 'describeSecurityGroups', region]);
15041503
}
15051504

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

15341531
let getLambdaTargetELBs = function(cache, source, region) {
15351532
let lambdaELBMap = {};
1536-
1533+
15371534
var describeLoadBalancersv2 = helpers.addSource(cache, source,
15381535
['elbv2', 'describeLoadBalancers', region]);
15391536

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

1548-
if (!describeTargetGroups || describeTargetGroups.err || !describeTargetGroups.data ||
1545+
if (!describeTargetGroups || describeTargetGroups.err || !describeTargetGroups.data ||
15491546
!describeTargetGroups.data.TargetGroups) return;
15501547

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

1555-
if (!describeTargetHealth || describeTargetHealth.err || !describeTargetHealth.data ||
1552+
if (!describeTargetHealth || describeTargetHealth.err || !describeTargetHealth.data ||
15561553
!describeTargetHealth.data.TargetHealthDescriptions) return;
15571554

15581555
describeTargetHealth.data.TargetHealthDescriptions.forEach(target => {
1559-
if (target.Target && target.Target.Id &&
1556+
if (target.Target && target.Target.Id &&
15601557
target.Target.Id.startsWith('arn:aws:lambda')) {
15611558
if (!lambdaELBMap[target.Target.Id]) {
15621559
lambdaELBMap[target.Target.Id] = [];
@@ -1567,21 +1564,21 @@ let getLambdaTargetELBs = function(cache, source, region) {
15671564
targetGroupArn: tg.TargetGroupArn,
15681565
targets: [target.Target]
15691566
});
1570-
1567+
15711568
// Check if there's an active listener for this target group
15721569
let hasListener = false;
15731570
var describeListeners = helpers.addSource(cache, source,
15741571
['elbv2', 'describeListeners', region, lb.DNSName]);
1575-
1576-
if (describeListeners && describeListeners.data &&
1572+
1573+
if (describeListeners && describeListeners.data &&
15771574
describeListeners.data.Listeners) {
15781575
hasListener = describeListeners.data.Listeners.some(listener =>
15791576
listener.DefaultActions.some(action =>
15801577
action.TargetGroupArn === tg.TargetGroupArn
15811578
)
15821579
);
15831580
}
1584-
1581+
15851582
if (hasListener) {
15861583
lambdaELBMap[target.Target.Id].push(lb);
15871584
}

helpers/azure/functions.js

Lines changed: 55 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -775,21 +775,29 @@ function checkSecurityGroup(securityGroups) {
775775
return {exposed: true};
776776
}
777777

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

781-
if (securityGroups && securityGroups.length) {
782-
// Scenario 1: check if security group allow all inbound traffic
783-
let exposedSG = checkSecurityGroup(securityGroups);
784-
if (exposedSG && exposedSG.exposed) {
785-
if (exposedSG.nsg) {
786-
return `nsg ${exposedSG.nsg}`
787-
} else {
788-
return '';
781+
const isFunctionApp = resource && resource.kind &&
782+
resource.kind.toLowerCase().includes('functionapp');
783+
784+
if (!isFunctionApp) {
785+
if (securityGroups && securityGroups.length) {
786+
// Scenario 1: check if security group allow all inbound traffic
787+
let exposedSG = checkSecurityGroup(securityGroups);
788+
if (exposedSG && exposedSG.exposed) {
789+
if (exposedSG.nsg) {
790+
return `nsg ${exposedSG.nsg}`
791+
} else {
792+
return '';
793+
}
789794
}
790795
}
791796
}
792797

798+
799+
const { applicationGateways, lbNames, frontDoors } = attachedResources;
800+
793801
if (lbNames && lbNames.length) {
794802
const loadBalancers = shared.addSource(cache, source,
795803
['loadBalancers', 'listAll', location]);
@@ -802,18 +810,51 @@ function checkNetworkExposure(cache, source, networkInterfaces, securityGroups,
802810
if (lb.frontendIPConfigurations && lb.frontendIPConfigurations.length) {
803811
isPublic = lb.frontendIPConfigurations.some(ipConfig => ipConfig.properties
804812
&& ipConfig.properties.publicIPAddress && ipConfig.properties.publicIPAddress.id);
805-
if (isPublic && ((lb.nboundNatRules && nboundNatRules.length) || (lb.loadBalancingRules && lb.loadBalancingRules.length))) {
806-
exposedPath += `lb ${lb.name}`;
807-
break;
813+
if (isPublic && ((lb.inboundNatRules && inboundNatRules.length) || (lb.loadBalancingRules && lb.loadBalancingRules.length))) {
814+
exposedPath += exposedPath.length ? `, lb ${lb.name}` : `lb ${lb.name}`;
808815
}
809816
}
810817
}
811818
}
812819
}
813820
}
821+
822+
823+
if (applicationGateways && applicationGateways.length) {
824+
for (const ag of applicationGateways) {
825+
if (ag.frontendIPConfigurations && ag.frontendIPConfigurations.some(config => config.publicIPAddress && config.publicIPAddress.id)) {
826+
exposedPath += exposedPath.length ? `, ag ${ag.name}` : `ag ${ag.name}`;
827+
}
828+
}
829+
}
830+
831+
if (frontDoors && frontDoors.length) {
832+
for (const fd of frontDoors) {
833+
if (!fd.associatedWafPolicies || !fd.associatedWafPolicies.length) {
834+
exposedPath += exposedPath.length ? `, fd ${fd.name}` : `fd ${fd.name}`;
835+
continue;
836+
}
837+
838+
// Check WAF policies
839+
let hasSecureWaf = false;
840+
for (const policy of fd.associatedWafPolicies) {
841+
if (policy.policySettings &&
842+
policy.policySettings.enabledState === 'Enabled' &&
843+
policy.policySettings.mode === 'Prevention') {
844+
hasSecureWaf = true;
845+
break;
846+
}
847+
}
848+
849+
if (!hasSecureWaf) {
850+
exposedPath += exposedPath.length ? `, fd ${fd.name}` : `fd ${fd.name}`;
851+
}
852+
}
853+
}
854+
855+
814856
return exposedPath;
815857
}
816-
817858
module.exports = {
818859
addResult: addResult,
819860
findOpenPorts: findOpenPorts,
@@ -830,3 +871,4 @@ module.exports = {
830871
checkNetworkExposure: checkNetworkExposure
831872

832873
};
874+

0 commit comments

Comments
 (0)