Skip to content

Commit

Permalink
fix: remove CVE-2023-46809 revert config (#683)
Browse files Browse the repository at this point in the history
revert #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>
  • Loading branch information
fengmk2 and coderabbitai[bot] authored May 17, 2024
1 parent ade9305 commit ff00e42
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 64 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 21]
node-version: [18, 20, 22]
os: [ubuntu-latest]

steps:
Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [16, 18, 20]
node-version: [18, 20, 22]
os: [ubuntu-latest]

steps:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,3 @@ jobs:
secrets:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
GIT_TOKEN: ${{ secrets.GIT_TOKEN }}
with:
checkTest: false
25 changes: 14 additions & 11 deletions app/common/CryptoUtil.ts
Original file line number Diff line number Diff line change
@@ -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 } {
Expand All @@ -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');
}
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,6 @@
"url": "git@github.com:cnpm/cnpmcore.git"
},
"egg": {
"revert": [
"CVE-2023-46809"
],
"typescript": true
},
"keywords": [
Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand Down
22 changes: 22 additions & 0 deletions test/common/CryptoUtil.test.ts
Original file line number Diff line number Diff line change
@@ -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);
});
});
});
51 changes: 5 additions & 46 deletions test/port/webauth/webauthController.test.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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 () => {
Expand All @@ -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', () => {
Expand All @@ -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}`)
Expand All @@ -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}`)
Expand All @@ -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}`)
Expand All @@ -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 () => {
Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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 () => {
Expand All @@ -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`);

Expand All @@ -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`);

Expand All @@ -289,7 +265,6 @@ describe('test/port/webauth/webauthController.test.ts', () => {
});

describe('/-/v1/login/sso/:sessionId', () => {

let sessionId = '';
beforeEach(async () => {
sessionId = crypto.randomUUID();
Expand All @@ -304,15 +279,13 @@ describe('test/port/webauth/webauthController.test.ts', () => {
});

it('should sso login work', async () => {

const res = await app.httpRequest()
.post(`/-/v1/login/sso/${sessionId}`);

assert.equal(res.status, 200);
});

it('should check sessionId exists', async () => {

const res = await app.httpRequest()
.post('/-/v1/login/sso/banana');

Expand Down Expand Up @@ -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();
Expand All @@ -368,48 +336,39 @@ 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()
.get(`/-/v1/login/done/session/${sessionId}`);

assert.equal(res.status, 200);
assert.equal(res.body.token, 'banana');

assert(await cacheAdapter.get(sessionId) === null);

assert.equal(await cacheAdapter.get(sessionId), null);
});
});
});

0 comments on commit ff00e42

Please sign in to comment.