fix(server, vue-app): address encoding issues with query params (#8748)

Co-authored-by: Pooya Parsa <pyapar@gmail.com>
This commit is contained in:
Daniel Roe 2021-02-04 18:52:13 +00:00 committed by GitHub
parent 0d4974968e
commit 07dd2cce9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 22 deletions

View File

@ -23,7 +23,8 @@
"pify": "^5.0.0", "pify": "^5.0.0",
"serve-placeholder": "^1.2.3", "serve-placeholder": "^1.2.3",
"serve-static": "^1.14.1", "serve-static": "^1.14.1",
"server-destroy": "^1.0.1" "server-destroy": "^1.0.1",
"ufo": "^0.6.1"
}, },
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"

View File

@ -1,6 +1,7 @@
import generateETag from 'etag' import generateETag from 'etag'
import fresh from 'fresh' import fresh from 'fresh'
import consola from 'consola' import consola from 'consola'
import { normalizeURL } from 'ufo'
import { getContext, TARGETS } from '@nuxt/utils' import { getContext, TARGETS } from '@nuxt/utils'
@ -9,7 +10,7 @@ export default ({ options, nuxt, renderRoute, resources }) => async function nux
const context = getContext(req, res) const context = getContext(req, res)
try { try {
const url = decodeURI(req.url) const url = normalizeURL(req.url)
res.statusCode = 200 res.statusCode = 200
const result = await renderRoute(url, context) const result = await renderRoute(url, context)

View File

@ -329,18 +329,24 @@ describe('server: nuxtMiddleware', () => {
expect(consola.error).toBeCalledWith(err) expect(consola.error).toBeCalledWith(err)
}) })
test('should return 400 if request is uri error', async () => { test('should return handle uri errors by normalizing', async () => {
const context = createContext() const context = createContext()
const result = { html: 'rendered html' } const result = { html: 'rendered html' }
context.renderRoute.mockReturnValue(result) context.renderRoute.mockReturnValue(result)
const nuxtMiddleware = createNuxtMiddleware(context) const nuxtMiddleware = createNuxtMiddleware(context)
const { req, res, next } = createServerContext() const { req, res, next } = createServerContext()
const paths = ['%c1%81', '%c1', '%']
const err = Error('URI malformed') for (const path of paths) {
err.name = 'URIError' await nuxtMiddleware(
{ ...req, url: 'http://localhost/test/server/' + path },
res,
next
)
await nuxtMiddleware({ ...req, url: 'http://localhost/test/server/%c1%81' }, res, next) expect(next).toBeCalledTimes(0)
expect(res.statusCode).toBe(200)
expect(next).toBeCalledWith(err) next.mockReset()
}
}) })
}) })

View File

@ -101,14 +101,6 @@ export const routerOptions = {
fallback: <%= router.fallback %> fallback: <%= router.fallback %>
} }
function decodeObj(obj) {
for (const key in obj) {
if (typeof obj[key] === 'string') {
obj[key] = decode(obj[key])
}
}
}
export function createRouter (ssrContext, config) { export function createRouter (ssrContext, config) {
const base = (config.app && config.app.basePath) || routerOptions.base const base = (config.app && config.app.basePath) || routerOptions.base
const router = new Router({ ...routerOptions, base }) const router = new Router({ ...routerOptions, base })
@ -124,11 +116,7 @@ export function createRouter (ssrContext, config) {
if (typeof to === 'string') { if (typeof to === 'string') {
to = normalizeURL(to) to = normalizeURL(to)
} }
const r = resolve(to, current, append) return resolve(to, current, append)
if (r && r.resolved && r.resolved.query) {
decodeObj(r.resolved.query)
}
return r
} }
return router return router

View File

@ -1,3 +1,4 @@
import fetch from 'node-fetch'
import { getPort, loadFixture, Nuxt, rp } from '../utils' import { getPort, loadFixture, Nuxt, rp } from '../utils'
let port let port
@ -22,7 +23,7 @@ describe('encoding', () => {
}) })
test('/ö/dynamic?q=food,coffee (encodeURIComponent)', async () => { test('/ö/dynamic?q=food,coffee (encodeURIComponent)', async () => {
const { body: response } = await rp(url('/ö/dynamic?q=food%252Ccoffee')) const { body: response } = await rp(url('/ö/dynamic?q=food,coffee'))
expect(response).toContain('food,coffee') expect(response).toContain('food,coffee')
}) })
@ -33,6 +34,41 @@ describe('encoding', () => {
expect(response).toContain('About') expect(response).toContain('About')
}) })
test('query params', async () => {
const queryStrings = {
'?email=some%20email.com': { email: 'some email.com' },
'?str=%26&str2=%2526': { str: '&', str2: '%26' },
'?t=coffee%2Cfood%2C': { t: 'coffee,food,' },
'?redirect=%2Fhomologation%2Flist': { redirect: '/homologation/list' },
'?email=some@email.com&token=DvtiwbIzry319e6KWimopA%3D%3D': {
email: 'some@email.com',
token: 'DvtiwbIzry319e6KWimopA=='
}
}
for (const [param, result] of Object.entries(queryStrings)) {
const { body: response } = await rp(url('/ö/dynamic/test') + param)
expect(response).toContain(
JSON.stringify(result)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
)
}
})
test('invalidly encoded route params are handled', async () => {
const paths = ['%c1%81', '%c1', '%']
for (const path of paths) {
// We use node-fetch because got uses decodeURI on url and throws its own error
const response = await fetch(url('/ö/dynamic/') + path)
expect(response.ok).toBeTruthy()
expect(await response.text()).toContain(
JSON.stringify({ id: path })
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
)
}
})
// Close server and ask nuxt to stop listening to file changes // Close server and ask nuxt to stop listening to file changes
afterAll(async () => { afterAll(async () => {
await nuxt.close() await nuxt.close()

View File

@ -26,6 +26,8 @@ export default {
'/@about', '/@about',
'/тест', '/тест',
encodeURI('/тест'), encodeURI('/тест'),
'/dynamic/%c',
'/dynamic/%',
'/dynamic/سلام چطوری?q=cofee,food,دسر', '/dynamic/سلام چطوری?q=cofee,food,دسر',
encodeURI('/dynamic/سلام چطوری?q=cofee,food,دسر'), encodeURI('/dynamic/سلام چطوری?q=cofee,food,دسر'),
// Using encodeURIComponent on each segment // Using encodeURIComponent on each segment