Skip to content

Commit

Permalink
fix(config): set npmToken in npmrc when not in encrypted (#31815)
Browse files Browse the repository at this point in the history
  • Loading branch information
RahulGautamSingh authored Nov 8, 2024
1 parent d7d701c commit 2194c22
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 36 deletions.
26 changes: 1 addition & 25 deletions lib/config/decrypt.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import is from '@sindresorhus/is';
import { logger } from '../logger';
import { maskToken } from '../util/mask';
import { regEx } from '../util/regex';
import { addSecretForSanitizing } from '../util/sanitize';
import { ensureTrailingSlash } from '../util/url';
Expand Down Expand Up @@ -166,31 +165,8 @@ export async function decryptConfig(
logger.debug(`Decrypted ${eKey}`);
if (eKey === 'npmToken') {
const token = decryptedStr.replace(regEx(/\n$/), '');
decryptedConfig[eKey] = token;
addSecretForSanitizing(token);
logger.debug(
{ decryptedToken: maskToken(token) },
'Migrating npmToken to npmrc',
);
if (is.string(decryptedConfig.npmrc)) {
/* eslint-disable no-template-curly-in-string */
if (decryptedConfig.npmrc.includes('${NPM_TOKEN}')) {
logger.debug('Replacing ${NPM_TOKEN} with decrypted token');
decryptedConfig.npmrc = decryptedConfig.npmrc.replace(
regEx(/\${NPM_TOKEN}/g),
token,
);
} else {
logger.debug('Appending _authToken= to end of existing npmrc');
decryptedConfig.npmrc = decryptedConfig.npmrc.replace(
regEx(/\n?$/),
`\n_authToken=${token}\n`,
);
}
/* eslint-enable no-template-curly-in-string */
} else {
logger.debug('Adding npmrc to config');
decryptedConfig.npmrc = `//registry.npmjs.org/:_authToken=${token}\n`;
}
} else {
decryptedConfig[eKey] = decryptedStr;
addSecretForSanitizing(decryptedStr);
Expand Down
15 changes: 4 additions & 11 deletions lib/config/decrypt/legacy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ describe('config/decrypt/legacy', () => {
};
const res = await decryptConfig(config, repository);
expect(res.encrypted).toBeUndefined();
expect(res.npmToken).toBeUndefined();
expect(res.npmrc).toBe(
'//registry.npmjs.org/:_authToken=abcdef-ghijklm-nopqf-stuvwxyz\n//registry.npmjs.org/:_authToken=abcdef-ghijklm-nopqf-stuvwxyz\n',
);
expect(res.npmToken).toBe('abcdef-ghijklm-nopqf-stuvwxyz');
});

it('appends npm token in npmrc', async () => {
Expand All @@ -59,10 +56,7 @@ describe('config/decrypt/legacy', () => {
};
const res = await decryptConfig(config, repository);
expect(res.encrypted).toBeUndefined();
expect(res.npmToken).toBeUndefined();
expect(res.npmrc).toBe(
`foo=bar\n_authToken=abcdef-ghijklm-nopqf-stuvwxyz\n`,
);
expect(res.npmToken).toBe('abcdef-ghijklm-nopqf-stuvwxyz');
});

it('decrypts nested', async () => {
Expand All @@ -88,9 +82,8 @@ describe('config/decrypt/legacy', () => {
expect(res.packageFiles[0].devDependencies.branchPrefix).toBe(
'abcdef-ghijklm-nopqf-stuvwxyz',
);
expect(res.packageFiles[0].devDependencies.npmToken).toBeUndefined();
expect(res.packageFiles[0].devDependencies.npmrc).toBe(
'//registry.npmjs.org/:_authToken=abcdef-ghijklm-nopqf-stuvwxyz\n',
expect(res.packageFiles[0].devDependencies.npmToken).toBe(
'abcdef-ghijklm-nopqf-stuvwxyz',
);
});
});
Expand Down
83 changes: 83 additions & 0 deletions lib/workers/repository/init/merge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
platform,
scm,
} from '../../../../test/util';
import * as decrypt from '../../../config/decrypt';
import { getConfig } from '../../../config/defaults';
import * as _migrateAndValidate from '../../../config/migrate-validate';
import * as _migrate from '../../../config/migration';
Expand All @@ -20,6 +21,7 @@ import {
checkForRepoConfigError,
detectRepoFileConfig,
mergeRenovateConfig,
setNpmTokenInNpmrc,
} from './merge';

jest.mock('../../../util/fs');
Expand Down Expand Up @@ -385,5 +387,86 @@ describe('workers/repository/init/merge', () => {
}),
).toBeDefined();
});

