From fe02e735933367d87f9109cc53bb932f78c47365 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:10:04 -0400 Subject: [PATCH] fix(@angular/cli): validate registry option is a valid URL in ng add Ensure that the --registry option passed to ng add is parsed as a valid absolute URL. --- packages/angular/cli/src/commands/add/cli.ts | 12 ++++++++ .../e2e/tests/commands/add/registry-option.ts | 28 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/angular/cli/src/commands/add/cli.ts b/packages/angular/cli/src/commands/add/cli.ts index 136704947e69..dd186c46b397 100644 --- a/packages/angular/cli/src/commands/add/cli.ts +++ b/packages/angular/cli/src/commands/add/cli.ts @@ -16,6 +16,7 @@ import npa from 'npm-package-arg'; import semver, { Range, compare, intersects, prerelease, satisfies, valid } from 'semver'; import { Argv } from 'yargs'; import { + CommandModuleError, CommandModuleImplementation, Options, OtherOptions, @@ -131,6 +132,17 @@ export default class AddCommandModule type: 'boolean', default: false, }) + .check(({ registry }) => { + if (registry === undefined) { + return true; + } + + if (typeof registry === 'string' && URL.canParse(registry)) { + return true; + } + + throw new CommandModuleError('Option --registry must be a valid URL.'); + }) // Prior to downloading we don't know the full schema and therefore we cannot be strict on the options. // Possibly in the future update the logic to use the following syntax: // `ng add @angular/localize -- --package-options`. diff --git a/tests/e2e/tests/commands/add/registry-option.ts b/tests/e2e/tests/commands/add/registry-option.ts index dc336cdd5b30..1343cebf4dd1 100644 --- a/tests/e2e/tests/commands/add/registry-option.ts +++ b/tests/e2e/tests/commands/add/registry-option.ts @@ -1,11 +1,37 @@ import { getGlobalVariable } from '../../../utils/env'; import { expectFileToExist } from '../../../utils/fs'; -import { ng } from '../../../utils/process'; +import { execAndCaptureError, ng } from '../../../utils/process'; import { expectToFail } from '../../../utils/utils'; export default async function () { const testRegistry = getGlobalVariable('package-registry'); + // Validate that a non-URL registry string fails with a validation error + const error = await execAndCaptureError('ng', [ + 'add', + '--registry=not-a-valid-url', + '@angular/pwa', + '--skip-confirmation', + ]); + if (!error.message.includes('Option --registry must be a valid URL.')) { + throw new Error( + `Expected registry validation error, but got different error: ${error.message}`, + ); + } + + // Validate that an empty registry string fails with a validation error + const errorEmpty = await execAndCaptureError('ng', [ + 'add', + '--registry=', + '@angular/pwa', + '--skip-confirmation', + ]); + if (!errorEmpty.message.includes('Option --registry must be a valid URL.')) { + throw new Error( + `Expected registry validation error for empty string, but got: ${errorEmpty.message}`, + ); + } + // Set an invalid registry process.env['NPM_CONFIG_REGISTRY'] = 'http://127.0.0.1:9999';