Skip to content
Open
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 helpers/azure/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ var calls = {
},
pricings: {
list: {
url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Security/pricings?api-version=2018-06-01'
url: 'https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Security/pricings?api-version=2024-01-01'
},
sendIntegration: serviceMap['Defender'][0]
},
Expand Down
19 changes: 17 additions & 2 deletions helpers/azure/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,27 @@ function checkFlexibleServerConfigs(servers, cache, source, location, results, s
});
}

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

let pricingData = pricings.data.find((pricing) => pricing.name.toLowerCase() === serviceName);
if (pricingData) {
if (pricingData.pricingTier.toLowerCase() === 'standard') {
addResult(results, 0, `Azure Defender is enabled for ${serviceDisplayName}`, location, pricingData.id);
if (requiredExtensions && requiredExtensions.length) {
let extensions = pricingData.extensions || [];

let missingExtensions = requiredExtensions.filter((extensionName) => {
let extension = extensions.find((ext) => ext.name && ext.name.toLowerCase() === extensionName.toLowerCase());
return !extension || !extension.isEnabled || extension.isEnabled.toString().toLowerCase() !== 'true';
});

if (missingExtensions.length) {
addResult(results, 2, `Azure Defender for ${serviceDisplayName} is enabled but the following extensions are not enabled: ${missingExtensions.join(', ')}`, location, pricingData.id);
} else {
addResult(results, 0, `Azure Defender is enabled for ${serviceDisplayName} with all required extensions enabled`, location, pricingData.id);
}
} else {
addResult(results, 0, `Azure Defender is enabled for ${serviceDisplayName}`, location, pricingData.id);
}
} else {
addResult(results, 2, `Azure Defender is not enabled for ${serviceDisplayName}`, location, pricingData.id);
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/aws/iam/accessKeysLastUsed.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const accessKeysLastUsed = require('./accessKeysLastUsed');
var warnDate = new Date();
warnDate.setMonth(warnDate.getMonth() - 4);
var passDate = new Date();
passDate.setMonth(passDate.getMonth() - 1);
passDate.setDate(passDate.getDate() - 15);
var failDate = new Date();
failDate.setMonth(failDate.getMonth() - 7);

Expand Down
8 changes: 6 additions & 2 deletions plugins/azure/applicationGateway/agWafEnabled.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,12 @@ module.exports = {
continue;
}

if (appGateway.webApplicationFirewallConfiguration && appGateway.webApplicationFirewallConfiguration.enabled
&& appGateway.webApplicationFirewallConfiguration.enabled === true) {
// WAF can either be attached as a separate firewall policy or configured inline on the gateway
let firewallPolicyAttached = appGateway.firewallPolicy && appGateway.firewallPolicy.id;
let inlineWafEnabled = appGateway.webApplicationFirewallConfiguration &&
appGateway.webApplicationFirewallConfiguration.enabled === true;

if (firewallPolicyAttached || inlineWafEnabled) {
helpers.addResult(results, 0, 'Web Application Firewall is enabled for Application Gateway', location, appGateway.id);
} else {
helpers.addResult(results, 2, 'Web Application Firewall is not enabled for Application Gateway', location, appGateway.id);
Expand Down
22 changes: 22 additions & 0 deletions plugins/azure/applicationGateway/agWafEnabled.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ const appGateway = [
"enabled": false,
"firewallMode": "Detection",
},
},
{ "sku": {
"tier": "WAF_v2"
},
"name": 'test-gateway-policy',
"id": '/subscriptions/123/resourceGroups/aqua-resource-group/providers/Microsoft.Network/applicationGateways/test-gateway-policy',
"type": "Microsoft.Network/applicationGateways",
"location": "eastus",
"firewallPolicy": {
"id": '/subscriptions/123/resourceGroups/aqua-resource-group/providers/Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies/test-waf-policy'
},
}
];

Expand Down Expand Up @@ -120,6 +131,17 @@ describe('agWafEnabled', function() {
done();
});
});

it('should give passing result if Application Gateway has a firewall policy attached', function(done) {
const cache = createCache([appGateway[3]]);
agWafEnabled.run(cache, {}, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(0);
expect(results[0].message).to.include('Web Application Firewall is enabled for Application Gateway');
expect(results[0].region).to.equal('eastus');
done();
});
});
});
});

