fix: remove CVE-2023-46809 revert config (#683)
revert https://github.com/cnpm/cnpmcore/pull/650 run test on Node.js 22 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Chores** - Updated Node.js version support to 18, 20, and 22. - Removed unnecessary configuration from the release workflow. - Cleaned up outdated security-related entries in the package configuration. - Updated encryption functions in CryptoUtil.ts for improved security. - Refactored import statements for ES module syntax in webauthController.test.ts. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
4
.github/workflows/nodejs.yml
vendored
4
.github/workflows/nodejs.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [18, 20, 21]
|
||||
node-version: [18, 20, 22]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
@@ -83,7 +83,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node-version: [16, 18, 20]
|
||||
node-version: [18, 20, 22]
|
||||
os: [ubuntu-latest]
|
||||
|
||||
steps:
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -10,5 +10,3 @@ jobs:
|
||||
secrets:
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
|
||||
with:
|
||||
checkTest: false
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { generateKeyPairSync, publicEncrypt, privateDecrypt, constants } from 'crypto';
|
||||
import { generateKeyPairSync } from 'crypto';
|
||||
import NodeRSA from 'node-rsa';
|
||||
|
||||
// generate rsa key pair
|
||||
export function genRSAKeys(): { publicKey: string, privateKey: string } {
|
||||
@@ -17,17 +18,19 @@ export function genRSAKeys(): { publicKey: string, privateKey: string } {
|
||||
}
|
||||
|
||||
// encrypt rsa private key
|
||||
export function encryptRSA(publicKey: string, data: string): string {
|
||||
return publicEncrypt({
|
||||
key: publicKey,
|
||||
padding: constants.RSA_PKCS1_PADDING,
|
||||
}, Buffer.from(data, 'utf8')).toString('base64');
|
||||
export function encryptRSA(publicKey: string, plainText: string): string {
|
||||
const key = new NodeRSA(publicKey, 'pkcs1-public-pem', {
|
||||
encryptionScheme: 'pkcs1',
|
||||
environment: 'browser',
|
||||
});
|
||||
return key.encrypt(plainText, 'base64');
|
||||
}
|
||||
|
||||
// decrypt rsa private key
|
||||
export function decryptRSA(privateKey: string, data: string) {
|
||||
return privateDecrypt({
|
||||
key: privateKey,
|
||||
padding: constants.RSA_PKCS1_PADDING,
|
||||
}, Buffer.from(data, 'base64')).toString('utf8');
|
||||
export function decryptRSA(privateKey: string, encryptedBase64: string): string {
|
||||
const key = new NodeRSA(privateKey, 'pkcs1-private-pem', {
|
||||
encryptionScheme: 'pkcs1',
|
||||
environment: 'browser',
|
||||
});
|
||||
return key.decrypt(encryptedBase64, 'utf8');
|
||||
}
|
||||
|
||||
@@ -60,9 +60,6 @@
|
||||
"url": "git@github.com:cnpm/cnpmcore.git"
|
||||
},
|
||||
"egg": {
|
||||
"revert": [
|
||||
"CVE-2023-46809"
|
||||
],
|
||||
"typescript": true
|
||||
},
|
||||
"keywords": [
|
||||
@@ -105,6 +102,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"mime-types": "^2.1.35",
|
||||
"mysql2": "^3.9.4",
|
||||
"node-rsa": "^1.1.1",
|
||||
"npm-package-arg": "^10.1.0",
|
||||
"oss-cnpm": "^5.0.1",
|
||||
"p-map": "^4.0.0",
|
||||
@@ -126,6 +124,7 @@
|
||||
"@types/mime-types": "^2.1.1",
|
||||
"@types/mocha": "^10.0.1",
|
||||
"@types/mysql": "^2.15.21",
|
||||
"@types/node-rsa": "^1.1.4",
|
||||
"@types/npm-package-arg": "^6.1.1",
|
||||
"@types/semver": "^7.3.12",
|
||||
"@types/tar": "^6.1.4",
|
||||
|
||||
22
test/common/CryptoUtil.test.ts
Normal file
22
test/common/CryptoUtil.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { strict as assert } from 'node:assert';
|
||||
import { genRSAKeys, encryptRSA, decryptRSA } from '../../app/common/CryptoUtil';
|
||||
|
||||
describe('test/common/CryptoUtil.test.ts', () => {
|
||||
describe('genRSAKeys()', () => {
|
||||
it('should work', () => {
|
||||
const keys = genRSAKeys();
|
||||
assert(keys.publicKey);
|
||||
assert(keys.privateKey);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encryptRSA(), decryptRSA()', () => {
|
||||
it('should work', () => {
|
||||
const keys = genRSAKeys();
|
||||
// const plainText = 'hello world 中文😄';
|
||||
const plainText = 'hello world 中文';
|
||||
const encryptText = encryptRSA(keys.publicKey, plainText);
|
||||
assert.equal(decryptRSA(keys.privateKey, encryptText), plainText);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import assert from 'assert';
|
||||
import crypto from 'crypto';
|
||||
import { basename } from 'path';
|
||||
import { strict as assert } from 'node:assert';
|
||||
import crypto from 'node:crypto';
|
||||
import { basename } from 'node:path';
|
||||
import { app, mock } from 'egg-mock/bootstrap';
|
||||
import { AuthAdapter } from '../../../app/infra/AuthAdapter';
|
||||
import { CacheAdapter } from '../../../app/common/adapter/CacheAdapter';
|
||||
@@ -32,13 +32,10 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
assert.equal(res.body.error, "[INVALID_PARAM] must have required property 'hostname'");
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('GET /-/v1/login/request/session/:sessionId', () => {
|
||||
|
||||
let sessionId = '';
|
||||
const rsaKeys = genRSAKeys();
|
||||
beforeEach(async () => {
|
||||
@@ -48,14 +45,12 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
await cacheAdapter.set(`${sessionId}_privateKey`, rsaKeys.privateKey);
|
||||
});
|
||||
|
||||
|
||||
it('should check sessionId type', async () => {
|
||||
const res = await app.httpRequest()
|
||||
.get('/-/v1/login/request/session/123');
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
assert.equal(res.body.error, '[INVALID_PARAM] sessionId: must NOT have fewer than 36 characters');
|
||||
|
||||
});
|
||||
|
||||
it('should check sessionId exists', async () => {
|
||||
@@ -65,7 +60,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
assert.equal(res.status, 404);
|
||||
assert(/Session not found/.test(res.text));
|
||||
assert.equal(res.headers['content-type'], 'text/html; charset=utf-8');
|
||||
|
||||
});
|
||||
|
||||
it('should render login.html', async () => {
|
||||
@@ -74,13 +68,10 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
assert(/<title>Sign in to CNPM<\/title>/.test(res.text));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('POST /-/v1/login/request/session/:sessionId', () => {
|
||||
|
||||
let sessionId = '';
|
||||
const rsaKeys = genRSAKeys();
|
||||
beforeEach(async () => {
|
||||
@@ -90,14 +81,12 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
await cacheAdapter.set(`${sessionId}_privateKey`, rsaKeys.privateKey);
|
||||
});
|
||||
|
||||
|
||||
it('should check sessionId type', async () => {
|
||||
const res = await app.httpRequest()
|
||||
.post('/-/v1/login/request/session/123');
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
assert.equal(res.body.error, '[INVALID_PARAM] sessionId: must NOT have fewer than 36 characters');
|
||||
|
||||
});
|
||||
|
||||
it('should check sessionId exists', async () => {
|
||||
@@ -107,7 +96,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
assert.equal(res.status, 200);
|
||||
assert(/Session not found/.test(res.text));
|
||||
assert.equal(res.headers['content-type'], 'application/json; charset=utf-8');
|
||||
|
||||
});
|
||||
|
||||
describe('should verify login request body', () => {
|
||||
@@ -122,7 +110,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
});
|
||||
|
||||
it('should login success', async () => {
|
||||
|
||||
const password = encryptRSA(rsaKeys.publicKey, 'flymetothemoon');
|
||||
const res = await app.httpRequest()
|
||||
.post(`/-/v1/login/request/session/${sessionId}`)
|
||||
@@ -138,7 +125,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
});
|
||||
|
||||
it('should check password', async () => {
|
||||
|
||||
const password = encryptRSA(rsaKeys.publicKey, 'incorrect_password');
|
||||
const res = await app.httpRequest()
|
||||
.post(`/-/v1/login/request/session/${sessionId}`)
|
||||
@@ -151,11 +137,9 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
assert(/Please check your login name and password/.test(res.body.message));
|
||||
|
||||
});
|
||||
|
||||
it('should check user params', async () => {
|
||||
|
||||
const password = encryptRSA(rsaKeys.publicKey, 'incorrect_password');
|
||||
const res = await app.httpRequest()
|
||||
.post(`/-/v1/login/request/session/${sessionId}`)
|
||||
@@ -168,7 +152,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
assert(/Unauthorized, Validation Failed/.test(res.body.message));
|
||||
|
||||
});
|
||||
|
||||
it('should check authentication user (unbound webauthn) when enableWebauthn', async () => {
|
||||
@@ -222,11 +205,8 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
assert(/Public registration is not allowed/.test(res.body.message));
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('/-/v1/login/request/prepare/:sessionId', () => {
|
||||
@@ -256,7 +236,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
assert.equal(res.body.error, '[INVALID_PARAM] sessionId: must NOT have fewer than 36 characters');
|
||||
|
||||
});
|
||||
|
||||
it('should check sessionId exists', async () => {
|
||||
@@ -266,11 +245,9 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
assert.equal(res.status, 200);
|
||||
assert(/Session not found/.test(res.text));
|
||||
assert.equal(res.headers['content-type'], 'application/json; charset=utf-8');
|
||||
|
||||
});
|
||||
|
||||
it('should get prepare with authentication options', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.get(`/-/v1/login/request/prepare/${sessionId}?name=banana`);
|
||||
|
||||
@@ -279,7 +256,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
});
|
||||
|
||||
it('should get prepare with registration options', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.get(`/-/v1/login/request/prepare/${sessionId}?name=apple`);
|
||||
|
||||
@@ -289,7 +265,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
});
|
||||
|
||||
describe('/-/v1/login/sso/:sessionId', () => {
|
||||
|
||||
let sessionId = '';
|
||||
beforeEach(async () => {
|
||||
sessionId = crypto.randomUUID();
|
||||
@@ -304,7 +279,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
});
|
||||
|
||||
it('should sso login work', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.post(`/-/v1/login/sso/${sessionId}`);
|
||||
|
||||
@@ -312,7 +286,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
});
|
||||
|
||||
it('should check sessionId exists', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.post('/-/v1/login/sso/banana');
|
||||
|
||||
@@ -340,25 +313,20 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
assert.equal(res.status, 403);
|
||||
assert.equal(res.body.error, '[FORBIDDEN] invalid user info');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('/-/v1/login/request/success', () => {
|
||||
|
||||
it('should work', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.get('/-/v1/login/request/success');
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(res.headers['content-type'], 'text/html; charset=utf-8');
|
||||
assert(/Authorization Successful/.test(res.text));
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('/-/v1/login/done/session/:sessionId', () => {
|
||||
|
||||
let sessionId = '';
|
||||
beforeEach(async () => {
|
||||
sessionId = crypto.randomUUID();
|
||||
@@ -368,38 +336,31 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
|
||||
it('should check sessionId type', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.get('/-/v1/login/done/session/123');
|
||||
|
||||
assert.equal(res.status, 422);
|
||||
assert.equal(res.body.error, '[INVALID_PARAM] sessionId: must NOT have fewer than 36 characters');
|
||||
|
||||
});
|
||||
|
||||
it('should check sessionId exists', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.get(`/-/v1/login/done/session/${crypto.randomUUID()}`);
|
||||
|
||||
assert.equal(res.status, 404);
|
||||
assert.equal(res.body.error, '[NOT_FOUND] session not found');
|
||||
|
||||
});
|
||||
|
||||
it('should re-validate sessionId', async () => {
|
||||
|
||||
const res = await app.httpRequest()
|
||||
.get(`/-/v1/login/done/session/${sessionId}`);
|
||||
|
||||
assert.equal(res.status, 202);
|
||||
assert.equal(res.body.message, 'processing');
|
||||
assert.equal(res.headers['retry-after'], 1);
|
||||
|
||||
assert.equal(res.headers['retry-after'], '1');
|
||||
});
|
||||
|
||||
it('should check sessionId exists', async () => {
|
||||
|
||||
const cacheAdapter = await app.getEggObject(CacheAdapter);
|
||||
await cacheAdapter.set(sessionId, 'banana');
|
||||
const res = await app.httpRequest()
|
||||
@@ -407,9 +368,7 @@ describe('test/port/webauth/webauthController.test.ts', () => {
|
||||
|
||||
assert.equal(res.status, 200);
|
||||
assert.equal(res.body.token, 'banana');
|
||||
|
||||
assert(await cacheAdapter.get(sessionId) === null);
|
||||
|
||||
assert.equal(await cacheAdapter.get(sessionId), null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user