renovate/lib/datasource/npm/npmrc.ts
RahulGautamSingh 4b16903ff1
fix: modified regex to use RE2 (#12025)
Co-authored-by: Rhys Arkins <rhys@arkins.net>
2021-10-19 14:53:34 +02:00

129 lines
3.6 KiB
TypeScript

import url from 'url';
import is from '@sindresorhus/is';
import ini from 'ini';
import registryAuthToken from 'registry-auth-token';
import getRegistryUrl from 'registry-auth-token/registry-url';
import { getGlobalConfig } from '../../config/global';
import { logger } from '../../logger';
import type { OutgoingHttpHeaders } from '../../util/http/types';
import { maskToken } from '../../util/mask';
import { regEx } from '../../util/regex';
import { add } from '../../util/sanitize';
import type { Npmrc, PackageResolution } from './types';
let npmrc: Record<string, any> = {};
let npmrcRaw = '';
export function getNpmrc(): Npmrc | null {
return npmrc;
}
function envReplace(value: any, env = process.env): any {
// istanbul ignore if
if (!is.string(value)) {
return value;
}
const ENV_EXPR = regEx(/(\\*)\$\{([^}]+)\}/g);
return value.replace(ENV_EXPR, (match, esc, envVarName) => {
if (env[envVarName] === undefined) {
logger.warn('Failed to replace env in config: ' + match);
throw new Error('env-replace');
}
return env[envVarName];
});
}
const envRe = regEx(/(\\*)\$\{([^}]+)\}/);
// TODO: better add to host rules (#9588)
function sanitize(key: string, val: string): void {
if (!val || envRe.test(val)) {
return;
}
if (key.endsWith('_authToken') || key.endsWith('_auth')) {
add(val);
} else if (key.endsWith(':_password')) {
add(val);
const password = Buffer.from(val, 'base64').toString();
add(password);
const username: string = npmrc[key.replace(':_password', ':username')];
add(Buffer.from(`${username}:${password}`).toString('base64'));
}
}
export function setNpmrc(input?: string): void {
if (input) {
if (input === npmrcRaw) {
return;
}
const existingNpmrc = npmrc;
npmrcRaw = input;
logger.debug('Setting npmrc');
npmrc = ini.parse(input.replace(regEx(/\\n/g), '\n'));
const { exposeAllEnv } = getGlobalConfig();
for (const [key, val] of Object.entries(npmrc)) {
if (!exposeAllEnv) {
sanitize(key, val);
}
if (
!exposeAllEnv &&
key.endsWith('registry') &&
val &&
val.includes('localhost')
) {
logger.debug(
{ key, val },
'Detected localhost registry - rejecting npmrc file'
);
npmrc = existingNpmrc;
return;
}
}
if (!exposeAllEnv) {
return;
}
for (const key of Object.keys(npmrc)) {
npmrc[key] = envReplace(npmrc[key]);
sanitize(key, npmrc[key]);
}
} else if (npmrc) {
logger.debug('Resetting npmrc');
npmrc = {};
npmrcRaw = '';
}
}
export function resolvePackage(packageName: string): PackageResolution {
const scope = packageName.split('/')[0];
let registryUrl: string;
try {
registryUrl = getRegistryUrl(scope, getNpmrc());
} catch (err) {
registryUrl = 'https://registry.npmjs.org/';
}
const packageUrl = url.resolve(
registryUrl,
encodeURIComponent(packageName).replace(regEx(/^%40/), '@')
);
const headers: OutgoingHttpHeaders = {};
let authInfo = registryAuthToken(registryUrl, { npmrc, recursive: true });
if (
!authInfo &&
npmrc &&
npmrc._authToken &&
registryUrl.replace(regEx(/\/?$/), '/') ===
npmrc.registry?.replace(/\/?$/, '/') // TODO #12070
) {
authInfo = { type: 'Bearer', token: npmrc._authToken };
}
if (authInfo?.type && authInfo.token) {
headers.authorization = `${authInfo.type} ${authInfo.token}`;
logger.trace(
{ token: maskToken(authInfo.token), npmName: packageName },
'Using auth (via npmrc) for npm lookup'
);
}
return { headers, packageUrl, registryUrl };
}