it('sets npmToken to npmrc when it is not inside encrypted', async () => {
scm.getFileList.mockResolvedValue(['package.json', '.renovaterc.json']);
fs.readLocalFile.mockResolvedValue(
'{"npmToken": "{{ secrets.NPM_TOKEN }}", "npmrc": "something_authToken=${NPM_TOKEN}"}',
);
migrateAndValidate.migrateAndValidate.mockResolvedValue({
...config,
npmToken: '{{ secrets.NPM_TOKEN }}',
npmrc: 'something_authToken=${NPM_TOKEN}',
warnings: [],
errors: [],
});
migrate.migrateConfig.mockImplementation((c) => ({
isMigrated: true,
migratedConfig: c,
}));
config.secrets = {
NPM_TOKEN: 'confidential',
};
const res = await mergeRenovateConfig(config);
expect(res.npmrc).toBe('something_authToken=confidential');
});

it('sets npmToken to npmrc when it is inside encrypted', async () => {
scm.getFileList.mockResolvedValue(['package.json', '.renovaterc.json']);
fs.readLocalFile.mockResolvedValue(
'{"encrypted": { "npmToken": "encrypted-token" }, "npmrc": "something_authToken=${NPM_TOKEN}"}',
);
migrateAndValidate.migrateAndValidate.mockResolvedValue({
...config,
npmrc: 'something_authToken=${NPM_TOKEN}',
encrypted: {
npmToken: 'encrypted-token',
},
warnings: [],
errors: [],
});
migrate.migrateConfig.mockImplementation((c) => ({
isMigrated: true,
migratedConfig: c,
}));
jest.spyOn(decrypt, 'decryptConfig').mockResolvedValueOnce({
...config,
npmrc: 'something_authToken=${NPM_TOKEN}',
npmToken: 'token',
});
const res = await mergeRenovateConfig(config);
expect(res.npmrc).toBe('something_authToken=token');
});
});

describe('setNpmTokenInNpmrc', () => {
it('skips in no npmToken found', () => {
const config = {};
setNpmTokenInNpmrc(config);
expect(config).toMatchObject({});
});

it('adds default npmrc registry if it does not exist', () => {
const config = { npmToken: 'token' };
setNpmTokenInNpmrc(config);
expect(config).toMatchObject({
npmrc: '//registry.npmjs.org/:_authToken=token\n',
});
});

it('adds npmToken at end of npmrc string if ${NPM_TOKEN} string not found', () => {
const config = { npmToken: 'token', npmrc: 'something\n' };
setNpmTokenInNpmrc(config);
expect(config).toMatchObject({ npmrc: 'something\n_authToken=token\n' });
});

it('replaces ${NPM_TOKEN} with npmToken value', () => {
const config = {
npmToken: 'token',
npmrc: 'something_auth=${NPM_TOKEN}\n',
};
setNpmTokenInNpmrc(config);
expect(config).toMatchObject({ npmrc: 'something_auth=token\n' });
});
});
});
34 changes: 34 additions & 0 deletions lib/workers/repository/init/merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { readLocalFile } from '../../../util/fs';
import * as hostRules from '../../../util/host-rules';
import * as queue from '../../../util/http/queue';
import * as throttle from '../../../util/http/throttle';
import { maskToken } from '../../../util/mask';
import { regEx } from '../../../util/regex';
import { getOnboardingConfig } from '../onboarding/branch/config';
import { getDefaultConfigFileName } from '../onboarding/branch/create';
import {
Expand Down Expand Up @@ -216,6 +218,7 @@ export async function mergeRenovateConfig(
const repository = config.repository!;
// Decrypt before resolving in case we need npm authentication for any presets
const decryptedConfig = await decryptConfig(migratedConfig, repository);
setNpmTokenInNpmrc(decryptedConfig);
// istanbul ignore if
if (is.string(decryptedConfig.npmrc)) {
logger.debug('Found npmrc in decrypted config - setting');
Expand All @@ -237,6 +240,7 @@ export async function mergeRenovateConfig(
logger.trace({ config: resolvedConfig }, 'resolved config after migrating');
resolvedConfig = migrationResult.migratedConfig;
}
setNpmTokenInNpmrc(resolvedConfig);
// istanbul ignore if
if (is.string(resolvedConfig.npmrc)) {
logger.debug(
Expand Down Expand Up @@ -278,3 +282,33 @@ export async function mergeRenovateConfig(
}
return returnConfig;
}

/** needed when using portal secrets for npmToken */
export function setNpmTokenInNpmrc(config: RenovateConfig): void {
if (!is.string(config.npmToken)) {
return;
}

const token = config.npmToken;
logger.debug({ npmToken: maskToken(token) }, 'Migrating npmToken to npmrc');

if (!is.string(config.npmrc)) {
logger.debug('Adding npmrc to config');
config.npmrc = `//registry.npmjs.org/:_authToken=${token}\n`;
delete config.npmToken;
return;
}

if (config.npmrc.includes(`\${NPM_TOKEN}`)) {
logger.debug(`Replacing \${NPM_TOKEN} with npmToken`);
config.npmrc = config.npmrc.replace(regEx(/\${NPM_TOKEN}/g), token);
} else {
logger.debug('Appending _authToken= to end of existing npmrc');
config.npmrc = config.npmrc.replace(
regEx(/\n?$/),
`\n_authToken=${token}\n`,
);
}

delete config.npmToken;
}

0 comments on commit 2194c22

Please # to comment.