VYPR
High severity7.5NVD Advisory· Published Mar 6, 2026· Updated Apr 14, 2026

CVE-2026-29087

CVE-2026-29087

Description

@hono/node-server allows running the Hono application on Node.js. Prior to version 1.19.10, when using @hono/node-server's static file serving together with route-based middleware protections (e.g. protecting /admin/*), inconsistent URL decoding can allow protected static resources to be accessed without authorization. In particular, paths containing encoded slashes (%2F) may be evaluated differently by routing/middleware matching versus static file path resolution, enabling a bypass where middleware does not run but the static file is still served. This issue has been patched in version 1.19.10.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@hono/node-servernpm
< 1.19.101.19.10

Affected products

1
  • cpe:2.3:a:hono:node-server:*:*:*:*:*:node.js:*:*
    Range: <1.19.10

Patches

1
455015be1697

Merge commit from fork

https://github.com/honojs/node-serverYusuke WadaMar 3, 2026via ghsa
3 files changed · +45 2
  • src/serve-static.ts+20 1 modified
    @@ -68,6 +68,25 @@ const getStats = (path: string) => {
       return stats
     }
     
    +type Decoder = (str: string) => string
    +
    +const tryDecode = (str: string, decoder: Decoder): string => {
    +  try {
    +    return decoder(str)
    +  } catch {
    +    // Decode only valid %xx sequences in chunks; keep undecodable parts as-is
    +    return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => {
    +      try {
    +        return decoder(match)
    +      } catch {
    +        return match
    +      }
    +    })
    +  }
    +}
    +
    +const tryDecodeURI = (str: string) => tryDecode(str, decodeURI)
    +
     // eslint-disable-next-line @typescript-eslint/no-explicit-any
     export const serveStatic = <E extends Env = any>(
       options: ServeStaticOptions<E> = { root: '' }
    @@ -91,7 +110,7 @@ export const serveStatic = <E extends Env = any>(
           filename = optionPath
         } else {
           try {
    -        filename = decodeURIComponent(c.req.path)
    +        filename = tryDecodeURI(c.req.path)
             if (/(?:^|[\/\\])\.\.(?:$|[\/\\])/.test(filename)) {
               throw new Error()
             }
    
  • test/assets/static/admin/secret.txt+1 0 added
    @@ -0,0 +1 @@
    +secret
    \ No newline at end of file
    
  • test/serve-static.test.ts+24 1 modified
    @@ -330,7 +330,7 @@ describe('Serve Static Middleware', () => {
         })
       })
     
    -  describe('Security tests', () => {
    +  describe('Path traversal security tests', () => {
         const app = new Hono()
         const server = createAdaptorServer(app)
         app.use('/static/*', serveStatic({ root: './test/assets' }))
    @@ -361,6 +361,29 @@ describe('Serve Static Middleware', () => {
         })
       })
     
    +  describe('Path mismatch security tests', () => {
    +    const app = new Hono()
    +    const server = createAdaptorServer(app)
    +
    +    app.use('/static/admin/*', async (c, next) => {
    +      c.header('X-Authorized', 'true')
    +      await next()
    +    })
    +
    +    app.use('/static/*', serveStatic({ root: './test/assets' }))
    +
    +    it('Should not allow bypass via path mismatch between middleware and serveStatic', async () => {
    +      const res = await request(server).get('/static/admin/secret.txt')
    +      expect(res.headers['x-authorized']).toBe('true')
    +      expect(res.text).toBe('secret')
    +
    +      const res2 = await request(server).get('/static/admin%2Fsecret.txt')
    +      expect(res2.status).toBe(404)
    +      expect(res2.headers['x-authorized']).toBeUndefined()
    +      expect(res2.text).not.toBe('secret')
    +    })
    +  })
    +
       describe('Stream error handling', () => {
         const testFile = path.join(__dirname, 'assets', 'static', 'plain.txt')
         console.log(testFile)
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.