From c0f87172fab63d9410fe1db52fcd2e0ab41bf41c Mon Sep 17 00:00:00 2001
From: ikisialeu <36271792+ikisialeu@users.noreply.github.com>
Date: Mon, 12 Mar 2018 06:24:45 +0300
Subject: [PATCH] fix: validate timezones (#1638)

`Adds a config validation check for timezone, checking it against the list in moment-timezome.js`. Configs with invalid timezones will raise a configuration error.

Closes #1483
---
 lib/config/validation.js                          | 13 ++++++++++++-
 lib/workers/branch/schedule.js                    | 14 +++++++++++++-
 test/config/__snapshots__/validation.spec.js.snap |  4 ++++
 test/config/validation.spec.js                    |  4 +++-
 test/workers/branch/schedule.spec.js              | 14 ++++++++++++++
 5 files changed, 46 insertions(+), 3 deletions(-)

diff --git a/lib/config/validation.js b/lib/config/validation.js
index e0fe44d25e..1186a3ac27 100644
--- a/lib/config/validation.js
+++ b/lib/config/validation.js
@@ -1,6 +1,9 @@
 const semver = require('semver');
 const options = require('./definitions').getOptions();
-const { hasValidSchedule } = require('../workers/branch/schedule');
+const {
+  hasValidSchedule,
+  hasValidTimezone,
+} = require('../workers/branch/schedule');
 
 let optionTypes;
 
@@ -61,6 +64,14 @@ function validateConfig(config) {
             message: `Invalid schedule: \`${errorMessage}\``,
           });
         }
+      } else if (key === 'timezone' && val !== null) {
+        const [validTimezone, errorMessage] = hasValidTimezone(val);
+        if (!validTimezone) {
+          errors.push({
+            depName: 'Configuration Error',
+            message: errorMessage,
+          });
+        }
       } else if (key === 'allowedVersions' && val !== null) {
         if (!semver.validRange(val)) {
           errors.push({
diff --git a/lib/workers/branch/schedule.js b/lib/workers/branch/schedule.js
index cf01df4c05..714de6d3e0 100644
--- a/lib/workers/branch/schedule.js
+++ b/lib/workers/branch/schedule.js
@@ -2,6 +2,7 @@ const later = require('later');
 const moment = require('moment-timezone');
 
 module.exports = {
+  hasValidTimezone,
   hasValidSchedule,
   isScheduledNow,
 };
@@ -10,6 +11,13 @@ function fixShortHours(input) {
   return input.replace(/( \d?\d)((a|p)m)/g, '$1:00$2');
 }
 
+function hasValidTimezone(timezone) {
+  if (!moment.tz.zone(timezone)) {
+    return [false, `Invalid timezone: ${timezone}`];
+  }
+  return [true];
+}
+
 function hasValidSchedule(schedule) {
   let message;
   // check if any of the schedules fail to parse
@@ -69,7 +77,11 @@ function isScheduledNow(config) {
   logger.debug(`now=${now.format()}`);
   // Adjust the time if repo is in a different timezone to renovate
   if (config.timezone) {
-    // TODO: check for validity manually
+    const [validTimezone, error] = hasValidTimezone(config.timezone);
+    if (!validTimezone) {
+      logger.warn(error);
+      return true;
+    }
     now = now.tz(config.timezone);
     logger.debug(`now=${now.format()}`);
   }
diff --git a/test/config/__snapshots__/validation.spec.js.snap b/test/config/__snapshots__/validation.spec.js.snap
index fcc172e25b..bb96c609c0 100644
--- a/test/config/__snapshots__/validation.spec.js.snap
+++ b/test/config/__snapshots__/validation.spec.js.snap
@@ -14,6 +14,10 @@ Array [
     "depName": "Configuration Error",
     "message": "Invalid schedule: \`Schedule \\"every 15 mins every weekday\\" should not specify minutes\`",
   },
+  Object {
+    "depName": "Configuration Error",
+    "message": "Invalid timezone: Asia",
+  },
   Object {
     "depName": "Configuration Error",
     "message": "Configuration option \`labels\` should be a list (Array)",
diff --git a/test/config/validation.spec.js b/test/config/validation.spec.js
index 3c2f725da2..bee1d24067 100644
--- a/test/config/validation.spec.js
+++ b/test/config/validation.spec.js
@@ -6,6 +6,7 @@ describe('config/validation', () => {
       const config = {
         foo: 1,
         schedule: ['after 5pm'],
+        timezone: 'Asia/Singapore',
         prBody: 'some-body',
         lockFileMaintenance: {
           bar: 2,
@@ -21,6 +22,7 @@ describe('config/validation', () => {
         allowedVersions: 'foo',
         enabled: 1,
         schedule: ['every 15 mins every weekday'],
+        timezone: 'Asia',
         labels: 5,
         semanticCommitType: 7,
         lockFileMaintenance: false,
@@ -32,7 +34,7 @@ describe('config/validation', () => {
       };
       const { warnings, errors } = configValidation.validateConfig(config);
       expect(warnings).toHaveLength(0);
-      expect(errors).toHaveLength(7);
+      expect(errors).toHaveLength(8);
       expect(errors).toMatchSnapshot();
     });
   });
diff --git a/test/workers/branch/schedule.spec.js b/test/workers/branch/schedule.spec.js
index 9bd78798ad..1f55a95435 100644
--- a/test/workers/branch/schedule.spec.js
+++ b/test/workers/branch/schedule.spec.js
@@ -2,6 +2,14 @@ const mockDate = require('mockdate');
 const schedule = require('../../../lib/workers/branch/schedule');
 
 describe('workers/branch/schedule', () => {
+  describe('hasValidTimezone(schedule)', () => {
+    it('returns false for invalid timezone', () => {
+      expect(schedule.hasValidTimezone('Asia')[0]).toBe(false);
+    });
+    it('returns true for valid timezone', () => {
+      expect(schedule.hasValidTimezone('Asia/Singapore')[0]).toBe(true);
+    });
+  });
   describe('hasValidSchedule(schedule)', () => {
     beforeEach(() => {
       jest.resetAllMocks();
@@ -91,6 +99,12 @@ describe('workers/branch/schedule', () => {
       const res = schedule.isScheduledNow(config);
       expect(res).toBe(true);
     });
+    it('returns true if invalid timezone', () => {
+      config.schedule = ['after 4:00pm'];
+      config.timezone = 'Asia';
+      const res = schedule.isScheduledNow(config);
+      expect(res).toBe(true);
+    });
     it('supports before hours true', () => {
       config.schedule = ['before 4:00pm'];
       const res = schedule.isScheduledNow(config);
-- 
GitLab