jenkins-bot has submitted this change and it was merged.
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, 125 insertions(+), 50 deletions(-)
Approvals:
BearND: Looks good to me, approved
jenkins-bot: Verified
diff --git a/lib/dateUtil.js b/lib/dateUtil.js
index f74b042..ea4f1a9 100644
--- a/lib/dateUtil.js
+++ b/lib/dateUtil.js
@@ -1,6 +1,7 @@
'use strict';
var sUtil = require('../lib/util');
+var HTTPError = sUtil.HTTPError;
var ONE_DAY = 86400000;
@@ -40,6 +41,10 @@
return req.params.yyyy + req.params.mm + req.params.dd;
}
+function hyphenDelimitedDateString(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';
}
@@ -66,12 +71,50 @@
'-' + pad(date.getUTCDate());
}
+//Validate the input date by checking whether UTC-format constructed from the
+//input components matches the values actually provided.
+function isValidDate(dateString, year, month, day) {
+ return new Date(Date.UTC(year, month-1,
day)).toISOString().split('T').shift() === dateString;
+}
+
+//Check that @param year is in the interval (2001 <= year <= (current year +
1))
+function isWithinBounds(year) {
+ return year >= 2001 && year <= new Date().getUTCFullYear() + 1;
+}
+
+function throwDateError() {
+ throw new HTTPError({
+ status: 404,
+ type: 'not_found',
+ title: 'Not found.',
+ detail: 'Invalid date provided. Please request a valid date of format
yyyy\/mm\/dd'
+ });
+}
+
+// Validates that the input string is a valid date in the expected format
+function validate(dateString) {
+ var parts = dateString.split("-");
+ var year = parseInt(parts[0], 10);
+ var month = parseInt(parts[1], 10);
+ var day = parseInt(parts[2], 10);
+
+ try {
+ if (!(isValidDate(dateString, year, month, day) &&
isWithinBounds(year))) {
+ throwDateError();
+ }
+ } catch (error) {
+ throwDateError();
+ }
+}
+
module.exports = {
formatDateEnglish: formatDateEnglish,
getRequestedDate: getRequestedDate,
dateStringFrom: dateStringFrom,
+ hyphenDelimitedDateString: hyphenDelimitedDateString,
iso8601DateFrom: iso8601DateFrom,
formatISODate: formatISODate,
+ validate: validate,
pad: pad,
ONE_DAY: ONE_DAY
};
diff --git a/lib/feed/featured-image.js b/lib/feed/featured-image.js
index a0121e4..fc46578 100644
--- a/lib/feed/featured-image.js
+++ b/lib/feed/featured-image.js
@@ -95,6 +95,8 @@
}
function promise(app, req, dontThrow) {
+ var date = dateUtil.hyphenDelimitedDateString(req);
+ dateUtil.validate(date);
return requestPictureOfTheDay(app, req.params.domain,
dateUtil.getRequestedDate(req), dontThrow)
.then(function (response) {
mwapi.checkForQueryPagesInResponse(req, response);
diff --git a/lib/feed/featured.js b/lib/feed/featured.js
index b4b9e8c..2be2b3c 100644
--- a/lib/feed/featured.js
+++ b/lib/feed/featured.js
@@ -71,6 +71,8 @@
}
function promise(app, req, dontThrow) {
+ var date = dateUtil.hyphenDelimitedDateString(req);
+ dateUtil.validate(date);
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 5a9bf44..a23e513 100644
--- a/lib/feed/most-read.js
+++ b/lib/feed/most-read.js
@@ -47,7 +47,9 @@
function promise(app, req, aggregated) {
var goodTitles;
- return getTopPageviews(app, req, aggregated).then(function (response) {
+ var date = dateUtil.hyphenDelimitedDateString(req);
+ dateUtil.validate(date);
+ return getTopPageviews(app, req).then(function (response) {
var queryTitlesList, rankedTitles = response.body
&& response.body.items
&& response.body.items[0]
diff --git a/spec.yaml b/spec.yaml
index f9b85aa..a0b8442 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:
@@ -147,31 +147,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:
@@ -196,31 +196,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:
@@ -251,31 +251,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..bcc1742 100644
--- a/test/lib/dateUtil/date-util-test.js
+++ b/test/lib/dateUtil/date-util-test.js
@@ -2,11 +2,12 @@
var assert = require('../../utils/assert.js');
var dateUtil = require('../../../lib/dateUtil');
+var HTTPError = require('../../../lib/util').HTTPError;
describe('lib:dateUtil', function() {
this.timeout(20000);
- it('getRequestedDate(2016/04/15) should return a valid Date object',
function() {
+ it('getRequestedDate(2016-04-15) should return a valid Date object',
function() {
var actual = dateUtil.getRequestedDate({
params: {
yyyy: 2016,
@@ -18,4 +19,29 @@
assert.equal(actual.getUTCMonth(), 4 - 1);
assert.equal(actual.getUTCDate(), 15);
});
+
+ it('date format validation should reject invalid formats', function() {
+ assert.throws(function() { dateUtil.validate('2016-7-4'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-07-4'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-7-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('00000002016-07-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-000000007-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-07-000000004'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2039-07-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-13-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-07-34'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('0000-07-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-00-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-07-00'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('abcd-07-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-ef-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-07-gh'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('*!@#-07-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-*!-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-07-@#'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('\u00002016-07-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-\u000007-04'); },
HTTPError);
+ assert.throws(function() { dateUtil.validate('2016-07-\u000004'); },
HTTPError);
+ assert.doesNotThrow(function() { dateUtil.validate('2016-07-04'); });
+ })
});
--
To view, visit https://gerrit.wikimedia.org/r/297344
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: Iaf6d5feac1c1d6b7f8ebc3032af6acd7bc491efe
Gerrit-PatchSet: 5
Gerrit-Project: mediawiki/services/mobileapps
Gerrit-Branch: master
Gerrit-Owner: Mholloway <[email protected]>
Gerrit-Reviewer: BearND <[email protected]>
Gerrit-Reviewer: Dbrant <[email protected]>
Gerrit-Reviewer: Fjalapeno <[email protected]>
Gerrit-Reviewer: GWicke <[email protected]>
Gerrit-Reviewer: Jhernandez <[email protected]>
Gerrit-Reviewer: Mholloway <[email protected]>
Gerrit-Reviewer: Mhurd <[email protected]>
Gerrit-Reviewer: Mobrovac <[email protected]>
Gerrit-Reviewer: Niedzielski <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits