From f6cf6fa33de943a14734250524ac2070e31c0a08 Mon Sep 17 00:00:00 2001
From: eduardo aleixo <eh-am@users.noreply.github.com>
Date: Mon, 11 Jan 2021 07:21:51 -0300
Subject: [PATCH] fix(helm-requirements): validate fields (#8227)

* refactor(helm-requirements): harden validation of requirements.yaml file

check for the presence of name, version and repository fields

* refactor(helm-requirements): harden validation of requirements.yaml file

check for the presence of name, version and repository fields

* refactor(helm-requirements): skip fields in order -> name, version, repository

* chore(helm-requirements): fix linting rules

* chore(helm-requirements): rename https://kubernetes-charts.storage.googleapis.com to https://charts.helm.sh/stable

Co-authored-by: Rhys Arkins <rhys@arkins.net>
---
 .../__snapshots__/extract.spec.ts.snap        |   6 +-
 lib/manager/helm-requirements/extract.spec.ts | 143 ++++++++++++++++--
 lib/manager/helm-requirements/extract.ts      |  54 ++++---
 3 files changed, 166 insertions(+), 37 deletions(-)

diff --git a/lib/manager/helm-requirements/__snapshots__/extract.spec.ts.snap b/lib/manager/helm-requirements/__snapshots__/extract.spec.ts.snap
index d0924ea3f8..50e5657e88 100644
--- a/lib/manager/helm-requirements/__snapshots__/extract.spec.ts.snap
+++ b/lib/manager/helm-requirements/__snapshots__/extract.spec.ts.snap
@@ -8,14 +8,14 @@ Object {
       "currentValue": "0.9.0",
       "depName": "redis",
       "registryUrls": Array [
-        "https://kubernetes-charts.storage.googleapis.com/",
+        "https://charts.helm.sh/stable/",
       ],
     },
     Object {
       "currentValue": "0.8.1",
       "depName": "postgresql",
       "registryUrls": Array [
-        "https://kubernetes-charts.storage.googleapis.com/",
+        "https://charts.helm.sh/stable/",
       ],
     },
   ],
