mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-30 22:34:10 -05:00
feat: add forceEnhancedLegacySignature (#3655)
* feat: add forceEnhancedLegacySignature * Create rare-planets-travel.md * Update run-server.spec.ts * Update run-server.spec.ts
This commit is contained in:
parent
ddb6a22396
commit
dc571aabd2
11 changed files with 69 additions and 18 deletions
9
.changeset/rare-planets-travel.md
Normal file
9
.changeset/rare-planets-travel.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
'@verdaccio/config': minor
|
||||||
|
'@verdaccio/core': minor
|
||||||
|
'@verdaccio/types': minor
|
||||||
|
'@verdaccio/signature': minor
|
||||||
|
'@verdaccio/test-helper': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: add forceEnhancedLegacySignature
|
|
@ -28,7 +28,6 @@ const debug = buildDebug('verdaccio:config');
|
||||||
* @return {String} the config file path
|
* @return {String} the config file path
|
||||||
*/
|
*/
|
||||||
function findConfigFile(configPath?: string): string {
|
function findConfigFile(configPath?: string): string {
|
||||||
// console.log(process.env);
|
|
||||||
if (typeof configPath !== 'undefined') {
|
if (typeof configPath !== 'undefined') {
|
||||||
return path.resolve(configPath);
|
return path.resolve(configPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,8 @@ import assert from 'assert';
|
||||||
import buildDebug from 'debug';
|
import buildDebug from 'debug';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { APP_ERROR } from '@verdaccio/core';
|
import { APP_ERROR, warningUtils } from '@verdaccio/core';
|
||||||
|
import { Codes } from '@verdaccio/core/build/warning-utils';
|
||||||
import {
|
import {
|
||||||
Config as AppConfig,
|
Config as AppConfig,
|
||||||
AuthConf,
|
AuthConf,
|
||||||
|
@ -45,9 +46,11 @@ class Config implements AppConfig {
|
||||||
public users: any;
|
public users: any;
|
||||||
public auth: AuthConf;
|
public auth: AuthConf;
|
||||||
public server_id: string;
|
public server_id: string;
|
||||||
// @deprecated use configPath instead
|
|
||||||
public config_path: string;
|
|
||||||
public configPath: string;
|
public configPath: string;
|
||||||
|
/**
|
||||||
|
* @deprecated use configPath or config.getConfigPath();
|
||||||
|
*/
|
||||||
|
public self_path: string;
|
||||||
public storage: string | void;
|
public storage: string | void;
|
||||||
|
|
||||||
public plugins: string | void | null;
|
public plugins: string | void | null;
|
||||||
|
@ -57,14 +60,24 @@ class Config implements AppConfig {
|
||||||
public secret: string;
|
public secret: string;
|
||||||
public flags: FlagsConfig;
|
public flags: FlagsConfig;
|
||||||
public userRateLimit: RateLimit;
|
public userRateLimit: RateLimit;
|
||||||
public constructor(config: ConfigYaml & { config_path: string }) {
|
private configOptions: { forceEnhancedLegacySignature: boolean };
|
||||||
|
public constructor(
|
||||||
|
config: ConfigYaml & { config_path: string },
|
||||||
|
configOptions = { forceEnhancedLegacySignature: true }
|
||||||
|
) {
|
||||||
const self = this;
|
const self = this;
|
||||||
|
this.configOptions = configOptions;
|
||||||
this.storage = process.env.VERDACCIO_STORAGE_PATH || config.storage;
|
this.storage = process.env.VERDACCIO_STORAGE_PATH || config.storage;
|
||||||
if (!config.configPath) {
|
if (!config.configPath) {
|
||||||
throw new Error('config_path is required');
|
// backport self_path for previous to version 6
|
||||||
|
// @ts-expect-error
|
||||||
|
config.configPath = config.config_path ?? config.self_path;
|
||||||
|
if (!config.configPath) {
|
||||||
|
throw new Error('configPath property is required');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.config_path = config.config_path ?? (config.configPath as string);
|
|
||||||
this.configPath = config.configPath;
|
this.configPath = config.configPath;
|
||||||
|
this.self_path = this.configPath;
|
||||||
debug('config path: %s', this.configPath);
|
debug('config path: %s', this.configPath);
|
||||||
this.plugins = config.plugins;
|
this.plugins = config.plugins;
|
||||||
this.security = _.merge(defaultSecurity, config.security);
|
this.security = _.merge(defaultSecurity, config.security);
|
||||||
|
@ -117,6 +130,10 @@ class Config implements AppConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getConfigPath() {
|
||||||
|
return this.configPath;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for package spec
|
* Check for package spec
|
||||||
*/
|
*/
|
||||||
|
@ -127,18 +144,35 @@ class Config implements AppConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store or create whether receive a secret key
|
* Store or create whether receive a secret key
|
||||||
|
* @secret external secret key
|
||||||
*/
|
*/
|
||||||
public checkSecretKey(secret?: string): string {
|
public checkSecretKey(secret?: string): string {
|
||||||
debug('check secret key');
|
debug('check secret key');
|
||||||
if (_.isString(secret) && _.isEmpty(secret) === false) {
|
if (typeof secret === 'string' && _.isEmpty(secret) === false) {
|
||||||
this.secret = secret;
|
this.secret = secret;
|
||||||
debug('reusing previous key');
|
debug('reusing previous key');
|
||||||
return secret;
|
return secret;
|
||||||
}
|
}
|
||||||
// it generates a secret key
|
// generate a new a secret key
|
||||||
// FUTURE: this might be an external secret key, perhaps within config file?
|
// FUTURE: this might be an external secret key, perhaps within config file?
|
||||||
debug('generate a new key');
|
debug('generate a new key');
|
||||||
|
//
|
||||||
|
if (this.configOptions.forceEnhancedLegacySignature) {
|
||||||
this.secret = generateRandomSecretKey();
|
this.secret = generateRandomSecretKey();
|
||||||
|
} else {
|
||||||
|
this.secret =
|
||||||
|
this.security.enhancedLegacySignature === true
|
||||||
|
? generateRandomSecretKey()
|
||||||
|
: generateRandomHexString(32);
|
||||||
|
// set this to false allow use old token signature and is not recommended
|
||||||
|
// only use for migration reasons, major release will remove this property and
|
||||||
|
// set it by default
|
||||||
|
if (this.security.enhancedLegacySignature === false) {
|
||||||
|
warningUtils.emit(Codes.VERWAR005);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug('generated a new secret key %s', this.secret?.length);
|
||||||
return this.secret;
|
return this.secret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ export const TOKEN_VALID_LENGTH = 32;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Secret key must have 32 characters.
|
* Secret key must have 32 characters.
|
||||||
* @deprecated
|
|
||||||
*/
|
*/
|
||||||
export function generateRandomSecretKey(): string {
|
export function generateRandomSecretKey(): string {
|
||||||
return randomBytes(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);
|
return randomBytes(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "../utils"
|
"path": "../utils"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../core/core"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ export enum Codes {
|
||||||
VERWAR002 = 'VERWAR002',
|
VERWAR002 = 'VERWAR002',
|
||||||
VERWAR003 = 'VERWAR003',
|
VERWAR003 = 'VERWAR003',
|
||||||
VERWAR004 = 'VERWAR004',
|
VERWAR004 = 'VERWAR004',
|
||||||
|
VERWAR005 = 'VERWAR005',
|
||||||
// deprecation warnings
|
// deprecation warnings
|
||||||
VERDEP003 = 'VERDEP003',
|
VERDEP003 = 'VERDEP003',
|
||||||
}
|
}
|
||||||
|
@ -39,6 +40,12 @@ host:port (e.g. "localhost:4873") or full url '(e.g. "http://localhost:4873/")
|
||||||
https://verdaccio.org/docs/en/configuration#listen-port`
|
https://verdaccio.org/docs/en/configuration#listen-port`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
warningInstance.create(
|
||||||
|
verdaccioWarning,
|
||||||
|
Codes.VERWAR005,
|
||||||
|
'disable enhanced legacy signature is considered a security risk, please reconsider enable it'
|
||||||
|
);
|
||||||
|
|
||||||
warningInstance.create(
|
warningInstance.create(
|
||||||
verdaccioDeprecation,
|
verdaccioDeprecation,
|
||||||
Codes.VERDEP003,
|
Codes.VERDEP003,
|
||||||
|
|
|
@ -185,6 +185,7 @@ export interface APITokenOptions {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Security {
|
export interface Security {
|
||||||
|
enhancedLegacySignature?: boolean;
|
||||||
web: JWTOptions;
|
web: JWTOptions;
|
||||||
api: APITokenOptions;
|
api: APITokenOptions;
|
||||||
}
|
}
|
||||||
|
@ -266,8 +267,6 @@ export interface ConfigYaml {
|
||||||
flags?: FlagsConfig;
|
flags?: FlagsConfig;
|
||||||
userRateLimit?: RateLimit;
|
userRateLimit?: RateLimit;
|
||||||
// internal objects, added by internal yaml to JS config parser
|
// internal objects, added by internal yaml to JS config parser
|
||||||
// @deprecated use configPath instead
|
|
||||||
config_path?: string;
|
|
||||||
// save the configuration file path
|
// save the configuration file path
|
||||||
configPath?: string;
|
configPath?: string;
|
||||||
}
|
}
|
||||||
|
@ -282,6 +281,8 @@ export interface Config extends Omit<ConfigYaml, 'packages' | 'security' | 'conf
|
||||||
secret: string;
|
secret: string;
|
||||||
// save the configuration file path, it's fails without thi configPath
|
// save the configuration file path, it's fails without thi configPath
|
||||||
configPath: string;
|
configPath: string;
|
||||||
|
// @deprecated use configPath
|
||||||
|
self_path?: string;
|
||||||
// packages from yaml file looks different from packages inside the config file
|
// packages from yaml file looks different from packages inside the config file
|
||||||
packages: PackageList;
|
packages: PackageList;
|
||||||
// security object defaults is added by the config file but optional in the yaml file
|
// security object defaults is added by the config file but optional in the yaml file
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe('startServer via API', () => {
|
||||||
|
|
||||||
test('should fail on start with empty configuration', async () => {
|
test('should fail on start with empty configuration', async () => {
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
await expect(runServer({})).rejects.toThrow('config_path is required');
|
await expect(runServer({})).rejects.toThrow('configPath property is required');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should fail on start with null as entry', async () => {
|
test('should fail on start with null as entry', async () => {
|
||||||
|
|
|
@ -43,8 +43,7 @@ export function aesDecryptDeprecated(buf: Buffer, secret: string): Buffer {
|
||||||
export const TOKEN_VALID_LENGTH_DEPRECATED = 64;
|
export const TOKEN_VALID_LENGTH_DEPRECATED = 64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Genrate a secret key of 64 characters.
|
* Generate a secret key of 64 characters.
|
||||||
* @deprecated keys should be length max of 64
|
|
||||||
*/
|
*/
|
||||||
export function generateRandomSecretKeyDeprecated(): string {
|
export function generateRandomSecretKeyDeprecated(): string {
|
||||||
return generateRandomHexString(6);
|
return generateRandomHexString(6);
|
||||||
|
|
|
@ -20,7 +20,7 @@ const VERDACCIO_LEGACY_ENCRYPTION_KEY = process.env.VERDACCIO_LEGACY_ENCRYPTION_
|
||||||
|
|
||||||
export function aesEncrypt(value: string, key: string): string | void {
|
export function aesEncrypt(value: string, key: string): string | void {
|
||||||
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
|
// https://nodejs.org/api/crypto.html#crypto_crypto_createcipher_algorithm_password_options
|
||||||
// https://www.grainger.xyz/changing-from-cipher-to-cipheriv/
|
// https://www.grainger.xyz/posts/changing-from-cipher-to-cipheriv
|
||||||
debug('encrypt %o', value);
|
debug('encrypt %o', value);
|
||||||
debug('algorithm %o', defaultAlgorithm);
|
debug('algorithm %o', defaultAlgorithm);
|
||||||
// IV must be a buffer of length 16
|
// IV must be a buffer of length 16
|
||||||
|
|
|
@ -19,7 +19,7 @@ export async function initializeServer(
|
||||||
Storage
|
Storage
|
||||||
): Promise<Application> {
|
): Promise<Application> {
|
||||||
const app = express();
|
const app = express();
|
||||||
const config = new Config(configName);
|
const config = new Config(configName, { forceEnhancedLegacySignature: true });
|
||||||
config.storage = path.join(os.tmpdir(), '/storage', generateRandomHexString());
|
config.storage = path.join(os.tmpdir(), '/storage', generateRandomHexString());
|
||||||
// httpass would get path.basename() for configPath thus we need to create a dummy folder
|
// httpass would get path.basename() for configPath thus we need to create a dummy folder
|
||||||
// to avoid conflics
|
// to avoid conflics
|
||||||
|
|
Loading…
Reference in a new issue