client/general: improve URL escaping

Specifically, cater for /, + and % in URL components.
This commit is contained in:
rr- 2016-09-04 01:25:19 +02:00
parent a22fe306d1
commit 7fa8593b0a
15 changed files with 39 additions and 26 deletions

View file

@ -151,6 +151,7 @@ the one in the `config.yaml`, so that client knows how to access the backend!
server {
listen 80;
server_name great.dude;
merge_slashes off; # to support post tags such as ///
location ~ ^/api$ {
return 302 /api/;

View file

@ -63,12 +63,15 @@ class Api extends events.EventTarget {
_process(url, requestFactory, data, files, options) {
options = options || {};
const fullUrl = this._getFullUrl(url);
const [fullUrl, query] = this._getFullUrl(url);
return new Promise((resolve, reject) => {
if (!options.noProgress) {
nprogress.start();
}
let req = requestFactory(fullUrl);
if (query) {
req.query(query);
}
if (data) {
req.attach('metadata', new Blob([JSON.stringify(data)]));
}
@ -176,8 +179,12 @@ class Api extends events.EventTarget {
}
_getFullUrl(url) {
return (config.apiUrl + '/' + encodeURI(url))
.replace(/([^:])\/+/g, '$1/');
const fullUrl =
(config.apiUrl + '/' + url).replace(/([^:])\/+/g, '$1/');
const matches = fullUrl.match(/^([^?]*)\??(.*)$/);
const baseUrl = matches[1];
const request = matches[2];
return [baseUrl, request];
}
}

View file

@ -32,7 +32,7 @@ class CommentsController {
},
requestPage: page => {
return PostList.search(
'sort:comment-date+comment-count-min:1', page, 10, fields);
'sort:comment-date comment-count-min:1', page, 10, fields);
},
pageRenderer: pageCtx => {
Object.assign(pageCtx, {

View file

@ -255,7 +255,7 @@ class PostController {
}
module.exports = router => {
router.enter('/post/:id/edit/:parameters?',
router.enter('/post/:id/edit/:parameters(.*)?',
(ctx, next) => { misc.parseUrlParametersRoute(ctx, next); },
(ctx, next) => {
// restore parameters from history state
@ -265,7 +265,7 @@ module.exports = router => {
ctx.controller = new PostController(ctx.parameters.id, true, ctx);
});
router.enter(
'/post/:id/:parameters?',
'/post/:id/:parameters(.*)?',
(ctx, next) => { misc.parseUrlParametersRoute(ctx, next); },
(ctx, next) => {
// restore parameters from history state

View file

@ -120,7 +120,7 @@ class PostListController {
module.exports = router => {
router.enter(
'/posts/:parameters?',
'/posts/:parameters(.*)?',
(ctx, next) => { misc.parseUrlParametersRoute(ctx, next); },
(ctx, next) => { ctx.controller = new PostListController(ctx); });
};

View file

@ -121,16 +121,16 @@ class TagController {
}
module.exports = router => {
router.enter('/tag/:name', (ctx, next) => {
ctx.controller = new TagController(ctx, 'summary');
});
router.enter('/tag/:name/edit', (ctx, next) => {
router.enter('/tag/:name(.+?)/edit', (ctx, next) => {
ctx.controller = new TagController(ctx, 'edit');
});
router.enter('/tag/:name/merge', (ctx, next) => {
router.enter('/tag/:name(.+?)/merge', (ctx, next) => {
ctx.controller = new TagController(ctx, 'merge');
});
router.enter('/tag/:name/delete', (ctx, next) => {
router.enter('/tag/:name(.+?)/delete', (ctx, next) => {
ctx.controller = new TagController(ctx, 'delete');
});
router.enter('/tag/:name(.+)', (ctx, next) => {
ctx.controller = new TagController(ctx, 'summary');
});
};

View file

@ -75,7 +75,7 @@ class TagListController {
module.exports = router => {
router.enter(
'/tags/:parameters?',
'/tags/:parameters(.*)?',
(ctx, next) => { misc.parseUrlParametersRoute(ctx, next); },
(ctx, next) => { ctx.controller = new TagListController(ctx); });
};

View file

@ -69,7 +69,7 @@ class UserListController {
module.exports = router => {
router.enter(
'/users/:parameters?',
'/users/:parameters(.*)?',
(ctx, next) => { misc.parseUrlParametersRoute(ctx, next); },
(ctx, next) => { ctx.controller = new UserListController(ctx); });
};

View file

@ -6,7 +6,10 @@ const Post = require('./post.js');
class PostList extends AbstractList {
static getAround(id, searchQuery) {
return api.get(`/post/${id}/around?fields=id&query=${searchQuery}`)
const url =
`/post/${id}/around?fields=id` +
`&query=${encodeURIComponent(searchQuery)}`;
return api.get(url)
.then(response => {
return Promise.resolve(response);
}).catch(response => {
@ -16,7 +19,7 @@ class PostList extends AbstractList {
static search(text, page, pageSize, fields) {
const url =
`/posts/?query=${text}` +
`/posts/?query=${encodeURIComponent(text)}` +
`&page=${page}` +
`&pageSize=${pageSize}` +
`&fields=${fields.join(',')}`;

View file

@ -7,7 +7,7 @@ const Snapshot = require('./snapshot.js');
class SnapshotList extends AbstractList {
static search(text, page, pageSize) {
const url =
`/snapshots/?query=${text}` +
`/snapshots/?query=${encodeURIComponent(text)}` +
`&page=${page}` +
`&pageSize=${pageSize}`;
return api.get(url).then(response => {

View file

@ -7,7 +7,7 @@ const Tag = require('./tag.js');
class TagList extends AbstractList {
static search(text, page, pageSize, fields) {
const url =
`/tags/?query=${text}` +
`/tags/?query=${encodeURIComponent(text)}` +
`&page=${page}` +
`&pageSize=${pageSize}` +
`&fields=${fields.join(',')}`;

View file

@ -6,7 +6,9 @@ const User = require('./user.js');
class UserList extends AbstractList {
static search(text, page) {
const url = `/users/?query=${text}&page=${page}&pageSize=30`;
const url =
`/users/?query=${encodeURIComponent(text)}` +
`&page=${page}&pageSize=30`;
return api.get(url).then(response => {
return Promise.resolve(Object.assign(
{},

View file

@ -75,7 +75,7 @@ class Route {
const keys = this.keys;
const qsIndex = path.indexOf('?');
const pathname = ~qsIndex ? path.slice(0, qsIndex) : path;
const m = this.regexp.exec(decodeURIComponent(pathname));
const m = this.regexp.exec(pathname);
if (!m) {
return false;

View file

@ -165,7 +165,7 @@ function formatUrlParameters(dict) {
continue;
}
if (value) {
result.push(`${key}=${value}`);
result.push(`${key}=${encodeURIComponent(value)}`);
}
}
return result.join(';');

View file

@ -56,14 +56,14 @@ def create_tag(ctx, _params=None):
return _serialize(ctx, tag)
@routes.get('/tag/(?P<tag_name>[^/]+)/?')
@routes.get('/tag/(?P<tag_name>.+)')
def get_tag(ctx, params):
auth.verify_privilege(ctx.user, 'tags:view')
tag = tags.get_tag_by_name(params['tag_name'])
return _serialize(ctx, tag)
@routes.put('/tag/(?P<tag_name>[^/]+)/?')
@routes.put('/tag/(?P<tag_name>.+)')
def update_tag(ctx, params):
tag = tags.get_tag_by_name(params['tag_name'])
versions.verify_version(tag, ctx)
@ -97,7 +97,7 @@ def update_tag(ctx, params):
return _serialize(ctx, tag)
@routes.delete('/tag/(?P<tag_name>[^/]+)/?')
@routes.delete('/tag/(?P<tag_name>.+)')
def delete_tag(ctx, params):
tag = tags.get_tag_by_name(params['tag_name'])
versions.verify_version(tag, ctx)
@ -126,7 +126,7 @@ def merge_tags(ctx, _params=None):
return _serialize(ctx, target_tag)
@routes.get('/tag-siblings/(?P<tag_name>[^/]+)/?')
@routes.get('/tag-siblings/(?P<tag_name>.+)')
def get_tag_siblings(ctx, params):
auth.verify_privilege(ctx.user, 'tags:view')
tag = tags.get_tag_by_name(params['tag_name'])