Socket
Socket
Sign inDemoInstall

dexie-encrypted

Package Overview
Dependencies
13
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.2.2 to 2.0.0-beta.0

dist/checkForKeyChange.d.ts

407

dist/index.js

@@ -1,372 +0,49 @@

'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var Dexie = _interopDefault(require('dexie'));
var nacl = _interopDefault(require('tweetnacl'));
var Typeson = _interopDefault(require('typeson'));
var builtinTypes = _interopDefault(require('typeson-registry/presets/builtin'));
// Import some usable helper functions
const override = Dexie.override;
const Promise = Dexie.Promise;
const tableEncryptionOptions = {
DATA: 'NON_INDEXED_FIELDS',
NON_INDEXED_FIELDS: 'NON_INDEXED_FIELDS',
// DATA_AND_INDICES: 'DATA_AND_INDICES', // not implemented.
WHITELIST: 'WHITELIST',
BLACKLIST: 'BLACKLIST',
};
const cryptoOptions = tableEncryptionOptions;
/* options example:
{
table1: cryptoOptions.NON_INDEXED_FIELDS,
table2: {
type: cryptoOptions.WHITELIST,
fields: ['harmlessData1', 'harmlessId']
},
table3: {
type: cryptoOptions.BLACKLIST,
fields: ['sensitiveField1', 'sensitiveField2']
}
}
*/
const tson = new Typeson().register([builtinTypes]);
function overrideParseStoresSpec(origFunc) {
return function(stores, dbSchema) {
stores._encryptionSettings = '++id';
origFunc.call(this, stores, dbSchema);
};
}
function compareArrays(a, b) {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
const encoder = new TextEncoder();
const decoder = new TextDecoder();
function encryptObject(key, object, nonce) {
nonce = nonce || nacl.randomBytes(nacl.secretbox.nonceLength);
const stringToEncrypt = tson.stringify(object);
const encoded = encoder.encode(stringToEncrypt);
const encrypted = nacl.secretbox(encoded, nonce, key);
const data = new Uint8Array(nonce.length + encrypted.length);
data.set(nonce);
data.set(encrypted, nonce.length);
return data;
}
// this prevents changing the shape of the object so
// the underlying engine can optimize the hidden class
function hideValue(input) {
switch (typeof input) {
case 'number':
return 0;
case 'string':
return '';
case 'boolean':
return false;
case 'undefined':
return undefined;
case 'symbol':
return undefined;
}
return {};
}
function encrypt(db, keyOrPromise, cryptoSettings, onKeyChange, nonceOverride) {
let keyPromise;
if (keyOrPromise.then) {
keyPromise = keyOrPromise;
} else if (keyOrPromise instanceof Uint8Array && keyOrPromise.length === 32) {
keyPromise = Dexie.Promise.resolve(keyOrPromise);
} else {
throw new Error('Dexie-encrypted requires a UInt8Array of length 32 for a encryption key.');
}
db.Version.prototype._parseStoresSpec = override(
db.Version.prototype._parseStoresSpec,
overrideParseStoresSpec
);
if (db.verno > 0) {
// Make sure new tables are added if calling encrypt after defining versions.
try {
db.version(db.verno).stores({});
} catch (error) {
throw new Error(
'Dexie-encrypt: The call to encrypt() cannot be done on an open database'
);
}
}
function encryptWithRule(table, entity, rule) {
if (rule === undefined) {
return entity;
}
const toEncrypt = {};
if (rule.type === cryptoOptions.BLACKLIST) {
for (let i = 0; i < rule.fields.length; i++) {
toEncrypt[rule.fields[i]] = entity[rule.fields[i]];
entity[rule.fields[i]] = hideValue(entity[rule.fields[i]]);
}
} else {
const indices = table.schema.indexes.map(index => index.name);
const whitelist = rule.type === cryptoOptions.WHITELIST ? rule.fields : [];
for (const key in entity) {
if (
key !== table.schema.primKey.name &&
entity.hasOwnProperty(key) &&
indices.includes(key) === false &&
whitelist.includes(key) === false
) {
toEncrypt[key] = entity[key];
entity[key] = hideValue(entity[key]);
}
}
}
entity.__encryptedData = encryptObject(key, toEncrypt, nonceOverride);
return entity;
}
function decryptWithRule(entity, rule) {
if (rule === undefined) {
return entity;
}
if (entity && entity.__encryptedData) {
const nonce = entity.__encryptedData.slice(0, nacl.secretbox.nonceLength);
const message = entity.__encryptedData.slice(
nacl.secretbox.nonceLength,
entity.__encryptedData.length
);
const rawDecrypted = nacl.secretbox.open(message, nonce, key);
const stringified = decoder.decode(rawDecrypted);
const decrypted = tson.parse(stringified);
const toReturn = {};
for (const k in entity) {
if (decrypted.hasOwnProperty(k)) {
toReturn[k] = decrypted[k];
} else if (entity.hasOwnProperty(k) && k !== '__encryptedData') {
toReturn[k] = entity[k];
}
}
return toReturn;
}
return entity;
}
let key;
db.on('ready', function() {
let encryptionSettings;
try {
encryptionSettings = db.table('_encryptionSettings');
} catch (error) {
throw new Error(
"Dexie-encrypted can't find its encryption table. You may need to bump your database version."
);
}
return keyPromise
.then(receivedKey => {
if (receivedKey instanceof Uint8Array && receivedKey.length === 32) {
key = receivedKey;
} else {
throw new Error(
'Dexie-encrypted requires a UInt8Array of length 32 for a encryption key.'
);
}
})
.then(() =>
encryptionSettings
.toCollection()
.last()
.then(oldSettings => {
const changeDetectionObj = oldSettings
? oldSettings['__key_change_detection']
: null;
let onKeyChangeResult;
let keyChangePromise = Promise.resolve();
if (changeDetectionObj) {
const nonce = changeDetectionObj.slice(0, nacl.secretbox.nonceLength);
const message = changeDetectionObj.slice(
nacl.secretbox.nonceLength,
changeDetectionObj.length
);
const rawDecrypted = nacl.secretbox.open(message, nonce, key);
if (!rawDecrypted) {
// The key has changed. Let's call the handler
onKeyChangeResult = onKeyChange(db);
keyChangePromise = onKeyChangeResult.then
? onKeyChangeResult
: new Promise(resolve => {
resolve(onKeyChangeResult);
});
}
}
return keyChangePromise.then(() => {
return Promise.all(
db.tables.map(function(table) {
const oldSetting = oldSettings
? oldSettings[table.name]
: undefined;
const newSetting = cryptoSettings[table.name];
function setupHooks() {
if (newSetting === undefined) {
return;
}
table.hook('creating', function(primKey, obj) {
const preservedValue = { ...obj };
encryptWithRule(table, obj, newSetting);
this.onsuccess = () => {
delete obj.__encryptedData;
Object.assign(obj, preservedValue);
};
this.onerror = () => {
delete obj.__encryptedData;
Object.assign(obj, preservedValue);
};
});
table.hook('updating', function(modifications) {
const encrypted = encryptWithRule(
table,
{...this.value},
newSetting
);
return encrypted;
});
table.hook('reading', function(obj) {
return decryptWithRule(obj, newSetting);
});
}
if (oldSetting === newSetting) {
// no upgrade needed.
setupHooks();
return;
}
if (oldSetting === undefined || newSetting === undefined) ; else if (
typeof oldSetting !== 'string' &&
typeof newSetting !== 'string'
) {
// both non-strings. Figure out if they're the same.
if (newSetting.type === oldSetting.type) {
if (
compareArrays(newSetting.fields, oldSetting.fields)
) {
// no upgrade needed.
setupHooks();
return;
}
}
}
return table
.toCollection()
.modify(function(entity, ref) {
const decrypted = decryptWithRule(entity, oldSetting);
ref.value = encryptWithRule(
table,
decrypted,
newSetting
);
return true;
})
.then(setupHooks);
})
);
});
})
.then(function() {
return encryptionSettings.clear();
})
.then(function() {
return encryptionSettings.put({
__key_change_detection: encryptObject(
key,
[1, 2, 3, 4, 5],
new Uint8Array(24)
),
...cryptoSettings,
});
})
.catch(error => {
if (error.name === 'NotFoundError') {
throw new Error(
"Dexie-encrypted can't find its encryption table. You may need to bump your database version."
);
} else {
return Promise.reject(error);
}
})
);
});
}
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.encryptDatabase = exports.clearEncryptedTables = exports.clearAllTables = exports.UNENCRYPTED_LIST = exports.ENCRYPT_LIST = exports.NON_INDEXED_FIELDS = void 0;
const tslib_1 = require("tslib");
const encryptDatabase_1 = require("./encryptDatabase");
const encryptionMethods_1 = require("./encryptionMethods");
const types_1 = require("./types");
var types_2 = require("./types");
Object.defineProperty(exports, "cryptoOptions", { enumerable: true, get: function () { return types_2.cryptoOptions; } });
exports.NON_INDEXED_FIELDS = types_1.cryptoOptions.NON_INDEXED_FIELDS;
exports.ENCRYPT_LIST = types_1.cryptoOptions.ENCRYPT_LIST;
exports.UNENCRYPTED_LIST = types_1.cryptoOptions.UNENCRYPTED_LIST;
function clearAllTables(db) {
return Promise.all(
db.tables.map(function(table) {
return table.clear();
})
);
return Promise.all(db.tables.map(function (table) {
return table.clear();
}));
}
async function clearEncryptedTables(db) {
let encryptionSettings;
try {
encryptionSettings = await db
exports.clearAllTables = clearAllTables;
function clearEncryptedTables(db) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let encryptionSettings = (yield db
.table('_encryptionSettings')
.toCollection()
.last();
} catch (error) {
throw new Error(
"Dexie-encrypted can't find its encryption table. You may need to bump your database version."
);
}
const promises = Object.keys(encryptionSettings).map(async function(key) {
const encryptionSettingValue = encryptionSettings[key];
if (tableEncryptionOptions[encryptionSettingValue]) {
await db.table(key).clear();
}
.last()
.catch(() => {
throw new Error("Dexie-encrypted can't find its encryption table. You may need to bump your database version.");
}));
const promises = Object.keys(encryptionSettings.settings).map(function (key) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
yield db.table(key).clear();
});
});
return Promise.all(promises);
});
return Promise.all(promises);
}
Object.assign(encrypt, cryptoOptions, {
clearAllTables: clearAllTables,
clearEncryptedTables: clearEncryptedTables,
});
exports.clearAllTables = clearAllTables;
exports.clearEncryptedTables = clearEncryptedTables;
exports.cryptoOptions = cryptoOptions;
exports.default = encrypt;
exports.tableEncryptionOptions = tableEncryptionOptions;
function encryptDatabase(db, encryptionKey, tableSettings, onKeyChange, nonceOverrideForTesting) {
encryptDatabase_1.encryptDatabaseWithCustomEncryption({
db,
encryptionKey,
tableSettings,
encrypt: encryptionMethods_1.encryptWithNacl,
decrypt: encryptionMethods_1.decryptWithNacl,
onKeyChange,
nonceOverrideForTesting,
});
}
exports.encryptDatabase = encryptDatabase;
//# sourceMappingURL=index.js.map
{
"name": "dexie-encrypted",
"version": "1.2.2",
"description": "Encryption middleware for Dexie",
"main": "dist/index.js",
"license": "MIT",
"files": [
"dist/index.js"
],
"repository": {
"type": "git",
"url": "https://github.com/mark43/dexie-encrypted.git"
},
"dependencies": {
"tweetnacl": "^1.0.1",
"typeson": "^5.13.0",
"typeson-registry": "^1.0.0-alpha.28"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0",
"dexie": "^2.0.4",
"dexie-export-import": "^1.0.0-beta.13",
"eslint": "^6.3.0",
"fake-indexeddb": "^2.1.1",
"jest": "^24.9.0",
"prettier": "^1.18.2",
"rollup": "^1.20.3",
"text-encoding": "^0.7.0"
},
"peerDependencies": {
"dexie": "^2.0.4 | ^3.0.0"
},
"scripts": {
"test": "jest",
"build": "rollup index.js --format commonjs --file dist/index.js",
"format": "prettier index.js README.md --write"
}
"name": "dexie-encrypted",
"version": "2.0.0-beta.0",
"description": "Encryption middleware for Dexie",
"main": "dist",
"license": "MIT",
"files": [
"dist/*"
],
"repository": {
"type": "git",
"url": "https://github.com/mark43/dexie-encrypted.git"
},
"dependencies": {
"@stablelib/utf8": "1.0.0",
"tweetnacl": "1.0.3",
"typeson": "5.18.2",
"typeson-registry": "1.0.0-alpha.38"
},
"devDependencies": {
"@types/jest": "26.0.10",
"dexie": "^3.0.0",
"dexie-export-import": "^1.0.0-beta.13",
"eslint": "^6.3.0",
"fake-indexeddb": "^2.1.1",
"jest": "26.4.0",
"prettier": "^1.18.2",
"text-encoding": "^0.7.0",
"ts-jest": "26.2.0",
"tsc": "1.20150623.0",
"typescript": "3.9.7"
},
"peerDependencies": {
"dexie": "^3.0.0"
},
"scripts": {
"test": "jest",
"build": "tsc",
"format": "prettier index.js README.md --write"
}
}

@@ -59,7 +59,7 @@ # Dexie-encrypted

Dexie-encrypted can be configured to encrypt all the data of a table, to whitelist fields that are non-sensitive, or to blacklist sensitive fields.
Dexie-encrypted can be configured to encrypt all the data of a table, to select fields that are senesitvie or non-sensitive.
- `encrypt.NON_INDEXED_FIELDS` - all data other than indices will be encrypted.
- `encrypt.WHITELIST` - all data other than indices and whitelisted fields will be encrypted.
- `encrypt.BLACKLIST` - listed fields will be encrypted.
- `encrypt.UNENCRYPTED_LIST` - all data other than indices and whitelisted fields will be encrypted.
- `encrypt.ENCRYPT_LIST` - listed fields will be encrypted.

@@ -70,7 +70,7 @@ ```javascript

friends: {
type: encrypt.WHITELIST,
type: encrypt.UNENCRYPTED_LIST,
fields: ['street', 'picture'], // these two fields and indices will be plain text
},
enemies: {
type: encrypt.BLACKLIST,
type: encrypt.ENCRYPT_LIST,
fields: ['picture', 'isMortalEnemy'], // note: these cannot be indices

@@ -77,0 +77,0 @@ },

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc