Skip to content

Commit

Permalink
fix(encoding): decode params (#3350)
Browse files Browse the repository at this point in the history
* fix(encoding): decode params

This change forces users to encode their `path` in routes but also fixes
existing problems with route location that were provided as string and
not encoded. Specially with the slash character, allowing it to be
encoded and decoded properly.

* feat: warn against unencoded routes
  • Loading branch information
posva authored Nov 5, 2020
1 parent da34ccd commit 63c749c
Show file tree
Hide file tree
Showing 5 changed files with 29 additions and 19 deletions.
8 changes: 4 additions & 4 deletions examples/basic/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const router = new VueRouter({
{ path: '/', component: Home },
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
{ path: '/é', component: Unicode },
{ path: encodeURI('/é'), component: Unicode },
{ path: '/query/:q', component: Query }
]
})
Expand Down Expand Up @@ -76,9 +76,9 @@ const vueInstance = new Vue({
<router-link tag="li" to="/bar" :event="['mousedown', 'touchstart']">
<a>/bar</a>
</router-link>
<li><router-link to="">/é</router-link></li>
<li><router-link to="/é?t=%25ñ">/é?t=%ñ</router-link></li>
<li><router-link to="/é#%25ñ">/é#%25ñ</router-link></li>
<li><router-link :to="encodeURI('/é')">/é</router-link></li>
<li><router-link :to="encodeURI('/é?t=%ñ')">/é?t=%ñ</router-link></li>
<li><router-link :to="encodeURI('/é#%ñ')">/é#%25ñ</router-link></li>
<router-link to="/foo" v-slot="props">
<li :class="[props.isActive && 'active', props.isExactActive && 'exact-active']">
<a :href="props.href" @click="props.navigate">{{ props.route.path }} (with v-slot).</a>
Expand Down
12 changes: 6 additions & 6 deletions examples/hash-mode/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ const router = new VueRouter({
{ path: '/', component: Home }, // all paths are defined without the hash.
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar },
{ path: '/é', component: Unicode },
{ path: '/é/:unicode', component: Unicode },
{ path: encodeURI('/é'), component: Unicode },
{ path: encodeURI('/é/:unicode'), component: Unicode },
{ path: '/query/:q', component: Query, name: 'param' }
]
})
Expand All @@ -64,10 +64,10 @@ const vueInstance = new Vue({
<li><router-link to="/foo">/foo</router-link></li>
<li><router-link to="/bar">/bar</router-link></li>
<router-link tag="li" to="/bar">/bar</router-link>
<li><router-link to="">/é</router-link></li>
<li><router-link to="/é/ñ">/é/ñ</router-link></li>
<li><router-link to="/é/ñ?t=%25ñ">/é/ñ?t=%ñ</router-link></li>
<li><router-link to="/é/ñ#é">/é/ñ#é</router-link></li>
<li><router-link :to="encodeURI('/é')">/é</router-link></li>
<li><router-link :to="encodeURI('/é/ñ')">/é/ñ</router-link></li>
<li><router-link :to="encodeURI('/é/ñ?t=%ñ')">/é/ñ?t=%ñ</router-link></li>
<li><router-link :to="encodeURI('/é/ñ#é')">/é/ñ#é</router-link></li>
<li><router-link to="/query/A%25">/query/A%</router-link></li>
<li><router-link :to="{ name: 'param', params: { q: 'A%' }}">/query/A% (object)</router-link></li>
<li><router-link to="/query/A%2FE">/query/A%2FE</router-link></li>
Expand Down
11 changes: 2 additions & 9 deletions src/create-matcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { createRoute } from './util/route'
import { fillParams } from './util/params'
import { createRouteMap } from './create-route-map'
import { normalizeLocation } from './util/location'
import { decode } from './util/query'

export type Matcher = {
match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
Expand Down Expand Up @@ -175,14 +176,6 @@ function matchRoute (
path: string,
params: Object
): boolean {
try {
path = decodeURI(path)
} catch (err) {
if (process.env.NODE_ENV !== 'production') {
warn(false, `Error decoding "${path}". Leaving it intact.`)
}
}
const m = path.match(regex)
if (!m) {
Expand All @@ -195,7 +188,7 @@ function matchRoute (
const key = regex.keys[i - 1]
if (key) {
// Fix #1994: using * with props: true generates a param named 0
params[key.name || 'pathMatch'] = m[i]
params[key.name || 'pathMatch'] = typeof m[i] === 'string' ? decode(m[i]) : m[i]
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/create-route-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ function addRouteRecord (
path || name
)} cannot be a ` + `string id. Use an actual component instead.`
)

warn(
// eslint-disable-next-line no-control-regex
!/[^\u0000-\u007F]+/.test(path),
`Route with path "${path}" contains unencoded characters, make sure ` +
`your path is correctly encoded before passing it to the router. Use ` +
`encodeURI to encode static segments of your path.`
)
}

const pathToRegexpOptions: PathToRegexpOptions =
Expand Down
9 changes: 9 additions & 0 deletions test/unit/specs/create-map.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,15 @@ describe('Creating Route Map', function () {
)
})

it('warns about unencoded entities', function () {
process.env.NODE_ENV = 'development'
maps = createRouteMap([{ path: '/é', component: Home }])
expect(console.warn).toHaveBeenCalledTimes(1)
expect(console.warn.calls.argsFor(0)[0]).toMatch(
'vue-router] Route with path "/é"'
)
})

it('in development, throws if path is missing', function () {
process.env.NODE_ENV = 'development'
expect(() => {
Expand Down

0 comments on commit 63c749c

Please sign in to comment.