Skip to content

Commit 35becbc

Browse files
feat: respect logLevel when registering plugin (#186)
* feat: respect logLevel option When registering the plugin, you could specify the logLevel as an option, but this was ignored by the plugin. * fix: ensure logLevel is a string I don't know why someone would pass in undefined, but it should fail early. * docs: mention logLevel option
1 parent f53f6c1 commit 35becbc

File tree

4 files changed

+108
-4
lines changed

4 files changed

+108
-4
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Apart from these safeguards, it is extremely important to [use HTTPS for your we
159159
| `getUserInfo` | A sync function to get a string of user-specific information to prevent cookie tossing. |
160160
| `sessionPlugin` | The session plugin that you are using (if applicable). |
161161
| `csrfOpts` | The csrf options. See [@fastify/csrf](https://github.com/fastify/csrf). |
162+
| `logLevel` | The log level for `fastify.csrfProtection` errors. |
162163

163164
### `reply.generateCsrf([opts])`
164165

index.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ const defaultOptions = {
1414
sessionKey: '_csrf',
1515
getToken: getTokenDefault,
1616
getUserInfo: getUserInfoDefault,
17-
sessionPlugin: '@fastify/cookie'
17+
sessionPlugin: '@fastify/cookie',
18+
logLevel: 'warn'
1819
}
1920

2021
async function fastifyCsrfProtection (fastify, opts) {
@@ -24,7 +25,8 @@ async function fastifyCsrfProtection (fastify, opts) {
2425
sessionKey,
2526
getToken,
2627
getUserInfo,
27-
sessionPlugin
28+
sessionPlugin,
29+
logLevel
2830
} = Object.assign({}, defaultOptions, opts)
2931

3032
const csrfOpts = opts?.csrfOpts ? opts.csrfOpts : {}
@@ -34,6 +36,7 @@ async function fastifyCsrfProtection (fastify, opts) {
3436
assert(typeof getToken === 'function', 'getToken should be a function')
3537
assert(typeof getUserInfo === 'function', 'getUserInfo should be a function')
3638
assert(typeof cookieOpts === 'object', 'cookieOpts should be a object')
39+
assert(typeof logLevel === 'string', 'logLevel should be a string')
3740
assert(
3841
['@fastify/cookie', '@fastify/session', '@fastify/secure-session'].includes(sessionPlugin),
3942
"sessionPlugin should be one of the following: '@fastify/cookie', '@fastify/session', '@fastify/secure-session'"
@@ -113,11 +116,11 @@ async function fastifyCsrfProtection (fastify, opts) {
113116
function csrfProtection (req, reply, next) {
114117
const secret = getSecret(req, reply)
115118
if (!secret) {
116-
req.log.warn('Missing csrf secret')
119+
req.log[logLevel]('Missing csrf secret')
117120
return reply.send(new MissingCSRFSecretError())
118121
}
119122
if (!tokens.verify(secret, getToken(req), getUserInfo(req))) {
120-
req.log.warn('Invalid csrf token')
123+
req.log[logLevel]('Invalid csrf token')
121124
return reply.send(new InvalidCSRFTokenError())
122125
}
123126
next()

test/basic.test.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,18 @@ test('Validation', async t => {
157157
t.assert.strictEqual(err.message, "sessionPlugin should be one of the following: '@fastify/cookie', '@fastify/session', '@fastify/secure-session'")
158158
}
159159
})
160+
161+
await t.test('logLevel', async t => {
162+
t.plan(1)
163+
try {
164+
const fastify = Fastify()
165+
await fastify.register(fastifyCookie)
166+
await fastify.register(fastifyCsrf, { logLevel: undefined })
167+
await fastify.ready()
168+
} catch (err) {
169+
t.assert.strictEqual(err.message, 'logLevel should be a string')
170+
}
171+
})
160172
})
161173

162174
test('csrf options', async () => {
@@ -177,6 +189,93 @@ test('csrf options', async () => {
177189
sinon.assert.calledWith(csrf, csrfOpts)
178190
})
179191

192+
const spyLogger = {
193+
warn: sinon.spy(),
194+
error: sinon.spy(),
195+
info: sinon.spy(),
196+
debug: sinon.spy(),
197+
fatal: sinon.spy(),
198+
trace: sinon.spy(),
199+
child: () => spyLogger
200+
}
201+
202+
test('logLevel options', async t => {
203+
async function load (logLevel) {
204+
const opts = logLevel ? { logLevel } : {}
205+
const fastify = Fastify({ loggerInstance: spyLogger })
206+
await fastify.register(fastifyCookie)
207+
await fastify.register(fastifyCsrf, opts)
208+
fastify.get('/', async (_req, reply) => {
209+
reply.generateCsrf()
210+
return {}
211+
})
212+
213+
fastify.post('/', {
214+
onRequest: fastify.csrfProtection
215+
}, async () => {
216+
return {}
217+
})
218+
await fastify.ready()
219+
return fastify
220+
}
221+
222+
async function makeRequests (fastify) {
223+
const response = await fastify.inject({
224+
method: 'GET',
225+
path: '/'
226+
})
227+
228+
const cookie = response.cookies[0]
229+
230+
// missing csrf secret
231+
await fastify.inject({
232+
method: 'POST',
233+
payload: { hello: 'world' },
234+
path: '/',
235+
})
236+
237+
// invalid csrf token
238+
await fastify.inject({
239+
method: 'POST',
240+
payload: { hello: 'world' },
241+
path: '/',
242+
cookies: {
243+
[cookie.name]: cookie.value
244+
}
245+
})
246+
}
247+
248+
t.afterEach(() => {
249+
spyLogger.warn.resetHistory()
250+
spyLogger.error.resetHistory()
251+
})
252+
253+
await t.test('default log level', async t => {
254+
t.plan(1)
255+
const fastify = await load()
256+
await makeRequests(fastify)
257+
258+
t.assert.strictEqual(spyLogger.warn.callCount, 2)
259+
})
260+
261+
await t.test('custom log level', async t => {
262+
t.plan(2)
263+
const fastify = await load('error')
264+
await makeRequests(fastify)
265+
266+
t.assert.strictEqual(spyLogger.error.callCount, 2)
267+
t.assert.ok(spyLogger.warn.notCalled)
268+
})
269+
270+
await t.test('silent log level', async t => {
271+
t.plan(1)
272+
const fastify = await load('silent')
273+
await makeRequests(fastify)
274+
275+
t.assert.ok(spyLogger.warn.notCalled)
276+
})
277+
})
278+
180279
async function runtTest (t, load, tkn, hook = 'onRequest') {
181280
await t.test(`Token in ${tkn.place}`, async t => {
182281
const fastify = await load()

types/index.test-d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,5 +69,6 @@ fastify.register(FastifyCsrfProtection, { csrfOpts: { }, sessionPlugin: '@fastif
6969
fastify.register(FastifyCsrfProtection, { csrfOpts: { }, sessionPlugin: '@fastify/secure-session' })
7070
fastify.register(FastifyCsrfProtection, { sessionPlugin: '@fastify/session' })
7171
fastify.register(FastifyCsrfProtection, { sessionPlugin: '@fastify/secure-session' })
72+
fastify.register(FastifyCsrfProtection, { logLevel: 'info' })
7273

7374
expectDeprecated({} as FastifyCsrfOptions)

0 commit comments

Comments
 (0)