Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

comdb

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

comdb - npm Package Compare versions

Comparing version 1.0.5-alpha to 1.1.0-beta

138

index.js

@@ -11,3 +11,3 @@ 'use strict'

: promise.then((result) => {
return callback(null, result || [])
return callback(null, result)
}).catch((error) => {

@@ -18,64 +18,56 @@ return callback(error)

function processEncryptedChange (decrypted, encrypted, ids = []) {
if (ids.length === 0) return Promise.resolve()
const promises = ids.map((id) => {
return encrypted.get(id).then(({ payload }) => {
return decrypted.decrypt(payload)
}).then((newDoc) => {
return decrypted.get(newDoc._id).then((doc) => {
return isEqual(newDoc, doc) ? undefined : newDoc
}).catch((error) => {
if (error.name === 'not_found') {
return newDoc
module.exports = function (PouchDB) {
// save originals
const destroy = PouchDB.prototype.destroy
const bulkDocs = PouchDB.prototype.bulkDocs
const replicate = PouchDB.replicate
// private helpers
async function processEncryptedChange (decrypted, encrypted, ids = []) {
if (ids.length === 0) { return }
const changes = []
for (const id of ids) {
const { payload } = await encrypted.get(id)
const newDoc = await decrypted.decrypt(payload)
let change
try {
const doc = decrypted.get(newDoc._id)
change = isEqual(newDoc, doc) ? undefined : newDoc
} catch (err) {
if (err.name === 'not_found') {
change = newDoc
} else {
return Promise.reject(error)
throw err
}
})
})
})
return Promise.all(promises).then((results) => {
const decryptedDocs = results.filter((x) => { return !!x })
const deletedDocs = decryptedDocs.filter((doc) => {
return doc._deleted
})
}
changes.push(change)
}
const decryptedDocs = changes.filter(x => !!x)
const deletedDocs = changes.filter(doc => !!doc._deleted)
return Promise.all([
decrypted.bulkDocs(decryptedDocs, { new_edits: false }),
decrypted.bulkDocs(deletedDocs)
bulkDocs.call(decrypted, decryptedDocs, { new_edits: false }),
bulkDocs.call(decrypted, deletedDocs)
]).then(([results1, results2]) => {
return results1.concat(results2)
return [...results1, ...results2]
})
})
}
}
function processDecryptedChange (decrypted, encrypted, ids) {
if (ids.length === 0) return Promise.resolve()
const promises = ids.map((id) => {
return decrypted.get(id).then((doc) => {
return decrypted.encrypt(doc)
}).then((payload) => {
async function processDecryptedChange (decrypted, encrypted, ids) {
if (ids.length === 0) { return }
const encryptedDocs = []
for (const id of ids) {
const doc = await decrypted.get(id)
const payload = await decrypted.encrypt(doc)
const encryptedDoc = { payload }
return encrypted.changes({
include_docs: true,
limit: 1
}).then((result) => {
if (result.results && result.results.length) {
if (payload !== result.results[0].doc.payload) {
return encryptedDoc
}
} else {
return encryptedDoc
const latestChanges = await encrypted.changes({ limit: 1, doc_ids: [id] })
if (latestChanges.results && latestChanges.results.length) {
const latestChange = latestChanges.results[0].doc
if (payload !== latestChange.payload) {
encryptedDocs.push(encryptedDoc)
}
})
})
})
return Promise.all(promises).then((encryptedDocs) => {
return encrypted.bulkDocs(encryptedDocs)
})
}
module.exports = function (PouchDB) {
// save originals
const destroy = PouchDB.prototype.destroy
const bulkDocs = PouchDB.prototype.bulkDocs
const replicate = PouchDB.replicate
} else {
encryptedDocs.push(encryptedDoc)
}
}
return bulkDocs.call(encrypted, encryptedDocs)
}
// replication wrapper; handles ComDB instances transparently

@@ -120,13 +112,21 @@ PouchDB.replicate = function (source, target, opts = {}, callback) {

// - encrypts docs and maybe saves them to the encrypted db
PouchDB.prototype.bulkDocs = function (docs, opts = {}, callback) {
PouchDB.prototype.bulkDocs = async function (docs, opts = {}, callback) {
if (typeof opts === 'function') {
opts = {}
callback = opts
}
if (!this._crypt) {
const promise = bulkDocs.call(this, docs, opts)
return cbify(promise, callback)
}
const processChange = processDecryptedChange.bind(null, this, this._encrypted)
const promise = bulkDocs.call(this, docs, opts).then((results) => {
const ids = results.map(({ id }) => { return id }).filter((id) => {
const results = await bulkDocs.call(this, docs, opts)
const ids = results
.map(({ id }) => { return id })
.filter((id) => {
const d = ((docs && docs.docs) || docs)
return !d.map(({ _id }) => { return _id }).includes(id)
})
return processChange(ids).then(() => {
return results
})
})
await processChange(ids)
const promise = Promise.resolve(results)
return cbify(promise, callback)

@@ -148,7 +148,15 @@ }

PouchDB.prototype.destroy = function (opts = {}, callback) {
const promise = destroy.call(this._encrypted, opts).then(() => {
return destroy.call(this, opts)
})
let promise
if (!this._encrypted || opts.unencrypted_only) {
promise = destroy.call(this, opts)
} else if (opts.encrypted_only) {
promise = destroy.call(this._encrypted, opts)
} else {
promise = Promise.all([
destroy.call(this._encrypted, opts),
destroy.call(this, opts)
])
}
return cbify(promise, callback)
}
}
{
"name": "comdb",
"version": "1.0.5-alpha",
"version": "1.1.0-beta",
"description": "A PouchDB plugin that transparently encrypts and decrypts its data.",

@@ -5,0 +5,0 @@ "main": "index.js",

@@ -163,4 +163,7 @@ # ComDB

ComDB wraps PouchDB's database destruction method so that both the encrypted and decrypted databases are destroyed.
ComDB wraps PouchDB's database destruction method so that both the encrypted and decrypted databases are destroyed. ComDB adds two options to the method:
- `encrypted_only`: Destroy only the encrypted database. This is useful when a backup has become compromised and you need to burn it.
- `unencrypted_only`: Destroy only the unencrypted database. This is useful if you are using a remote encrypted backup and want to burn the local device so you can restore from backup on a fresh one.
Original: [db.destroy](https://pouchdb.com/api.html#delete_database)

@@ -167,0 +170,0 @@

@@ -24,40 +24,57 @@ /* global describe, it, before, after */

it('should encrypt writes', function () {
it('should encrypt writes', async function () {
this.timeout(4 * 1000) // sometimes runs long
return this.db.post({ hello: 'world' }).then(({ id }) => {
return this.db.get(id)
}).then((doc) => {
return this.db._encrypted.allDocs({ include_docs: true }).then(({ rows }) => {
const { payload } = rows[0].doc
return this.db.decrypt(payload)
}).then((plainDoc) => {
assert(isEqual(doc, plainDoc), 'Unencrypted and decrypted documents differ.')
})
})
const { id } = await this.db.post({ hello: 'world' })
const doc = await this.db.get(id)
const { rows } = await this.db._encrypted.allDocs({ include_docs: true })
const { payload } = rows[0].doc
const plainDoc = await this.db.decrypt(payload)
assert(isEqual(doc, plainDoc), 'Unencrypted and decrypted documents differ.')
})
it('should decrypt and handle deletions', function () {
// this test fails for reasons i do not understand
return this.db.post({ hello: 'galaxy' }).then(({ id }) => {
return this.db.get(id)
}).then((doc) => {
doc._deleted = true
return this.db.encrypt(doc).then((payload) => {
return this.db._encrypted.post({ payload }).then(({ id }) => {
return this.db._encrypted.get(id)
}).then(({ payload }) => {
return this.db.decrypt(payload)
}).then((plainDoc) => {
assert.strictEqual(plainDoc._deleted, true)
})
}).then(() => {
let caught = false
return this.db.get(doc._id).catch((error) => {
assert.strictEqual(error.name, 'not_found')
caught = true
}).then((doc) => {
assert(caught, 'Document was not deleted!')
})
})
it('should decrypt and handle deletions', async function () {
// delete a document by inserting a payload into the encrypted db
const { id } = await this.db.post({ hello: 'galaxy' })
const doc = await this.db.get(id)
doc._deleted = true
const payload = await this.db.encrypt(doc)
const { id: encryptedId } = await this.db._encrypted.post({ payload })
const encryptedDoc = await this.db._encrypted.get(encryptedId)
const plainDoc = await this.db.decrypt(encryptedDoc.payload)
assert.strictEqual(plainDoc._deleted, true)
let caught = false
// now test that it was deleted in the decrypted copy
try {
await this.db.get(doc._id)
} catch (error) {
assert.strictEqual(error.name, 'not_found')
caught = true
}
assert(caught, 'Document was not deleted!')
})
describe('destroy', function () {
before(function () {
this.db_destroyable_1 = new PouchDB('test-destroy-1')
this.db_destroyable_2 = new PouchDB('test-destroy-2')
this.db_destroyable_1.setPassword('goodpassword')
this.db_destroyable_2.setPassword('goodpassword')
})
after(async function () {
await this.db_destroyable_1._encrypted.destroy()
await this.db_destroyable_2.destroy({ unencrypted_only: true })
})
it('should optionally not destroy the encrypted copy', async function () {
await this.db_destroyable_1.destroy({ unencrypted_only: true })
assert.equal(this.db_destroyable_1._encrypted._destroyed, undefined)
assert.equal(this.db_destroyable_1._destroyed, true)
})
it('should optionally not destroy the unencrypted copy', async function () {
await this.db_destroyable_2.destroy({ encrypted_only: true })
assert.equal(this.db_destroyable_2._encrypted._destroyed, true)
assert.equal(this.db_destroyable_2._destroyed, undefined)
})
})

@@ -92,2 +109,24 @@

})
describe('issues', function () {
it('should handle destruction without a set password', async function () {
const db3 = new PouchDB('.test-destruction')
await db3.destroy()
assert(true)
})
it('should handle calls to bulkDocs', async function () {
const docs = []
const k = 10
for (let i = 0; i < k; i++) {
docs.push({
_id: String(Math.floor(Math.random() * Date.now())),
a: Math.random(),
b: Math.random() * Date.now()
})
}
const result = await this.db.bulkDocs({ docs })
assert.equal(result.length, k)
})
})
})
SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc