CVE-2023-50710 (TypeScript, published 2023-12-14T18:15Z)
Description: value: Hono is a web framework written in TypeScript. Prior to version 3.11.7, clients may override named path parameter values from previous requests if the application is using TrieRouter. So, there is a risk that a privileged user may use unintended parameters when deleting REST API resources. TrieRouter is used either explicitly or when the application matches a pattern that is not supported by the default RegExpRouter. Version 3.11.7 includes the change to fix this issue. As a workaround, avoid using TrieRouter directly.
Severity: nan (CVSS v3 base score: 4.3)
Weakness: CWE-94 \u2014 Improper Control of Generation of Code ('Code Injection')
The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.
Repository: https://github.com/honojs/hono
Files changed:
deno_dist/router/trie-router/node.ts
src/router/trie-router/node.test.ts
src/router/trie-router/node.ts
Commit message: Merge pull request from GHSA-f6gv-hh8j-q8vq

* fix(trie-router): don't remain with the values of named param

* denoify

* fix: don't share `params`

* denoify

Vulnerable code:
  params: Record<string, string>
      m[method] = { handler, params: {}, possibleKeys: [], score: 0, name: this.name }
      params: {},
  private gHSets(node: Node<T>, method: string, params: Record<string, string>): HandlerSet<T>[] {
    const handlerSets: HandlerSet<T>[] = []
      const handlerSet = m[method] || m[METHOD_NAME_ALL]
    const handlerSets: HandlerSet<T>[] = []
              handlerSets.push(
                ...this.gHSets(nextNode.children['*'], method, { ...params, ...node.params })
              )
            handlerSets.push(...this.gHSets(nextNode, method, { ...params, ...node.params }))
              handlerSets.push(...this.gHSets(astNode, method, { ...params, ...node.params }))
  params: Record<string, string>
      m[method] = { handler, params: {}, possibleKeys: [], score: 0, name: this.name }
      params: {},
  private gHSets(node: Node<T>, method: string, params: Record<string, string>): HandlerSet<T>[] {
    const handlerSets: HandlerSet<T>[] = []
      const handlerSet = m[method] || m[METHOD_NAME_ALL]
    const handlerSets: HandlerSet<T>[] = []
              handlerSets.push(
                ...this.gHSets(nextNode.children['*'], method, { ...params, ...node.params })
              )
            handlerSets.push(...this.gHSets(nextNode, method, { ...params, ...node.params }))
              handlerSets.push(...this.gHSets(astNode, method, { ...params, ...node.params }))

Fixed code:
type HandlerParamsSet<T> = HandlerSet<T> & {
  params: Record<string, string>
}

      m[method] = { handler, possibleKeys: [], score: 0, name: this.name }
  private gHSets(
    node: Node<T>,
    method: string,
    params: Record<string, string>
  ): HandlerParamsSet<T>[] {
    const handlerSets: HandlerParamsSet<T>[] = []
      const handlerSet = (m[method] || m[METHOD_NAME_ALL]) as HandlerParamsSet<T>
        handlerSet.params = {}
    const handlerSets: HandlerParamsSet<T>[] = []
              handlerSets.push(...this.gHSets(nextNode.children['*'], method, node.params))
            handlerSets.push(...this.gHSets(nextNode, method, node.params))
              handlerSets.push(...this.gHSets(astNode, method, node.params))

  it('Should not return a previous param value', () => {
    const node = new Node()
    node.insert('delete', '/resource/:id', 'resource')
    const [resA] = node.search('delete', '/resource/a')
    const [resB] = node.search('delete', '/resource/b')
    expect(resA).not.toBeNull()
    expect(resA.length).toBe(1)
    expect(resA[0][0]).toEqual('resource')
    expect(resA[0][1]).toEqual({ id: 'a' })
    expect(resB).not.toBeNull()
    expect(resB.length).toBe(1)
    expect(resB[0][0]).toEqual('resource')
    expect(resB[0][1]).toEqual({ id: 'b' })
  })
type HandlerParamsSet<T> = HandlerSet<T> & {
  params: Record<string, string>
}

      m[method] = { handler, possibleKeys: [], score: 0, name: this.name }
  private gHSets(
    node: Node<T>,
    method: string,
    params: Record<string, string>
  ): HandlerParamsSet<T>[] {
    const handlerSets: HandlerParamsSet<T>[] = []
      const handlerSet = (m[method] || m[METHOD_NAME_ALL]) as HandlerParamsSet<T>
        handlerSet.params = {}
    const handlerSets: HandlerParamsSet<T>[] = []
              handlerSets.push(...this.gHSets(nextNode.children['*'], method, node.params))
            handlerSets.push(...this.gHSets(nextNode, method, node.params))
              handlerSets.push(...this.gHSets(astNode, method, node.params))