Skip to content

Commit

Permalink
Merge pull request from GHSA-mpgr-2cx9-327h
Browse files Browse the repository at this point in the history
Fix get() returning decrypted messages
  • Loading branch information
mixmix authored Jun 11, 2020
2 parents 9ade588 + ee98372 commit 43334d0
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 51 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,10 +278,12 @@ you may receive a old message in real time - but for old messages, it makes sens

all standard options are supported.

### db.createUserStream ({id: feed_id, lt,lte,gt,gte: sequence, reverse,old,live,raw: boolean, limit: number})
### db.createUserStream ({id: feed_id, lt,lte,gt,gte: sequence, reverse,old,live,raw: boolean, limit: number, private: boolean})

`createUserStream` is like `createHistoryStream`, except all options are supported. Local access is allowed, but not
remote anonymous access. `createUserStream` does decrypt private messages.
`createUserStream` is like `createHistoryStream`, except all options are
supported. Local access is allowed, but not remote anonymous access.
`createUserStream` can decrypt private messages if you pass the option
`{ private: true }`.

### db.links({source: feedId?, dest: feedId|msgId|blobId?, rel: string?, meta: true?, keys: true?, values: false?, live:false?, reverse: false?}) -> PullSource

Expand Down
19 changes: 16 additions & 3 deletions create.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ module.exports = function create (path, opts, keys) {
if (err) return cb(err)

if (!isPrivate) {
if (meta) cb(null, { key, value: data.value, timestamp: data.timestamp })
else cb(null, data.value)
if (meta) cb(null, { key, value: u.originalValue(data.value), timestamp: data.timestamp })
else cb(null, u.originalValue(data.value))
}
else {
const result = db._unbox(data, unbox)
Expand Down Expand Up @@ -133,12 +133,25 @@ module.exports = function create (path, opts, keys) {
}

db.createRawLogStream = function (opts) {
return db.stream(opts)
return pull(
db.stream(opts),
pull.map(({ seq, value }) => {
return { seq, value: u.originalData(value) }
})
)
}

// pull in the features that are needed to pass the tests
// and that sbot, etc uses but are slow.
extras(db, opts, keys)
// - adds indexes: links, feed, time
// - adds methods:
// - db.createLogStream
// - db.createFeedStream
// - db.creareUserStream
// - db.latest
// - db.latestSequence
// - db.getLatest

// writeStream - used in (legacy) replication.
db.createWriteStream = function (cb) {
Expand Down
58 changes: 26 additions & 32 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ var osenv = require('osenv')
var mkdirp = require('mkdirp')
var rimraf = require('rimraf')
var valid = require('./lib/validators')
var pkg = require('./package.json')
var version = require('./package.json').version
var help = require('./help')

const pull = require('pull-stream')
const pullNotify = require('pull-notify')
const pullCat = require('pull-cat')
Expand Down Expand Up @@ -35,7 +37,7 @@ var manifest = {
status: 'sync',
getVectorClock: 'async',
version: 'sync',
help: 'sync',
help: 'sync'
}

module.exports = {
Expand Down Expand Up @@ -106,42 +108,23 @@ module.exports = {
ssb.since(sequenceNotifier)

return self = {
id : feed.id,
keys : opts.keys,
id : feed.id,

ready : function () {
return ssb.ready.value
},

progress : function () {
return ssb.progress
whoami : () => {
return { id: feed.id }
},

status : function () {
version : () => version,
ready : () => ssb.ready.value,
progress : () => ssb.progress,
status : () => {
return {
progress: self.progress(),
progress: ssb.progress,
db: ssb.status,
sync: since()
}
},

version : function () {
return pkg.version
},

createSequenceStream: () => {
// If the initial value is `undefined` we want it to be `-1`.
// This is because `-1` is a magic sequence number for an empty log.
const initialValue = ssb.since.value !== undefined
? ssb.since.value
: -1

return pullCat([
pull.values([initialValue]),
sequenceNotifier.listen()
])
},

//temporary!
_flumeUse : function (name, flumeview) {
ssb.use(name, flumeview)
Expand All @@ -164,20 +147,31 @@ module.exports = {
getLatest : valid.async(ssb.getLatest, 'feedId'),
latestSequence : valid.async(ssb.latestSequence, 'feedId'),
createFeed : ssb.createFeed,
whoami : function () { return { id: feed.id } },
createFeedStream : valid.source(ssb.createFeedStream, 'readStreamOpts?'),
createHistoryStream : valid.source(ssb.createHistoryStream, ['createHistoryStreamOpts'], ['feedId', 'number?', 'boolean?']),
createLogStream : valid.source(ssb.createLogStream, 'readStreamOpts?'),
createUserStream : valid.source(ssb.createUserStream, 'createUserStreamOpts'),
createSequenceStream : () => {
// If the initial value is `undefined` we want it to be `-1`.
// This is because `-1` is a magic sequence number for an empty log.
const initialValue = ssb.since.value !== undefined
? ssb.since.value
: -1

return pullCat([
pull.values([initialValue]),
sequenceNotifier.listen()
])
},
links : valid.source(ssb.links, 'linksOpts'),
sublevel : ssb.sublevel,
// sublevel : ssb.sublevel, // Disabled as does not appear to be used
messagesByType : valid.source(ssb.messagesByType, 'string|messagesByTypeOpts'),
createWriteStream : ssb.createWriteStream,
getVectorClock : ssb.getVectorClock,
getAtSequence : ssb.getAtSequence,
addBoxer : ssb.addBoxer,
addUnboxer : ssb.addUnboxer,
help : function () { return require('./help') }
help : () => help
}
}
}
94 changes: 81 additions & 13 deletions test/box-unbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ var tape = require('tape')
var pull = require('pull-stream')
var ssbKeys = require('ssb-keys')
var box1 = require('ssb-private1/box1')
const { promisify } = require('util')

var createSSB = require('./create-ssb')
var { originalValue } = require('../util')

module.exports = function (opts) {
module.exports = function () {
var alice = ssbKeys.generate()
var bob = ssbKeys.generate()
var charles = ssbKeys.generate()
Expand All @@ -34,13 +35,12 @@ module.exports = function (opts) {
tape('error when trying to encrypt without boxer', (t) => {
t.plan(2);
const darlene = ssbKeys.generate()
const darleneSSB = createSSB('test-ssb-darlene', { keys: darlene })
const darleneFeed = ssb.createFeed(darlene)
darleneFeed.add(
{ type: "error", recps: [alice, darlene] },
(err, msg) => {
t.ok(err);
t.notOk(msg);
t.ok(err);
t.notOk(msg);
t.end()
})
})
Expand All @@ -52,7 +52,7 @@ module.exports = function (opts) {
var postObserved
var listener = ssb.post(msg => { postObserved = msg })

feed.add(boxed, function (err, msg) {
feed.add(boxed, function (err) {
if (err) throw err
t.notOk(err)

Expand Down Expand Up @@ -145,7 +145,7 @@ module.exports = function (opts) {
var listener = ssb.post(msg => { postObserved = msg })

// secret message sent to self
feed.add({ type: 'secret2', secret: "it's a secret!", recps: feed.id }, function (err, msg) {
feed.add({ type: 'secret2', secret: "it's a secret!", recps: feed.id }, function (err) {
if (err) throw err
t.notOk(err)

Expand All @@ -165,7 +165,7 @@ module.exports = function (opts) {
)

listener()
t.true(typeof postObserved.value.content === 'string', 'post obs messages should not be decrypted')
t.true(typeof postObserved.value.content === 'string', 'db.post obs messages should not be decrypted')

t.end()
})
Expand Down Expand Up @@ -264,7 +264,7 @@ module.exports = function (opts) {
})

tape('addUnboxer (simple)', function (t) {
const unboxer = function (ciphertext, value) {
const unboxer = function (ciphertext) {
if (!ciphertext.endsWith('.box.hah')) return

const base64 = ciphertext.replace('.box.hah', '')
Expand Down Expand Up @@ -301,12 +301,12 @@ module.exports = function (opts) {
done()
}, 500)
},
key: function (ciphertext, value) {
key: function (ciphertext) {
if (!ciphertext.endsWith('.box.hah')) return

return '"the msgKey"'
},
value: function (ciphertext, msgKey) {
value: function (ciphertext) {
const base64 = ciphertext.replace('.box.hah', '')
return JSON.parse(
Buffer.from(base64, 'base64').toString('utf8')
Expand All @@ -319,19 +319,87 @@ module.exports = function (opts) {
const content = {
type: 'poke',
reason: 'why not',
recps: [ '!test' ]
recps: [ '!test' ],
myFriend: alice.id// Necessary to test links()
}
const ciphertext = Buffer.from(JSON.stringify(content)).toString('base64') + '.box.hah'

feed.publish(ciphertext, (_, msg) => {
t.true(initDone, 'unboxer completed initialisation before publish')

ssb.get({ id: msg.key, private: true, meta: true }, (err, msg) => {
if (err) throw err
ssb.get({ id: msg.key, private: true, meta: true }, async (err, msg) => {
t.error(err)

t.true(initDone, 'unboxer completed initialisation before get')
t.deepEqual(msg.value.content, content, 'auto unboxing works')

const assertBoxed = (methodName, message) => {
if (typeof message.key === 'string') {
t.equal(message.key, msg.key, `${methodName}() returned correct message`)
t.equal(typeof message.value.content, 'string', `${methodName}() does not unbox by default`)
} else {
t.equal(typeof message.content, 'string', `${methodName}() does not unbox by default`)
}
}

const assertBoxedAsync = async (methodName, options) => {
assertBoxed(methodName, await promisify(ssb[methodName])(options))
if (typeof options === 'object' && Array.isArray(options) === false) {
assertBoxed(methodName, await promisify(ssb[methodName])({ ...options, private: false } ))
}
}

// This tests the default behavior of `ssb.get()`, which should never
// decrypt messages by default. This is **very important**.
await assertBoxedAsync('get', msg.key)
await assertBoxedAsync('get', { id: msg.key })
await assertBoxedAsync('get', { id: msg.key, meta: true })
await assertBoxedAsync('getAtSequence', [msg.value.author, msg.value.sequence])
await assertBoxedAsync('getLatest', msg.value.author)

const assertBoxedSourceOnce = (methodName, options) => new Promise((resolve) => {
pull(
ssb[methodName](options),
pull.collect((err, val) => {
t.error(err, `${methodName}() does not error`)
switch (methodName) {
case 'createRawLogStream':
assertBoxed(methodName, val[0].value)
break;
case 'createFeedStream':
case 'createUserStream':
case 'messagesByType':
// Apparently some methods take `{ private: false }` to mean
// "don't return any private messages". :/
if (options.private === undefined) {
assertBoxed(methodName, val[0].value)
}
break
default:
assertBoxed(methodName, val[0])
}
resolve()
})
)
})

// Test the default **and** `{ private: false }`.
const assertBoxedSource = async (methodName, options) => {
await assertBoxedSourceOnce(methodName, options)
await assertBoxedSourceOnce(methodName, { ...options, private: false })
}

await assertBoxedSource('createLogStream', { limit: 1, reverse: true })
await assertBoxedSource('createHistoryStream', { id: msg.value.author, seq: msg.value.sequence, reverse: true})
await assertBoxedSource('messagesByType', { type: 'poke', limit: 1, reverse: true })
await assertBoxedSource('createFeedStream', { id: msg.value.author, seq: msg.value.sequence, reverse: true})
await assertBoxedSource('createUserStream', { id: msg.value.author, seq: msg.value.sequence, reverse: true})
await assertBoxedSource('links', { source: msg.value.author, limit: 1, values: true})
await assertBoxedSource('createRawLogStream', { source: msg.value.author, limit: 1, reverse: true, values: true})
// createRawLogStream currently not exported as a method

t.end()

})
})
})
Expand Down
23 changes: 23 additions & 0 deletions test/manifest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict'
var tape = require('tape')
var { manifest, init } = require('../')

module.exports = function () {
tape('manifest', t => {
const _api = {}
const opts = {
path: `/tmp/ssb-manifest-test-${Date.now()}-${Math.random()}`
}
const api = init(_api, opts)

Object.keys(api).forEach(m => console.log(m))

Object.keys(manifest).forEach(method => {
t.equal(typeof api[method], 'function', `api.${method}`)
})

t.end()
})
}

if (!module.parent) { module.exports({}) }

0 comments on commit 43334d0

Please # to comment.