From 4f8b5b66a1ed4e92f440a89b0029629cde062550 Mon Sep 17 00:00:00 2001 From: Heikki Hellgren Date: Thu, 29 Aug 2024 08:40:14 +0300 Subject: [PATCH] feat: allow signing git commits from scaffolder adds support to sign git commits with configured PGP key in scaffolder actions. configuration can be done either by integration or by using the default signing key in the scaffolder config. note that this pgp-plugin is used for signing and that it is limited to using RSA keys and signatures made with SHA1 hashing algorithm. this change does not support github commit signing as it cannot be done the same way due to GitHub app and REST API being used. closes #25934 refers to #26333 Signed-off-by: Heikki Hellgren --- .changeset/silver-camels-brake.md | 14 ++ packages/integration/config.d.ts | 32 +++++ packages/integration/report.api.md | 7 + packages/integration/src/azure/config.ts | 6 + packages/integration/src/bitbucket/config.ts | 6 + .../integration/src/bitbucketCloud/config.ts | 4 + .../integration/src/bitbucketServer/config.ts | 6 + packages/integration/src/gerrit/config.ts | 6 + packages/integration/src/gitea/config.ts | 6 + packages/integration/src/gitlab/config.ts | 13 +- .../report.api.md | 1 + .../src/actions/azure.ts | 24 +++- .../report.api.md | 1 + .../src/actions/bitbucketCloud.ts | 19 ++- .../report.api.md | 1 + .../src/actions/bitbucketServer.ts | 19 ++- .../report.api.md | 1 + .../src/actions/bitbucket.ts | 18 ++- .../report.api.md | 2 + .../src/actions/gerrit.ts | 19 ++- .../src/actions/gerritReview.ts | 18 ++- .../report.api.md | 1 + .../src/actions/gitea.ts | 17 +++ .../report.api.md | 1 + .../src/actions/gitlab.test.ts | 26 ++++ .../src/actions/gitlab.ts | 23 +++- plugins/scaffolder-backend/config.d.ts | 4 + plugins/scaffolder-node/package.json | 1 + plugins/scaffolder-node/report.api.md | 3 + .../src/actions/gitHelpers.test.ts | 34 ++++- .../scaffolder-node/src/actions/gitHelpers.ts | 9 ++ plugins/scaffolder-node/src/scm/git.test.ts | 11 +- plugins/scaffolder-node/src/scm/git.ts | 26 +++- yarn.lock | 120 +++++++++++++++++- 34 files changed, 473 insertions(+), 26 deletions(-) create mode 100644 .changeset/silver-camels-brake.md diff --git a/.changeset/silver-camels-brake.md b/.changeset/silver-camels-brake.md new file mode 100644 index 0000000000..f044bc9441 --- /dev/null +++ b/.changeset/silver-camels-brake.md @@ -0,0 +1,14 @@ +--- +'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch +'@backstage/plugin-scaffolder-backend-module-bitbucket': patch +'@backstage/plugin-scaffolder-backend-module-gerrit': patch +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +'@backstage/plugin-scaffolder-backend-module-azure': patch +'@backstage/plugin-scaffolder-backend-module-gitea': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +'@backstage/integration': patch +--- + +Allow signing git commits using configured private PGP key in scaffolder diff --git a/packages/integration/config.d.ts b/packages/integration/config.d.ts index 1e4c8d373d..07155944b3 100644 --- a/packages/integration/config.d.ts +++ b/packages/integration/config.d.ts @@ -62,6 +62,10 @@ export interface Config { tenantId?: string; personalAccessToken?: string; }[]; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** @@ -94,6 +98,10 @@ export interface Config { * @visibility secret */ appPassword?: string; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** Integration configuration for Bitbucket Cloud */ @@ -108,6 +116,10 @@ export interface Config { * @visibility secret */ appPassword: string; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** Integration configuration for Bitbucket Server */ @@ -137,6 +149,10 @@ export interface Config { * @visibility frontend */ apiBaseUrl?: string; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** Integration configuration for Gerrit */ @@ -172,6 +188,10 @@ export interface Config { * @visibility secret */ password?: string; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** Integration configuration for GitHub */ @@ -233,6 +253,10 @@ export interface Config { */ allowedInstallationOwners?: string[]; }>; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** Integration configuration for GitLab */ @@ -269,6 +293,10 @@ export interface Config { * @visibility frontend */ baseUrl?: string; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** Integration configuration for Google Cloud Storage */ @@ -349,6 +377,10 @@ export interface Config { * @visibility secret */ password?: string; + /** + * PGP signing key for signing commits. + */ + commitSigningKey?: string; }>; /** Integration configuration for Harness Code */ harness?: Array<{ diff --git a/packages/integration/report.api.md b/packages/integration/report.api.md index 6bb48b8185..0f7c6dd00b 100644 --- a/packages/integration/report.api.md +++ b/packages/integration/report.api.md @@ -201,6 +201,7 @@ export type AzureIntegrationConfig = { token?: string; credential?: AzureDevOpsCredential; credentials?: AzureDevOpsCredential[]; + commitSigningKey?: string; }; // @public @@ -237,6 +238,7 @@ export type BitbucketCloudIntegrationConfig = { username?: string; appPassword?: string; token?: string; + commitSigningKey?: string; }; // @public @deprecated @@ -267,6 +269,7 @@ export type BitbucketIntegrationConfig = { token?: string; username?: string; appPassword?: string; + signingKey?: string; }; // @public @@ -297,6 +300,7 @@ export type BitbucketServerIntegrationConfig = { token?: string; username?: string; password?: string; + commitSigningKey?: string; }; // @public @deprecated @@ -395,6 +399,7 @@ export type GerritIntegrationConfig = { gitilesBaseUrl: string; username?: string; password?: string; + commitSigningKey?: string; }; // @public @@ -630,6 +635,7 @@ export type GiteaIntegrationConfig = { baseUrl?: string; username?: string; password?: string; + commitSigningKey?: string; }; // @public @@ -744,6 +750,7 @@ export type GitLabIntegrationConfig = { apiBaseUrl: string; token?: string; baseUrl: string; + commitSigningKey?: string; }; // @public diff --git a/packages/integration/src/azure/config.ts b/packages/integration/src/azure/config.ts index 5ed1914ea2..2910387829 100644 --- a/packages/integration/src/azure/config.ts +++ b/packages/integration/src/azure/config.ts @@ -57,6 +57,11 @@ export type AzureIntegrationConfig = { * If no credentials are specified at all, either a default credential (for Azure DevOps) or anonymous access (for Azure DevOps Server) is used. */ credentials?: AzureDevOpsCredential[]; + + /** + * Signing key for commits + */ + commitSigningKey?: string; }; /** @@ -349,6 +354,7 @@ export function readAzureIntegrationConfig( return { host, credentials, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/bitbucket/config.ts b/packages/integration/src/bitbucket/config.ts index 4b6296b0e9..195e7302e8 100644 --- a/packages/integration/src/bitbucket/config.ts +++ b/packages/integration/src/bitbucket/config.ts @@ -62,6 +62,11 @@ export type BitbucketIntegrationConfig = { * See https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/ */ appPassword?: string; + + /** + * Signing key for commits + */ + signingKey?: string; }; /** @@ -100,6 +105,7 @@ export function readBitbucketIntegrationConfig( token, username, appPassword, + signingKey: config.getOptionalString('signingKey'), }; } diff --git a/packages/integration/src/bitbucketCloud/config.ts b/packages/integration/src/bitbucketCloud/config.ts index 32a2af9313..eb4b5c4904 100644 --- a/packages/integration/src/bitbucketCloud/config.ts +++ b/packages/integration/src/bitbucketCloud/config.ts @@ -51,6 +51,9 @@ export type BitbucketCloudIntegrationConfig = { * The access token to use for requests to Bitbucket Cloud (bitbucket.org). */ token?: string; + + /** PGP private key for signing commits. */ + commitSigningKey?: string; }; /** @@ -74,6 +77,7 @@ export function readBitbucketCloudIntegrationConfig( apiBaseUrl, username, appPassword, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/bitbucketServer/config.ts b/packages/integration/src/bitbucketServer/config.ts index 1412b7ab17..b2a243b3b3 100644 --- a/packages/integration/src/bitbucketServer/config.ts +++ b/packages/integration/src/bitbucketServer/config.ts @@ -64,6 +64,11 @@ export type BitbucketServerIntegrationConfig = { * See https://developer.atlassian.com/server/bitbucket/how-tos/command-line-rest/#authentication */ password?: string; + + /** + * Signing key for commits + */ + commitSigningKey?: string; }; /** @@ -99,6 +104,7 @@ export function readBitbucketServerIntegrationConfig( token, username, password, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/gerrit/config.ts b/packages/integration/src/gerrit/config.ts index a5fdc8dabf..fe88750687 100644 --- a/packages/integration/src/gerrit/config.ts +++ b/packages/integration/src/gerrit/config.ts @@ -60,6 +60,11 @@ export type GerritIntegrationConfig = { * The password or http token to use for authentication. */ password?: string; + + /** + * The signing key to use for signing commits. + */ + commitSigningKey?: string; }; /** @@ -119,6 +124,7 @@ export function readGerritIntegrationConfig( gitilesBaseUrl, username, password, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/gitea/config.ts b/packages/integration/src/gitea/config.ts index ae1fe5ee87..461ee60715 100644 --- a/packages/integration/src/gitea/config.ts +++ b/packages/integration/src/gitea/config.ts @@ -45,6 +45,11 @@ export type GiteaIntegrationConfig = { * The password or http token to use for authentication. */ password?: string; + + /** + * Signing key to to sign commits + */ + commitSigningKey?: string; }; /** @@ -79,5 +84,6 @@ export function readGiteaConfig(config: Config): GiteaIntegrationConfig { baseUrl, username, password, + commitSigningKey: config.getOptionalString('commitSigningKey'), }; } diff --git a/packages/integration/src/gitlab/config.ts b/packages/integration/src/gitlab/config.ts index 79161561f3..636919291c 100644 --- a/packages/integration/src/gitlab/config.ts +++ b/packages/integration/src/gitlab/config.ts @@ -54,6 +54,11 @@ export type GitLabIntegrationConfig = { * If no baseUrl is provided, it will default to `https://${host}` */ baseUrl: string; + + /** + * Signing key to sign commits + */ + commitSigningKey?: string; }; /** @@ -95,7 +100,13 @@ export function readGitLabIntegrationConfig( ); } - return { host, token, apiBaseUrl, baseUrl }; + return { + host, + token, + apiBaseUrl, + baseUrl, + commitSigningKey: config.getOptionalString('commitSigningKey'), + }; } /** diff --git a/plugins/scaffolder-backend-module-azure/report.api.md b/plugins/scaffolder-backend-module-azure/report.api.md index e15aba0fa4..9e7592e145 100644 --- a/plugins/scaffolder-backend-module-azure/report.api.md +++ b/plugins/scaffolder-backend-module-azure/report.api.md @@ -27,6 +27,7 @@ export function createPublishAzureAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, JsonObject >; diff --git a/plugins/scaffolder-backend-module-azure/src/actions/azure.ts b/plugins/scaffolder-backend-module-azure/src/actions/azure.ts index 518d2acbf7..acb4754520 100644 --- a/plugins/scaffolder-backend-module-azure/src/actions/azure.ts +++ b/plugins/scaffolder-backend-module-azure/src/actions/azure.ts @@ -20,10 +20,10 @@ import { ScmIntegrationRegistry, } from '@backstage/integration'; import { - initRepoAndPush, - getRepoSourceDirectory, - parseRepoUrl, createTemplateAction, + getRepoSourceDirectory, + initRepoAndPush, + parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; import { GitRepositoryCreateOptions } from 'azure-devops-node-api/interfaces/GitInterfaces'; import { @@ -54,6 +54,7 @@ export function createPublishAzureAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:azure', examples, @@ -103,6 +104,11 @@ export function createPublishAzureAction(options: { type: 'string', description: 'The token to use for authorization to Azure', }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -134,6 +140,7 @@ export function createPublishAzureAction(options: { gitCommitMessage = 'initial commit', gitAuthorName, gitAuthorEmail, + signCommit, } = ctx.input; const { project, repo, host, organization } = parseRepoUrl( @@ -151,6 +158,7 @@ export function createPublishAzureAction(options: { const credentialProvider = DefaultAzureDevOpsCredentialsProvider.fromIntegrations(integrations); const credentials = await credentialProvider.getCredentials({ url: url }); + const integrationConfig = integrations.azure.byHost(host); if (credentials === undefined && ctx.input.token === undefined) { throw new InputError( @@ -212,6 +220,15 @@ export function createPublishAzureAction(options: { password: ctx.input.token ?? credentials!.token, }; + const signingKey = + integrationConfig?.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, @@ -222,6 +239,7 @@ export function createPublishAzureAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); ctx.output('commitHash', commitResult?.commitHash); diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md b/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md index ad74479b6e..428e21c027 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/report.api.md @@ -39,6 +39,7 @@ export function createPublishBitbucketCloudAction(options: { gitCommitMessage?: string; sourcePath?: string; token?: string; + signCommit?: boolean; }, JsonObject >; diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts index 00c27f6a54..945aab1216 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/src/actions/bitbucketCloud.ts @@ -18,8 +18,8 @@ import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; @@ -113,6 +113,7 @@ export function createPublishBitbucketCloudAction(options: { gitCommitMessage?: string; sourcePath?: string; token?: string; + signCommit?: boolean; }>({ id: 'publish:bitbucketCloud', examples, @@ -158,6 +159,11 @@ export function createPublishBitbucketCloudAction(options: { description: 'The token to use for authorization to BitBucket Cloud', }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -185,6 +191,7 @@ export function createPublishBitbucketCloudAction(options: { defaultBranch = 'master', gitCommitMessage, repoVisibility = 'private', + signCommit, } = ctx.input; const { workspace, project, repo, host } = parseRepoUrl( @@ -256,6 +263,15 @@ export function createPublishBitbucketCloudAction(options: { }; } + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, ctx.input.sourcePath), remoteUrl, @@ -266,6 +282,7 @@ export function createPublishBitbucketCloudAction(options: { gitCommitMessage || config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); ctx.output('commitHash', commitResult?.commitHash); diff --git a/plugins/scaffolder-backend-module-bitbucket-server/report.api.md b/plugins/scaffolder-backend-module-bitbucket-server/report.api.md index e92c85b284..21eee65d1c 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/report.api.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/report.api.md @@ -29,6 +29,7 @@ export function createPublishBitbucketServerAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, JsonObject >; diff --git a/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts b/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts index 612d090547..3f50d5f274 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts +++ b/plugins/scaffolder-backend-module-bitbucket-server/src/actions/bitbucketServer.ts @@ -21,8 +21,8 @@ import { } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; @@ -137,6 +137,7 @@ export function createPublishBitbucketServerAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:bitbucketServer', description: @@ -197,6 +198,11 @@ export function createPublishBitbucketServerAction(options: { type: 'string', description: `Sets the author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -227,6 +233,7 @@ export function createPublishBitbucketServerAction(options: { gitCommitMessage = 'initial commit', gitAuthorName, gitAuthorEmail, + signCommit, } = ctx.input; const { project, repo, host } = parseRepoUrl(repoUrl, integrations); @@ -279,6 +286,15 @@ export function createPublishBitbucketServerAction(options: { : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const auth = authConfig.token ? { token: token!, @@ -298,6 +314,7 @@ export function createPublishBitbucketServerAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); if (enableLFS) { diff --git a/plugins/scaffolder-backend-module-bitbucket/report.api.md b/plugins/scaffolder-backend-module-bitbucket/report.api.md index b90f2f8483..0b19806b51 100644 --- a/plugins/scaffolder-backend-module-bitbucket/report.api.md +++ b/plugins/scaffolder-backend-module-bitbucket/report.api.md @@ -44,6 +44,7 @@ export function createPublishBitbucketAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, JsonObject >; diff --git a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts index 7a58efa10a..09fff190ce 100644 --- a/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts +++ b/plugins/scaffolder-backend-module-bitbucket/src/actions/bitbucket.ts @@ -21,8 +21,8 @@ import { } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; import { Config } from '@backstage/config'; @@ -220,6 +220,7 @@ export function createPublishBitbucketAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:bitbucket', description: @@ -280,6 +281,11 @@ export function createPublishBitbucketAction(options: { type: 'string', description: `Sets the default author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -313,6 +319,7 @@ export function createPublishBitbucketAction(options: { gitCommitMessage = 'initial commit', gitAuthorName, gitAuthorEmail, + signCommit, } = ctx.input; const { workspace, project, repo, host } = parseRepoUrl( @@ -380,6 +387,14 @@ export function createPublishBitbucketAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.signingKey ?? + config.getOptionalString('scaffolder.defaultSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } let auth; @@ -409,6 +424,7 @@ export function createPublishBitbucketAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); if (enableLFS && host !== 'bitbucket.org') { diff --git a/plugins/scaffolder-backend-module-gerrit/report.api.md b/plugins/scaffolder-backend-module-gerrit/report.api.md index a3f23d7aa7..b5af8f0acf 100644 --- a/plugins/scaffolder-backend-module-gerrit/report.api.md +++ b/plugins/scaffolder-backend-module-gerrit/report.api.md @@ -22,6 +22,7 @@ export function createPublishGerritAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }, JsonObject >; @@ -38,6 +39,7 @@ export function createPublishGerritReviewAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }, JsonObject >; diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts index db0ae2ba70..23f28ae934 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerrit.ts @@ -24,8 +24,8 @@ import { } from '@backstage/integration'; import { createTemplateAction, - initRepoAndPush, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; import { examples } from './gerrit.examples'; @@ -99,6 +99,7 @@ export function createPublishGerritAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }>({ id: 'publish:gerrit', supportsDryRun: true, @@ -143,6 +144,11 @@ export function createPublishGerritAction(options: { type: 'string', description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -172,6 +178,7 @@ export function createPublishGerritAction(options: { gitAuthorEmail, gitCommitMessage = 'initial commit', sourcePath, + signCommit, } = ctx.input; const { repo, host, owner, workspace } = parseRepoUrl( repoUrl, @@ -233,6 +240,15 @@ export function createPublishGerritAction(options: { email: gitEmail, }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const commitResult = await initRepoAndPush({ dir: getRepoSourceDirectory(ctx.workspacePath, sourcePath), remoteUrl, @@ -241,6 +257,7 @@ export function createPublishGerritAction(options: { logger: ctx.logger, commitMessage: generateCommitMessage(config, gitCommitMessage), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); ctx.output('remoteUrl', remoteUrl); diff --git a/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts b/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts index b4f844b08f..b4ab3afef7 100644 --- a/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts +++ b/plugins/scaffolder-backend-module-gerrit/src/actions/gerritReview.ts @@ -19,8 +19,8 @@ import { InputError } from '@backstage/errors'; import { Config } from '@backstage/config'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { - createTemplateAction, commitAndPushRepo, + createTemplateAction, getRepoSourceDirectory, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; @@ -48,6 +48,7 @@ export function createPublishGerritReviewAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; }>({ id: 'publish:gerrit:review', description: 'Creates a new Gerrit review.', @@ -88,6 +89,11 @@ export function createPublishGerritReviewAction(options: { type: 'string', description: `Sets the default author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -112,6 +118,7 @@ export function createPublishGerritReviewAction(options: { gitAuthorName, gitAuthorEmail, gitCommitMessage, + signCommit, } = ctx.input; const { host, repo } = parseRepoUrl(repoUrl, integrations); @@ -139,6 +146,14 @@ export function createPublishGerritReviewAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } const changeId = generateGerritChangeId(); const commitMessage = `${gitCommitMessage}\n\nChange-Id: ${changeId}`; @@ -150,6 +165,7 @@ export function createPublishGerritReviewAction(options: { gitAuthorInfo, branch, remoteRef: `refs/for/${branch}`, + signingKey: signCommit ? signingKey : undefined, }); const repoContentsUrl = `${integrationConfig.config.gitilesBaseUrl}/${repo}/+/refs/heads/${branch}`; diff --git a/plugins/scaffolder-backend-module-gitea/report.api.md b/plugins/scaffolder-backend-module-gitea/report.api.md index 8456b7183c..9a057dfb45 100644 --- a/plugins/scaffolder-backend-module-gitea/report.api.md +++ b/plugins/scaffolder-backend-module-gitea/report.api.md @@ -23,6 +23,7 @@ export function createPublishGiteaAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }, JsonObject >; diff --git a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts index 6b29ff2a4c..4321de2167 100644 --- a/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts +++ b/plugins/scaffolder-backend-module-gitea/src/actions/gitea.ts @@ -219,6 +219,7 @@ export function createPublishGiteaAction(options: { gitAuthorName?: string; gitAuthorEmail?: string; sourcePath?: string; + signCommit?: boolean; }>({ id: 'publish:gitea', description: @@ -268,6 +269,11 @@ export function createPublishGiteaAction(options: { type: 'string', description: `Path within the workspace that will be used as the repository root. If omitted, the entire workspace will be published as the repository.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, }, }, output: { @@ -298,6 +304,7 @@ export function createPublishGiteaAction(options: { gitAuthorEmail, gitCommitMessage = 'initial commit', sourcePath, + signCommit, } = ctx.input; const { repo, host, owner } = parseRepoUrl(repoUrl, integrations); @@ -338,6 +345,16 @@ export function createPublishGiteaAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + // The owner to be used should be either the org name or user authenticated with the gitea server const remoteUrl = `${integrationConfig.config.baseUrl}/${owner}/${repo}.git`; const commitResult = await initRepoAndPush({ diff --git a/plugins/scaffolder-backend-module-gitlab/report.api.md b/plugins/scaffolder-backend-module-gitlab/report.api.md index d50104e98e..1ba70a7ba6 100644 --- a/plugins/scaffolder-backend-module-gitlab/report.api.md +++ b/plugins/scaffolder-backend-module-gitlab/report.api.md @@ -144,6 +144,7 @@ export function createPublishGitlabAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; setUserAsOwner?: boolean; topics?: string[]; settings?: { diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts index e255652f15..45d92449bc 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.test.ts @@ -74,6 +74,7 @@ describe('publish:gitlab', () => { host: 'gitlab.com', token: 'tokenlols', apiBaseUrl: 'https://api.gitlab.com', + signingKey: 'test-signing-key', }, { host: 'hosted.gitlab.com', @@ -420,6 +421,31 @@ describe('publish:gitlab', () => { }); }); + it('should call initRepoAndPush with the signing key', async () => { + mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); + mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); + mockGitlabClient.Groups.allProjects.mockResolvedValue([]); + mockGitlabClient.Projects.create.mockResolvedValue({ + http_url_to_repo: 'http://mockurl.git', + }); + + await action.handler({ + ...mockContext, + input: { ...mockContext.input, signCommit: true }, + }); + + expect(initRepoAndPush).toHaveBeenCalledWith({ + dir: mockContext.workspacePath, + defaultBranch: 'master', + remoteUrl: 'http://mockurl.git', + auth: { username: 'oauth2', password: 'tokenlols' }, + logger: mockContext.logger, + commitMessage: 'initial commit', + gitAuthorInfo: {}, + signingKey: 'test-signing-key', + }); + }); + it('should not call initRepoAndPush when sourcePath is false', async () => { mockGitlabClient.Users.showCurrentUser.mockResolvedValue({ id: 12345 }); mockGitlabClient.Namespaces.show.mockResolvedValue({ id: 1234 }); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts index b2641e6656..e78cd95f4b 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlab.ts @@ -16,13 +16,13 @@ import { InputError } from '@backstage/errors'; import { ScmIntegrationRegistry } from '@backstage/integration'; -import { createTemplateAction } from '@backstage/plugin-scaffolder-node'; -import { Gitlab, VariableType } from '@gitbeaker/rest'; import { - initRepoAndPush, + createTemplateAction, getRepoSourceDirectory, + initRepoAndPush, parseRepoUrl, } from '@backstage/plugin-scaffolder-node'; +import { Gitlab, VariableType } from '@gitbeaker/rest'; import { Config } from '@backstage/config'; import { examples } from './gitlab.examples'; @@ -49,6 +49,7 @@ export function createPublishGitlabAction(options: { gitCommitMessage?: string; gitAuthorName?: string; gitAuthorEmail?: string; + signCommit?: boolean; setUserAsOwner?: boolean; /** @deprecated in favour of settings.topics field */ topics?: string[]; @@ -122,6 +123,11 @@ export function createPublishGitlabAction(options: { type: 'string', description: `Sets the default author email for the commit.`, }, + signCommit: { + title: 'Sign commit', + type: 'boolean', + description: 'Sign commit with configured PGP private key', + }, sourcePath: { title: 'Source Path', description: @@ -349,6 +355,7 @@ export function createPublishGitlabAction(options: { branches = [], projectVariables = [], skipExisting = false, + signCommit, } = ctx.input; const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); @@ -447,6 +454,15 @@ export function createPublishGitlabAction(options: { ? gitAuthorEmail : config.getOptionalString('scaffolder.defaultAuthor.email'), }; + const signingKey = + integrationConfig.config.commitSigningKey ?? + config.getOptionalString('scaffolder.defaultCommitSigningKey'); + if (signCommit && !signingKey) { + throw new Error( + 'Signing commits is enabled but no signing key is provided in the configuration', + ); + } + const shouldSkipPublish = typeof ctx.input.sourcePath === 'boolean' && !ctx.input.sourcePath; if (!shouldSkipPublish) { @@ -469,6 +485,7 @@ export function createPublishGitlabAction(options: { ? gitCommitMessage : config.getOptionalString('scaffolder.defaultCommitMessage'), gitAuthorInfo, + signingKey: signCommit ? signingKey : undefined, }); if (branches) { diff --git a/plugins/scaffolder-backend/config.d.ts b/plugins/scaffolder-backend/config.d.ts index a391f97264..5fe7859f98 100644 --- a/plugins/scaffolder-backend/config.d.ts +++ b/plugins/scaffolder-backend/config.d.ts @@ -27,6 +27,10 @@ export interface Config { email?: string; }; + /** + * Default PGP signing key for signing commits. + */ + defaultCommitSigningKey?: string; /** * The commit message used when new components are created. */ diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index d7f8fc042b..770e66cf62 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -60,6 +60,7 @@ "@backstage/integration": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/types": "workspace:^", + "@isomorphic-git/pgp-plugin": "^0.0.7", "concat-stream": "^2.0.0", "fs-extra": "^11.2.0", "globby": "^11.0.0", diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index 333b47a6d1..afa611804d 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -106,6 +106,7 @@ export function commitAndPushBranch(options: { branch?: string; remoteRef?: string; remote?: string; + signingKey?: string; }): Promise<{ commitHash: string; }>; @@ -129,6 +130,7 @@ export function commitAndPushRepo(input: { }; branch?: string; remoteRef?: string; + signingKey?: string; }): Promise<{ commitHash: string; }>; @@ -242,6 +244,7 @@ export function initRepoAndPush(input: { name?: string; email?: string; }; + signingKey?: string; }): Promise<{ commitHash: string; }>; diff --git a/plugins/scaffolder-node/src/actions/gitHelpers.test.ts b/plugins/scaffolder-node/src/actions/gitHelpers.test.ts index 2e8eb9366f..d70ddf3bc0 100644 --- a/plugins/scaffolder-node/src/actions/gitHelpers.test.ts +++ b/plugins/scaffolder-node/src/actions/gitHelpers.test.ts @@ -16,12 +16,12 @@ import { Git } from '../scm'; import { - commitAndPushRepo, - initRepoAndPush, - commitAndPushBranch, addFiles, - createBranch, cloneRepo, + commitAndPushBranch, + commitAndPushRepo, + createBranch, + initRepoAndPush, } from './gitHelpers'; import { mockServices } from '@backstage/backend-test-utils'; import { loggerToWinstonLogger } from './loggerToWinstonLogger'; @@ -224,6 +224,32 @@ describe('commitAndPushRepo', () => { }); }); + it('creates commit with signing key', async () => { + await commitAndPushRepo({ + dir: '/test/repo/dir/', + auth: { + username: 'test-user', + password: 'test-password', + }, + logger: loggerToWinstonLogger(mockServices.logger.mock()), + commitMessage: 'commit message', + signingKey: 'test-signing-key', + }); + expect(mockedGit.commit).toHaveBeenCalledWith({ + dir: '/test/repo/dir/', + message: 'commit message', + author: { + name: 'Scaffolder', + email: 'scaffolder@backstage.io', + }, + committer: { + name: 'Scaffolder', + email: 'scaffolder@backstage.io', + }, + signingKey: 'test-signing-key', + }); + }); + it('pushes to the remote', () => { expect(mockedGit.push).toHaveBeenCalledWith({ dir: '/test/repo/dir/', diff --git a/plugins/scaffolder-node/src/actions/gitHelpers.ts b/plugins/scaffolder-node/src/actions/gitHelpers.ts index e24c0cf025..8922975274 100644 --- a/plugins/scaffolder-node/src/actions/gitHelpers.ts +++ b/plugins/scaffolder-node/src/actions/gitHelpers.ts @@ -31,6 +31,7 @@ export async function initRepoAndPush(input: { defaultBranch?: string; commitMessage?: string; gitAuthorInfo?: { name?: string; email?: string }; + signingKey?: string; }): Promise<{ commitHash: string }> { const { dir, @@ -40,6 +41,7 @@ export async function initRepoAndPush(input: { defaultBranch = 'master', commitMessage = 'Initial commit', gitAuthorInfo, + signingKey, } = input; const git = Git.fromAuth({ ...auth, @@ -64,6 +66,7 @@ export async function initRepoAndPush(input: { message: commitMessage, author: authorInfo, committer: authorInfo, + signingKey, }); await git.push({ @@ -89,6 +92,7 @@ export async function commitAndPushRepo(input: { gitAuthorInfo?: { name?: string; email?: string }; branch?: string; remoteRef?: string; + signingKey?: string; }): Promise<{ commitHash: string }> { const { dir, @@ -98,6 +102,7 @@ export async function commitAndPushRepo(input: { gitAuthorInfo, branch = 'master', remoteRef, + signingKey, } = input; const git = Git.fromAuth({ @@ -120,6 +125,7 @@ export async function commitAndPushRepo(input: { message: commitMessage, author: authorInfo, committer: authorInfo, + signingKey, }); await git.push({ @@ -213,6 +219,7 @@ export async function commitAndPushBranch(options: { branch?: string; remoteRef?: string; remote?: string; + signingKey?: string; }): Promise<{ commitHash: string }> { const { dir, @@ -223,6 +230,7 @@ export async function commitAndPushBranch(options: { branch = 'master', remoteRef, remote = 'origin', + signingKey, } = options; const git = Git.fromAuth({ ...auth, @@ -240,6 +248,7 @@ export async function commitAndPushBranch(options: { message: commitMessage, author: authorInfo, committer: authorInfo, + signingKey, }); await git.push({ diff --git a/plugins/scaffolder-node/src/scm/git.test.ts b/plugins/scaffolder-node/src/scm/git.test.ts index 4a472a675a..dfbdd9cd16 100644 --- a/plugins/scaffolder-node/src/scm/git.test.ts +++ b/plugins/scaffolder-node/src/scm/git.test.ts @@ -16,6 +16,12 @@ jest.mock('isomorphic-git'); jest.mock('isomorphic-git/http/node'); jest.mock('fs-extra'); +jest.mock('@isomorphic-git/pgp-plugin', () => ({ + ...jest.requireActual('@isomorphic-git/pgp-plugin'), + pgp: { + sign: jest.fn().mockResolvedValue({ signature: 'sign' }), + }, +})); import * as isomorphic from 'isomorphic-git'; import { Git } from './git'; @@ -140,8 +146,9 @@ describe('Git', () => { name: 'comitter', email: 'test@backstage.io', }; + const signingKey = 'test-signing-key'; - await git.commit({ dir, message, author, committer }); + await git.commit({ dir, message, author, committer, signingKey }); expect(isomorphic.commit).toHaveBeenCalledWith({ fs, @@ -149,6 +156,8 @@ describe('Git', () => { message, author, committer, + signingKey, + onSign: expect.any(Function), }); }); }); diff --git a/plugins/scaffolder-node/src/scm/git.ts b/plugins/scaffolder-node/src/scm/git.ts index 760be73114..42926fd901 100644 --- a/plugins/scaffolder-node/src/scm/git.ts +++ b/plugins/scaffolder-node/src/scm/git.ts @@ -15,14 +15,16 @@ */ import git, { - ProgressCallback, - MergeResult, - ReadCommitResult, AuthCallback, + MergeResult, + ProgressCallback, + ReadCommitResult, } from 'isomorphic-git'; import http from 'isomorphic-git/http/node'; import fs from 'fs-extra'; import { LoggerService } from '@backstage/backend-plugin-api'; +// @ts-ignore +import { pgp } from '@isomorphic-git/pgp-plugin'; function isAuthCallbackOptions( options: StaticAuthOptions | AuthCallbackOptions, @@ -137,12 +139,21 @@ export class Git { message: string; author: { name: string; email: string }; committer: { name: string; email: string }; + signingKey?: string; }): Promise { - const { dir, message, author, committer } = options; + const { dir, message, author, committer, signingKey } = options; this.config.logger?.info( `Committing file to repo {dir=${dir},message=${message}}`, ); - return git.commit({ fs, dir, message, author, committer }); + return git.commit({ + fs, + dir, + message, + author, + committer, + signingKey, + onSign: signingKey ? pgp.sign : undefined, + }); } /** https://isomorphic-git.org/docs/en/clone */ @@ -241,8 +252,9 @@ export class Git { ours?: string; author: { name: string; email: string }; committer: { name: string; email: string }; + signingKey?: string; }): Promise { - const { dir, theirs, ours, author, committer } = options; + const { dir, theirs, ours, author, committer, signingKey } = options; this.config.logger?.info( `Merging branch '${theirs}' into '${ours}' for repository {dir=${dir}}`, ); @@ -255,6 +267,8 @@ export class Git { theirs, author, committer, + signingKey, + onSign: signingKey ? pgp.sign : undefined, }); } diff --git a/yarn.lock b/yarn.lock index 0ea358c567..cbe6d897ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7790,6 +7790,7 @@ __metadata: "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-common": "workspace:^" "@backstage/types": "workspace:^" + "@isomorphic-git/pgp-plugin": ^0.0.7 concat-stream: ^2.0.0 fs-extra: ^11.2.0 globby: ^11.0.0 @@ -10929,6 +10930,59 @@ __metadata: languageName: node linkType: hard +"@isomorphic-git/pgp-plugin@npm:^0.0.7": + version: 0.0.7 + resolution: "@isomorphic-git/pgp-plugin@npm:0.0.7" + dependencies: + "@isomorphic-pgp/sign-and-verify": ^0.0.10 + "@isomorphic-pgp/util": ^0.0.6 + checksum: 5361b4e58933f87747607b4bcfdf7ce2e4d75f591d5839ecd313fba55196020ee0e867c307fbfa2cd0aa85442942a5efca40653dfac0acabe8043e80f1dbebc1 + languageName: node + linkType: hard + +"@isomorphic-pgp/parser@npm:^0.0.3": + version: 0.0.3 + resolution: "@isomorphic-pgp/parser@npm:0.0.3" + dependencies: + array-buffer-to-hex: ^1.0.0 + base64-js: ^1.3.0 + bn.js: ^4.11.8 + clz-buffer: ^1.0.0 + concat-buffers: ^1.0.0 + crc: ^3.8.0 + isomorphic-textencoder: ^1.0.1 + select-case: ^1.0.0 + checksum: 3ddfb1d39e7a9c0da98940c95bf2a9b7a59819a1ee0a4839fcbf3152c386aa974a16854043e6cd5f987db2f9475b480c10f3ef5686f0da1495675cb399cdd22f + languageName: node + linkType: hard + +"@isomorphic-pgp/sign-and-verify@npm:^0.0.10": + version: 0.0.10 + resolution: "@isomorphic-pgp/sign-and-verify@npm:0.0.10" + dependencies: + "@isomorphic-pgp/parser": ^0.0.3 + "@isomorphic-pgp/util": ^0.0.6 + "@wmhilton/crypto-hash": ^1.0.2 + array-buffer-to-hex: ^1.0.0 + isomorphic-textencoder: ^1.0.1 + jsbn: ^1.1.0 + sha.js: ^2.4.11 + checksum: 3d43408abdb0d53c4514797ef47ec1d8f634e4a6b4a80f0df9ac826b9ba51df1a9cedcb26f7ddbee4594827de9676d7cda57549d321ea87bc95facfaa7127146 + languageName: node + linkType: hard + +"@isomorphic-pgp/util@npm:^0.0.6": + version: 0.0.6 + resolution: "@isomorphic-pgp/util@npm:0.0.6" + dependencies: + "@isomorphic-pgp/parser": ^0.0.3 + array-buffer-to-hex: ^1.0.0 + concat-buffers: ^1.0.0 + sha.js: ^2.4.11 + checksum: 721a15ddb4597f90b7b897b2d18a5819e86988ac44510f9aee9dc6a7823a62c3f1ba5b12eb35792c803b3b1c6d4af924ae0bc31123f21fbc6f5a8e8e7962a412 + languageName: node + linkType: hard + "@istanbuljs/load-nyc-config@npm:^1.0.0": version: 1.0.0 resolution: "@istanbuljs/load-nyc-config@npm:1.0.0" @@ -21810,6 +21864,13 @@ __metadata: languageName: node linkType: hard +"@wmhilton/crypto-hash@npm:^1.0.2": + version: 1.0.2 + resolution: "@wmhilton/crypto-hash@npm:1.0.2" + checksum: 99c7e46856d75dbd828922ed8088dcd85ac405d58976f785e3d3062e3e4044b11073ef43a6c74976d42abd2c320820993afcd6a1d41db22f19bf7a9fe5557140 + languageName: node + linkType: hard + "@xmldom/is-dom-node@npm:^1.0.1": version: 1.0.1 resolution: "@xmldom/is-dom-node@npm:1.0.1" @@ -23059,6 +23120,13 @@ __metadata: languageName: node linkType: hard +"array-buffer-to-hex@npm:^1.0.0": + version: 1.0.0 + resolution: "array-buffer-to-hex@npm:1.0.0" + checksum: 8de16fbb42fc79c839e5579e3e7dbf9a36dfa52cfcb4c0576a0e9cc6eeb5bf3f066b671858a292c97fcf9bd117c6eebc121085325e836dfb62b2737babd29264 + languageName: node + linkType: hard + "array-differ@npm:^3.0.0": version: 3.0.0 resolution: "array-differ@npm:3.0.0" @@ -23985,7 +24053,7 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^4.0.0, bn.js@npm:^4.1.0, bn.js@npm:^4.11.9": +"bn.js@npm:^4.0.0, bn.js@npm:^4.1.0, bn.js@npm:^4.11.8, bn.js@npm:^4.11.9": version: 4.12.0 resolution: "bn.js@npm:4.12.0" checksum: 39afb4f15f4ea537b55eaf1446c896af28ac948fdcf47171961475724d1bb65118cca49fa6e3d67706e4790955ec0e74de584e45c8f1ef89f46c812bee5b5a12 @@ -24310,7 +24378,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.5.0, buffer@npm:^5.7.1": +"buffer@npm:^5.1.0, buffer@npm:^5.5.0, buffer@npm:^5.7.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -25149,6 +25217,13 @@ __metadata: languageName: node linkType: hard +"clz-buffer@npm:^1.0.0": + version: 1.0.0 + resolution: "clz-buffer@npm:1.0.0" + checksum: 00a177ec2e7732e2c6393e3825de5f9d4a530d77fefb1797cdf1782a3d72407c43941e0c30d4511dd6c0536ed0652cf17d356ce8408e4c3234e80e1d5c88e980 + languageName: node + linkType: hard + "cmd-extension@npm:^1.0.2": version: 1.0.2 resolution: "cmd-extension@npm:1.0.2" @@ -25537,6 +25612,13 @@ __metadata: languageName: node linkType: hard +"concat-buffers@npm:^1.0.0": + version: 1.0.0 + resolution: "concat-buffers@npm:1.0.0" + checksum: 5b1d0215687a1ab27d9181d9a5036664e0f86f7da5bda5a9e446962572736e5c345eb3ca40b7d276198efad703754f8870fd216e70a36990d231b0b27c5fe9d7 + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -25938,6 +26020,15 @@ __metadata: languageName: node linkType: hard +"crc@npm:^3.8.0": + version: 3.8.0 + resolution: "crc@npm:3.8.0" + dependencies: + buffer: ^5.1.0 + checksum: dabbc4eba223b206068b92ca82bb471d583eb6be2384a87f5c3712730cfd6ba4b13a45e8ba3ef62174d5a781a2c5ac5c20bf36cf37bba73926899bd0aa19186f + languageName: node + linkType: hard + "create-ecdh@npm:^4.0.4": version: 4.0.4 resolution: "create-ecdh@npm:4.0.4" @@ -29385,6 +29476,13 @@ __metadata: languageName: node linkType: hard +"fast-text-encoding@npm:^1.0.0": + version: 1.0.6 + resolution: "fast-text-encoding@npm:1.0.6" + checksum: 9d58f694314b3283e785bf61954902536da228607ad246905e30256f9ab8331f780ac987e7222c9f5eafd04168d07e12b8054c85cedb76a2c05af0e82387a903 + languageName: node + linkType: hard + "fast-uri@npm:^3.0.1": version: 3.0.1 resolution: "fast-uri@npm:3.0.1" @@ -33006,6 +33104,15 @@ __metadata: languageName: node linkType: hard +"isomorphic-textencoder@npm:^1.0.1": + version: 1.0.1 + resolution: "isomorphic-textencoder@npm:1.0.1" + dependencies: + fast-text-encoding: ^1.0.0 + checksum: 9bd0c04bff7b1dc9e410d2f9a2705ca4cc288872929d88fbb7758f06b5147de8dbaea5f95ce6cf4d314f064643f3d42d69a07bbb68df0261e81153d2d56f77f3 + languageName: node + linkType: hard + "isomorphic-timers-promises@npm:^1.0.1": version: 1.0.1 resolution: "isomorphic-timers-promises@npm:1.0.1" @@ -33803,7 +33910,7 @@ __metadata: languageName: node linkType: hard -"jsbn@npm:1.1.0": +"jsbn@npm:1.1.0, jsbn@npm:^1.1.0": version: 1.1.0 resolution: "jsbn@npm:1.1.0" checksum: 944f924f2bd67ad533b3850eee47603eed0f6ae425fd1ee8c760f477e8c34a05f144c1bd4f5a5dd1963141dc79a2c55f89ccc5ab77d039e7077f3ad196b64965 @@ -43002,6 +43109,13 @@ __metadata: languageName: node linkType: hard +"select-case@npm:^1.0.0": + version: 1.0.0 + resolution: "select-case@npm:1.0.0" + checksum: 19cf1c8fd16ae4b811dea9fc4a11860dc764b9f7d2e6f7c8910814d91617e0d0d508ce2186aa9f99a3708834caf261e963769fed3a313552e1dae3d28d64042a + languageName: node + linkType: hard + "select-hose@npm:^2.0.0": version: 2.0.0 resolution: "select-hose@npm:2.0.0"