Skip to content

Commit

Permalink
Validate request body for /inbox endpoint
Browse files Browse the repository at this point in the history
using json-schema with ajv library
  • Loading branch information
mrkvon committed Oct 14, 2023
1 parent 2b00db4 commit 3169f81
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 12 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
"@koa/cors": "^4.0.0",
"@koa/router": "^12.0.0",
"@solid/access-token-verifier": "^2.0.5",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"bcryptjs": "^2.4.3",
"cross-fetch": "^4.0.0",
"dotenv": "^16.0.0",
Expand Down
20 changes: 19 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
import { getStatus } from './controllers/status'
import { webhookReceiver } from './controllers/webhookReceiver'
import { solidAuth } from './middlewares/solidAuth'
import { validateBody } from './middlewares/validate'

const app = new Koa()
const router = new Router()
Expand All @@ -20,7 +21,24 @@ router
ctx.response.body =
'Hello world! This is a Solid email notifier. Read more at https://github.com/openHospitalityNetwork/solid-email-notifications'
})
.post('/inbox', solidAuth, initializeIntegration)
.post(
'/inbox',
solidAuth,
validateBody({
type: 'object',
properties: {
'@context': { const: 'https://www.w3.org/ns/activitystreams' },
'@id': { type: 'string' },
'@type': { const: 'Add' },
actor: { type: 'string', format: 'uri' },
object: { type: 'string', format: 'uri' },
target: { type: 'string', format: 'email' },
},
required: ['@context', '@type', 'actor', 'object', 'target'],
additionalProperties: false,
}),
initializeIntegration,
)
.get('/verify-email', checkVerificationLink, finishIntegration)
.post('/webhook-receiver', webhookReceiver)
.get('/status', solidAuth, getStatus)
Expand Down
2 changes: 1 addition & 1 deletion src/middlewares/solidAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const solidAuth: Middleware = async (ctx, next) => {
(error as Error).message
}`

ctx.throw(401, { error: message })
ctx.throw(401, message)
}

// on success continue
Expand Down
20 changes: 20 additions & 0 deletions src/middlewares/validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import addFormats from 'ajv-formats'
import Ajv2020 from 'ajv/dist/2020'
import type { Middleware } from 'koa'

const ajv = new Ajv2020({ allErrors: true })
addFormats(ajv)

export const validateBody =
(schema: Parameters<typeof ajv.compile>[0]): Middleware =>
async (ctx, next) => {
const validate = ajv.compile(schema)
const isValid = validate(ctx.request.body)

if (isValid) return await next()
else {
ctx.response.status = 400
ctx.response.type = 'json'
ctx.response.body = validate.errors
}
}
30 changes: 20 additions & 10 deletions src/test/integration-start.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ describe('Mailer integration via /inbox', () => {
expect(response.status).to.equal(200)
})

it('[invalid request body] should respond with 400', async () => {
const response = await authenticatedFetch(`${baseUrl}/inbox`, {
method: 'post',
headers: {
'content-type':
'application/ld+json;profile="https://www.w3.org/ns/activitystreams"',
},
body: JSON.stringify({
'@context': 'https://www.w3.org/ns/activitystrams',
'@id': '',
'@type': 'ads',
actor: 'asdf',
object: '/inbox',
target: 'yay',
}),
})

expect(response.status).to.equal(400)
})

context('person not signed in', () => {
it('should respond with 401', async () => {
const response = await fetch(`${baseUrl}/inbox`, {
Expand Down Expand Up @@ -89,16 +109,6 @@ describe('Mailer integration via /inbox', () => {
})
})

it('should check that the person requesting is the authenticated person', async () => {
const response = await authenticatedFetch(`${baseUrl}/inbox`, {
method: 'post',
headers: { 'content-type': 'application/ld+json' },
body: JSON.stringify({}),
})

expect(response.status).to.equal(403)
})

it(
'should check that the inbox belongs to the person requesting subscription',
)
Expand Down
27 changes: 27 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3089,6 +3089,13 @@ aggregate-error@^3.0.0:
clean-stack "^2.0.0"
indent-string "^4.0.0"

ajv-formats@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==
dependencies:
ajv "^8.0.0"

ajv@^6.10.0, ajv@^6.12.4:
version "6.12.6"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
Expand All @@ -3099,6 +3106,16 @@ ajv@^6.10.0, ajv@^6.12.4:
json-schema-traverse "^0.4.1"
uri-js "^4.2.2"

ajv@^8.0.0, ajv@^8.12.0:
version "8.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1"
integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==
dependencies:
fast-deep-equal "^3.1.1"
json-schema-traverse "^1.0.0"
require-from-string "^2.0.2"
uri-js "^4.2.2"

[email protected]:
version "4.1.1"
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
Expand Down Expand Up @@ -5109,6 +5126,11 @@ json-schema-traverse@^0.4.1:
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==

json-schema-traverse@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==

json-stable-stringify-without-jsonify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
Expand Down Expand Up @@ -6473,6 +6495,11 @@ require-directory@^2.1.1:
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==

require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==

requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
Expand Down

0 comments on commit 3169f81

Please sign in to comment.