Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: remove CVE-2023-46809 revert config #683

Merged
merged 3 commits into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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',
});
fengmk2 marked this conversation as resolved.
Show resolved Hide resolved
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',
});
fengmk2 marked this conversation as resolved.
Show resolved Hide resolved
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
24 changes: 24 additions & 0 deletions test/common/CryptoUtil.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
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);
// console.log(keys);
fengmk2 marked this conversation as resolved.
Show resolved Hide resolved
});
});

describe('encryptRSA(), decryptRSA()', () => {
it('should work', () => {
const keys = genRSAKeys();
// const plainText = 'hello world 中文😄';
const plainText = 'hello world 中文';
const encryptText = encryptRSA(keys.publicKey, plainText);
// console.log(encryptText);
fengmk2 marked this conversation as resolved.
Show resolved Hide resolved
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);
});
});
});
Loading