lgtm
This commit is contained in:
parent
a08b5d2ae0
commit
b98f0e3904
38 changed files with 3732 additions and 483 deletions
|
|
@ -8,11 +8,27 @@ export interface ValidatedScreenshotRequest {
|
|||
}
|
||||
|
||||
const MAX_REPEATED_PARAMS = 40;
|
||||
const MAX_QUERY_KEYS = 80;
|
||||
const MAX_VALUE_LENGTH = 500;
|
||||
const NUMERIC_RE = /^-?(?:\d+|\d*\.\d+)$/;
|
||||
const PATH_RE = /^\/(?:invite\/[A-Za-z0-9]{1,20})?$/;
|
||||
const QUERY_KEY_RE = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
||||
const SAFE_VALUE_RE = /^[^\u0000-\u001f\u007f]+$/;
|
||||
const REPEATED_KEYS = ['filter', 'school', 'crime', 'voteShare', 'ethnicity', 'poi', 'tt'] as const;
|
||||
const REPEATED_KEYS = [
|
||||
'filter',
|
||||
'school',
|
||||
'crime',
|
||||
'voteShare',
|
||||
'ethnicity',
|
||||
'amenityDistance',
|
||||
'transportDistance',
|
||||
'amenityCount2km',
|
||||
'amenityCount5km',
|
||||
'poi',
|
||||
'tt',
|
||||
] as const;
|
||||
const PASSTHROUGH_SINGLE_KEYS = ['share', 'pc'] as const;
|
||||
const RESERVED_QUERY_KEYS = new Set(['path', 'screenshot', '_auth']);
|
||||
|
||||
type Query = Record<string, unknown>;
|
||||
|
||||
|
|
@ -56,6 +72,20 @@ function assertSafeValue(key: string, value: string): void {
|
|||
}
|
||||
}
|
||||
|
||||
function assertSafeKey(key: string): void {
|
||||
if (!QUERY_KEY_RE.test(key)) {
|
||||
validationError(`${key} is invalid`);
|
||||
}
|
||||
}
|
||||
|
||||
function appendSafeValues(qs: URLSearchParams, query: Query, key: string): void {
|
||||
assertSafeKey(key);
|
||||
for (const value of repeatedStrings(query, key)) {
|
||||
assertSafeValue(key, value);
|
||||
qs.append(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
function appendBoundedNumber(
|
||||
qs: URLSearchParams,
|
||||
query: Query,
|
||||
|
|
@ -76,7 +106,22 @@ function appendBoundedNumber(
|
|||
}
|
||||
|
||||
export function buildScreenshotRequest(query: Query): ValidatedScreenshotRequest {
|
||||
const queryKeys = Object.keys(query);
|
||||
if (queryKeys.length > MAX_QUERY_KEYS) {
|
||||
validationError('query has too many parameters');
|
||||
}
|
||||
|
||||
const qs = new URLSearchParams();
|
||||
const handledKeys = new Set<string>([
|
||||
'lat',
|
||||
'lon',
|
||||
'zoom',
|
||||
'tab',
|
||||
'og',
|
||||
'path',
|
||||
...PASSTHROUGH_SINGLE_KEYS,
|
||||
...REPEATED_KEYS,
|
||||
]);
|
||||
|
||||
appendBoundedNumber(qs, query, 'lat', -90, 90);
|
||||
appendBoundedNumber(qs, query, 'lon', -180, 180);
|
||||
|
|
@ -98,11 +143,23 @@ export function buildScreenshotRequest(query: Query): ValidatedScreenshotRequest
|
|||
qs.set('og', og);
|
||||
}
|
||||
|
||||
for (const key of PASSTHROUGH_SINGLE_KEYS) {
|
||||
const value = firstString(query, key);
|
||||
if (value == null) continue;
|
||||
assertSafeValue(key, value);
|
||||
qs.set(key, value);
|
||||
}
|
||||
|
||||
for (const key of REPEATED_KEYS) {
|
||||
for (const value of repeatedStrings(query, key)) {
|
||||
assertSafeValue(key, value);
|
||||
qs.append(key, value);
|
||||
appendSafeValues(qs, query, key);
|
||||
}
|
||||
|
||||
for (const key of queryKeys) {
|
||||
if (handledKeys.has(key)) continue;
|
||||
if (RESERVED_QUERY_KEYS.has(key)) {
|
||||
validationError(`${key} is reserved`);
|
||||
}
|
||||
appendSafeValues(qs, query, key);
|
||||
}
|
||||
|
||||
const pagePath = firstString(query, 'path') ?? '/';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue