-
Notifications
You must be signed in to change notification settings - Fork 688
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revised keyVaultKeyExpiryNonRbac (2) (#2106)
* Revised keyVaultKeyExpiryNonRbac (2) * Apply suggestions from code review suggested fixes Co-authored-by: Fatima <66124862+fatima99s@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Fatima <66124862+fatima99s@users.noreply.github.com> * Apply suggestions from code review * Update plugins/azure/keyvaults/keyVaultKeyExpiryNonRbac.js --------- Co-authored-by: AkhtarAmir <AkhtarAmir> Co-authored-by: Fatima <66124862+fatima99s@users.noreply.github.com> Co-authored-by: alphadev4 <113519745+alphadev4@users.noreply.github.com>
- Loading branch information
1 parent
e42c809
commit f75a336
Showing
3 changed files
with
277 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
var async = require('async'); | ||
var helpers = require('../../../helpers/azure'); | ||
|
||
module.exports = { | ||
title: 'Key Vault Key Expiry Non RBAC', | ||
category: 'Key Vaults', | ||
domain: 'Application Integration', | ||
severity: 'High', | ||
description: 'Ensures that expiration date is set for all keys in non RBAC Key Vaults.', | ||
more_info: 'Setting an expiration date on keys helps in key lifecycle management and ensures that keys are rotated regularly.', | ||
recommended_action: 'Modify keys in non-RBAC Key Vaults to have an expiration date set.', | ||
link: 'https://learn.microsoft.com/en-us/azure/key-vault/about-keys-secrets-and-certificates', | ||
apis: ['vaults:list', 'vaults:getKeys'], | ||
settings: { | ||
non_rbac_key_vault_key_expiry_fail: { | ||
name: 'Key Vault Key Expiry Fail', | ||
description: 'Return a failing result when key expiration date is within this number of days in the future', | ||
regex: '^[1-9]{1}[0-9]{0,3}$', | ||
default: '30' | ||
} | ||
}, | ||
realtime_triggers: ['microsoftkeyvault:vaults:write', 'microsoftkeyvault:vaults:delete'], | ||
|
||
run: function(cache, settings, callback) { | ||
var results = []; | ||
var source = {}; | ||
var locations = helpers.locations(settings.govcloud); | ||
var config = { | ||
key_vault_key_expiry_fail: parseInt(settings.non_rbac_key_vault_key_expiry_fail || this.settings.non_rbac_key_vault_key_expiry_fail.default) | ||
}; | ||
|
||
async.each(locations.vaults, function(location, rcb) { | ||
var vaults = helpers.addSource(cache, source, | ||
['vaults', 'list', location]); | ||
|
||
if (!vaults) return rcb(); | ||
|
||
if (vaults.err || !vaults.data) { | ||
helpers.addResult(results, 3, 'Unable to query for Key Vaults: ' + helpers.addError(vaults), location); | ||
return rcb(); | ||
} | ||
|
||
if (!vaults.data.length) { | ||
helpers.addResult(results, 0, 'No Key Vaults found', location); | ||
return rcb(); | ||
} | ||
|
||
vaults.data.forEach(function(vault) { | ||
if (!vault || !vault.properties) { | ||
helpers.addResult(results, 3, 'Unable to read vault properties', location, vault.id); | ||
return; | ||
} | ||
|
||
if (vault.properties.enableRbacAuthorization) { | ||
return; | ||
} | ||
|
||
var keys = helpers.addSource(cache, source, | ||
['vaults', 'getKeys', location, vault.id]); | ||
|
||
if (!keys || keys.err || !keys.data) { | ||
helpers.addResult(results, 3, | ||
'Unable to query for Key Vault keys: ' + helpers.addError(keys), location, vault.id); | ||
} else if (!keys.data.length) { | ||
helpers.addResult(results, 0, | ||
'No Key Vault keys found in non RBAC vault', location, vault.id); | ||
} else { | ||
keys.data.forEach(function(key) { | ||
var keyName = key.kid.substring(key.kid.lastIndexOf('/') + 1); | ||
var keyId = `${vault.id}/keys/${keyName}`; | ||
|
||
if (!key.attributes || !key.attributes.enabled) { | ||
helpers.addResult(results, 0, | ||
'Key in non RBAC vault is not enabled', location, keyId); | ||
} else if (key.attributes && (key.attributes.expires || key.attributes.exp)) { | ||
let keyExpiry = key.attributes.exp ? key.attributes.exp * 1000 : key.attributes.expires; | ||
let difference = Math.round((new Date(keyExpiry).getTime() - (new Date).getTime())/(24*60*60*1000)); | ||
if (difference > config.key_vault_key_expiry_fail) { | ||
helpers.addResult(results, 0, | ||
`Key in non RBAC vault expires in ${difference} days`, location, keyId); | ||
} else if (difference > 0){ | ||
helpers.addResult(results, 2, | ||
`Key in non RBAC vault expires in ${difference} days`, location, keyId); | ||
} else { | ||
helpers.addResult(results, 2, | ||
`Key in non RBAC vault expired ${Math.abs(difference)} days ago`, location, keyId); | ||
} | ||
} else { | ||
helpers.addResult(results, 0, | ||
'Key expiration is not enabled in non RBAC vault', location, keyId); | ||
} | ||
}); | ||
} | ||
}); | ||
|
||
rcb(); | ||
}, function() { | ||
callback(null, results, source); | ||
}); | ||
} | ||
}; |
175 changes: 175 additions & 0 deletions
175
plugins/azure/keyvaults/keyVaultKeyExpiryNonRbac.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
var expect = require('chai').expect; | ||
var auth = require('./keyVaultKeyExpiryNonRbac'); | ||
|
||
var keyExpiryPass = new Date(); | ||
keyExpiryPass.setMonth(keyExpiryPass.getMonth() + 2); | ||
|
||
var keyExpiryFail = new Date(); | ||
keyExpiryFail.setMonth(keyExpiryFail.getMonth() + 1); | ||
|
||
var keyExpired = new Date(); | ||
keyExpired.setMonth(keyExpired.getMonth() - 1); | ||
|
||
const listKeyVaults = [ | ||
{ | ||
id: '/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-vault', | ||
name: 'test-vault', | ||
type: 'Microsoft.KeyVault/vaults', | ||
location: 'eastus', | ||
properties: { | ||
enableRbacAuthorization: false, | ||
vaultUri: 'https://test-vault.vault.azure.net/' | ||
} | ||
}, | ||
{ | ||
id: '/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-vault-2', | ||
name: 'test-vault-2', | ||
type: 'Microsoft.KeyVault/vaults', | ||
location: 'eastus', | ||
properties: { | ||
enableRbacAuthorization: true, | ||
vaultUri: 'https://test-vault-2.vault.azure.net/' | ||
} | ||
} | ||
]; | ||
|
||
const getKeys = [ | ||
{ | ||
"attributes": { | ||
"created": "2022-04-10T17:57:43+00:00", | ||
"enabled": true, | ||
"expires": null, | ||
"notBefore": null, | ||
"updated": "2022-04-10T17:57:43+00:00" | ||
}, | ||
"kid": "https://test-vault.vault.azure.net/keys/test-key", | ||
"name": "test-key" | ||
}, | ||
{ | ||
"attributes": { | ||
"created": "2022-04-10T17:57:43+00:00", | ||
"enabled": true, | ||
"expires": keyExpiryPass, | ||
"notBefore": null, | ||
"updated": "2022-04-10T17:57:43+00:00" | ||
}, | ||
"kid": "https://test-vault.vault.azure.net/keys/test-key-2", | ||
"name": "test-key-2" | ||
}, | ||
{ | ||
"attributes": { | ||
"created": "2022-04-10T17:57:43+00:00", | ||
"enabled": true, | ||
"expires": keyExpiryFail, | ||
"notBefore": null, | ||
"updated": "2022-04-10T17:57:43+00:00" | ||
}, | ||
"kid": "https://test-vault.vault.azure.net/keys/test-key-3", | ||
"name": "test-key-3" | ||
}, | ||
{ | ||
"attributes": { | ||
"created": "2022-04-10T17:57:43+00:00", | ||
"enabled": true, | ||
"expires": keyExpired, | ||
"notBefore": null, | ||
"updated": "2022-04-10T17:57:43+00:00" | ||
}, | ||
"kid": "https://test-vault.vault.azure.net/keys/test-key-4", | ||
"name": "test-key-4" | ||
} | ||
]; | ||
|
||
const createCache = (err, list, keys) => { | ||
return { | ||
vaults: { | ||
list: { | ||
'eastus': { | ||
err: err, | ||
data: list | ||
} | ||
}, | ||
getKeys: { | ||
'eastus': { | ||
'/subscriptions/123/resourceGroups/test-rg/providers/Microsoft.KeyVault/vaults/test-vault': { | ||
err: err, | ||
data: keys | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}; | ||
|
||
describe('keyVaultKeyExpiryNonRbac', function() { | ||
describe('run', function() { | ||
it('should give passing result if no key vaults found', function(done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.equal(1); | ||
expect(results[0].status).to.equal(0); | ||
expect(results[0].message).to.include('No Key Vaults found'); | ||
expect(results[0].region).to.equal('eastus'); | ||
done() | ||
}; | ||
|
||
auth.run(createCache(null, [], {}), {}, callback); | ||
}); | ||
|
||
it('should give passing result if no non-RBAC key vaults found', function(done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.equal(0); // No results since we skip RBAC vaults | ||
done() | ||
}; | ||
|
||
auth.run(createCache(null, [listKeyVaults[1]], []), {}, callback); | ||
}); | ||
|
||
it('should give passing result if expiration is not set on keys in non-RBAC vault', function(done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.equal(1); | ||
expect(results[0].status).to.equal(0); | ||
expect(results[0].message).to.include('Key expiration is not enabled in non RBAC vault'); | ||
expect(results[0].region).to.equal('eastus'); | ||
done() | ||
}; | ||
|
||
auth.run(createCache(null, [listKeyVaults[0]], [getKeys[0]]), {}, callback); | ||
}); | ||
|
||
it('should give passing result if expiry date is not yet reached in non-RBAC vault', function(done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.equal(1); | ||
expect(results[0].status).to.equal(0); | ||
expect(results[0].message).to.include('Key in non RBAC vault expires in'); | ||
expect(results[0].region).to.equal('eastus'); | ||
done() | ||
}; | ||
|
||
auth.run(createCache(null, [listKeyVaults[0]], [getKeys[1]]), { key_vault_key_expiry_fail: '30' }, callback); | ||
}); | ||
|
||
it('should give failing result if the key has expired in non-RBAC vault', function(done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.equal(1); | ||
expect(results[0].status).to.equal(2); | ||
expect(results[0].message).to.include('Key in non RBAC vault expired'); | ||
expect(results[0].region).to.equal('eastus'); | ||
done() | ||
}; | ||
|
||
auth.run(createCache(null, [listKeyVaults[0]], [getKeys[3]]), { key_vault_key_expiry_fail: '40' }, callback); | ||
}); | ||
|
||
it('should give failing result if the key expires within failure expiry date in non-RBAC vault', function(done) { | ||
const callback = (err, results) => { | ||
expect(results.length).to.equal(1); | ||
expect(results[0].status).to.equal(2); | ||
expect(results[0].message).to.include('Key in non RBAC vault expires'); | ||
expect(results[0].region).to.equal('eastus'); | ||
done() | ||
}; | ||
|
||
auth.run(createCache(null, [listKeyVaults[0]], [getKeys[2]]), { key_vault_key_expiry_fail: '40' }, callback); | ||
}); | ||
}); | ||
}); |