Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: disable semicolon separator by default #336

Merged
merged 3 commits into from
Dec 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ router.on('GET', '/', (req, res, params, store, searchParams) => {
router.lookup({ method: 'GET', url: '/?foo=bar&baz=faz' }, null)
```

According to [RFC3986](https://www.rfc-editor.org/rfc/rfc3986#section-3.4), find-my-way separates path and query string with `?` character. But earlier versions also used `;` as delimiter character. To support this behaviour, add the `useSemicolonDelimiter` option to `true`:

```js
const router = require('find-my-way')({
useSemicolonDelimiter: true
})
```

You can assign a `buildPrettyMeta` function to sanitize a route's `store` object to use with the `prettyPrint` functions. This function should accept a single object and return an object.

```js
Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ function Router (opts) {
this.maxParamLength = opts.maxParamLength || 100
this.allowUnsafeRegex = opts.allowUnsafeRegex || false
this.constrainer = new Constrainer(opts.constraints)
this.useSemicolonDelimiter = opts.useSemicolonDelimiter || false
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't forget to set it true in the Fastify.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this option should be disabled in Fastify as well, but I don't know the best way to do it: leave the default false here and open issue/PR to update dependencies in Fastify, or make it true here and send PR to Fastify with the option disabled

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would leave the false here, because it's a correct behavior and we can release a new major of fmw at any moment. In current version of Fastify it should be set to true by default, because otherwise it's a breaking change.

About changing it to false in the fastify in a future release. It's better to open an issue about this in fastify, but IMHO it never would be set to true by defauld in the Fastify, because it will break too many apps. It just doesn't worth it even if it's true according to the spec.


this.routes = []
this.trees = {}
Expand Down Expand Up @@ -419,7 +420,7 @@ Router.prototype.find = function find (method, path, derivedConstraints) {
let shouldDecodeParam

try {
sanitizedUrl = safeDecodeURI(path)
sanitizedUrl = safeDecodeURI(path, this.useSemicolonDelimiter)
path = sanitizedUrl.path
querystring = sanitizedUrl.querystring
shouldDecodeParam = sanitizedUrl.shouldDecodeParam
Expand Down
6 changes: 3 additions & 3 deletions lib/url-sanitizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function decodeComponentChar (highCharCode, lowCharCode) {
return null
}

function safeDecodeURI (path) {
function safeDecodeURI (path, useSemicolonDelimiter) {
let shouldDecode = false
let shouldDecodeParam = false

Expand All @@ -61,8 +61,8 @@ function safeDecodeURI (path) {
}
// Some systems do not follow RFC and separate the path and query
// string with a `;` character (code 59), e.g. `/foo;jsessionid=123456`.
// Thus, we need to split on `;` as well as `?` and `#`.
} else if (charCode === 63 || charCode === 59 || charCode === 35) {
// Thus, we need to split on `;` as well as `?` and `#` if the useSemicolonDelimiter option is enabled.
} else if (charCode === 63 || charCode === 35 || (charCode === 59 && useSemicolonDelimiter)) {
querystring = path.slice(i + 1)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is any perf difference (I doubt), you can move the useSemicolonDelimiteradditional check under the charCode checking if. That would remove it from a hot path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, given that the ; symbol is rare, that's correct. I fixed

path = path.slice(0, i)
break
Expand Down
18 changes: 16 additions & 2 deletions test/querystring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ test('should sanitize the url - hash', t => {
findMyWay.lookup({ method: 'GET', url: '/test#hello', headers: {} }, null)
})

test('handles path and query separated by ;', t => {
test('handles path and query separated by ; with useSemicolonDelimiter enabled', t => {
t.plan(2)
const findMyWay = FindMyWay()
const findMyWay = FindMyWay({
useSemicolonDelimiter: true
})

findMyWay.on('GET', '/test', (req, res, params, store, query) => {
t.same(query, { jsessionid: '123456' })
Expand All @@ -39,3 +41,15 @@ test('handles path and query separated by ;', t => {

findMyWay.lookup({ method: 'GET', url: '/test;jsessionid=123456', headers: {} }, null)
})

test('handles path and query separated by ? using ; in the path', t => {
t.plan(2)
const findMyWay = FindMyWay()

findMyWay.on('GET', '/test;jsessionid=123456', (req, res, params, store, query) => {
t.same(query, { foo: 'bar' })
t.ok('inside the handler')
})

findMyWay.lookup({ method: 'GET', url: '/test;jsessionid=123456?foo=bar', headers: {} }, null)
})