From 44c83b0901a4a835c1c667d3b33220da1d835766 Mon Sep 17 00:00:00 2001
From: Rhys Arkins <rhys@arkins.net>
Date: Fri, 13 Dec 2024 12:31:15 +0100
Subject: [PATCH] feat: raise error when encrypted + no privateKey (#33085)

---
 docs/usage/self-hosted-experimental.md |  4 ++++
 lib/config/decrypt.spec.ts             | 10 ++++++++++
 lib/config/decrypt.ts                  | 11 ++++++++++-
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/docs/usage/self-hosted-experimental.md b/docs/usage/self-hosted-experimental.md
index 61629fab74..f80219fd96 100644
--- a/docs/usage/self-hosted-experimental.md
+++ b/docs/usage/self-hosted-experimental.md
@@ -39,6 +39,10 @@ This includes the following:
 
 If set to any value, Renovate will stop using the Docker Hub API (`https://hub.docker.com`) to fetch tags and instead use the normal Docker API for images pulled from `https://index.docker.io`.
 
+## `RENOVATE_X_ENCRYPTED_STRICT`
+
+If set to `"true"`, a config error Issue will be raised in case repository config contains `encrypted` objects without any `privateKey` defined.
+
 ## `RENOVATE_X_EXEC_GPID_HANDLE`
 
 If set, Renovate will terminate the whole process group of a terminated child process spawned by Renovate.
diff --git a/lib/config/decrypt.spec.ts b/lib/config/decrypt.spec.ts
index 66d73753eb..3a056690db 100644
--- a/lib/config/decrypt.spec.ts
+++ b/lib/config/decrypt.spec.ts
@@ -12,6 +12,7 @@ describe('config/decrypt', () => {
     beforeEach(() => {
       config = {};
       GlobalConfig.reset();
+      delete process.env.RENOVATE_X_ENCRYPTED_STRICT;
     });
 
     it('returns empty with no privateKey', async () => {
@@ -30,5 +31,14 @@ describe('config/decrypt', () => {
       expect(res.encrypted).toBeUndefined();
       expect(res.a).toBeUndefined();
     });
+
+    it('throws exception if encrypted found but no privateKey', async () => {
+      config.encrypted = { a: '1' };
+      process.env.RENOVATE_X_ENCRYPTED_STRICT = 'true';
+
+      await expect(decryptConfig(config, repository)).rejects.toThrow(
+        'config-validation',
+      );
+    });
   });
 });
diff --git a/lib/config/decrypt.ts b/lib/config/decrypt.ts
index ac560c4d45..80cddcd490 100644
--- a/lib/config/decrypt.ts
+++ b/lib/config/decrypt.ts
@@ -1,4 +1,5 @@
 import is from '@sindresorhus/is';
+import { CONFIG_VALIDATION } from '../constants/error-messages';
 import { logger } from '../logger';
 import { regEx } from '../util/regex';
 import { addSecretForSanitizing } from '../util/sanitize';
@@ -173,7 +174,15 @@ export async function decryptConfig(
           }
         }
       } else {
-        logger.error('Found encrypted data but no privateKey');
+        if (process.env.RENOVATE_X_ENCRYPTED_STRICT === 'true') {
+          const error = new Error(CONFIG_VALIDATION);
+          error.validationSource = 'config';
+          error.validationError = 'Encrypted config unsupported';
+          error.validationMessage = `This config contains an encrypted object at location \`$.${key}\` but no privateKey is configured. To support encrypted config, the Renovate administrator must configure a \`privateKey\` in Global Configuration.`;
+          throw error;
+        } else {
+          logger.error('Found encrypted data but no privateKey');
+        }
       }
       delete decryptedConfig.encrypted;
     } else if (is.array(val)) {
-- 
GitLab