Skip to content

Commit 35adaff

Browse files
test: remove insecure TLS bypasses and expand HTTPS coverage (#141)
1 parent 7d0a445 commit 35adaff

3 files changed

Lines changed: 205 additions & 67 deletions

File tree

‎test/certs/server.crt‎

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
-----BEGIN CERTIFICATE-----
2-
MIIDEDCCAfgCAQEwDQYJKoZIhvcNAQELBQAwTjELMAkGA1UEBhMCVVMxCzAJBgNV
3-
BAgMAldBMREwDwYDVQQHDAhCZWxsZXZ1ZTEPMA0GA1UECgwGR29vZ2xlMQ4wDAYD
4-
VQQLDAVDbG91ZDAeFw0yNTEwMTQxNjMzMTNaFw0zNTEwMTIxNjMzMTNaME4xCzAJ
5-
BgNVBAYTAlVTMQswCQYDVQQIDAJXQTERMA8GA1UEBwwIQmVsbGV2dWUxDzANBgNV
6-
BAoMBkdvb2dsZTEOMAwGA1UECwwFQ2xvdWQwggEiMA0GCSqGSIb3DQEBAQUAA4IB
7-
DwAwggEKAoIBAQCpmjJqERR7zxw/Jcn+JkFxeFD+8vrKgjWDYUrIDUeFctv1trYC
8-
yiz7T3ffF8Pavir322KV22haf/O0r4AO3ZJI3oQcwitB/o9BzvwKK4UcQlHhOTas
9-
RxMF4gE7VyNLJv4tWM6SCOX58kCLpFEq6HyyP4UNdzxQX1+ZcsljVw+cYpbC0PjD
10-
kIhc0fsGFcNdEtO31KpBVYeSpIMNmSIWyN7RESW6NkVvuV1Dwfw8P4n6llyNaVK/
11-
bKJsX8XeMFEZ3q7hbTsOVCoCuiEgdR3HYsSLx16aKaRfFZzXoQg7Osyct3VApRUy
12-
drfcqEbh0b+xKxB+sW56hC42Zl0/Q52QPDEZAgMBAAEwDQYJKoZIhvcNAQELBQAD
13-
ggEBAEZNCifdR2DZ29H4xp+Dkza7JiT1SbddGEIn2iXdFPfFGvRL68LTFr2hZkf+
14-
ga8HwRtxVvktvX41byzjjYoz5ALJ75UvHKw1Q/yqS08HwODNyMzRdROFGr6UzLtj
15-
GGfYnzUhmUUKBTNF3GHSxZRsARtMnH38lNfRJ5xXXjWa18VyKXAFA6CKWRD+1ey8
16-
/ehZX7BHZGZpSb/PLEEa3PRZMb9rAjk+pFNOrCF1QEkxJ+ZlYmRWTcpuxinPuY+8
17-
5y5FyRYObGYzXxdFotxrc9Jg6orT5YhDW8hORWMs1cHQKyKIiqZpiS53rK+4+uYR
18-
1IlO4QjunTe6r3xRt8G2ef47VGg=
2+
MIIDJTCCAg2gAwIBAgIUCHJi6SkNut17iBSU+6zwrzsTwHwwDQYJKoZIhvcNAQEL
3+
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDQxMjAwNTkyNFoXDTM2MDQw
4+
OTAwNTkyNFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
5+
AAOCAQ8AMIIBCgKCAQEAy4X8/+UjtlhXxHzHr34HWQmSNeHR9FL7RUvsDxlZZ6VL
6+
TMC1hCXUO0iKmBigtgQ54jJ9753T5yMmDGk4mTU33acoYm3d/EHhxMv4Pu2o0R0Q
7+
yhU6HStxcCACBOI7Kehuy0ZDFf2YiIsZHMTLZvfBKuZP9LtPmaDrOSV+cXaEsvnc
8+
CCaB855HprGFE3fukslCQ8sPM8xudRF0raj5hL1/aAMbKaGtaBEc1tj2k3GlArBw
9+
Uu89qZeC77MSmlDxoFWpcEbMZ7lPzYAoC6vMIHHBCuHXvJUudIPIdZseYE57aW+5
10+
jyBQ/6kRZSpOSyvhIOovFmjNnSSio+/IsXQX3UrLDQIDAQABo28wbTAdBgNVHQ4E
11+
FgQUGh54PJnDeLmHtM5N27Pl+pvtIg0wHwYDVR0jBBgwFoAUGh54PJnDeLmHtM5N
12+
27Pl+pvtIg0wDwYDVR0TAQH/BAUwAwEB/zAaBgNVHREEEzARgglsb2NhbGhvc3SH
13+
BH8AAAEwDQYJKoZIhvcNAQELBQADggEBACoXlufc6bCHtuGEfKw9+x9b4ZiZrNX+
14+
rIDS4+tzq0SkbRF9naNepUfNU3Z0cx+kTDvTmH8bjBRxm2YORLUOot/mtrsI6hrK
15+
poCcSrJA15STe2ftvNVlft9v4xPswzusWrsxTZ2oS5C1ys1UbOjFTb2qCkeYEjBw
16+
4dfl/0lat9x9XRbfYBCWCdFo5izRhGXbgtBAl25fkEyKG7bz8q6zWFKm0xw8978U
17+
kCKz2laAcuETBm1jCuVEd3OHAlv2/7vDKH2PpOvKwtWe2kBhILnjuDsa5LyzNJDR
18+
ECVpOnoxrGGd8c2ezkbfH5r55aFRjFWNivt6cdbXpAgU+iqE8fISlmA=
1919
-----END CERTIFICATE-----

‎test/certs/server.key‎

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
-----BEGIN PRIVATE KEY-----
2-
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCpmjJqERR7zxw/
3-
Jcn+JkFxeFD+8vrKgjWDYUrIDUeFctv1trYCyiz7T3ffF8Pavir322KV22haf/O0
4-
r4AO3ZJI3oQcwitB/o9BzvwKK4UcQlHhOTasRxMF4gE7VyNLJv4tWM6SCOX58kCL
5-
pFEq6HyyP4UNdzxQX1+ZcsljVw+cYpbC0PjDkIhc0fsGFcNdEtO31KpBVYeSpIMN
6-
mSIWyN7RESW6NkVvuV1Dwfw8P4n6llyNaVK/bKJsX8XeMFEZ3q7hbTsOVCoCuiEg
7-
dR3HYsSLx16aKaRfFZzXoQg7Osyct3VApRUydrfcqEbh0b+xKxB+sW56hC42Zl0/
8-
Q52QPDEZAgMBAAECggEAHeOIPev7paQGsNQ6iDj6sIIJGBz++IBA6QHU2XOJpqlq
9-
vR8xbUVu7uz+zYiVIfAAh4SYl5s+hTBmZPHGyhffJ0tbkevgDrXLTkgpX6tyvlur
10-
rVrVWT//SLqfB/Ofyyc4hQpRAShjSwyXtmRDT6IAE7OhAZIBRZLBOLwjZIfbvu3R
11-
e3uDzeqVq0MsiJiQIzrjMIfvtVFd35TjsfdHNv88jWGnysy8Xc95JIXeTaWSzXaL
12-
+VNGqQnXDOFKuI6vHCfCwtTAuWQqb0K7oHBMxXRHrfRH8p/zFeTuiKYwvFgGq9qm
13-
omQa476QHA0JZ6O196uEZGGKQfhU8lvLLm+d5tjkWQKBgQDTZ228nuHk/Xv6KiJg
14-
YC8y68y2JyWV0i9w3vu0vrT6fnXg6t+FOrc+Mc9I6hmVFG/C+o0bKywIXaiQcCeQ
15-
hxz/l1S5UpNVYqe55jWVIxdNcxMZooiKgzR/CLaFh+yrOuQlsU1x7Ym8CFMXINLB
16-
cbb/efojCQzsc0LOr8IX3ehMNQKBgQDNYVP6jqWJkGnsbX+C9HzncKxfIjh5S3N6
17-
5RqRvqZCqte1zKJR/P/tkyDMIoGffqQzro+/IAFtog2QKAmrHA1kiM0v82QYGTxU
18-
6kExBvO+EKuJ046qb8RWD/pRK8Hx0VUtsK9Z8wDTNWzh5NAZiUPEPVEnNquDiLdr
19-
+jQwyBDF1QKBgQCFWEX1pdoi0Gj1AMKyO7lJy0ZS1xp0CCH4dg4akfgh0MaV2lCm
20-
/sQ6rLxs54y/Ziagu91pd6/MjxcWFEhAd5ko9tFwG8/nGdVmAvllWr8GEUHI96Zc
21-
iCoCvwIx8+yqjPj/dXi+FfC89BtFCWUms42UU+IdW0YVlxZavK4W09gEyQKBgQCL
22-
otbEDeRCqWs7Eh+V28BoYtTvQYcAAOqc7dOor+S+gwVyV5UtBUTENDoiUHutAx+E
23-
+/RDz/DopzttfatFKSd56QEIzwSI1e+NFFAKk115JkBazvm6q47jrK1WLtgIH3k4
24-
PV6bW3p+H17OHxHVqtvmOoJIlQT7wyJiSZTebcfpmQKBgQDNIkhkoH8CEgK4ecbe
25-
Q3CoWALKYc82b5y1Dnej2ZehKG+ImDz50NIupotqZeaMQiu2NtQGLyYTgOekVFnu
26-
AcLXsQAYVvTzPid1JDOic6tAuSGgcY/OuXnZc9/rBrOsL/xteJmJCaeFNjLKl1JM
27-
rdhCOgf3FSzPy3+PzriASBICTw==
2+
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLhfz/5SO2WFfE
3+
fMevfgdZCZI14dH0UvtFS+wPGVlnpUtMwLWEJdQ7SIqYGKC2BDniMn3vndPnIyYM
4+
aTiZNTfdpyhibd38QeHEy/g+7ajRHRDKFTodK3FwIAIE4jsp6G7LRkMV/ZiIixkc
5+
xMtm98Eq5k/0u0+ZoOs5JX5xdoSy+dwIJoHznkemsYUTd+6SyUJDyw8zzG51EXSt
6+
qPmEvX9oAxspoa1oERzW2PaTcaUCsHBS7z2pl4LvsxKaUPGgValwRsxnuU/NgCgL
7+
q8wgccEK4de8lS50g8h1mx5gTntpb7mPIFD/qRFlKk5LK+Eg6i8WaM2dJKKj78ix
8+
dBfdSssNAgMBAAECggEAH1y3Xut9dFTquXGNB+MA2NybtkflJaQ/6i7H4o/CcjFL
9+
iDlN/14g9T25dbSd106RaOTk+S61/7Ev5Mu2GA3WXQasN3a8tWmlk8DmsTPIRukW
10+
tD1PJj2SjnewN/ZwrYWOxS4/mjzo62mb5g4BVg2pq6Wv/oN6wa7FcJnxgSSFUFrg
11+
DpUhecBXTSLHMisls2Oyn90g2NcKbvuxjPxet1F9+Swa0wU1kjr9u9ecElTkO4yg
12+
Qjy34rQmEX0h7T5F2OPiPpoYS/78dVP7c5vCKDWp7QT7Z4P7DgIyXRXtLEsrfQpp
13+
EAWU75B2ypwfqPY1zAw2ijfR5Z5Fl3DV8ZjLD5j2QQKBgQDlvsXTYlVklzYBkZIz
14+
ZFBuP0Op7QjrRLJ0OfZTTRUUlccfpk1JvNIP9PCcteilBMpYIayUN7UzVpp8Qybn
15+
vmrgGD3nBpE4E0v6iuM6l9rq/TmqUqUOt6nqmOWwUw/e9FBOLZ7jhGYQ4MkYANTG
16+
MkOZ+i0l6MnGurvcCPEM2iaR0QKBgQDiyBfAUJk9+bSkXHK50w2gcYk6Yvrkp8mH
17+
v3fY2OxYZqamvhz8lqyIpAWbJAGlqXb4Gp/RB4TA3eNk+fNiPsCNNCHEGAr0gnM5
18+
9baaGjLbKBKu71CdA1CkkO7dQJ5PMDOExQjx2bo8It02dzYbW0vk32/Yin/WhNXL
19+
L8F2BqoYfQKBgQC+XCnSEmIq7NeEyTdYeb/i+Wx5ObvwJIWwo+4j63SSD+BjqwnT
20+
FS8ApbVQQ7G2OZfnGk/Cp73uAc4TNBjiX/ZyI+P2roxY6DRGLhpFDFoJ5zOGmt/E
21+
qA2UIof+Z3R5CfoYLNjAL43aYkZ9KwMiDbfRt2b9SDsX/NV3ZblFKuRWYQKBgGlU
22+
dCufg71USEF6qtKCIzcc5JbYuB0RjTnehSSThBp++vJBJKdwuAvy2qO28ojmD9qm
23+
SwpECrWlmWMh8Jf1+2raBsDURepQ2IHYDQrAFlTR5POZNYDntEHrCvZ6d8zh31vQ
24+
RBpIfQZHTyVn8xp7qeFQodsaYMvbAI2RzbIq4D/lAoGAcAGtnnCta3vsqInMICOP
25+
cgElgTgrBvcCYOQAwMZimhwl6gJjmm0PXEAplHK0BIeO4Xu18ZjvIN/pJynToo9W
26+
bvpgh3W9P9LkVdkaHEr9zlSvhAGE80NNjUeO3Ur7GonkclwNXwpbET/Qiv594Osn
27+
gm6J9stVFd5jicl3SyYh5iw=
2828
-----END PRIVATE KEY-----

‎test/test.js‎

Lines changed: 162 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import fs from 'node:fs';
22
import https from 'node:https';
3-
import process from 'node:process';
43
import express from 'express';
54
import { describe, it } from 'mocha';
65
import request from 'supertest';
76
import yes from '../lib/index.js';
87

9-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0;
8+
const TEST_SERVER_CERT = fs.readFileSync('./test/certs/server.crt');
109

1110
describe('yes', () => {
1211
it('should perform the 301 for an http request', (done) => {
@@ -40,16 +39,15 @@ describe('yes', () => {
4039

4140
// Verify the request returns the right header when using https
4241
const server = createSecureServer(app);
43-
request('https://localhost:8443')
42+
request(server)
4443
.get('/test')
44+
.ca(TEST_SERVER_CERT)
4545
.expect('Strict-Transport-Security', 'max-age=86400; includeSubDomains')
4646
.expect(200)
4747
.end((error) => {
4848
if (error) {
4949
throw error;
5050
}
51-
52-
server.close();
5351
done();
5452
});
5553
}).timeout(60_000);
@@ -66,16 +64,15 @@ describe('yes', () => {
6664
});
6765

6866
const server = createSecureServer(app);
69-
request('https://localhost:8443')
67+
request(server)
7068
.get('/test')
69+
.ca(TEST_SERVER_CERT)
7170
.expect('Strict-Transport-Security', 'max-age=86400')
7271
.expect(200)
7372
.end((error) => {
7473
if (error) {
7574
throw error;
7675
}
77-
78-
server.close();
7976
done();
8077
});
8178
}).timeout(60_000);
@@ -92,16 +89,15 @@ describe('yes', () => {
9289
});
9390

9491
const server = createSecureServer(app);
95-
request('https://localhost:8443')
92+
request(server)
9693
.get('/test')
94+
.ca(TEST_SERVER_CERT)
9795
.expect('Strict-Transport-Security', 'max-age=86400')
9896
.expect(200)
9997
.end((error) => {
10098
if (error) {
10199
throw error;
102100
}
103-
104-
server.close();
105101
done();
106102
});
107103
}).timeout(60_000);
@@ -131,20 +127,162 @@ describe('yes', () => {
131127
done();
132128
});
133129
});
130+
131+
it('should include preload when configured', (done) => {
132+
const app = express();
133+
app.use(yes({ preload: true }));
134+
app.get('/test', (_request, response) => {
135+
response.sendStatus(200);
136+
});
137+
138+
const server = createSecureServer(app);
139+
request(server)
140+
.get('/test')
141+
.ca(TEST_SERVER_CERT)
142+
.expect(
143+
'Strict-Transport-Security',
144+
'max-age=86400; includeSubDomains; preload',
145+
)
146+
.expect(200)
147+
.end((error) => {
148+
if (error) {
149+
throw error;
150+
}
151+
done();
152+
});
153+
}).timeout(60_000);
154+
155+
it('should omit includeSubDomains when disabled', (done) => {
156+
const app = express();
157+
app.use(yes({ includeSubDomains: false }));
158+
app.get('/test', (_request, response) => {
159+
response.sendStatus(200);
160+
});
161+
162+
const server = createSecureServer(app);
163+
request(server)
164+
.get('/test')
165+
.ca(TEST_SERVER_CERT)
166+
.expect('Strict-Transport-Security', 'max-age=86400')
167+
.expect(200)
168+
.end((error) => {
169+
if (error) {
170+
throw error;
171+
}
172+
done();
173+
});
174+
}).timeout(60_000);
175+
176+
it('should include includeSubDomains when explicitly enabled', (done) => {
177+
const app = express();
178+
app.use(yes({ includeSubDomains: true }));
179+
app.get('/test', (_request, response) => {
180+
response.sendStatus(200);
181+
});
182+
183+
const server = createSecureServer(app);
184+
request(server)
185+
.get('/test')
186+
.ca(TEST_SERVER_CERT)
187+
.expect('Strict-Transport-Security', 'max-age=86400; includeSubDomains')
188+
.expect(200)
189+
.end((error) => {
190+
if (error) {
191+
throw error;
192+
}
193+
done();
194+
});
195+
}).timeout(60_000);
196+
197+
describe('includeSubDomains', () => {
198+
it('should include the directive by default over a secure connection', () => {
199+
return expectSecureHeader({}, 'max-age=86400; includeSubDomains');
200+
});
201+
202+
it('should include the directive when explicitly enabled over a secure connection', () => {
203+
return expectSecureHeader(
204+
{ includeSubDomains: true },
205+
'max-age=86400; includeSubDomains',
206+
);
207+
});
208+
209+
it('should omit the directive when disabled over a secure connection', () => {
210+
return expectSecureHeader({ includeSubDomains: false }, 'max-age=86400');
211+
});
212+
213+
it('should compose correctly with preload and maxAge when enabled', () => {
214+
return expectSecureHeader(
215+
{ includeSubDomains: true, preload: true, maxAge: 31_536_000 },
216+
'max-age=31536000; includeSubDomains; preload',
217+
);
218+
});
219+
220+
it('should compose correctly with preload and maxAge when disabled', () => {
221+
return expectSecureHeader(
222+
{ includeSubDomains: false, preload: true, maxAge: 31_536_000 },
223+
'max-age=31536000; preload',
224+
);
225+
});
226+
227+
it('should include the directive by default for forwarded https requests', () => {
228+
return expectForwardedSecureHeader(
229+
{},
230+
'max-age=86400; includeSubDomains',
231+
);
232+
});
233+
234+
it('should honor an explicit true value for forwarded https requests', () => {
235+
return expectForwardedSecureHeader(
236+
{ includeSubDomains: true },
237+
'max-age=86400; includeSubDomains',
238+
);
239+
});
240+
241+
it('should honor an explicit false value for forwarded https requests', () => {
242+
return expectForwardedSecureHeader(
243+
{ includeSubDomains: false },
244+
'max-age=86400',
245+
);
246+
});
247+
});
134248
});
135249

136250
function createSecureServer(app) {
137-
// Server the app over https
138-
return https
139-
.createServer(
140-
{
141-
key: fs.readFileSync('./test/certs/server.key'),
142-
cert: fs.readFileSync('./test/certs/server.crt'),
143-
ca: fs.readFileSync('./test/certs/ca.crt'),
144-
requestCert: true,
145-
rejectUnauthorized: false,
146-
},
147-
app,
148-
)
149-
.listen('8443');
251+
return https.createServer(
252+
{
253+
key: fs.readFileSync('./test/certs/server.key'),
254+
cert: fs.readFileSync('./test/certs/server.crt'),
255+
},
256+
app,
257+
);
258+
}
259+
260+
function expectSecureHeader(options, expectedHeader) {
261+
const app = express();
262+
app.use(yes(options));
263+
app.get('/test', (_request, response) => {
264+
response.sendStatus(200);
265+
});
266+
267+
const server = createSecureServer(app);
268+
return request(server)
269+
.get('/test')
270+
.ca(TEST_SERVER_CERT)
271+
.expect('Strict-Transport-Security', expectedHeader)
272+
.expect(200);
273+
}
274+
275+
function expectForwardedSecureHeader(options, expectedHeader) {
276+
const app = express();
277+
app.use(yes(options));
278+
app.get('/test', (_request, response) => {
279+
response.sendStatus(200);
280+
});
281+
282+
return request(app)
283+
.get('/test')
284+
.set('X-Forwarded-Proto', 'https')
285+
.set('Host', 'example.com')
286+
.expect('Strict-Transport-Security', expectedHeader)
287+
.expect(200);
150288
}

0 commit comments

Comments
 (0)