Skip to content

Commit 43334d0

Browse files
authored
Merge pull request from GHSA-mpgr-2cx9-327h
Fix get() returning decrypted messages
2 parents 9ade588 + ee98372 commit 43334d0

File tree

5 files changed

+151
-51
lines changed

5 files changed

+151
-51
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,12 @@ you may receive a old message in real time - but for old messages, it makes sens
278278

279279
all standard options are supported.
280280

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

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

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

create.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,8 @@ module.exports = function create (path, opts, keys) {
8484
if (err) return cb(err)
8585

8686
if (!isPrivate) {
87-
if (meta) cb(null, { key, value: data.value, timestamp: data.timestamp })
88-
else cb(null, data.value)
87+
if (meta) cb(null, { key, value: u.originalValue(data.value), timestamp: data.timestamp })
88+
else cb(null, u.originalValue(data.value))
8989
}
9090
else {
9191
const result = db._unbox(data, unbox)
@@ -133,12 +133,25 @@ module.exports = function create (path, opts, keys) {
133133
}
134134

135135
db.createRawLogStream = function (opts) {
136-
return db.stream(opts)
136+
return pull(
137+
db.stream(opts),
138+
pull.map(({ seq, value }) => {
139+
return { seq, value: u.originalData(value) }
140+
})
141+
)
137142
}
138143

139144
// pull in the features that are needed to pass the tests
140145
// and that sbot, etc uses but are slow.
141146
extras(db, opts, keys)
147+
// - adds indexes: links, feed, time
148+
// - adds methods:
149+
// - db.createLogStream
150+
// - db.createFeedStream
151+
// - db.creareUserStream
152+
// - db.latest
153+
// - db.latestSequence
154+
// - db.getLatest
142155

143156
// writeStream - used in (legacy) replication.
144157
db.createWriteStream = function (cb) {

index.js

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ var osenv = require('osenv')
55
var mkdirp = require('mkdirp')
66
var rimraf = require('rimraf')
77
var valid = require('./lib/validators')
8-
var pkg = require('./package.json')
8+
var version = require('./package.json').version
9+
var help = require('./help')
10+
911
const pull = require('pull-stream')
1012
const pullNotify = require('pull-notify')
1113
const pullCat = require('pull-cat')
@@ -35,7 +37,7 @@ var manifest = {
3537
status: 'sync',
3638
getVectorClock: 'async',
3739
version: 'sync',
38-
help: 'sync',
40+
help: 'sync'
3941
}
4042

4143
module.exports = {
@@ -106,42 +108,23 @@ module.exports = {
106108
ssb.since(sequenceNotifier)
107109

108110
return self = {
109-
id : feed.id,
110111
keys : opts.keys,
112+
id : feed.id,
111113

112-
ready : function () {
113-
return ssb.ready.value
114-
},
115-
116-
progress : function () {
117-
return ssb.progress
114+
whoami : () => {
115+
return { id: feed.id }
118116
},
119-
120-
status : function () {
117+
version : () => version,
118+
ready : () => ssb.ready.value,
119+
progress : () => ssb.progress,
120+
status : () => {
121121
return {
122-
progress: self.progress(),
122+
progress: ssb.progress,
123123
db: ssb.status,
124124
sync: since()
125125
}
126126
},
127127

128-
version : function () {
129-
return pkg.version
130-
},
131-
132-
createSequenceStream: () => {
133-
// If the initial value is `undefined` we want it to be `-1`.
134-
// This is because `-1` is a magic sequence number for an empty log.
135-
const initialValue = ssb.since.value !== undefined
136-
? ssb.since.value
137-
: -1
138-
139-
return pullCat([
140-
pull.values([initialValue]),
141-
sequenceNotifier.listen()
142-
])
143-
},
144-
145128
//temporary!
146129
_flumeUse : function (name, flumeview) {
147130
ssb.use(name, flumeview)
@@ -164,20 +147,31 @@ module.exports = {
164147
getLatest : valid.async(ssb.getLatest, 'feedId'),
165148
latestSequence : valid.async(ssb.latestSequence, 'feedId'),
166149
createFeed : ssb.createFeed,
167-
whoami : function () { return { id: feed.id } },
168150
createFeedStream : valid.source(ssb.createFeedStream, 'readStreamOpts?'),
169151
createHistoryStream : valid.source(ssb.createHistoryStream, ['createHistoryStreamOpts'], ['feedId', 'number?', 'boolean?']),
170152
createLogStream : valid.source(ssb.createLogStream, 'readStreamOpts?'),
171153
createUserStream : valid.source(ssb.createUserStream, 'createUserStreamOpts'),
154+
createSequenceStream : () => {
155+
// If the initial value is `undefined` we want it to be `-1`.
156+
// This is because `-1` is a magic sequence number for an empty log.
157+
const initialValue = ssb.since.value !== undefined
158+
? ssb.since.value
159+
: -1
160+
161+
return pullCat([
162+
pull.values([initialValue]),
163+
sequenceNotifier.listen()
164+
])
165+
},
172166
links : valid.source(ssb.links, 'linksOpts'),
173-
sublevel : ssb.sublevel,
167+
// sublevel : ssb.sublevel, // Disabled as does not appear to be used
174168
messagesByType : valid.source(ssb.messagesByType, 'string|messagesByTypeOpts'),
175169
createWriteStream : ssb.createWriteStream,
176170
getVectorClock : ssb.getVectorClock,
177171
getAtSequence : ssb.getAtSequence,
178172
addBoxer : ssb.addBoxer,
179173
addUnboxer : ssb.addUnboxer,
180-
help : function () { return require('./help') }
174+
help : () => help
181175
}
182176
}
183177
}

test/box-unbox.js

Lines changed: 81 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ var tape = require('tape')
33
var pull = require('pull-stream')
44
var ssbKeys = require('ssb-keys')
55
var box1 = require('ssb-private1/box1')
6+
const { promisify } = require('util')
67

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

10-
module.exports = function (opts) {
11+
module.exports = function () {
1112
var alice = ssbKeys.generate()
1213
var bob = ssbKeys.generate()
1314
var charles = ssbKeys.generate()
@@ -34,13 +35,12 @@ module.exports = function (opts) {
3435
tape('error when trying to encrypt without boxer', (t) => {
3536
t.plan(2);
3637
const darlene = ssbKeys.generate()
37-
const darleneSSB = createSSB('test-ssb-darlene', { keys: darlene })
3838
const darleneFeed = ssb.createFeed(darlene)
3939
darleneFeed.add(
4040
{ type: "error", recps: [alice, darlene] },
4141
(err, msg) => {
42-
t.ok(err);
43-
t.notOk(msg);
42+
t.ok(err);
43+
t.notOk(msg);
4444
t.end()
4545
})
4646
})
@@ -52,7 +52,7 @@ module.exports = function (opts) {
5252
var postObserved
5353
var listener = ssb.post(msg => { postObserved = msg })
5454

55-
feed.add(boxed, function (err, msg) {
55+
feed.add(boxed, function (err) {
5656
if (err) throw err
5757
t.notOk(err)
5858

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

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

@@ -165,7 +165,7 @@ module.exports = function (opts) {
165165
)
166166

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

170170
t.end()
171171
})
@@ -264,7 +264,7 @@ module.exports = function (opts) {
264264
})
265265

266266
tape('addUnboxer (simple)', function (t) {
267-
const unboxer = function (ciphertext, value) {
267+
const unboxer = function (ciphertext) {
268268
if (!ciphertext.endsWith('.box.hah')) return
269269

270270
const base64 = ciphertext.replace('.box.hah', '')
@@ -301,12 +301,12 @@ module.exports = function (opts) {
301301
done()
302302
}, 500)
303303
},
304-
key: function (ciphertext, value) {
304+
key: function (ciphertext) {
305305
if (!ciphertext.endsWith('.box.hah')) return
306306

307307
return '"the msgKey"'
308308
},
309-
value: function (ciphertext, msgKey) {
309+
value: function (ciphertext) {
310310
const base64 = ciphertext.replace('.box.hah', '')
311311
return JSON.parse(
312312
Buffer.from(base64, 'base64').toString('utf8')
@@ -319,19 +319,87 @@ module.exports = function (opts) {
319319
const content = {
320320
type: 'poke',
321321
reason: 'why not',
322-
recps: [ '!test' ]
322+
recps: [ '!test' ],
323+
myFriend: alice.id// Necessary to test links()
323324
}
324325
const ciphertext = Buffer.from(JSON.stringify(content)).toString('base64') + '.box.hah'
325326

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

329-
ssb.get({ id: msg.key, private: true, meta: true }, (err, msg) => {
330-
if (err) throw err
330+
ssb.get({ id: msg.key, private: true, meta: true }, async (err, msg) => {
331+
t.error(err)
331332

332333
t.true(initDone, 'unboxer completed initialisation before get')
333334
t.deepEqual(msg.value.content, content, 'auto unboxing works')
335+
336+
const assertBoxed = (methodName, message) => {
337+
if (typeof message.key === 'string') {
338+
t.equal(message.key, msg.key, `${methodName}() returned correct message`)
339+
t.equal(typeof message.value.content, 'string', `${methodName}() does not unbox by default`)
340+
} else {
341+
t.equal(typeof message.content, 'string', `${methodName}() does not unbox by default`)
342+
}
343+
}
344+
345+
const assertBoxedAsync = async (methodName, options) => {
346+
assertBoxed(methodName, await promisify(ssb[methodName])(options))
347+
if (typeof options === 'object' && Array.isArray(options) === false) {
348+
assertBoxed(methodName, await promisify(ssb[methodName])({ ...options, private: false } ))
349+
}
350+
}
351+
352+
// This tests the default behavior of `ssb.get()`, which should never
353+
// decrypt messages by default. This is **very important**.
354+
await assertBoxedAsync('get', msg.key)
355+
await assertBoxedAsync('get', { id: msg.key })
356+
await assertBoxedAsync('get', { id: msg.key, meta: true })
357+
await assertBoxedAsync('getAtSequence', [msg.value.author, msg.value.sequence])
358+
await assertBoxedAsync('getLatest', msg.value.author)
359+
360+
const assertBoxedSourceOnce = (methodName, options) => new Promise((resolve) => {
361+
pull(
362+
ssb[methodName](options),
363+
pull.collect((err, val) => {
364+
t.error(err, `${methodName}() does not error`)
365+
switch (methodName) {
366+
case 'createRawLogStream':
367+
assertBoxed(methodName, val[0].value)
368+
break;
369+
case 'createFeedStream':
370+
case 'createUserStream':
371+
case 'messagesByType':
372+
// Apparently some methods take `{ private: false }` to mean
373+
// "don't return any private messages". :/
374+
if (options.private === undefined) {
375+
assertBoxed(methodName, val[0].value)
376+
}
377+
break
378+
default:
379+
assertBoxed(methodName, val[0])
380+
}
381+
resolve()
382+
})
383+
)
384+
})
385+
386+
// Test the default **and** `{ private: false }`.
387+
const assertBoxedSource = async (methodName, options) => {
388+
await assertBoxedSourceOnce(methodName, options)
389+
await assertBoxedSourceOnce(methodName, { ...options, private: false })
390+
}
391+
392+
await assertBoxedSource('createLogStream', { limit: 1, reverse: true })
393+
await assertBoxedSource('createHistoryStream', { id: msg.value.author, seq: msg.value.sequence, reverse: true})
394+
await assertBoxedSource('messagesByType', { type: 'poke', limit: 1, reverse: true })
395+
await assertBoxedSource('createFeedStream', { id: msg.value.author, seq: msg.value.sequence, reverse: true})
396+
await assertBoxedSource('createUserStream', { id: msg.value.author, seq: msg.value.sequence, reverse: true})
397+
await assertBoxedSource('links', { source: msg.value.author, limit: 1, values: true})
398+
await assertBoxedSource('createRawLogStream', { source: msg.value.author, limit: 1, reverse: true, values: true})
399+
// createRawLogStream currently not exported as a method
400+
334401
t.end()
402+
335403
})
336404
})
337405
})

test/manifest.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict'
2+
var tape = require('tape')
3+
var { manifest, init } = require('../')
4+
5+
module.exports = function () {
6+
tape('manifest', t => {
7+
const _api = {}
8+
const opts = {
9+
path: `/tmp/ssb-manifest-test-${Date.now()}-${Math.random()}`
10+
}
11+
const api = init(_api, opts)
12+
13+
Object.keys(api).forEach(m => console.log(m))
14+
15+
Object.keys(manifest).forEach(method => {
16+
t.equal(typeof api[method], 'function', `api.${method}`)
17+
})
18+
19+
t.end()
20+
})
21+
}
22+
23+
if (!module.parent) { module.exports({}) }

0 commit comments

Comments
 (0)