mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-24 21:15:51 +01: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
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
|
||||
*/
|
||||
function findConfigFile(configPath?: string): string {
|
||||
// console.log(process.env);
|
||||
if (typeof configPath !== 'undefined') {
|
||||
return path.resolve(configPath);
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ import assert from 'assert';
|
||||
import buildDebug from 'debug';
|
||||
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 {
|
||||
Config as AppConfig,
|
||||
AuthConf,
|
||||
@ -45,9 +46,11 @@ class Config implements AppConfig {
|
||||
public users: any;
|
||||
public auth: AuthConf;
|
||||
public server_id: string;
|
||||
// @deprecated use configPath instead
|
||||
public config_path: string;
|
||||
public configPath: string;
|
||||
/**
|
||||
* @deprecated use configPath or config.getConfigPath();
|
||||
*/
|
||||
public self_path: string;
|
||||
public storage: string | void;
|
||||
|
||||
public plugins: string | void | null;
|
||||
@ -57,14 +60,24 @@ class Config implements AppConfig {
|
||||
public secret: string;
|
||||
public flags: FlagsConfig;
|
||||
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;
|
||||
this.configOptions = configOptions;
|
||||
this.storage = process.env.VERDACCIO_STORAGE_PATH || config.storage;
|
||||
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.self_path = this.configPath;
|
||||
debug('config path: %s', this.configPath);
|
||||
this.plugins = config.plugins;
|
||||
this.security = _.merge(defaultSecurity, config.security);
|
||||
@ -117,6 +130,10 @@ class Config implements AppConfig {
|
||||
}
|
||||
}
|
||||
|
||||
public getConfigPath() {
|
||||
return this.configPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for package spec
|
||||
*/
|
||||
@ -127,18 +144,35 @@ class Config implements AppConfig {
|
||||
|
||||
/**
|
||||
* Store or create whether receive a secret key
|
||||
* @secret external secret key
|
||||
*/
|
||||
public checkSecretKey(secret?: string): string {
|
||||
debug('check secret key');
|
||||
if (_.isString(secret) && _.isEmpty(secret) === false) {
|
||||
if (typeof secret === 'string' && _.isEmpty(secret) === false) {
|
||||
this.secret = secret;
|
||||
debug('reusing previous key');
|
||||
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?
|
||||
debug('generate a new key');
|
||||
this.secret = generateRandomSecretKey();
|
||||
//
|
||||
if (this.configOptions.forceEnhancedLegacySignature) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ export const TOKEN_VALID_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* Secret key must have 32 characters.
|
||||
* @deprecated
|
||||
*/
|
||||
export function generateRandomSecretKey(): string {
|
||||
return randomBytes(TOKEN_VALID_LENGTH).toString('base64').substring(0, TOKEN_VALID_LENGTH);
|
||||
|
@ -9,6 +9,9 @@
|
||||
"references": [
|
||||
{
|
||||
"path": "../utils"
|
||||
},
|
||||
{
|
||||
"path": "../core/core"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ export enum Codes {
|
||||
VERWAR002 = 'VERWAR002',
|
||||
VERWAR003 = 'VERWAR003',
|
||||
VERWAR004 = 'VERWAR004',
|
||||
VERWAR005 = 'VERWAR005',
|
||||
// deprecation warnings
|
||||
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`
|
||||
);
|
||||
|
||||
warningInstance.create(
|
||||
verdaccioWarning,
|
||||
Codes.VERWAR005,
|
||||
'disable enhanced legacy signature is considered a security risk, please reconsider enable it'
|
||||
);
|
||||
|
||||
warningInstance.create(
|
||||
verdaccioDeprecation,
|
||||
Codes.VERDEP003,
|
||||
|
@ -185,6 +185,7 @@ export interface APITokenOptions {
|
||||
}
|
||||
|
||||
export interface Security {
|
||||
enhancedLegacySignature?: boolean;
|
||||
web: JWTOptions;
|
||||
api: APITokenOptions;
|
||||
}
|
||||
@ -266,8 +267,6 @@ export interface ConfigYaml {
|
||||
flags?: FlagsConfig;
|
||||
userRateLimit?: RateLimit;
|
||||
// internal objects, added by internal yaml to JS config parser
|
||||
// @deprecated use configPath instead
|
||||
config_path?: string;
|
||||
// save the configuration file path
|
||||
configPath?: string;
|
||||
}
|
||||
@ -282,6 +281,8 @@ export interface Config extends Omit<ConfigYaml, 'packages' | 'security' | 'conf
|
||||
secret: string;
|
||||
// save the configuration file path, it's fails without thi configPath
|
||||
configPath: string;
|
||||
// @deprecated use configPath
|
||||
self_path?: string;
|
||||
// packages from yaml file looks different from packages inside the config file
|
||||
packages: PackageList;
|
||||
// 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 () => {
|
||||
// @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 () => {
|
||||
|
@ -43,8 +43,7 @@ export function aesDecryptDeprecated(buf: Buffer, secret: string): Buffer {
|
||||
export const TOKEN_VALID_LENGTH_DEPRECATED = 64;
|
||||
|
||||
/**
|
||||
* Genrate a secret key of 64 characters.
|
||||
* @deprecated keys should be length max of 64
|
||||
* Generate a secret key of 64 characters.
|
||||
*/
|
||||
export function generateRandomSecretKeyDeprecated(): string {
|
||||
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 {
|
||||
// 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('algorithm %o', defaultAlgorithm);
|
||||
// IV must be a buffer of length 16
|
||||
|
@ -19,7 +19,7 @@ export async function initializeServer(
|
||||
Storage
|
||||
): Promise<Application> {
|
||||
const app = express();
|
||||
const config = new Config(configName);
|
||||
const config = new Config(configName, { forceEnhancedLegacySignature: true });
|
||||
config.storage = path.join(os.tmpdir(), '/storage', generateRandomHexString());
|
||||
// httpass would get path.basename() for configPath thus we need to create a dummy folder
|
||||
// to avoid conflics
|
||||
|
Loading…
Reference in New Issue
Block a user