koa-classic-server 1.1.0 โ†’ 2.0.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 CHANGED
@@ -1,149 +1,324 @@
1
- # koa-classic-file
1
+ # koa-classic-server
2
2
 
3
- koa-cleassic-server is a mildwere aiming for similar but not identical behavior to apache2 . the contents of a folder on the server will be shown remotely and if you want to access a file, click on it. note: not a highly inexperienced programmer use this code with caution, suggestions are welcome.
3
+ ๐Ÿ”’ **Secure Koa middleware for serving static files** with Apache-like directory listing, template engine support, and comprehensive security fixes.
4
4
 
5
- middleware serving static files from a directory. The middleware accepts an options object that allows customization of the serving behavior. For example, it allows setting the supported HTTP methods, whether to show the contents of a directory, the name of the index file, an array of URLs that are reserved and not accessible, and an optional template rendering function for certain file types.
5
+ [![npm version](https://img.shields.io/npm/v/koa-classic-server.svg)](https://www.npmjs.com/package/koa-classic-server)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Tests](https://img.shields.io/badge/tests-71%20passing-brightgreen.svg)]()
6
8
 
7
- The middleware takes in two arguments: rootDir which is the directory that contains the static files and opts which is the options object.
9
+ ## โš ๏ธ Version 1.2.0 - Critical Security Update
8
10
 
9
- In the options object, the following properties are set to default values if they are not provided:
10
- method: an array of supported HTTP methods. Default is ['GET'].
11
- showDirContents: a boolean value indicating whether the contents of a directory should be shown. Default is true.
12
- index: the name of the index file. Default is an empty string.
13
- urlPrefix: the prefix of the path , such as localhost:3000/views. Default is an empty string.
14
- urlsReserved: an array of reserved URLs that files cannot be read from. Default is an empty array.
15
- template: an object with two properties:
16
- render: a function for rendering templates. Default is undefined.
17
- ext: an array of file extensions for which the render function should be used. Default is an empty array.
11
+ Version 1.2.0 includes **critical security fixes** for path traversal vulnerabilities and other important improvements. **Upgrade immediately** if you're using version 1.1.0 or earlier.
18
12
 
19
- The middleware then checks if the requested HTTP method is in the list of supported methods. If it is not, the middleware calls next and returns.
13
+ ### What's New in 1.2.0
20
14
 
21
- The middleware then checks if the pageHref is a sub-path of the urlPrefix. If it is not, the middleware calls next and returns.
15
+ โœ… **Fixed Path Traversal Vulnerability** - No more unauthorized file access
16
+ โœ… **Proper HTTP 404 Status Codes** - Standards-compliant error handling
17
+ โœ… **Template Error Handling** - No more server crashes
18
+ โœ… **XSS Protection** - HTML escaping in directory listings
19
+ โœ… **Race Condition Fixes** - Robust file access
20
+ โœ… **71 Tests Passing** - Comprehensive test coverage
22
21
 
23
- The middleware checks if the requested URL is in the urlsReserved array. If it is, the middleware calls next and returns.
22
+ [See full changelog](./CHANGELOG.md)
24
23
 
25
- The middleware then generates the file path by combining the rootDir and the pathname of the pageHref.
24
+ ## Features
26
25
 
27
- The middleware then checks if the file exists, and if it does, it sets the content type of the response and sends the file contents. If the file is a directory and showDirContents is true, the contents of the directory are shown. If the index file exists in the directory, it is shown instead.
26
+ koa-classic-server is a middleware for serving static files from a directory with Apache 2-like behavior. The contents of a folder on the server will be shown remotely and if you want to access a file, click on it.
28
27
 
29
- If the file does not exist, the middleware calls next to let the next middleware handle the request.
28
+ **Key Features:**
30
29
 
31
- If the file extension is in the template.ext array and the template.render function is provided, the function is called to render the file contents.
30
+ - ๐Ÿ—‚๏ธ **Directory Listing** - Apache-style browseable directories
31
+ - ๐Ÿ“„ **Static File Serving** - Automatic MIME type detection
32
+ - ๐ŸŽจ **Template Engine Support** - Integrate EJS, Pug, Handlebars, etc.
33
+ - ๐Ÿ”’ **Security** - Path traversal protection, XSS prevention
34
+ - โš™๏ธ **Configurable** - URL prefixes, reserved paths, index files
35
+ - ๐Ÿงช **Well-Tested** - 71 tests with security coverage
36
+ - ๐Ÿ“ฆ **Dual Module Support** - CommonJS and ES Modules
32
37
 
33
38
  ## Installation
34
39
 
35
- ```js
36
- npm i koa-classic-server
40
+ ```bash
41
+ npm install koa-classic-server
37
42
  ```
38
43
 
39
- next import
44
+ ## Quick Start
40
45
 
41
- ```js
46
+ ```javascript
47
+ const Koa = require('koa');
42
48
  const koaClassicServer = require('koa-classic-server');
49
+
50
+ const app = new Koa();
51
+
52
+ // Serve files from "public" directory
53
+ app.use(koaClassicServer(__dirname + '/public'));
54
+
55
+ app.listen(3000);
56
+ console.log('Server running on http://localhost:3000');
43
57
  ```
44
- or
45
- ```js
46
- import koaClassicServer from "koa-classic-server";
47
- '''
48
58
 
49
- ## API
59
+ ## Usage
50
60
 
51
- ## Options
61
+ ### Import
52
62
 
53
- ```js
54
- opts = {
55
- method: Array("GET"), // methods enabled, otherwise it will have called the next() function
56
- showDirContents: true, //show or not the contents of the current directory
57
- index: "", // the index file , if a file with this name is found it will be loaded automatically Es index.html
58
- //indexExt: array(),// futures supported extensions for the index file
59
- urlPrefix: "", // prefix of the URL that will be skipped es "/admin"
60
- urlsReserved: Array(), //paths on disk that will not be accessible remotely e.g. array('/api','/views') warning nested folders are not allowed
61
- template: {
62
- render: undefined, //function that will take care of the rendering if there is a template engine ES --> const templateRender = async ( ctx, next, filePath) => {
63
- ext: Array(), // template engine file extension ES :Array("ejs", "EJS"),
64
- }, // emd template
65
- }; // end optio
63
+ ```javascript
64
+ // CommonJS
65
+ const koaClassicServer = require('koa-classic-server');
66
+
67
+ // ES Modules
68
+ import koaClassicServer from 'koa-classic-server';
66
69
  ```
67
70
 
68
- ## Exsample
71
+ ### Basic Examples
69
72
 
70
- ### exsaple0
73
+ #### Example 1: Simple File Server
71
74
 
72
- ```js
73
- const Koa = require("koa");
74
- const koaClassicServer = require("koa-classic-server");
75
+ ```javascript
76
+ const Koa = require('koa');
77
+ const koaClassicServer = require('koa-classic-server');
75
78
 
76
79
  const app = new Koa();
77
80
 
78
- app.use(koaClassicServer(__dirname + "/public"));
81
+ app.use(koaClassicServer(__dirname + '/public', {
82
+ showDirContents: true,
83
+ index: ['index.html'] // Array format (recommended)
84
+ }));
79
85
 
80
86
  app.listen(3000);
81
87
  ```
82
88
 
83
- ### exsample1
84
-
85
- ```js
86
- const koa = require("koa");
87
- const app = new koa();
88
- const port = 3000;
89
-
90
- const classicServer = require("koa-classic-server");
91
-
92
- const ejs = require("ejs");
93
-
94
- app.use(
95
- classicServer(
96
- __dirname + "/public",
97
- (opt = {
98
- showDirContents: true,
99
- template: {
100
- render: async (ctx, next, filePath) => {
101
- ctx.body = await ejs.renderFile(filePath, {
102
- filePath: filePath,
103
- href: ctx.href,
104
- query: ctx.query,
105
- });
106
- },
107
- ext: Array("ejs", "EJS"),
108
- },
109
- })
110
- )
111
- );
112
-
113
- app.listen(port, console.log("server started on port:" + port));
89
+ #### Example 2: With URL Prefix
90
+
91
+ ```javascript
92
+ const Koa = require('koa');
93
+ const koaClassicServer = require('koa-classic-server');
94
+
95
+ const app = new Koa();
96
+
97
+ // Files accessible under /static
98
+ // e.g., http://localhost:3000/static/image.png
99
+ app.use(koaClassicServer(__dirname + '/public', {
100
+ urlPrefix: '/static',
101
+ showDirContents: true
102
+ }));
103
+
104
+ app.listen(3000);
114
105
  ```
115
106
 
116
- ### exsample2
107
+ #### Example 3: With Template Engine (EJS)
117
108
 
118
- ```js
119
- const koa = require("koa");
120
- const app = new koa();
121
- const port = 3000;
109
+ ```javascript
110
+ const Koa = require('koa');
111
+ const koaClassicServer = require('koa-classic-server');
112
+ const ejs = require('ejs');
122
113
 
123
- const classicServer = require("koa-classic-server");
114
+ const app = new Koa();
124
115
 
125
- const ejs = require("ejs");
116
+ app.use(koaClassicServer(__dirname + '/views', {
117
+ template: {
118
+ render: async (ctx, next, filePath) => {
119
+ ctx.body = await ejs.renderFile(filePath, {
120
+ title: 'My App',
121
+ user: ctx.state.user
122
+ });
123
+ },
124
+ ext: ['ejs', 'html']
125
+ }
126
+ }));
126
127
 
127
- const templateRender = async (ctx, next, filePath) => {
128
- ctx.body = await ejs.renderFile(filePath, { filePath: filePath });
128
+ app.listen(3000);
129
+ ```
130
+
131
+ ## API
132
+
133
+ ### koaClassicServer(rootDir, options)
134
+
135
+ Creates a Koa middleware for serving static files.
136
+
137
+ **Parameters:**
138
+
139
+ - `rootDir` (String, required): Absolute path to the directory containing static files
140
+ - `options` (Object, optional): Configuration options
141
+
142
+ **Returns:** Koa middleware function
143
+
144
+ ## Options
145
+
146
+ ```javascript
147
+ const options = {
148
+ // HTTP methods allowed (default: ['GET'])
149
+ method: ['GET', 'HEAD'],
150
+
151
+ // Show directory contents (default: true)
152
+ showDirContents: true,
153
+
154
+ // Index file configuration (default: [])
155
+ // RECOMMENDED: Use array format (string format is deprecated)
156
+ // Formats:
157
+ // - Array of strings: ['index.html', 'index.htm', 'default.html']
158
+ // - Array of RegExp: [/index\.html/i] (case-insensitive)
159
+ // - Mixed array: ['index.html', /INDEX\.HTM/i]
160
+ // Priority: First match wins (array order determines search priority)
161
+ //
162
+ // DEPRECATED: String format 'index.html' still works but will be removed
163
+ // in future versions. Please use array format: ['index.html']
164
+ //
165
+ // See INDEX_OPTION_PRIORITY.md for detailed behavior documentation
166
+ index: ['index.html'],
167
+
168
+ // URL path prefix (default: '')
169
+ // Files will be served under this prefix
170
+ urlPrefix: '/static',
171
+
172
+ // Reserved paths (default: [])
173
+ // These directories won't be accessible
174
+ // Note: Only works for first-level directories
175
+ urlsReserved: ['/admin', '/private'],
176
+
177
+ // Template engine configuration
178
+ template: {
179
+ // Template rendering function
180
+ render: async (ctx, next, filePath) => {
181
+ // Your rendering logic
182
+ ctx.body = await yourTemplateEngine.render(filePath, data);
183
+ },
184
+
185
+ // File extensions to process with template.render
186
+ ext: ['ejs', 'pug', 'hbs']
187
+ }
129
188
  };
189
+ ```
190
+
191
+ ### Options Details
192
+
193
+ | Option | Type | Default | Description |
194
+ |--------|------|---------|-------------|
195
+ | `method` | Array | `['GET']` | Allowed HTTP methods |
196
+ | `showDirContents` | Boolean | `true` | Show directory listing |
197
+ | `index` | String | `''` | Index file name |
198
+ | `urlPrefix` | String | `''` | URL path prefix |
199
+ | `urlsReserved` | Array | `[]` | Reserved directory paths |
200
+ | `template.render` | Function | `undefined` | Template rendering function |
201
+ | `template.ext` | Array | `[]` | Extensions for template rendering |
202
+
203
+ ## Security
204
+
205
+ ### Path Traversal Protection
206
+
207
+ koa-classic-server 1.2.0 protects against path traversal attacks:
208
+
209
+ ```javascript
210
+ // โŒ These requests are blocked (return 403 Forbidden)
211
+ GET /../../../etc/passwd
212
+ GET /../config/database.yml
213
+ GET /%2e%2e%2fpackage.json
214
+ ```
215
+
216
+ ### Protected Directories
217
+
218
+ Use `urlsReserved` to protect sensitive directories:
130
219
 
131
- app.use(
132
- classicServer(
133
- __dirname + "/public",
134
- (opt = {
135
- showDirContents: true,
136
- template: {
137
- render: templateRender,
138
- ext: Array("ejs", "EJS"),
139
- },
140
- })
141
- )
142
- );
143
-
144
- app.listen(port, console.log("server started on port:" + port));
220
+ ```javascript
221
+ app.use(koaClassicServer(__dirname + '/www', {
222
+ urlsReserved: ['/config', '/private', '/.git', '/node_modules']
223
+ }));
145
224
  ```
146
225
 
226
+ ### XSS Protection
227
+
228
+ All filenames and paths in directory listings are HTML-escaped to prevent XSS attacks.
229
+
230
+ ## Error Handling
231
+
232
+ koa-classic-server properly handles errors:
233
+
234
+ - **404** - File/directory not found
235
+ - **403** - Forbidden (path traversal attempts, reserved directories)
236
+ - **500** - Template rendering errors, file access errors
237
+
238
+ ## Testing
239
+
240
+ ```bash
241
+ # Run all tests
242
+ npm test
243
+
244
+ # Run security tests only
245
+ npm run test:security
246
+ ```
247
+
248
+ ## Middleware Behavior
249
+
250
+ ### Directory Handling
251
+
252
+ 1. If `index` file exists โ†’ serve index file
253
+ 2. If `showDirContents: true` โ†’ show directory listing
254
+ 3. If `showDirContents: false` โ†’ return 404
255
+
256
+ ### File Handling
257
+
258
+ 1. Check if file extension matches `template.ext`
259
+ 2. If yes โ†’ call `template.render()`
260
+ 3. If no โ†’ serve static file with appropriate MIME type
261
+
262
+ ### Reserved URLs
263
+
264
+ Requests to reserved paths are passed to the next middleware.
265
+
266
+ ## Migration from 1.1.0
267
+
268
+ Upgrading is simple! No code changes required:
269
+
270
+ ```bash
271
+ npm update koa-classic-server
272
+ ```
273
+
274
+ **What changed:**
275
+ - 404 status codes now correct (was 200)
276
+ - Path traversal blocked (was allowed)
277
+ - Template errors return 500 (was crash)
278
+
279
+ See [CHANGELOG.md](./CHANGELOG.md) for detailed information.
280
+
281
+ ## Complete Documentation
282
+
283
+ For complete documentation with all features, examples, troubleshooting, and best practices, see:
284
+
285
+ - **[DOCUMENTATION.md](./DOCUMENTATION.md)** - Complete API reference and usage guide
286
+ - **[INDEX_OPTION_PRIORITY.md](./INDEX_OPTION_PRIORITY.md)** - Detailed priority behavior for `index` option (string, array, RegExp)
287
+ - **[EXAMPLES_INDEX_OPTION.md](./EXAMPLES_INDEX_OPTION.md)** - 10 practical examples of `index` option with RegExp
288
+ - **[PERFORMANCE_ANALYSIS.md](./PERFORMANCE_ANALYSIS.md)** - Performance optimization analysis
289
+ - **[PERFORMANCE_COMPARISON.md](./PERFORMANCE_COMPARISON.md)** - Before/after performance benchmarks
290
+
291
+ ## Contributing
292
+
293
+ Contributions are welcome! Please feel free to submit a Pull Request.
294
+
295
+ ## Known Limitations
296
+
297
+ - Reserved URLs only work for first-level directories
298
+ - Single index file name (no fallback array)
299
+
300
+ See [DEBUG_REPORT.md](./DEBUG_REPORT.md) for technical details.
301
+
147
302
  ## License
148
303
 
149
304
  MIT
305
+
306
+ ## Author
307
+
308
+ Italo Paesano
309
+
310
+ ## Changelog
311
+
312
+ See [CHANGELOG.md](./CHANGELOG.md)
313
+
314
+ ## Links
315
+
316
+ - [Full Documentation](./DOCUMENTATION.md)
317
+ - [Debug Report](./DEBUG_REPORT.md)
318
+ - [Changelog](./CHANGELOG.md)
319
+ - [Repository](https://github.com/italopaesano/koa-classic-server)
320
+ - [npm Package](https://www.npmjs.com/package/koa-classic-server)
321
+
322
+ ---
323
+
324
+ **โš ๏ธ Security Notice:** Version 1.2.0 fixes critical vulnerabilities. Update immediately if using 1.1.0 or earlier.