CVE-2024-32869 (TypeScript, published 2024-04-23T21:15Z)
Description: value: Hono is a Web application framework that provides support for any JavaScript runtime. Prior to version 4.2.7, when using serveStatic with deno, it is possible to traverse the directory where `main.ts` is located. This can result in retrieval of unexpected files. Version 4.2.7 contains a patch for the issue.
Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/honojs/hono
Files changed:
deno_dist/middleware/serve-static/index.ts
runtime_tests/deno/middleware.test.tsx
src/middleware/serve-static/index.test.ts
src/middleware/serve-static/index.ts
Commit message: Merge pull request from GHSA-3mpf-rcc7-5347



Vulnerable code:
    const url = new URL(c.req.url)
    let filename = options.path ?? decodeURI(url.pathname)
    const url = new URL(c.req.url)
    let filename = options.path ?? decodeURI(url.pathname)

Fixed code:
    let filename = options.path ?? decodeURI(c.req.path)

  res = await app.fetch({
    method: 'GET',
    url: 'http://localhost/static/%2e%2e/static/plain.txt',
  } as Request)
  assertEquals(res.status, 404)
  assertEquals(await res.text(), '404 Not Found')
import { createMiddleware } from '../../helper'
import { Hono } from '../../hono'
import { serveStatic as baseServeStatic } from '.'

describe('Serve Static Middleware', () => {
  const app = new Hono()

  const serveStatic = createMiddleware(async (c, next) => {
    const mw = baseServeStatic({
      getContent: (path) => {
        if (path.endsWith('not-found.txt')) {
          return undefined
        }
        return `Hello in ${path}`
      },
      pathResolve: (path) => {
        return `./${path}`
      },
    })
    return await mw(c, next)
  })

  app.get('/static/*', serveStatic)

  it('Should return 200 response - /static/hello.html', async () => {
    const res = await app.request('/static/hello.html')
    expect(res.status).toBe(200)
    expect(res.headers.get('Content-Type')).toMatch(/^text\/html/)
    expect(await res.text()).toBe('Hello in ./static/hello.html')
  })

  it('Should return 200 response - /static/sub', async () => {
    const res = await app.request('/static/sub')
    expect(res.status).toBe(200)
    expect(res.headers.get('Content-Type')).toMatch(/^text\/html/)
    expect(await res.text()).toBe('Hello in ./static/sub/index.html')
  })

  it('Should decode URI strings - /static/%E7%82%8E.txt', async () => {
    const res = await app.request('/static/%E7%82%8E.txt')
    expect(res.status).toBe(200)
    expect(await res.text()).toBe('Hello in ./static/炎.txt')
  })

  it('Should return 404 response - /static/not-found', async () => {
    const res = await app.request('/static/not-found.txt')
    expect(res.status).toBe(404)
    expect(await res.text()).toBe('404 Not Found')
  })

  it('Should not allow a directory traversal - /static/%2e%2e/static/hello.html', async () => {
    const res = await app.fetch({
      method: 'GET',
      url: 'http://localhost/static/%2e%2e/static/hello.html',
    } as Request)
    expect(res.status).toBe(404)
    expect(await res.text()).toBe('404 Not Found')
  })
})
    let filename = options.path ?? decodeURI(c.req.path)