From c76222fd53fd04c101f2f76da64a5def274b2593 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@keylocation.sg>
Date: Wed, 2 Aug 2017 07:52:28 +0200
Subject: [PATCH] fix: Validate array objects in configuration (#575)

---
 docs/configuration.md                          | 18 +++++++++++++++++-
 lib/config/definitions.js                      | 17 +++++++++++++++++
 lib/config/validation.js                       | 15 ++++++++++++++-
 .../__snapshots__/validation.spec.js.snap      |  4 ++++
 test/config/validation.spec.js                 |  7 ++++++-
 5 files changed, 58 insertions(+), 3 deletions(-)

diff --git a/docs/configuration.md b/docs/configuration.md
index 0b28c3763f..baaefc4fba 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -317,6 +317,22 @@ Obviously, you can't set repository or package file location with this method.
   <td></td>
   <td><td>
 </tr>
+<tr>
+  <td>`packageName`</td>
+  <td>A package name to match. Valid only within `packages` object</td>
+  <td>string</td>
+  <td><pre>null</pre></td>
+  <td></td>
+  <td><td>
+</tr>
+<tr>
+  <td>`packagePattern`</td>
+  <td>A package name pattern to match. Valid only within `packages` object.</td>
+  <td>string</td>
+  <td><pre>null</pre></td>
+  <td></td>
+  <td><td>
+</tr>
 <tr>
   <td>`pinVersions`</td>
   <td>Convert ranged versions in package.json to pinned versions</td>
@@ -385,7 +401,7 @@ Obviously, you can't set repository or package file location with this method.
   <td>`patch`</td>
   <td>Configuration to apply when an update type is patch. Only applies if `separatePatchReleases` is set to true</td>
   <td>json</td>
-  <td><pre>{"branchName": "renovate/{{depName}}-{{newVersionMajor}}.{newVersionMinor}}.x"}</pre></td>
+  <td><pre>{"branchName": "renovate/{{depName}}-{{newVersionMajor}}.{{newVersionMinor}}.x"}</pre></td>
   <td>`RENOVATE_PATCH`</td>
   <td><td>
 </tr>
diff --git a/lib/config/definitions.js b/lib/config/definitions.js
index 554240c8be..139e41bf98 100644
--- a/lib/config/definitions.js
+++ b/lib/config/definitions.js
@@ -170,6 +170,23 @@ const options = [
     cli: false,
     env: false,
   },
+  {
+    name: 'packageName',
+    description: 'A package name to match. Valid only within `packages` object',
+    type: 'string',
+    stage: 'depType',
+    cli: false,
+    env: false,
+  },
+  {
+    name: 'packagePattern',
+    description:
+      'A package name pattern to match. Valid only within `packages` object.',
+    type: 'string',
+    stage: 'depType',
+    cli: false,
+    env: false,
+  },
   // Version behaviour
   {
     name: 'pinVersions',
diff --git a/lib/config/validation.js b/lib/config/validation.js
index 4d4a38b635..54063f1c11 100644
--- a/lib/config/validation.js
+++ b/lib/config/validation.js
@@ -28,6 +28,10 @@ function validateConfig(config) {
     return Object.prototype.toString.call(obj) === '[object Object]';
   }
 
+  function isString(val) {
+    return typeof val === 'string' || val instanceof String;
+  }
+
   const foundDeprecated = [];
   for (const key of Object.keys(config)) {
     let val = config[key];
@@ -60,9 +64,18 @@ function validateConfig(config) {
               depName: 'Configuration Error',
               message: `Configuration option \`${key}\` should be a list (Array)`,
             });
+          } else {
+            // eslint-disable-next-line no-loop-func
+            val.forEach(subval => {
+              if (isObject(subval)) {
+                const subValidation = module.exports.validateConfig(subval);
+                warnings = warnings.concat(subValidation.warnings);
+                errors = errors.concat(subValidation.errors);
+              }
+            });
           }
         } else if (type === 'string') {
-          if (!(typeof val === 'string' || val instanceof String)) {
+          if (!isString(val)) {
             errors.push({
               depName: 'Configuration Error',
               message: `Configuration option \`${key}\` should be a string`,
diff --git a/test/config/__snapshots__/validation.spec.js.snap b/test/config/__snapshots__/validation.spec.js.snap
index e893793b75..17be67ac69 100644
--- a/test/config/__snapshots__/validation.spec.js.snap
+++ b/test/config/__snapshots__/validation.spec.js.snap
@@ -22,6 +22,10 @@ Array [
     "depName": "Configuration Error",
     "message": "Configuration option \`lockFileMaintenance\` should be a json object",
   },
+  Object {
+    "depName": "Configuration Error",
+    "message": "Invalid configuration option: \`foo\`",
+  },
 ]
 `;
 
diff --git a/test/config/validation.spec.js b/test/config/validation.spec.js
index 1c0db1422c..2e99501e08 100644
--- a/test/config/validation.spec.js
+++ b/test/config/validation.spec.js
@@ -23,10 +23,15 @@ describe('config/validation', () => {
         semanticPrefix: 7,
         githubAppId: 'none',
         lockFileMaintenance: false,
+        packages: [
+          {
+            foo: 1,
+          },
+        ],
       };
       const { warnings, errors } = configValidation.validateConfig(config);
       expect(warnings).toHaveLength(0);
-      expect(errors).toHaveLength(5);
+      expect(errors).toHaveLength(6);
       expect(errors).toMatchSnapshot();
     });
   });
-- 
GitLab