CVE-2024-22207: Initialization of a Resource with an Insecure Default (CWE-1188)
value: fastify-swagger-ui is a Fastify plugin for serving Swagger UI.  Prior to 2.1.0, the default configuration of `@fastify/swagger-ui` without `baseDir` set will lead to all files in the module's directory being exposed via http routes served by the module.  The vulnerability is fixed in v2.1.0. Setting the `baseDir` option can also work around this vulnerability.
CWE description: The product initializes or sets a resource with a default that is intended to be changed by the administrator, but the default is not secure.
CVSS v3 base score: 5.3
Published: 2024-01-15T16:15Z
Repository: https://github.com/fastify/fastify-swagger-ui
Language: JavaScript
Files changed: lib/routes.js, test/route.test.js
Commit date: 2024-01-15 14:57:58 +0100
Commit message: Merge pull request from GHSA-62jr-84gf-wmg4

Signed-off-by: Matteo Collina <hello@matteocollina.com>
Co-authored-by: Nick O'Leary <nick.oleary@gmail.com>
Diff: diff --git a/lib/routes.js b/lib/routes.js
index 4e133da..a179ff8 100644
--- a/lib/routes.js
+++ b/lib/routes.js
@@ -205,26 +205,28 @@ function fastifySwagger (fastify, opts, done) {
     root: opts.baseDir || path.join(__dirname, '..', 'static'),
     prefix: staticPrefix,
     decorateReply: false
   })
 
-  fastify.register(fastifyStatic, {
-    root: opts.baseDir || path.join(__dirname, '..'),
-    serve: false
-  })
+  if (opts.baseDir) {
+    fastify.register(fastifyStatic, {
+      root: opts.baseDir,
+      serve: false
+    })
 
-  // Handler for external documentation files passed via $ref
-  fastify.route({
-    url: '/*',
-    method: 'GET',
-    schema: { hide: true },
-    ...hooks,
-    handler: function (req, reply) {
-      const file = req.params['*']
-      reply.sendFile(file)
-    }
-  })
+    // Handler for external documentation files passed via $ref
+    fastify.route({
+      url: '/*',
+      method: 'GET',
+      schema: { hide: true },
+      ...hooks,
+      handler: function (req, reply) {
+        const file = req.params['*']
+        reply.sendFile(file)
+      }
+    })
+  }
 
   done()
 }
 
 module.exports = fastifySwagger
diff --git a/test/route.test.js b/test/route.test.js
index a55c19a..56e41aa 100644
--- a/test/route.test.js
+++ b/test/route.test.js
@@ -493,10 +493,23 @@ test('/documentation/ should redirect to ./static/index.html', async (t) => {
   })
   t.equal(res.statusCode, 302)
   t.equal(res.headers.location, './static/index.html')
 })
 
+test('/documentation/* should not return module files when baseDir not set', async (t) => {
+  t.plan(1)
+  const fastify = Fastify()
+  await fastify.register(fastifySwagger, swaggerOption)
+  await fastify.register(fastifySwaggerUi)
+
+  const res = await fastify.inject({
+    method: 'GET',
+    url: '/documentation/README.md'
+  })
+  t.equal(res.statusCode, 404)
+})
+
 test('should return silent log level of route /documentation', async (t) => {
   const fastify = Fastify()
 
   fastify.addHook('onRoute', function (route) {
     t.equal(route.logLevel, 'silent')

Vulnerable code:   fastify.register(fastifyStatic, {
    root: opts.baseDir || path.join(__dirname, '..'),
    serve: false
  })
  // Handler for external documentation files passed via $ref
  fastify.route({
    url: '/*',
    method: 'GET',
    schema: { hide: true },
    ...hooks,
    handler: function (req, reply) {
      const file = req.params['*']
      reply.sendFile(file)
    }
  })
Fixed code:   if (opts.baseDir) {
    fastify.register(fastifyStatic, {
      root: opts.baseDir,
      serve: false
    })
    // Handler for external documentation files passed via $ref
    fastify.route({
      url: '/*',
      method: 'GET',
      schema: { hide: true },
      ...hooks,
      handler: function (req, reply) {
        const file = req.params['*']
        reply.sendFile(file)
      }
    })
  }
test('/documentation/* should not return module files when baseDir not set', async (t) => {
  t.plan(1)
  const fastify = Fastify()
  await fastify.register(fastifySwagger, swaggerOption)
  await fastify.register(fastifySwaggerUi)

  const res = await fastify.inject({
    method: 'GET',
    url: '/documentation/README.md'
  })
  t.equal(res.statusCode, 404)
})