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

Reply via email to