40 changes: 31 additions & 9 deletions plugins/azure/databricks/workspaceDiagnosticLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ module.exports = {
const results = [];
const source = {};
const locations = helpers.locations(settings.govcloud);
const requiredCategories = ['accounts', 'clusters', 'notebook', 'jobs', 'filesystem'];

async.each(locations.databricks, function(location, rcb) {
const databricks = helpers.addSource(cache, source,
['databricks', 'listWorkspaces', location]);

if (!databricks) return rcb();

if (databricks.err || !databricks.data) {
helpers.addResult(results, 3, 'Unable to query for Databricks Workspaces: ' + helpers.addError(databricks), location);
return rcb();
Expand All @@ -45,19 +46,40 @@ module.exports = {
location, workspace.id);
continue;
}

var found = diagnosticSettings.data.find(ds => ds.logs && ds.logs.length);

if (found) {
helpers.addResult(results, 0, 'Databricks workspace has diagnostic logs enabled', location, workspace.id);

const hasDestination = diagnosticSettings.data.some(ds =>
ds.workspaceId || ds.storageAccountId || ds.eventHubAuthorizationRuleId
);

if (!hasDestination) {
helpers.addResult(results, 2,
'Databricks workspace does not have diagnostic logs configured with a valid destination',
location, workspace.id);
continue;
}

let missingLogs = requiredCategories.slice();
diagnosticSettings.data.forEach(ds => {
if (!ds.logs || !ds.logs.length) return;
missingLogs = missingLogs.filter(requiredCategory =>
!ds.logs.some(log =>
(log.category && log.category.toLowerCase() === requiredCategory && log.enabled) ||
(log.categoryGroup === 'allLogs' && log.enabled)
)
);
});

if (missingLogs.length) {
helpers.addResult(results, 2,
`Databricks workspace does not have diagnostic logs enabled for following: ${missingLogs.join(', ')}`,
location, workspace.id);
} else {
helpers.addResult(results, 2, 'Databricks workspace does not have diagnostic logs enabled', location, workspace.id);
}
helpers.addResult(results, 0, 'Databricks workspace has diagnostic logs enabled', location, workspace.id);
}
}

rcb();
}, function() {
// Global checking goes here
callback(null, results, source);
});
}
Expand Down
37 changes: 34 additions & 3 deletions plugins/azure/databricks/workspaceDiagnosticLogs.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const diagnosticSettings = [
serviceBusRuleId: null,
eventHubAuthorizationRuleId: null,
eventHubName: null,
workspaceId: '/subscriptions/1234/resourceGroups/test/providers/Microsoft.OperationalInsights/workspaces/test-law',
metrics: [ [Object] ],
logs: [
{
Expand All @@ -57,12 +58,31 @@ const diagnosticSettings = [
serviceBusRuleId: null,
eventHubAuthorizationRuleId: null,
eventHubName: null,
workspaceId: null,
metrics: [ [Object] ],
logs: [
{
category: null,
categoryGroup: 'allLogs',
enabled: true,
retentionPolicy: { enabled: false, days: 0 }
},
],
logAnalyticsDestinationType: null
},
{
id: '/subscriptions/1234/resourcegroups/cloudsploit-dev/providers/Microsoft.Databricks/workspace/test/providers/microsoft.insights/diagnosticSettings/test-missing',
type: 'Microsoft.Insights/diagnosticSettings',
name: 'test-missing',
storageAccountId: null,
eventHubAuthorizationRuleId: null,
workspaceId: '/subscriptions/1234/resourceGroups/test/providers/Microsoft.OperationalInsights/workspaces/test-law',
logs: [
{ category: 'accounts', categoryGroup: null, enabled: true },
{ category: 'clusters', categoryGroup: null, enabled: true }
]
}
]
];
const createCache = (workspace, diagnostics) => {
let diagnostic = {};
if (workspace.length) {
Expand Down Expand Up @@ -193,10 +213,21 @@ describe('workspaceDiagnosticLogs', function () {
workspaceDiagnosticLogs.run(cache, {}, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('Databricks workspace does not have diagnostic logs enabled');
expect(results[0].message).to.include('does not have diagnostic logs configured with a valid destination');
expect(results[0].region).to.equal('eastus');
done();
});
});

it('should give failing result if required log categories are missing', function (done) {
const cache = createCache([workspaces[0]], [diagnosticSettings[2]]);
workspaceDiagnosticLogs.run(cache, {}, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('does not have diagnostic logs enabled for following');
expect(results[0].region).to.equal('eastus');
done();
});
});
});
});
});
4 changes: 3 additions & 1 deletion plugins/azure/databricks/workspaceManagedDiskCmk.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ module.exports = {

if (workspace.sku && workspace.sku.name && workspace.sku.name.toLowerCase()!='premium') {
helpers.addResult(results, 0, 'Databricks workspace is not a premium workspace', location, workspace.id);
} else if (workspace.encryption && workspace.encryption.entities && workspace.encryption.entities.managedDisk) {
} else if (workspace.encryption && workspace.encryption.entities && workspace.encryption.entities.managedDisk &&
workspace.encryption.entities.managedDisk.keySource &&
workspace.encryption.entities.managedDisk.keySource.toLowerCase() === 'microsoft.keyvault') {
helpers.addResult(results, 0, 'Databricks workspace managed disk has CMK encryption enabled', location, workspace.id);
} else {
helpers.addResult(results, 2, 'Databricks workspace managed disk does not have CMK encryption enabled', location, workspace.id);
Expand Down
28 changes: 28 additions & 0 deletions plugins/azure/databricks/workspaceManagedDiskCmk.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@ const workspaces = [
},
"location": "eastus",
"tags": {}
},
{
"managedResourceGroupId": "/subscriptions/1234/resourceGroups/test",
"encryption": {
"entities": {
"managedDisk": {
"keySource": "Microsoft.Managed"
}
}
},
"id": "/subscriptions/1234/resourceGroups/test/providers/Microsoft.Databricks/workspaces/test-workspace",
"name": "test-workspace",
"type": "Microsoft.Databricks/workspaces",
"sku": {
"name": "premium"
},
"location": "eastus",
"tags": {}
}
];

Expand Down Expand Up @@ -148,5 +166,15 @@ describe('workspaceManagedDiskCmk', function () {
done();
});
});

it('should give failing result if managed disk encryption is not using Key Vault CMK', function (done) {
const cache = createCache([workspaces[3]], null);
workspaceManagedDiskCmk.run(cache, {}, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('Databricks workspace managed disk does not have CMK encryption enabled');
done();
});
});
});
});
4 changes: 3 additions & 1 deletion plugins/azure/databricks/workspaceManagedServicesCmk.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ module.exports = {

if (workspace.sku && workspace.sku.name && workspace.sku.name.toLowerCase()!='premium') {
helpers.addResult(results, 0, 'Databricks workspace is not a premium workspace', location, workspace.id);
} else if (workspace.encryption && workspace.encryption.entities && workspace.encryption.entities.managedServices) {
} else if (workspace.encryption && workspace.encryption.entities && workspace.encryption.entities.managedServices &&
workspace.encryption.entities.managedServices.keySource &&
workspace.encryption.entities.managedServices.keySource.toLowerCase() === 'microsoft.keyvault') {
helpers.addResult(results, 0, 'Databricks workspace managed services has CMK encryption enabled', location, workspace.id);
} else {
helpers.addResult(results, 2, 'Databricks workspace managed services does not have CMK encryption enabled', location, workspace.id);
Expand Down
28 changes: 28 additions & 0 deletions plugins/azure/databricks/workspaceManagedServicesCmk.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,24 @@ const workspaces = [
},
"location": "eastus",
"tags": {}
},
{
"managedResourceGroupId": "/subscriptions/1234/resourceGroups/test",
"encryption": {
"entities": {
"managedServices": {
"keySource": "Microsoft.Managed"
}
}
},
"id": "/subscriptions/1234/resourceGroups/test/providers/Microsoft.Databricks/workspaces/test-workspace",
"name": "test-workspace",
"type": "Microsoft.Databricks/workspaces",
"sku": {
"name": "premium"
},
"location": "eastus",
"tags": {}
}
];

Expand Down Expand Up @@ -140,5 +158,15 @@ describe('workspaceManagedServicesCmk', function () {
done();
});
});

it('should give failing result if managed services encryption is not using Key Vault CMK', function (done) {
const cache = createCache([workspaces[3]], null);
workspaceManagedServicesCmk.run(cache, {}, (err, results) => {
expect(results.length).to.equal(1);
expect(results[0].status).to.equal(2);
expect(results[0].message).to.include('Databricks workspace managed services does not have CMK encryption enabled');
done();
});
});
});
});
6 changes: 5 additions & 1 deletion plugins/azure/defender/enableDefenderForContainers.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ module.exports = {
apis: ['pricings:list'],
realtime_triggers: ['microsoftsecurity:pricings:write','microsoftsecurity:pricings:delete'],

// Per CIS: az security pricing show --name "Containers" --query [pricingTier,extensions[*].[name,isEnabled]]
requiredExtensions: ['ContainerRegistriesVulnerabilityAssessments', 'AgentlessDiscoveryForKubernetes', 'AgentlessVmScanning', 'ContainerSensor'],

run: function(cache, settings, callback) {
var results = [];
var source = {};
var locations = helpers.locations(settings.govcloud);
var requiredExtensions = this.requiredExtensions;

async.each(locations.pricings, function(location, rcb) {
var pricings = helpers.addSource(cache, source,
Expand All @@ -35,7 +39,7 @@ module.exports = {
return rcb();
}

helpers.checkMicrosoftDefender(pricings, 'containers', 'Containers', results, location);
helpers.checkMicrosoftDefender(pricings, 'containers', 'Containers', results, location, requiredExtensions);

rcb();
}, function(){
Expand Down
Loading
Loading