diff --git a/net/security.js b/net/security.js index 0473fc6..6e8da2a 100644 --- a/net/security.js +++ b/net/security.js @@ -44,7 +44,6 @@ function verifyAuthorization (req, res, next) { async function verifySignature (req, res, next) { const apex = req.app.locals.apex try { - // support for apps not using signature extension to ActivityPub if (!req.get('authorization') && !req.get('signature')) { if (req.app.get('env') !== 'development') { apex.logger.warn('Request rejected: missing http signature') diff --git a/spec/functional/inbox.spec.js b/spec/functional/inbox.spec.js index cc4fd7b..621a218 100644 --- a/spec/functional/inbox.spec.js +++ b/spec/functional/inbox.spec.js @@ -1,4 +1,6 @@ -/* global describe, beforeAll, beforeEach, it, expect, spyOn */ +/* global describe, beforeAll, beforeEach, afterAll, it, expect, spyOn */ +const crypto = require('crypto') +const httpSignature = require('http-signature') const request = require('supertest') const merge = require('deepmerge') const nock = require('nock') @@ -1218,6 +1220,71 @@ describe('inbox', function () { .expect(200) }) }) + describe('signature verification', function () { + beforeAll(() => { + app.set('env', 'production') + }) + afterAll(function () { + app.set('env', 'development') + }) + it('rejects missing signature', function () { + request(app) + .post('/inbox/test') + .set('Content-Type', 'application/activity+json') + .send(activity) + .expect(401) + }) + it('rejects invalid signature', function () { + request(app) + .post('/inbox/test') + .set('Content-Type', 'application/activity+json') + .set('Signature', 'asfdlajsflkjasklgja') + .send(activity) + .expect(403) + }) + it('handles unverifiable delete', function () { + const act = merge({}, activity) + act.id = 'https://mocked.com/s/abc123' + act.actor = 'https://mocked.com/u/mocked' + act.object = act.actor + nock('https://mocked.com') + .get('/u/mocked') + .reply(404) + request(app) + .post('/inbox/test') + .set('Content-Type', 'application/activity+json') + .set('Signature', 'asfdlajsflkjasklgja') + .send(activity) + .expect(200) + }) + it('validates valid signature', async function () { + const recip = await apex.createActor('recipient', 'recipient') + await apex.store.saveObject(recip) + const body = apex.stringifyPublicJSONLD(activity) + const headers = { + digest: crypto.createHash('sha256').update(body).digest('base64'), + host: 'localhost' + } + httpSignature.signRequest({ + getHeader: k => headers[k.toLowerCase()], + setHeader: (k, v) => (headers[k.toLowerCase()] = v), + method: 'POST', + path: '/inbox/test' + }, { + key: testUser._meta.privateKey, + keyId: testUser.id, + headers: ['(request-target)', 'host', 'date', 'digest'], + authorizationHeaderName: 'Signature' + }) + const signedReq = request(app) + .post('/inbox/test') + .set('Content-Type', 'application/activity+json') + Object.entries(headers).forEach((k, v) => signedReq.set(k, v)) + signedReq + .send(body) + .expect(200) + }) + }) }) describe('get', function () { let inbox diff --git a/spec/functional/outbox.spec.js b/spec/functional/outbox.spec.js index fef587b..30ff736 100644 --- a/spec/functional/outbox.spec.js +++ b/spec/functional/outbox.spec.js @@ -107,6 +107,13 @@ describe('outbox', function () { .send({ actor: 'bob', '@context': 'https://www.w3.org/ns/activitystreams' }) .expect(400, 'Invalid activity', done) }) + it('rejects unauthorized requests', function () { + return request(app) + .post('/outbox/test') + .set('Content-Type', 'application/activity+json') + .send(activity) + .expect(403) + }) // activity getTargetActor it('errors on unknown actor', function (done) { request(app)