koa-classic-server 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -120
- package/index.cjs +236 -21
- package/package.json +10 -5
- package/.github/workflows/npm-publish.yml +0 -98
- package/CLAUDE.md +0 -101
- package/__tests__/benchmark-results-baseline-v1.2.0.txt +0 -354
- package/__tests__/benchmark-results-optimized-v2.0.0.txt +0 -354
- package/__tests__/benchmark-results-v3.0.0.txt +0 -372
- package/__tests__/benchmark.js +0 -239
- package/__tests__/caching-headers.test.js +0 -556
- package/__tests__/compression-fixtures/data.json +0 -1
- package/__tests__/compression-fixtures/large.txt +0 -1
- package/__tests__/compression-fixtures/small.txt +0 -1
- package/__tests__/compression.test.js +0 -284
- package/__tests__/customTest/README.md +0 -6
- package/__tests__/customTest/loadConfig.util.js +0 -41
- package/__tests__/customTest/serversToLoad.util.js +0 -93
- package/__tests__/demo-regex-index.js +0 -140
- package/__tests__/deprecation-warnings.test.js +0 -105
- package/__tests__/directory-sorting-links.test.js +0 -135
- package/__tests__/dt-unknown.test.js +0 -635
- package/__tests__/ejs.test.js +0 -299
- package/__tests__/hidden-fixtures/.dot-dir/inside.txt +0 -1
- package/__tests__/hidden-fixtures/.env +0 -2
- package/__tests__/hidden-fixtures/.well-known/acme-challenge.txt +0 -1
- package/__tests__/hidden-fixtures/config/secrets/password.txt +0 -1
- package/__tests__/hidden-fixtures/data.key +0 -1
- package/__tests__/hidden-fixtures/file.secret +0 -1
- package/__tests__/hidden-fixtures/index.html +0 -1
- package/__tests__/hidden-fixtures/normal.txt +0 -1
- package/__tests__/hidden-fixtures/subdir/.env +0 -1
- package/__tests__/hidden-fixtures/subdir/regular.txt +0 -1
- package/__tests__/hidden-option.test.js +0 -407
- package/__tests__/hideExtension.test.js +0 -607
- package/__tests__/index-option.test.js +0 -449
- package/__tests__/index.test.js +0 -345
- package/__tests__/listing.test.js +0 -437
- package/__tests__/logger.test.js +0 -232
- package/__tests__/performance.test.js +0 -370
- package/__tests__/publicWwwTest/cartella/sottocartella/ciao.html +0 -11
- package/__tests__/publicWwwTest/cartella/sottocartella/provaEjs/testEjs.ejs +0 -11
- package/__tests__/publicWwwTest/cartella vuota con spazi nel nome/file con spazio nel nome .txt +0 -1
- package/__tests__/publicWwwTest/cartella vuota con spazi nel nome /file con spazio nel nome .txt +0 -1
- package/__tests__/publicWwwTest/ejs-templates/complex.ejs +0 -56
- package/__tests__/publicWwwTest/ejs-templates/index.ejs +0 -30
- package/__tests__/publicWwwTest/ejs-templates/simple.ejs +0 -13
- package/__tests__/publicWwwTest/ejs-templates/with-conditional.ejs +0 -28
- package/__tests__/publicWwwTest/ejs-templates/with-escaping.ejs +0 -26
- package/__tests__/publicWwwTest/ejs-templates/with-loop.ejs +0 -16
- package/__tests__/publicWwwTest/hideext-test/about/index.html +0 -1
- package/__tests__/publicWwwTest/hideext-test/about.EJS +0 -1
- package/__tests__/publicWwwTest/hideext-test/about.ejs +0 -2
- package/__tests__/publicWwwTest/hideext-test/blog/articolo.ejs +0 -2
- package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina +0 -1
- package/__tests__/publicWwwTest/hideext-test/conflict-test/pagina.ejs +0 -1
- package/__tests__/publicWwwTest/hideext-test/file.ejs.bak +0 -1
- package/__tests__/publicWwwTest/hideext-test/index.ejs +0 -2
- package/__tests__/publicWwwTest/hideext-test/photo.txt +0 -1
- package/__tests__/publicWwwTest/hideext-test/sezione/index.ejs +0 -1
- package/__tests__/publicWwwTest/hideext-test/style.css +0 -1
- package/__tests__/publicWwwTest/ile_vuoto.txt +0 -0
- package/__tests__/publicWwwTest/index.html +0 -11
- package/__tests__/publicWwwTest/prova file .txt +0 -2
- package/__tests__/publicWwwTest/semplicetxt.txt +0 -1
- package/__tests__/publicWwwTest/test-page.html +0 -1
- package/__tests__/publicWwwTest/test.txt +0 -1
- package/__tests__/range-fixtures/sample.txt +0 -1
- package/__tests__/range.test.js +0 -223
- package/__tests__/security-headers.test.js +0 -165
- package/__tests__/security.test.js +0 -322
- package/__tests__/server-cache-fixtures/large.txt +0 -1
- package/__tests__/server-cache-fixtures/small.txt +0 -1
- package/__tests__/server-cache.test.js +0 -594
- package/__tests__/setup-benchmark.js +0 -178
- package/__tests__/symlink.test.js +0 -441
- package/__tests__/template-timeout.test.js +0 -321
- package/__tests__/test-regex-quick.js +0 -158
- package/__tests__/useOriginalUrl.test.js +0 -213
- package/docs/ACTION_PLAN.md +0 -293
- package/docs/BENCHMARKS.md +0 -317
- package/docs/CHANGELOG.md +0 -880
- package/docs/CODE_REVIEW.md +0 -300
- package/docs/DEBUG_REPORT.md +0 -593
- package/docs/DOCUMENTATION.md +0 -1864
- package/docs/EXAMPLES_INDEX_OPTION.md +0 -395
- package/docs/FLOW_DIAGRAM.md +0 -954
- package/docs/INDEX_OPTION_PRIORITY.md +0 -527
- package/docs/OPTIMIZATION_HTTP_CACHING.md +0 -689
- package/docs/OPTIMIZATION_ROADMAP_for_V3.md +0 -864
- package/docs/PERFORMANCE_ANALYSIS.md +0 -839
- package/docs/PERFORMANCE_COMPARISON.md +0 -388
- package/docs/noteExports.md +0 -148
- package/docs/security_improvement_for_V3.md +0 -421
- package/docs/template-engine/TEMPLATE_ENGINE_GUIDE.md +0 -1734
- package/docs/template-engine/esempi-incrementali.js +0 -192
- package/docs/template-engine/examples/esempio1-nessun-dato.ejs +0 -12
- package/docs/template-engine/examples/esempio2-una-variabile.ejs +0 -11
- package/docs/template-engine/examples/esempio3-piu-variabili.ejs +0 -15
- package/docs/template-engine/examples/esempio4-condizionale.ejs +0 -18
- package/docs/template-engine/examples/esempio5-loop.ejs +0 -18
- package/docs/template-engine/examples/index-esempi.html +0 -181
- package/docs/template-engine/examples/index.html +0 -40
- package/docs/template-engine/examples/test.ejs +0 -64
- package/eslint.config.mjs +0 -17
- package/jest.config.js +0 -18
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
const supertest = require('supertest');
|
|
2
|
-
const koaClassicServer = require('../index.cjs');
|
|
3
|
-
const Koa = require('koa');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
const root = path.join(__dirname, 'hidden-fixtures');
|
|
7
|
-
|
|
8
|
-
function createApp(hiddenOpts) {
|
|
9
|
-
const app = new Koa();
|
|
10
|
-
app.use(koaClassicServer(root, { dirListing: { enabled: true }, hidden: hiddenOpts }));
|
|
11
|
-
return app.listen();
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// Note: prior to v3.0.0 the dotFiles default was 'hidden' (a v3-alpha
|
|
15
|
-
// security-by-default choice) and a once-per-process warning was emitted
|
|
16
|
-
// when the option was implicit. The default was reverted to 'visible' to
|
|
17
|
-
// align with the "HTTP file server first" design philosophy (see
|
|
18
|
-
// CLAUDE.md). The warning is no longer emitted because the default is no
|
|
19
|
-
// longer a surprising restriction; tests for it have been removed.
|
|
20
|
-
|
|
21
|
-
// ─── Option validation ────────────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
describe('hidden option — validation', () => {
|
|
24
|
-
test('throws when dotFiles.default is not "hidden" or "visible"', () => {
|
|
25
|
-
expect(() =>
|
|
26
|
-
koaClassicServer(root, { hidden: { dotFiles: { default: 'yes' } } })
|
|
27
|
-
).toThrow(/hidden\.dotFiles\.default must be "hidden" or "visible"/);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
test('throws when dotDirs.default is not "hidden" or "visible"', () => {
|
|
31
|
-
expect(() =>
|
|
32
|
-
koaClassicServer(root, { hidden: { dotDirs: { default: 'yes' } } })
|
|
33
|
-
).toThrow(/hidden\.dotDirs\.default must be "hidden" or "visible"/);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('does not throw when hidden option is omitted', () => {
|
|
37
|
-
expect(() => koaClassicServer(root, {})).not.toThrow();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test('does not throw when hidden is a valid object', () => {
|
|
41
|
-
expect(() =>
|
|
42
|
-
koaClassicServer(root, {
|
|
43
|
-
hidden: {
|
|
44
|
-
dotFiles: { default: 'hidden', whitelist: ['.well-known'], blacklist: ['.env'] },
|
|
45
|
-
dotDirs: { default: 'visible', blacklist: [/^\.git/] },
|
|
46
|
-
alwaysHide: ['*.secret', /\.key$/]
|
|
47
|
-
}
|
|
48
|
-
})
|
|
49
|
-
).not.toThrow();
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// ─── dotFiles — default visible (system default in v3.0+) ────────────────────
|
|
54
|
-
|
|
55
|
-
describe('dotFiles — default visible (system default)', () => {
|
|
56
|
-
let server;
|
|
57
|
-
beforeAll(() => { server = createApp(undefined); });
|
|
58
|
-
afterAll(() => server.close());
|
|
59
|
-
|
|
60
|
-
// Per the V3 "HTTP file server first" philosophy (see CLAUDE.md), dot-files
|
|
61
|
-
// are visible by default. Operators harden by setting hidden.dotFiles.default
|
|
62
|
-
// to 'hidden' or by using blacklist / alwaysHide.
|
|
63
|
-
test('GET /.env returns 200 by default', async () => {
|
|
64
|
-
const res = await supertest(server).get('/.env');
|
|
65
|
-
expect(res.status).toBe(200);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('GET /.gitignore returns 200 by default', async () => {
|
|
69
|
-
const res = await supertest(server).get('/.gitignore');
|
|
70
|
-
expect(res.status).toBe(200);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
test('directory listing includes dot-files', async () => {
|
|
74
|
-
const res = await supertest(server).get('/');
|
|
75
|
-
expect(res.status).toBe(200);
|
|
76
|
-
expect(res.text).toContain('.env');
|
|
77
|
-
expect(res.text).toContain('.gitignore');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('regular files remain accessible', async () => {
|
|
81
|
-
const res = await supertest(server).get('/normal.txt');
|
|
82
|
-
expect(res.status).toBe(200);
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// ─── dotFiles default: 'hidden' (opt-in hardening) ───────────────────────────
|
|
87
|
-
|
|
88
|
-
describe('dotFiles — explicit default hidden (opt-in)', () => {
|
|
89
|
-
let server;
|
|
90
|
-
beforeAll(() => {
|
|
91
|
-
server = createApp({ dotFiles: { default: 'hidden' } });
|
|
92
|
-
});
|
|
93
|
-
afterAll(() => server.close());
|
|
94
|
-
|
|
95
|
-
test('GET /.env returns 404 when dotFiles.default = "hidden"', async () => {
|
|
96
|
-
const res = await supertest(server).get('/.env');
|
|
97
|
-
expect(res.status).toBe(404);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test('GET /.gitignore returns 404', async () => {
|
|
101
|
-
const res = await supertest(server).get('/.gitignore');
|
|
102
|
-
expect(res.status).toBe(404);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('GET /subdir/.env returns 404 (dot-file in subdirectory)', async () => {
|
|
106
|
-
const res = await supertest(server).get('/subdir/.env');
|
|
107
|
-
expect(res.status).toBe(404);
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test('directory listing does not include .env', async () => {
|
|
111
|
-
const res = await supertest(server).get('/');
|
|
112
|
-
expect(res.status).toBe(200);
|
|
113
|
-
expect(res.text).not.toContain('.env');
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
test('directory listing does not include .gitignore', async () => {
|
|
117
|
-
const res = await supertest(server).get('/');
|
|
118
|
-
expect(res.text).not.toContain('.gitignore');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('regular files remain accessible', async () => {
|
|
122
|
-
const res = await supertest(server).get('/normal.txt');
|
|
123
|
-
expect(res.status).toBe(200);
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// ─── dotFiles default: 'visible' (kept for completeness with explicit opt-in) ─
|
|
128
|
-
|
|
129
|
-
describe('dotFiles — default visible', () => {
|
|
130
|
-
let server;
|
|
131
|
-
beforeAll(() => {
|
|
132
|
-
server = createApp({ dotFiles: { default: 'visible' } });
|
|
133
|
-
});
|
|
134
|
-
afterAll(() => server.close());
|
|
135
|
-
|
|
136
|
-
test('GET /.env returns 200 when dotFiles.default is "visible"', async () => {
|
|
137
|
-
const res = await supertest(server).get('/.env');
|
|
138
|
-
expect(res.status).toBe(200);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
test('directory listing includes .env when dotFiles.default is "visible"', async () => {
|
|
142
|
-
const res = await supertest(server).get('/');
|
|
143
|
-
expect(res.text).toContain('.env');
|
|
144
|
-
});
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
// ─── dotFiles whitelist ───────────────────────────────────────────────────────
|
|
148
|
-
|
|
149
|
-
describe('dotFiles — whitelist exceptions', () => {
|
|
150
|
-
let server;
|
|
151
|
-
beforeAll(() => {
|
|
152
|
-
server = createApp({
|
|
153
|
-
dotFiles: { default: 'hidden', whitelist: ['.well-known'] },
|
|
154
|
-
dotDirs: { default: 'hidden', whitelist: ['.well-known'] }
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
afterAll(() => server.close());
|
|
158
|
-
|
|
159
|
-
test('GET /.well-known/ is accessible (whitelisted dir)', async () => {
|
|
160
|
-
const res = await supertest(server).get('/.well-known/');
|
|
161
|
-
expect(res.status).toBe(200);
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test('GET /.well-known/acme-challenge.txt is accessible (inside whitelisted dir)', async () => {
|
|
165
|
-
const res = await supertest(server).get('/.well-known/acme-challenge.txt');
|
|
166
|
-
expect(res.status).toBe(200);
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test('.well-known appears in root listing', async () => {
|
|
170
|
-
const res = await supertest(server).get('/');
|
|
171
|
-
expect(res.text).toContain('.well-known');
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test('GET /.env still returns 404 (not whitelisted)', async () => {
|
|
175
|
-
const res = await supertest(server).get('/.env');
|
|
176
|
-
expect(res.status).toBe(404);
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test('whitelist with RegExp: /^\\.public/ matches .public-assets', async () => {
|
|
180
|
-
// This tests the regex matching logic; .public-assets doesn't exist so we
|
|
181
|
-
// expect 404 from "not found", not from "hidden" — indirectly confirms regex is not blocking
|
|
182
|
-
const server2 = createApp({
|
|
183
|
-
dotFiles: { default: 'hidden', whitelist: [/^\.public/] }
|
|
184
|
-
});
|
|
185
|
-
// .env is not matched by /^\.public/ so it stays hidden
|
|
186
|
-
const res = await supertest(server2).get('/.env');
|
|
187
|
-
expect(res.status).toBe(404);
|
|
188
|
-
server2.close();
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// ─── dotFiles blacklist ───────────────────────────────────────────────────────
|
|
193
|
-
|
|
194
|
-
describe('dotFiles — blacklist', () => {
|
|
195
|
-
let server;
|
|
196
|
-
beforeAll(() => {
|
|
197
|
-
server = createApp({
|
|
198
|
-
dotFiles: { default: 'visible', blacklist: ['.env'] }
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
afterAll(() => server.close());
|
|
202
|
-
|
|
203
|
-
test('GET /.env returns 404 (blacklisted even with default: visible)', async () => {
|
|
204
|
-
const res = await supertest(server).get('/.env');
|
|
205
|
-
expect(res.status).toBe(404);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
test('GET /.gitignore returns 200 (visible, not blacklisted)', async () => {
|
|
209
|
-
const res = await supertest(server).get('/.gitignore');
|
|
210
|
-
expect(res.status).toBe(200);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test('.env not in listing when blacklisted', async () => {
|
|
214
|
-
const res = await supertest(server).get('/');
|
|
215
|
-
expect(res.text).not.toContain('.env');
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// ─── blacklist beats whitelist ────────────────────────────────────────────────
|
|
220
|
-
|
|
221
|
-
describe('dotFiles — blacklist beats whitelist', () => {
|
|
222
|
-
let server;
|
|
223
|
-
beforeAll(() => {
|
|
224
|
-
server = createApp({
|
|
225
|
-
dotFiles: { default: 'visible', whitelist: ['.env'], blacklist: ['.env'] }
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
afterAll(() => server.close());
|
|
229
|
-
|
|
230
|
-
test('GET /.env returns 404 (blacklist wins over whitelist)', async () => {
|
|
231
|
-
const res = await supertest(server).get('/.env');
|
|
232
|
-
expect(res.status).toBe(404);
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// ─── dotDirs default: 'visible' (system default) ─────────────────────────────
|
|
237
|
-
|
|
238
|
-
describe('dotDirs — default visible (system default)', () => {
|
|
239
|
-
let server;
|
|
240
|
-
beforeAll(() => { server = createApp(undefined); });
|
|
241
|
-
afterAll(() => server.close());
|
|
242
|
-
|
|
243
|
-
test('.dot-dir appears in root listing by default', async () => {
|
|
244
|
-
const res = await supertest(server).get('/');
|
|
245
|
-
expect(res.text).toContain('.dot-dir');
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
test('GET /.dot-dir/ returns 200 (directory listing) by default', async () => {
|
|
249
|
-
const res = await supertest(server).get('/.dot-dir/');
|
|
250
|
-
expect(res.status).toBe(200);
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
test('GET /.dot-dir/inside.txt returns 200 by default', async () => {
|
|
254
|
-
const res = await supertest(server).get('/.dot-dir/inside.txt');
|
|
255
|
-
expect(res.status).toBe(200);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
// ─── dotDirs blacklist ────────────────────────────────────────────────────────
|
|
260
|
-
|
|
261
|
-
describe('dotDirs — blacklist', () => {
|
|
262
|
-
let server;
|
|
263
|
-
beforeAll(() => {
|
|
264
|
-
server = createApp({ dotDirs: { blacklist: ['.dot-dir'] } });
|
|
265
|
-
});
|
|
266
|
-
afterAll(() => server.close());
|
|
267
|
-
|
|
268
|
-
test('GET /.dot-dir/ returns 404 (blacklisted dir)', async () => {
|
|
269
|
-
const res = await supertest(server).get('/.dot-dir/');
|
|
270
|
-
expect(res.status).toBe(404);
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
test('GET /.dot-dir/inside.txt returns 404 (inside blocked dir)', async () => {
|
|
274
|
-
const res = await supertest(server).get('/.dot-dir/inside.txt');
|
|
275
|
-
expect(res.status).toBe(404);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
test('.dot-dir not in root listing when blacklisted', async () => {
|
|
279
|
-
const res = await supertest(server).get('/');
|
|
280
|
-
expect(res.text).not.toContain('.dot-dir');
|
|
281
|
-
});
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// ─── dotDirs blacklist with RegExp ────────────────────────────────────────────
|
|
285
|
-
|
|
286
|
-
describe('dotDirs — blacklist with RegExp', () => {
|
|
287
|
-
let server;
|
|
288
|
-
beforeAll(() => {
|
|
289
|
-
server = createApp({ dotDirs: { blacklist: [/^\.dot/] } });
|
|
290
|
-
});
|
|
291
|
-
afterAll(() => server.close());
|
|
292
|
-
|
|
293
|
-
test('GET /.dot-dir/ returns 404 (RegExp blacklist match)', async () => {
|
|
294
|
-
const res = await supertest(server).get('/.dot-dir/');
|
|
295
|
-
expect(res.status).toBe(404);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
test('.well-known accessible (does not match /^\\.dot/)', async () => {
|
|
299
|
-
const res = await supertest(server).get('/.well-known/');
|
|
300
|
-
expect(res.status).toBe(200);
|
|
301
|
-
});
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
// ─── alwaysHide ───────────────────────────────────────────────────────────────
|
|
305
|
-
|
|
306
|
-
describe('alwaysHide — glob and regex patterns', () => {
|
|
307
|
-
let server;
|
|
308
|
-
beforeAll(() => {
|
|
309
|
-
server = createApp({
|
|
310
|
-
dotFiles: { default: 'visible' }, // dot-files visible so we can test alwaysHide separately
|
|
311
|
-
alwaysHide: ['*.secret', /\.key$/]
|
|
312
|
-
});
|
|
313
|
-
});
|
|
314
|
-
afterAll(() => server.close());
|
|
315
|
-
|
|
316
|
-
test('GET /file.secret returns 404 (glob *.secret)', async () => {
|
|
317
|
-
const res = await supertest(server).get('/file.secret');
|
|
318
|
-
expect(res.status).toBe(404);
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
test('GET /data.key returns 404 (regex /\\.key$/)', async () => {
|
|
322
|
-
const res = await supertest(server).get('/data.key');
|
|
323
|
-
expect(res.status).toBe(404);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test('file.secret not in directory listing', async () => {
|
|
327
|
-
const res = await supertest(server).get('/');
|
|
328
|
-
expect(res.text).not.toContain('file.secret');
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
test('data.key not in directory listing', async () => {
|
|
332
|
-
const res = await supertest(server).get('/');
|
|
333
|
-
expect(res.text).not.toContain('data.key');
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
test('regular files remain accessible', async () => {
|
|
337
|
-
const res = await supertest(server).get('/normal.txt');
|
|
338
|
-
expect(res.status).toBe(200);
|
|
339
|
-
});
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
// ─── alwaysHide — path-anchored patterns ─────────────────────────────────────
|
|
343
|
-
|
|
344
|
-
describe('alwaysHide — path-anchored glob (config/secrets/**)', () => {
|
|
345
|
-
let server;
|
|
346
|
-
beforeAll(() => {
|
|
347
|
-
server = createApp({ alwaysHide: ['config/secrets/**'] });
|
|
348
|
-
});
|
|
349
|
-
afterAll(() => server.close());
|
|
350
|
-
|
|
351
|
-
test('GET /config/secrets/password.txt returns 404', async () => {
|
|
352
|
-
const res = await supertest(server).get('/config/secrets/password.txt');
|
|
353
|
-
expect(res.status).toBe(404);
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
test('password.txt not in /config/secrets/ listing', async () => {
|
|
357
|
-
const res = await supertest(server).get('/config/secrets/');
|
|
358
|
-
expect(res.status).toBe(200);
|
|
359
|
-
expect(res.text).not.toContain('password.txt');
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
test('GET /normal.txt remains accessible (not under config/secrets/)', async () => {
|
|
363
|
-
const res = await supertest(server).get('/normal.txt');
|
|
364
|
-
expect(res.status).toBe(200);
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
// ─── alwaysHide secondary to whitelist ───────────────────────────────────────
|
|
369
|
-
|
|
370
|
-
describe('alwaysHide — secondary to dotFiles whitelist', () => {
|
|
371
|
-
let server;
|
|
372
|
-
beforeAll(() => {
|
|
373
|
-
server = createApp({
|
|
374
|
-
dotFiles: { default: 'hidden', whitelist: ['.env'] },
|
|
375
|
-
alwaysHide: ['.env'] // both alwaysHide and whitelist target .env
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
afterAll(() => server.close());
|
|
379
|
-
|
|
380
|
-
test('GET /.env returns 200 (whitelist wins over alwaysHide)', async () => {
|
|
381
|
-
const res = await supertest(server).get('/.env');
|
|
382
|
-
expect(res.status).toBe(200);
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
|
|
386
|
-
// ─── deep tree: dot-files hidden at any depth ─────────────────────────────────
|
|
387
|
-
|
|
388
|
-
describe('hidden entries at any depth in directory tree (opt-in dotFiles.default=hidden)', () => {
|
|
389
|
-
let server;
|
|
390
|
-
beforeAll(() => { server = createApp({ dotFiles: { default: 'hidden' } }); });
|
|
391
|
-
afterAll(() => server.close());
|
|
392
|
-
|
|
393
|
-
test('GET /subdir/.env returns 404 (dot-file hidden at any depth)', async () => {
|
|
394
|
-
const res = await supertest(server).get('/subdir/.env');
|
|
395
|
-
expect(res.status).toBe(404);
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
test('/subdir/.env not in /subdir/ listing', async () => {
|
|
399
|
-
const res = await supertest(server).get('/subdir/');
|
|
400
|
-
expect(res.text).not.toContain('.env');
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
test('GET /subdir/regular.txt returns 200 (regular file accessible)', async () => {
|
|
404
|
-
const res = await supertest(server).get('/subdir/regular.txt');
|
|
405
|
-
expect(res.status).toBe(200);
|
|
406
|
-
});
|
|
407
|
-
});
|