@friggframework/encrypt
Advanced tools
Comparing version 1.0.0 to 1.1.0
188
encrypt.js
@@ -1,86 +0,120 @@ | ||
// const AWS = require('aws-sdk'); | ||
const crypto = require('crypto'); | ||
const aes = require('./aes'); | ||
const { Cryptor } = require('./cryptor'); | ||
const algorithm = 'aes-256-ctr'; | ||
const updateOneEvents = [ | ||
'updateOne', | ||
'replaceOne', | ||
'findOneAndUpdate', | ||
'findOneAndReplace', | ||
]; | ||
const findOneEvents = [ | ||
'findOne', | ||
'findOneAndDelete', | ||
'findOneAndRemove', | ||
'findOneAndUpdate', | ||
'findOneAndReplace', | ||
]; | ||
// const awsconfig = { | ||
// accessKey: '<Your AWS Access Key>', | ||
// secretAccessKey: '<Your AWS Secret Key>', | ||
// region: '<Your AWS region>', | ||
// cmkArn: '<Your KMS master Key arn >', // The identifier of the CMK to use to encrypt the data key. You can use the key ID or Amazon Resource Name (ARN) of the CMK, or the name or ARN of an alias that refers to the CMK. | ||
// }; | ||
// | ||
// // Creates the KMS client | ||
// function getKMSClient() { | ||
// const credentials = new AWS.Credentials(awsconfig.accessKey, awsconfig.secretAccessKey); | ||
// AWS.config.update({ | ||
// region: awsconfig.region, | ||
// credentials, | ||
// }); | ||
// | ||
// return new AWS.KMS(); | ||
// } | ||
// The Mongoose plug-in function | ||
function Encrypt(schema, options) { | ||
const { STAGE, KMS_KEY_ARN, AES_KEY_ID } = process.env; | ||
const isEnabledForStage = | ||
['staging', 'QA', 'prod', 'encryption-test'].indexOf(STAGE) > -1; | ||
// Below are the replacement functions in lieu of using AWS KMS | ||
function generateDataKey() { | ||
const { AES_KEY_ID } = process.env; | ||
const { AES_KEY } = process.env; | ||
// Random key generation | ||
const randomKey = crypto.randomBytes(32).toString('hex').slice(0, 32); | ||
return { | ||
KeyId: AES_KEY_ID, | ||
CiphertextBlob: aes.encrypt(randomKey, AES_KEY), | ||
Plaintext: randomKey, | ||
}; | ||
} | ||
// Decrypt the KMS Data key | ||
function decryptDataKey(CiphertextBlob, KeyId) { | ||
const availableKeys = { | ||
[process.env.AES_KEY_ID]: process.env.AES_KEY, | ||
[process.env.DEPRECATED_AES_KEY_ID]: process.env.DEPRECATED_AES_KEY, | ||
}; | ||
const encryptedKey = availableKeys[KeyId]; | ||
if (!encryptedKey) { | ||
console.log('No Encryption Key Found, returning raw string value.'); | ||
return 'No Encryption Key Found'; | ||
// No-op if not enabled | ||
if (!isEnabledForStage) { | ||
return; | ||
} | ||
return { | ||
KeyId, | ||
Plaintext: aes.decrypt(CiphertextBlob, encryptedKey), | ||
}; | ||
} | ||
if (KMS_KEY_ARN && AES_KEY_ID) { | ||
throw new Error( | ||
'Local and AWS encryption keys are both set in the environment.' | ||
); | ||
} | ||
module.exports = { | ||
encrypt(text) { | ||
if (['QA', 'prod', 'encryption-test'].indexOf(process.env.STAGE) > -1) { | ||
const dataKey = generateDataKey(); | ||
const encryptedKey = dataKey.CiphertextBlob; | ||
const encryptedText = aes.encrypt(text, dataKey.Plaintext); | ||
// Concatenate the encrypted to the buffer and return the base64 string | ||
return `${dataKey.KeyId}:${encryptedText}:${encryptedKey}`; | ||
} | ||
return text; | ||
}, | ||
const fields = Object.values(schema.paths) | ||
.map(({ path, options }) => (options.lhEncrypt === true ? path : '')) | ||
.filter(Boolean); | ||
decrypt(text) { | ||
if (['QA', 'prod', 'encryption-test'].indexOf(process.env.STAGE) > -1) { | ||
try { | ||
// Convert the base64 string to buffer | ||
const split = text.split(':'); | ||
const KeyId = split[0]; | ||
const encryptedKey = `${split[3]}:${split[4]}`; | ||
const encryptedText = `${split[1]}:${split[2]}`; | ||
const dataKey = decryptDataKey(encryptedKey, KeyId); | ||
if (dataKey === 'No Encryption Key Found') return text; | ||
const decrypted = aes.decrypt(encryptedText, dataKey.Plaintext); | ||
return decrypted; | ||
} catch (err) { | ||
console.log('Error decrypting'); | ||
throw new Error(err); | ||
const cryptor = new Cryptor({ | ||
// Use AWS if the CMK is present | ||
shouldUseAws: !!KMS_KEY_ARN, | ||
// Find all the fields in the schema with lhEncrypt === true | ||
fields: fields, | ||
}); | ||
// --------------------------------------------- | ||
// ### Encrypt fields before save/update/insert. | ||
// --------------------------------------------- | ||
schema.pre('save', async function encryptionPreSave() { | ||
// `this` will be a doc | ||
await cryptor.encryptFieldsInDocuments([this]); | ||
}); | ||
schema.pre( | ||
'insertMany', | ||
async function encryptionPreInsertMany(_, docs, options) { | ||
// `this` will be the model | ||
if (options?.rawResult) { | ||
throw new Error( | ||
'Raw result not supported for insertMany with Encrypt plugin' | ||
); | ||
} | ||
await cryptor.encryptFieldsInDocuments(docs); | ||
} | ||
return text; | ||
}, | ||
}; | ||
); | ||
schema.pre(updateOneEvents, async function encryptionPreUpdateOne() { | ||
// `this` will be a query | ||
await cryptor.encryptFieldsInQuery(this); | ||
}); | ||
schema.pre('updateMany', async function encryptionPreUpdateMany() { | ||
// `this` will be a query | ||
cryptor.expectNotToUpdateManyEncrypted(this.getUpdate()); | ||
}); | ||
schema.pre('update', async function encryptionPreUpdate() { | ||
// `this` will be a query | ||
const { multiple } = this.getOptions(); | ||
if (multiple) { | ||
cryptor.expectNotToUpdateManyEncrypted(this.getUpdate()); | ||
return; | ||
} | ||
await cryptor.encryptFieldsInQuery(this); | ||
}); | ||
// -------------------------------------------- | ||
// ### Decrypt documents after they are loaded. | ||
// -------------------------------------------- | ||
schema.post('save', async function encryptionPreSave() { | ||
// `this` will be a doc | ||
await cryptor.decryptFieldsInDocuments([this]); | ||
}); | ||
schema.post(findOneEvents, async function encryptionPostFindOne(doc) { | ||
// `this` will be a query | ||
const { rawResult } = this.getOptions(); | ||
if (rawResult) { | ||
return; | ||
} | ||
await cryptor.decryptFieldsInDocuments([doc]); | ||
}); | ||
schema.post('find', async function encryptionPostFind(docs) { | ||
// `this` will be a query | ||
await cryptor.decryptFieldsInDocuments(docs); | ||
}); | ||
schema.post('insertMany', async function encryptionPostInsertMany(docs) { | ||
// `this` will be the model | ||
await cryptor.decryptFieldsInDocuments(docs); | ||
}); | ||
} | ||
module.exports = { Encrypt }; |
{ | ||
"name": "@friggframework/encrypt", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"prettier": "@friggframework/prettier-config", | ||
"description": "", | ||
"main": "LHEncrypt.js", | ||
"main": "index.js", | ||
"devDependencies": { | ||
@@ -8,0 +9,0 @@ "@friggframework/eslint-config": "^1.0.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
11
24
60034
1284
1