Mholloway has uploaded a new change for review.
https://gerrit.wikimedia.org/r/297344
Change subject: Validate input dates prior to processing
......................................................................
Validate input dates prior to processing
Adds a function to check that dates are valid and in the correct format
before further processing and uses it where applicable.
Bug: T139315
Change-Id: Iaf6d5feac1c1d6b7f8ebc3032af6acd7bc491efe
---
M lib/dateUtil.js
M lib/feed/featured-image.js
M lib/feed/featured.js
M lib/feed/most-read.js
M spec.yaml
M test/lib/dateUtil/date-util-test.js
6 files changed, 136 insertions(+), 49 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/services/mobileapps
refs/changes/44/297344/1
diff --git a/lib/dateUtil.js b/lib/dateUtil.js
index 2ac33ac..1412166 100644
--- a/lib/dateUtil.js
+++ b/lib/dateUtil.js
@@ -40,6 +40,10 @@
return req.params.yyyy + req.params.mm + req.params.dd;
}
+function slashDelimitedDateString(req) {
+ return req.params.yyyy + '/' + req.params.mm + '/' + req.params.dd;
+}
+
function iso8601DateFrom(req) {
return req.params.yyyy + '-' + req.params.mm + '-' + req.params.dd + 'Z';
}
@@ -75,12 +79,43 @@
return req;
}
+// Validates that the input string is a valid date formatted as "yyyy/mm/dd"
+// Cribbed & updated from https://stackoverflow.com/a/6178341
+function validInputFormat(dateString) {
+ if(!/^\d{4}\/\d{2}\/\d{2}$/.test(dateString)) {
+ return false;
+ }
+
+ // Parse the date parts to integers
+ var parts = dateString.split("/");
+ var year = parseInt(parts[0], 10);
+ var month = parseInt(parts[1], 10);
+ var day = parseInt(parts[2], 10);
+
+ // Check the ranges of month and year
+ if(year < 1970 || year > new Date().getUTCFullYear() + 1 || month < 1 ||
month > 12) {
+ return false;
+ }
+
+ var monthLength = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
+
+ // Adjust for leap years
+ if (year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0)) {
+ monthLength[1] = 29;
+ }
+
+ // Check the range of the day
+ return day > 0 && day <= monthLength[month - 1];
+}
+
module.exports = {
formatDateEnglish: formatDateEnglish,
getRequestedDate: getRequestedDate,
dateStringFrom: dateStringFrom,
+ slashDelimitedDateString: slashDelimitedDateString,
iso8601DateFrom: iso8601DateFrom,
formatISODate: formatISODate,
+ validInputFormat: validInputFormat,
pad: pad,
yesterday: yesterday,
ONE_DAY: ONE_DAY
diff --git a/lib/feed/featured-image.js b/lib/feed/featured-image.js
index 7bfbbfe..c59326b 100644
--- a/lib/feed/featured-image.js
+++ b/lib/feed/featured-image.js
@@ -99,6 +99,15 @@
}
function promise(app, req) {
+ var date = dateUtil.slashDelimitedDateString(req);
+ if (!dateUtil.validInputFormat(date)) {
+ throw new HTTPError({
+ status: 400,
+ type: 'bad_request',
+ title: 'Invalid date format.',
+ detail: 'The date format is invalid. Please request a valid date
of format yyyy\/mm\/dd'
+ });
+ }
return requestPictureOfTheDay(app, req.params.domain,
dateUtil.getRequestedDate(req))
.then(function (response) {
mwapi.checkForQueryPagesInResponse(req, response);
diff --git a/lib/feed/featured.js b/lib/feed/featured.js
index b4b9e8c..0e61e55 100644
--- a/lib/feed/featured.js
+++ b/lib/feed/featured.js
@@ -71,6 +71,15 @@
}
function promise(app, req, dontThrow) {
+ var date = dateUtil.slashDelimitedDateString(req);
+ if (!dateUtil.validInputFormat(date)) {
+ throw new HTTPError({
+ status: 400,
+ type: 'bad_request',
+ title: 'Invalid date format.',
+ detail: 'The date format is invalid. Please request a valid date
of format yyyy\/mm\/dd'
+ });
+ }
if (req.params.domain.indexOf('en') !== 0) {
if (dontThrow) {
return {};
diff --git a/lib/feed/most-read.js b/lib/feed/most-read.js
index ba10051..9cc6b4f 100644
--- a/lib/feed/most-read.js
+++ b/lib/feed/most-read.js
@@ -9,7 +9,7 @@
var api = require('../api-util');
var mwapi = require('../mwapi');
var blacklist = require('./blacklist');
-
+var HTTPError = require('../util').HTTPError;
/**
* Construct a list of title strings from the array of good article objects.
@@ -43,6 +43,15 @@
function promise(app, req, dontThrow) {
var goodTitles;
+ var date = dateUtil.slashDelimitedDateString(req);
+ if (!dateUtil.validInputFormat(date)) {
+ throw new HTTPError({
+ status: 400,
+ type: 'bad_request',
+ title: 'Invalid date format.',
+ detail: 'The date format is invalid. Please request a valid date
of format yyyy\/mm\/dd'
+ });
+ }
return getTopPageviews(app, req).then(function (response) {
var queryTitlesList, rankedTitles = response.body
&& response.body.items
diff --git a/spec.yaml b/spec.yaml
index 64d56a6..5d0cc2b 100644
--- a/spec.yaml
+++ b/spec.yaml
@@ -67,31 +67,31 @@
- name: yyyy
in: path
description: "Year the aggregated content is requested for"
- type: integer
+ type: string
required: true
- minimum: 2016
- maximum: 2999
+ minimum: "2016"
+ maximum: "2999"
- name: mm
in: path
description: "Month the aggregated content is requested for"
- type: integer
+ type: string
required: true
- minimum: 01
- maximum: 12
+ minimum: "01"
+ maximum: "12"
- name: dd
in: path
description: "Day of the month the aggregated content is requested
for"
- type: integer
+ type: string
required: true
- minimum: 01
- maximum: 31
+ minimum: "01"
+ maximum: "31"
x-amples:
- title: retrieve aggregated feed content for April 29, 2016
request:
params:
- yyyy: 2016
- mm: 04
- dd: 29
+ yyyy: "2016"
+ mm: "04"
+ dd: "29"
response:
status: 200
headers:
@@ -136,31 +136,31 @@
- name: yyyy
in: path
description: "Year the featured article is requested for"
- type: integer
+ type: string
required: true
- minimum: 2016
- maximum: 2999
+ minimum: "2016"
+ maximum: "2999"
- name: mm
in: path
description: "Month the featured article is requested for"
- type: integer
+ type: string
required: true
- minimum: 1
- maximum: 12
+ minimum: "01"
+ maximum: "12"
- name: dd
in: path
description: "Day of the month the featured article is requested for"
- type: integer
+ type: string
required: true
- minimum: 1
- maximum: 31
+ minimum: "01"
+ maximum: "31"
x-amples:
- title: retrieve title of the featured article for April 29, 2016
request:
params:
- yyyy: 2016
- mm: 4
- dd: 29
+ yyyy: "2016"
+ mm: "04"
+ dd: "29"
response:
status: 200
headers:
@@ -185,31 +185,31 @@
- name: yyyy
in: path
description: "Year the featured image is requested for"
- type: integer
+ type: string
required: true
- minimum: 2016
- maximum: 2999
+ minimum: "2016"
+ maximum: "2999"
- name: mm
in: path
description: "Month the featured image is requested for"
- type: integer
+ type: string
required: true
- minimum: 1
- maximum: 12
+ minimum: "01"
+ maximum: "12"
- name: dd
in: path
description: "Day of the month the featured image is requested for"
- type: integer
+ type: string
required: true
- minimum: 1
- maximum: 31
+ minimum: "01"
+ maximum: "31"
x-amples:
- title: retrieve featured image data for April 29, 2016
request:
params:
- yyyy: 2016
- mm: 4
- dd: 29
+ yyyy: "2016"
+ mm: "04"
+ dd: "29"
response:
status: 200
headers:
@@ -238,31 +238,31 @@
- name: yyyy
in: path
description: "Year the most-read articles are requested for"
- type: integer
+ type: string
required: true
- minimum: 2016
- maximum: 2999
+ minimum: "2016"
+ maximum: "2999"
- name: mm
in: path
description: "Month the most-read articles are requested for"
- type: integer
+ type: string
required: true
- minimum: 01
- maximum: 12
+ minimum: "01"
+ maximum: "12"
- name: dd
in: path
description: "Day of the month the most-read articles are requested
for"
- type: integer
+ type: string
required: true
- minimum: 01
- maximum: 31
+ minimum: "01"
+ maximum: "31"
x-amples:
- title: retrieve the most-read articles for January 1, 2016
request:
params:
- yyyy: '2016'
- mm: '01'
- dd: '01'
+ yyyy: "2016"
+ mm: "01"
+ dd: "01"
response:
status: 200
headers:
diff --git a/test/lib/dateUtil/date-util-test.js
b/test/lib/dateUtil/date-util-test.js
index 85c6adb..6000853 100644
--- a/test/lib/dateUtil/date-util-test.js
+++ b/test/lib/dateUtil/date-util-test.js
@@ -18,4 +18,29 @@
assert.equal(actual.getUTCMonth(), 4 - 1);
assert.equal(actual.getUTCDate(), 15);
});
+
+ it('date format validation should reject invalid formats', function() {
+ assert.deepEqual(dateUtil.validInputFormat('2016/7/4'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/4'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/7/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('00000002016/07/04'),
false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/000000007/04'),
false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/000000004'),
false);
+ assert.deepEqual(dateUtil.validInputFormat('2039/07/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/13/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/34'), false);
+ assert.deepEqual(dateUtil.validInputFormat('0000/07/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/00/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/00'), false);
+ assert.deepEqual(dateUtil.validInputFormat('abcd/07/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/ef/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/gh'), false);
+ assert.deepEqual(dateUtil.validInputFormat('*!@#/07/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/*!/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/@#'), false);
+ assert.deepEqual(dateUtil.validInputFormat('\u00002016/07/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/\u000007/04'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/\u000004'), false);
+ assert.deepEqual(dateUtil.validInputFormat('2016/07/04'), true);
+ })
});
--
To view, visit https://gerrit.wikimedia.org/r/297344
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iaf6d5feac1c1d6b7f8ebc3032af6acd7bc491efe
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/services/mobileapps
Gerrit-Branch: master
Gerrit-Owner: Mholloway <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits