Skip to content

Commit 438a3c5

Browse files
committed
feat(): add test to validate advanced https agent configuration isn't overwritten
1 parent fdf1424 commit 438a3c5

1 file changed

Lines changed: 123 additions & 0 deletions

File tree

test/network.test.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import crypto from 'crypto';
2+
import https from 'https';
3+
import { ConnectionOptions } from 'tls';
4+
5+
import { MainClient } from '../src/index';
6+
import { getTestProxy } from './proxy.util';
7+
import { notAuthenticatedError } from './response.util';
8+
9+
// Expected pinned public key (SPKI SHA-256 hash)
10+
// You can extract it from the certificate using openssl:
11+
// openssl s_client -connect api.binance.com:443 </dev/null 2>/dev/null | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | base64
12+
const PINNED_PUBLIC_KEY = '8f+yoE6YBsp3ftzgATuaWqQiZna/x30yVX676Ky7lxY=';
13+
14+
// Load the trusted CA certificate (optional but recommended)
15+
// import fs from 'fs';
16+
// const trustedCert = fs.readFileSync('/path/to/certificate.pem');
17+
18+
const certificatePinningConfiguration: ConnectionOptions = {
19+
// ca: trustedCert, // Ensures only the specific CA is trusted
20+
checkServerIdentity: (host, cert) => {
21+
// Verify Subject Alternative Name (SAN)
22+
if (!cert.subjectaltname.includes('DNS:*.binance.com')) {
23+
throw new Error(
24+
`Certificate SAN mismatch: expected "*.binance.com", got ${cert.subjectaltname}`,
25+
);
26+
}
27+
const publicKey = cert.pubkey;
28+
const publicKeyHash = crypto
29+
.createHash('sha256')
30+
.update(publicKey)
31+
.digest('base64');
32+
33+
if (publicKeyHash !== PINNED_PUBLIC_KEY) {
34+
throw new Error(
35+
`Certificate pinning validation failed: expected ${PINNED_PUBLIC_KEY}, got ${publicKeyHash}`,
36+
);
37+
}
38+
return undefined;
39+
},
40+
};
41+
42+
describe('Test advanced https agent configuration', () => {
43+
// Simple positive check for working certificate pinning while keepAlive flag is active
44+
describe('pinned certificate', () => {
45+
const api = new MainClient(
46+
{
47+
keepAlive: true,
48+
},
49+
{
50+
...getTestProxy(),
51+
httpsAgent: new https.Agent({
52+
rejectUnauthorized: true,
53+
...certificatePinningConfiguration,
54+
}),
55+
},
56+
);
57+
58+
it('should throw for unauthenticated private calls', async () => {
59+
expect(() => api.getBalances()).rejects.toMatchObject(
60+
notAuthenticatedError(),
61+
);
62+
});
63+
64+
it('getServerTime() should return number', async () => {
65+
expect(await api.getServerTime()).toStrictEqual(expect.any(Number));
66+
});
67+
68+
it('getSystemStatus()', async () => {
69+
expect(await api.getSystemStatus()).toMatchObject({
70+
msg: 'normal',
71+
status: 0,
72+
});
73+
});
74+
75+
it('testConnectivity()', async () => {
76+
expect(await api.testConnectivity()).toStrictEqual({});
77+
});
78+
79+
it('getExchangeInfo()', async () => {
80+
expect(await api.getExchangeInfo()).toMatchObject({
81+
exchangeFilters: expect.any(Array),
82+
rateLimits: expect.any(Array),
83+
serverTime: expect.any(Number),
84+
symbols: expect.any(Array),
85+
timezone: expect.any(String),
86+
});
87+
});
88+
});
89+
90+
describe('mismatching pinned certificate', () => {
91+
const api = new MainClient(
92+
{
93+
keepAlive: true,
94+
},
95+
{
96+
...getTestProxy(),
97+
httpsAgent: new https.Agent({
98+
rejectUnauthorized: true,
99+
checkServerIdentity: (host, cert) => {
100+
const publicKeyHash = crypto
101+
.createHash('sha256')
102+
.update(cert.pubkey)
103+
.digest('base64');
104+
105+
const PINNED_PUBLIC_KEY = 'fakePublicKeyHashShouldMismatch==';
106+
if (publicKeyHash !== PINNED_PUBLIC_KEY) {
107+
throw new Error(
108+
`Certificate pinning validation failed: expected ${PINNED_PUBLIC_KEY}, got ${publicKeyHash}`,
109+
);
110+
// eslint-disable-next-line no-unreachable
111+
}
112+
113+
return undefined;
114+
},
115+
}),
116+
},
117+
);
118+
119+
it('getServerTime() should throw since the pinned certificate did not match', async () => {
120+
expect(api.getServerTime()).rejects.toThrow(expect.any(Object));
121+
});
122+
});
123+
});

0 commit comments

Comments
 (0)