Skip to content

Commit eb29ddd

Browse files
committed
Handle percent encoding.
1 parent 321dc6d commit eb29ddd

File tree

2 files changed

+46
-9
lines changed

2 files changed

+46
-9
lines changed

lib/resource-mapper.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,27 @@ class ResourceMapper {
2626

2727
// Maps the request for a given resource and representation format to a server file
2828
async mapUrlToFile ({ url, contentType, createIfNotExists }) {
29-
// Split the URL into components
29+
// Determine the full URL
3030
const { pathname, hostname } = typeof url === 'string' ? URL.parse(url) : url
31-
if (pathname.indexOf('/..') >= 0) {
31+
const fullPath = decodeURIComponent(`${this.getBasePath(hostname)}${pathname}`)
32+
if (fullPath.indexOf('/..') >= 0) {
3233
throw new Error('Disallowed /.. segment in URL')
3334
}
3435

36+
// Determine the path and content type
3537
let path
36-
const basePath = this.getBasePath(hostname)
37-
// Create the path for a new file
3838
if (createIfNotExists) {
39-
path = `${basePath}${pathname}`
39+
// Create the path for a new file
40+
path = fullPath
4041
// If the extension is not correct for the content type, append the correct extension
41-
if (getContentType(pathname) !== contentType) {
42+
if (getContentType(path) !== contentType) {
4243
path += contentType in extensions ? `$.${extensions[contentType][0]}` : '$.unknown'
4344
}
4445
// Determine the path of an existing file
4546
} else {
4647
// Read all files in the corresponding folder
47-
const filename = pathname.substr(pathname.lastIndexOf('/') + 1)
48-
const folder = `${basePath}${pathname.substr(0, pathname.length - filename.length)}`
48+
const filename = fullPath.substr(fullPath.lastIndexOf('/') + 1)
49+
const folder = fullPath.substr(0, fullPath.length - filename.length)
4950
const files = await this._readdir(folder)
5051

5152
// Find a file with the same name (minus the dollar extension)
@@ -64,7 +65,7 @@ class ResourceMapper {
6465
async mapFileToUrl ({ path, hostname }) {
6566
// Determine the URL by chopping off everything after the dollar sign
6667
const pathname = removeDollarExtension(path.substring(this._rootPath.length))
67-
const url = `${this.getBaseUrl(hostname)}${pathname}`
68+
const url = `${this.getBaseUrl(hostname)}${encodeURI(pathname)}`
6869
return { url, contentType: getContentType(path) }
6970
}
7071

test/unit/resource-mapper-test.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,29 @@ describe('ResourceMapper', () => {
164164
contentType: 'text/html'
165165
})
166166

167+
itMapsUrl(mapper, 'a URL of an existing file with encoded characters',
168+
{
169+
url: 'http://localhost/space/foo%20bar%20bar.html'
170+
},
171+
[
172+
`${rootPath}space/foo bar bar.html`
173+
],
174+
{
175+
path: `${rootPath}space/foo bar bar.html`,
176+
contentType: 'text/html'
177+
})
178+
179+
itMapsUrl(mapper, 'a URL of a new file with encoded characters',
180+
{
181+
url: 'http://localhost/space%2Ffoo%20bar%20bar.html',
182+
contentType: 'text/html',
183+
createIfNotExists: true
184+
},
185+
{
186+
path: `${rootPath}space/foo bar bar.html`,
187+
contentType: 'text/html'
188+
})
189+
167190
// Security cases
168191

169192
itMapsUrl(mapper, 'a URL with an unknown content type',
@@ -183,6 +206,12 @@ describe('ResourceMapper', () => {
183206
},
184207
new Error('Disallowed /.. segment in URL'))
185208

209+
itMapsUrl(mapper, 'a URL with an encoded /.. path segment',
210+
{
211+
url: 'http://localhost/space%2F..%2Fbar'
212+
},
213+
new Error('Disallowed /.. segment in URL'))
214+
186215
// File to URL mapping
187216

188217
itMapsFile(mapper, 'an HTML file',
@@ -254,6 +283,13 @@ describe('ResourceMapper', () => {
254283
url: 'http://localhost/space/foo',
255284
contentType: 'text/html'
256285
})
286+
287+
itMapsFile(mapper, 'a file with disallowed IRI characters',
288+
{ path: `${rootPath}space/foo bar bar.html` },
289+
{
290+
url: 'http://localhost/space/foo%20bar%20bar.html',
291+
contentType: 'text/html'
292+
})
257293
})
258294

259295
describe('A ResourceMapper instance for a multi-host setup', () => {

0 commit comments

Comments
 (0)