diff options
author | cel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519> | 2018-11-30 14:57:12 -1000 |
---|---|---|
committer | cel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519> | 2018-11-30 14:58:13 -1000 |
commit | a61c80c4140564b1a4daf19d9086c05a8f276c20 (patch) | |
tree | 858bb36795bd5699d99639f637bd3492e3fe1fa6 /lib | |
parent | 328b6ffd8dc1a0cb66111a0ae8b346c5c2aad8f5 (diff) | |
download | patchfoo-a61c80c4140564b1a4daf19d9086c05a8f276c20.tar.gz patchfoo-a61c80c4140564b1a4daf19d9086c05a8f276c20.zip |
Show git signatures
- Add /git/signature page to verify signatures using gpg
- Show link to signature pages for tag and commit objects
Diffstat (limited to 'lib')
-rw-r--r-- | lib/app.js | 62 | ||||
-rw-r--r-- | lib/serve.js | 74 |
2 files changed, 136 insertions, 0 deletions
@@ -19,6 +19,9 @@ var toPull = require('stream-to-pull-stream') var BoxStream = require('pull-box-stream') var crypto = require('crypto') var SsbNpmRegistry = require('ssb-npm-registry') +var os = require('os') +var path = require('path') +var fs = require('fs') var zeros = new Buffer(24); zeros.fill(0) @@ -1141,3 +1144,62 @@ App.prototype.sbotStatus = function (cb) { } if (typeof status === 'object' && status !== null) return cb(null, status) } + +function writeAll(fd, buf, cb) { + var offset = 0 + var remaining = buf.length + fs.write(fd, buf, function onWrite(err, bytesWritten) { + if (err) return cb(err) + offset += bytesWritten + remaining -= bytesWritten + if (remaining > 0) fs.write(fd, buf, offset, remaining, null, onWrite) + else cb() + }) +} + +App.prototype.verifyGitObjectSignature = function (obj, cb) { + var self = this + var tmpPath = path.join(os.tmpdir(), '.git_vtag_tmp' + Math.random().toString('36')) + // use a temp file to work around https://github.com/nodejs/node/issues/13542 + function closeEnd(err) { + fs.close(fd, function (err1) { + fs.unlink(tmpPath, function (err2) { + cb(err2 || err1 || err) + }) + }) + } + fs.open(tmpPath, 'w+', function (err, fd) { + if (err) return cb(err) + self.git.extractSignature(obj, function (err, parts) { + if (err) return closeEnd(err) + writeAll(fd, parts.signature, function (err) { + if (err) return closeEnd(err) + try { next(fd, parts) } + catch(e) { closeEnd(e) } + }) + }) + }) + function next(fd, parts) { + var readSig = fs.createReadStream(null, {fd: fd, start: 0}) + var done = multicb({pluck: 1, spread: true}) + var gpg = proc.spawn('gpg', ['--status-fd=1', '--keyid-format=long', + '--verify', '/dev/fd/3', '-'], { + stdio: ['pipe', 'pipe', 'pipe', readSig] + }).on('close', done().bind(null, null)) + .on('error', console.error.bind(console, 'gpg')) + gpg.stdin.end(parts.payload) + pull(toPull.source(gpg.stdout), u.pullConcat(done())) + pull(toPull.source(gpg.stderr), u.pullConcat(done())) + done(function (err, code, status, output) { + if (err) return closeEnd(err) + fs.unlink(tmpPath, function (err) { + if (err) return cb(err) + cb(null, { + goodsig: status.includes('\n[GNUPG:] GOODSIG '), + status: status.toString(), + output: output.toString() + }) + }) + }) + } +} diff --git a/lib/serve.js b/lib/serve.js index a28f758..fc8aa7b 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -1870,6 +1870,7 @@ Serve.prototype.git = function (url) { case 'blob': return this.gitBlob(m[2]) case 'raw': return this.gitRaw(m[2]) case 'diff': return this.gitDiff(m[2]) + case 'signature': return this.gitSignature(m[2]) case 'line-comment': return this.gitLineComment(m[2]) default: return this.respond(404, 'Not found') } @@ -1985,6 +1986,67 @@ Serve.prototype.gitObject = function (rev) { }) } +Serve.prototype.gitSignature = function (id) { + var self = this + if (!/[0-9a-f]{24}/.test(id)) { + return pull( + ph('div.error', 'not a git object id'), + self.wrapPage('git'), + self.respondSink(400) + ) + } + if (!u.isRef(self.query.msg)) return pull( + ph('div.error', 'missing message id'), + self.wrapPage('git signature for ' + id), + self.respondSink(400) + ) + + self.app.git.openObject({ + obj: id, + msg: self.query.msg, + type: self.query.type, + }, function (err, obj) { + if (err) return handleError(err) + var msgDate = new Date(obj.msg.value.timestamp) + self.app.verifyGitObjectSignature(obj, function (err, verification) { + if (err) return handleError(err) + var objPath = '/git/object/' + id + '?msg=' + encodeURIComponent(obj.msg.key) + pull( + ph('section', [ + ph('h3', [ + ph('a', {href: self.app.render.toUrl(objPath)}, id), ': ', + ph('a', {href: ''}, 'signature') + ]), + ph('div', [ + self.phIdLink(obj.msg.value.author), ' pushed ', + ph('a', { + href: self.app.render.toUrl(obj.msg.key), + title: msgDate.toLocaleString(), + }, htime(msgDate)) + ]), + ph('pre', u.escapeHTML(verification.output)) + /* + verification.goodsig ? 'good' : 'bad', + ph('pre', u.escapeHTML(verification.status)) + */ + ]), + self.wrapPage('git signature for ' + id), + self.respondSink(200) + ) + }) + }) + + function handleError(err) { + if (err && err.name === 'BlobNotFoundError') + return self.askWantBlobs(err.links) + if (err) return pull( + pull.once(u.renderError(err).outerHTML), + self.wrapPage('git signature for ' + id), + self.respondSink(400) + ) + } +} + Serve.prototype.gitCommit = function (rev) { var self = this if (!/[0-9a-f]{24}/.test(rev)) { @@ -2073,6 +2135,12 @@ Serve.prototype.gitCommit = function (rev) { pull.once(commit.tree), self.gitObjectLinks(obj.msg.key, 'tree') )]) : '', + commit.gpgsig ? ph('div', [ + ph('a', {href: self.app.render.toUrl( + '/git/signature/' + rev + '?msg=' + encodeURIComponent(self.query.msg) + )}, 'signature'), + commit.signatureVersion ? [' from ', ph('code', u.escapeHTML(commit.signatureVersion))] : '' + ]) : '', h('blockquote', self.app.render.gitCommitBody(commit.body)).outerHTML, ph('h4', 'files'), @@ -2203,6 +2271,12 @@ Serve.prototype.gitTag = function (rev) { self.gitObjectLinks(obj.msg.key, tag.type) ), ' ', ph('code', u.escapeHTML(tag.tag)), + tag.gpgsig ? ph('div', [ + ph('a', {href: self.app.render.toUrl( + '/git/signature/' + rev + '?msg=' + encodeURIComponent(self.query.msg) + )}, 'signature'), + tag.signatureVersion ? [' from ', ph('code', u.escapeHTML(tag.signatureVersion))] : '' + ]) : '', h('pre', self.app.render.linkify(tag.body)).outerHTML, ] ]), |