Skip to content

Commit

Permalink
fix(@angular/cli): handle unscoped authentication details in .npmrc f…
Browse files Browse the repository at this point in the history
…iles

Unless auth options are scope with the registry url it appears that npm-registry-fetch ignores them, even though they are documented.
https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/README.md
https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/auth.js#L45-L91
  • Loading branch information
alan-agius4 authored and clydin committed Jun 17, 2021
1 parent 86db1c9 commit 911b5e4
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 40 deletions.
54 changes: 33 additions & 21 deletions packages/angular/cli/src/commands/update/schematic/npm.ts
Expand Up @@ -16,7 +16,9 @@ const lockfile = require('@yarnpkg/lockfile');
const ini = require('ini');
const pacote = require('pacote');

type PackageManagerOptions = Record<string, unknown>;
interface PackageManagerOptions extends Record<string, unknown> {
forceAuth?: Record<string, unknown>;
}

const npmPackageJsonCache = new Map<string, Promise<Partial<NpmRepositoryPackageJson>>>();
let npmrc: PackageManagerOptions;
Expand All @@ -41,8 +43,8 @@ function readOptions(
}

const defaultConfigLocations = [
path.join(globalPrefix, 'etc', baseFilename),
path.join(homedir(), dotFilename),
(!yarn && process.env.NPM_CONFIG_GLOBALCONFIG) || path.join(globalPrefix, 'etc', baseFilename),
(!yarn && process.env.NPM_CONFIG_USERCONFIG) || path.join(homedir(), dotFilename),
];

const projectConfigLocations: string[] = [path.join(cwd, dotFilename)];
Expand All @@ -67,47 +69,57 @@ function readOptions(
// See: https://github.com/npm/npm-registry-fetch/blob/ebddbe78a5f67118c1f7af2e02c8a22bcaf9e850/index.js#L99-L126
const rcConfig: PackageManagerOptions = yarn ? lockfile.parse(data) : ini.parse(data);
for (const [key, value] of Object.entries(rcConfig)) {
let substitutedValue = value;

// Substitute any environment variable references.
if (typeof value === 'string') {
substitutedValue = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
}

switch (key) {
// Unless auth options are scope with the registry url it appears that npm-registry-fetch ignores them,
// even though they are documented.
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/README.md
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/auth.js#L45-L91
case '_authToken':
case 'token':
case 'username':
case 'password':
case '_auth':
case 'auth':
options['forceAuth'] ??= {};
options['forceAuth'][key] = substitutedValue;
break;
case 'noproxy':
case 'no-proxy':
options['noProxy'] = value;
options['noProxy'] = substitutedValue;
break;
case 'maxsockets':
options['maxSockets'] = value;
options['maxSockets'] = substitutedValue;
break;
case 'https-proxy':
case 'proxy':
options['proxy'] = value;
options['proxy'] = substitutedValue;
break;
case 'strict-ssl':
options['strictSSL'] = value;
options['strictSSL'] = substitutedValue;
break;
case 'local-address':
options['localAddress'] = value;
options['localAddress'] = substitutedValue;
break;
case 'cafile':
if (typeof value === 'string') {
const cafile = path.resolve(path.dirname(location), value);
if (typeof substitutedValue === 'string') {
const cafile = path.resolve(path.dirname(location), substitutedValue);
try {
options['ca'] = readFileSync(cafile, 'utf8').replace(/\r?\n/g, '\n');
} catch {}
}
break;
default:
options[key] = value;
options[key] = substitutedValue;
break;
}
}
} else if (showPotentials) {
logger.info(`Trying '${location}'...not found.`);
}
}

// Substitute any environment variable references
for (const key in options) {
const value = options[key];
if (typeof value === 'string') {
options[key] = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
}
}

Expand Down
50 changes: 31 additions & 19 deletions packages/angular/cli/utilities/package-metadata.ts
Expand Up @@ -58,7 +58,9 @@ export interface PackageMetadata {
'dist-tags'?: unknown;
}

type PackageManagerOptions = Record<string, unknown>;
interface PackageManagerOptions extends Record<string, unknown> {
forceAuth?: Record<string, unknown>;
}

let npmrc: PackageManagerOptions;

Expand Down Expand Up @@ -122,47 +124,57 @@ function readOptions(
// See: https://github.com/npm/npm-registry-fetch/blob/ebddbe78a5f67118c1f7af2e02c8a22bcaf9e850/index.js#L99-L126
const rcConfig: PackageManagerOptions = yarn ? lockfile.parse(data) : ini.parse(data);
for (const [key, value] of Object.entries(rcConfig)) {
let substitutedValue = value;

// Substitute any environment variable references.
if (typeof value === 'string') {
substitutedValue = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
}

switch (key) {
// Unless auth options are scope with the registry url it appears that npm-registry-fetch ignores them,
// even though they are documented.
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/README.md
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/auth.js#L45-L91
case '_authToken':
case 'token':
case 'username':
case 'password':
case '_auth':
case 'auth':
options['forceAuth'] ??= {};
options['forceAuth'][key] = substitutedValue;
break;
case 'noproxy':
case 'no-proxy':
options['noProxy'] = value;
options['noProxy'] = substitutedValue;
break;
case 'maxsockets':
options['maxSockets'] = value;
options['maxSockets'] = substitutedValue;
break;
case 'https-proxy':
case 'proxy':
options['proxy'] = value;
options['proxy'] = substitutedValue;
break;
case 'strict-ssl':
options['strictSSL'] = value;
options['strictSSL'] = substitutedValue;
break;
case 'local-address':
options['localAddress'] = value;
options['localAddress'] = substitutedValue;
break;
case 'cafile':
if (typeof value === 'string') {
const cafile = path.resolve(path.dirname(location), value);
if (typeof substitutedValue === 'string') {
const cafile = path.resolve(path.dirname(location), substitutedValue);
try {
options['ca'] = readFileSync(cafile, 'utf8').replace(/\r?\n/g, '\n');
} catch {}
}
break;
default:
options[key] = value;
options[key] = substitutedValue;
break;
}
}
} else if (showPotentials) {
logger.info(`Trying '${location}'...not found.`);
}
}

// Substitute any environment variable references
for (const key in options) {
const value = options[key];
if (typeof value === 'string') {
options[key] = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
}
}

Expand Down

0 comments on commit 911b5e4

Please sign in to comment.