@@ -74,7 +74,7 @@ Object {
       "currentValue": "0.9.0",
       "depName": "redis",
       "registryUrls": Array [
-        "https://kubernetes-charts.storage.googleapis.com/",
+        "https://charts.helm.sh/stable/",
       ],
     },
     Object {
diff --git a/lib/manager/helm-requirements/extract.spec.ts b/lib/manager/helm-requirements/extract.spec.ts
index ec06ca950e..d8487fe2a5 100644
--- a/lib/manager/helm-requirements/extract.spec.ts
+++ b/lib/manager/helm-requirements/extract.spec.ts
@@ -1,4 +1,5 @@
 import { fs } from '../../../test/util';
+import { SkipReason } from '../../types';
 import { extractPackageFile } from './extract';
 
 jest.mock('../../util/fs');
@@ -31,7 +32,7 @@ describe('lib/manager/helm-requirements/extract', () => {
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile(content, fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).not.toBeNull();
@@ -50,15 +51,15 @@ describe('lib/manager/helm-requirements/extract', () => {
       dependencies:
         - name: redis
           version: 0.9.0
-          repository: https://kubernetes-charts.storage.googleapis.com/
+          repository: https://charts.helm.sh/stable/
         - name: postgresql
           version: 0.8.1
-          repository: https://kubernetes-charts.storage.googleapis.com/
+          repository: https://charts.helm.sh/stable/
       `;
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile(content, fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).not.toBeNull();
@@ -74,7 +75,7 @@ describe('lib/manager/helm-requirements/extract', () => {
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile('', fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).toBeNull();
@@ -113,15 +114,15 @@ describe('lib/manager/helm-requirements/extract', () => {
       dependencies:
         - name: redis
           version: 0.9.0
-          repository: https://kubernetes-charts.storage.googleapis.com/
+          repository: https://charts.helm.sh/stable/
         - name: postgresql
           version: 0.8.1
-          repository: https://kubernetes-charts.storage.googleapis.com/
+          repository: https://charts.helm.sh/stable/
       `;
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile(content, fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).toBeNull();
@@ -138,7 +139,7 @@ describe('lib/manager/helm-requirements/extract', () => {
       dependencies:
         - name: redis
           version: 0.9.0
-          repository: https://kubernetes-charts.storage.googleapis.com/
+          repository: https://charts.helm.sh/stable/
         - name: postgresql
           version: 0.8.1
           repository: file:///some/local/path/
@@ -146,7 +147,7 @@ describe('lib/manager/helm-requirements/extract', () => {
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile(content, fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).not.toBeNull();
@@ -166,7 +167,7 @@ describe('lib/manager/helm-requirements/extract', () => {
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile(content, fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).toBeNull();
@@ -187,7 +188,7 @@ describe('lib/manager/helm-requirements/extract', () => {
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile(content, fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).toBeNull();
@@ -197,10 +198,126 @@ describe('lib/manager/helm-requirements/extract', () => {
       const fileName = 'requirements.yaml';
       const result = await extractPackageFile(content, fileName, {
         aliases: {
-          stable: 'https://kubernetes-charts.storage.googleapis.com/',
+          stable: 'https://charts.helm.sh/stable/',
         },
       });
       expect(result).toBeNull();
     });
+
+    describe.each([
+      {
+        content: `
+      dependencies:
+        - {}
+      `,
+        fieldName: 'name',
+        want: {
+          datasource: 'helm',
+          deps: [
+            {
+              currentValue: undefined,
+              depName: undefined,
+              skipReason: SkipReason.InvalidName,
+            },
+          ],
+        },
+      },
+      {
+        content: `
+      dependencies:
+        - name: postgres
+      `,
+        fieldName: 'version',
+        want: {
+          datasource: 'helm',
+          deps: [
+            {
+              currentValue: undefined,
+              depName: 'postgres',
+              skipReason: SkipReason.InvalidVersion,
+            },
+          ],
+        },
+      },
+      {
+        content: `
+      dependencies:
+        - name: postgres
+          version: 0.1.0
+      `,
+        fieldName: 'repository',
+        want: {
+          datasource: 'helm',
+          deps: [
+            {
+              currentValue: '0.1.0',
+              depName: 'postgres',
+              skipReason: SkipReason.NoRepository,
+            },
+          ],
+        },
+      },
+    ])('validates required fields', (params) => {
+      it(`validates ${params.fieldName} is required`, async () => {
+        fs.readLocalFile.mockResolvedValueOnce(`
+      apiVersion: v1
+      appVersion: "1.0"
+      description: A Helm chart for Kubernetes
+      name: example
+      version: 0.1.0
+      `);
+        const fileName = 'requirements.yaml';
+        const result = await extractPackageFile(params.content, fileName, {});
+        expect(result).toEqual(params.want);
+      });
+    });
+    it('skips only invalid dependences', async () => {
+      fs.readLocalFile.mockResolvedValueOnce(`
+      apiVersion: v1
+      appVersion: "1.0"
+      description: A Helm chart for Kubernetes
+      name: example
+      version: 0.1.0
+      `);
+      const content = `
+      dependencies:
+        - name: postgresql
+          repository: https://charts.helm.sh/stable/
+        - version: 0.0.1
+          repository: https://charts.helm.sh/stable/
+        - name: redis
+          version: 0.0.1
+        - name: redis
+          version: 0.0.1
+          repository: https://charts.helm.sh/stable/
+      `;
+      const fileName = 'requirements.yaml';
+      const result = await extractPackageFile(content, fileName, {});
+      expect(result).toEqual({
+        datasource: 'helm',
+        deps: [
+          {
+            currentValue: undefined,
+            depName: 'postgresql',
+            skipReason: 'invalid-version',
+          },
+          {
+            currentValue: '0.0.1',
+            depName: undefined,
+            skipReason: 'invalid-name',
+          },
+          {
+            currentValue: '0.0.1',
+            depName: 'redis',
+            skipReason: 'no-repository',
+          },
+          {
+            currentValue: '0.0.1',
+            depName: 'redis',
+            registryUrls: ['https://charts.helm.sh/stable/'],
+          },
+        ],
+      });
+    });
   });
 });
diff --git a/lib/manager/helm-requirements/extract.ts b/lib/manager/helm-requirements/extract.ts
index a43a3c89b2..ed437f1473 100644
--- a/lib/manager/helm-requirements/extract.ts
+++ b/lib/manager/helm-requirements/extract.ts
@@ -48,30 +48,42 @@ export async function extractPackageFile(
       depName: dep.name,
       currentValue: dep.version,
     };
-    if (dep.repository) {
-      res.registryUrls = [dep.repository];
-      if (dep.repository.startsWith('@')) {
-        const repoWithAtRemoved = dep.repository.slice(1);
-        const alias = config.aliases[repoWithAtRemoved];
-        if (alias) {
-          res.registryUrls = [alias];
-          return res;
-        }
 
-        res.skipReason = SkipReason.PlaceholderUrl;
-      } else {
-        try {
-          const url = new URL(dep.repository);
-          if (url.protocol === 'file:') {
-            res.skipReason = SkipReason.LocalDependency;
-          }
-        } catch (err) {
-          logger.debug({ err }, 'Error parsing url');
-          res.skipReason = SkipReason.InvalidUrl;
-        }
+    if (!res.depName) {
+      res.skipReason = SkipReason.InvalidName;
+      return res;
+    }
+
+    if (!res.currentValue) {
+      res.skipReason = SkipReason.InvalidVersion;
+      return res;
+    }
+
+    if (!dep.repository) {
+      res.skipReason = SkipReason.NoRepository;
+      return res;
+    }
+
+    res.registryUrls = [dep.repository];
+    if (dep.repository.startsWith('@')) {
+      const repoWithAtRemoved = dep.repository.slice(1);
+      const alias = config.aliases[repoWithAtRemoved];
+      if (alias) {
+        res.registryUrls = [alias];
+        return res;
       }
+
+      res.skipReason = SkipReason.PlaceholderUrl;
     } else {
-      res.skipReason = SkipReason.NoRepository;
+      try {
+        const url = new URL(dep.repository);
+        if (url.protocol === 'file:') {
+          res.skipReason = SkipReason.LocalDependency;
+        }
+      } catch (err) {
+        logger.debug({ err }, 'Error parsing url');
+        res.skipReason = SkipReason.InvalidUrl;
+      }
     }
     return res;
   });
-- 
GitLab