kempo-server 1.7.1 → 1.7.2
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 +21 -10
- package/package.json +1 -1
- package/src/defaultConfig.js +2 -2
- package/src/router.js +31 -5
- package/tests/config-flag.node-test.js +51 -0
- package/tests/customRoute-outside-root.node-test.js +1 -1
- package/tests/router-wildcard-double-asterisk.node-test.js +3 -3
- package/tests/router-wildcard.node-test.js +3 -3
package/README.md
CHANGED
|
@@ -213,6 +213,11 @@ export default async function(request, response) {
|
|
|
213
213
|
|
|
214
214
|
To configure the server, create a configuration file (by default `.config.json`) within the root directory of your server (`public` in the start example [above](#getting-started)). You can specify a different configuration file using the `--config` flag.
|
|
215
215
|
|
|
216
|
+
**Important:**
|
|
217
|
+
- When using a relative path for the `--config` flag, the config file must be located within the server root directory
|
|
218
|
+
- When using an absolute path for the `--config` flag, the config file can be located anywhere on the filesystem
|
|
219
|
+
- The server will throw an error if you attempt to use a relative config file path that points outside the root directory
|
|
220
|
+
|
|
216
221
|
This json file can have any of the following properties, any property not defined will use the "Default Config".
|
|
217
222
|
|
|
218
223
|
- [allowedMimes](#allowedmimes)
|
|
@@ -558,35 +563,41 @@ An array of regex patterns for paths that should not trigger a file system resca
|
|
|
558
563
|
|
|
559
564
|
An object mapping custom route paths to file paths. Useful for aliasing or serving files from outside the document root.
|
|
560
565
|
|
|
566
|
+
**Note:** All file paths in customRoutes are resolved relative to the server root directory (the `--root` path). This allows you to reference files both inside and outside the document root.
|
|
567
|
+
|
|
561
568
|
**Basic Routes:**
|
|
562
569
|
```json
|
|
563
570
|
{
|
|
564
571
|
"customRoutes": {
|
|
565
|
-
"/vendor/bootstrap.css": "
|
|
572
|
+
"/vendor/bootstrap.css": "../node_modules/bootstrap/dist/css/bootstrap.min.css",
|
|
566
573
|
"/api/status": "./status.js"
|
|
567
574
|
}
|
|
568
575
|
}
|
|
569
576
|
```
|
|
570
577
|
|
|
571
578
|
**Wildcard Routes:**
|
|
572
|
-
Wildcard routes allow you to map entire directory structures using the `*`
|
|
579
|
+
Wildcard routes allow you to map entire directory structures using the `*` and `**` wildcards:
|
|
573
580
|
|
|
574
581
|
```json
|
|
575
582
|
{
|
|
576
583
|
"customRoutes": {
|
|
577
|
-
"kempo/*": "
|
|
578
|
-
"assets/*": "
|
|
579
|
-
"docs/*": "
|
|
584
|
+
"kempo/*": "../node_modules/kempo/dist/*",
|
|
585
|
+
"assets/*": "../static-files/*",
|
|
586
|
+
"docs/*": "../documentation/*",
|
|
587
|
+
"src/**": "../src/**"
|
|
580
588
|
}
|
|
581
589
|
}
|
|
582
590
|
```
|
|
583
591
|
|
|
584
592
|
With wildcard routes:
|
|
585
|
-
- `kempo/styles.css` would serve
|
|
586
|
-
- `assets/logo.png` would serve
|
|
587
|
-
- `docs/readme.md` would serve
|
|
588
|
-
|
|
589
|
-
|
|
593
|
+
- `kempo/styles.css` would serve `../node_modules/kempo/dist/styles.css`
|
|
594
|
+
- `assets/logo.png` would serve `../static-files/logo.png`
|
|
595
|
+
- `docs/readme.md` would serve `../documentation/readme.md`
|
|
596
|
+
- `src/components/Button.js` would serve `../src/components/Button.js`
|
|
597
|
+
|
|
598
|
+
The `*` wildcard matches any single path segment (anything between `/` characters).
|
|
599
|
+
The `**` wildcard matches any number of path segments, allowing you to map entire directory trees.
|
|
600
|
+
Multiple wildcards can be used in a single route pattern.
|
|
590
601
|
|
|
591
602
|
### maxRescanAttempts
|
|
592
603
|
|
package/package.json
CHANGED
package/src/defaultConfig.js
CHANGED
|
@@ -88,8 +88,8 @@ export default {
|
|
|
88
88
|
],
|
|
89
89
|
maxRescanAttempts: 3,
|
|
90
90
|
customRoutes: {
|
|
91
|
-
// Example: "/vendor/bootstrap.css": "
|
|
92
|
-
// Wildcard example: "kempo/*": "
|
|
91
|
+
// Example: "/vendor/bootstrap.css": "../node_modules/bootstrap/dist/css/bootstrap.min.css"
|
|
92
|
+
// Wildcard example: "kempo/*": "../node_modules/kempo/dust/*"
|
|
93
93
|
},
|
|
94
94
|
middleware: {
|
|
95
95
|
// Built-in middleware configuration
|
package/src/router.js
CHANGED
|
@@ -27,6 +27,25 @@ export default async (flags, log) => {
|
|
|
27
27
|
const configPath = path.isAbsolute(configFileName)
|
|
28
28
|
? configFileName
|
|
29
29
|
: path.join(rootPath, configFileName);
|
|
30
|
+
|
|
31
|
+
log(`Config file name: ${configFileName}`, 2);
|
|
32
|
+
log(`Config path: ${configPath}`, 2);
|
|
33
|
+
|
|
34
|
+
// Validate that config file is within the server root directory
|
|
35
|
+
// Allow absolute paths (user explicitly specified location)
|
|
36
|
+
if (!path.isAbsolute(configFileName)) {
|
|
37
|
+
const relativeConfigPath = path.relative(rootPath, configPath);
|
|
38
|
+
log(`Relative config path: ${relativeConfigPath}`, 2);
|
|
39
|
+
log(`Starts with '..': ${relativeConfigPath.startsWith('..')}`, 2);
|
|
40
|
+
if (relativeConfigPath.startsWith('..') || path.isAbsolute(relativeConfigPath)) {
|
|
41
|
+
log(`Validation failed - throwing error`, 2);
|
|
42
|
+
throw new Error(`Config file must be within the server root directory. Config path: ${configPath}, Root path: ${rootPath}`);
|
|
43
|
+
}
|
|
44
|
+
log(`Validation passed`, 2);
|
|
45
|
+
} else {
|
|
46
|
+
log(`Config file name is absolute, skipping validation`, 2);
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
log(`Loading config from: ${configPath}`, 2);
|
|
31
50
|
const configContent = await readFile(configPath, 'utf8');
|
|
32
51
|
const userConfig = JSON.parse(configContent);
|
|
@@ -53,6 +72,11 @@ export default async (flags, log) => {
|
|
|
53
72
|
};
|
|
54
73
|
log('User config loaded and merged with defaults', 2);
|
|
55
74
|
} catch (e){
|
|
75
|
+
// Only fall back to default config for file reading/parsing errors
|
|
76
|
+
// Let validation errors propagate up
|
|
77
|
+
if (e.message.includes('Config file must be within the server root directory')) {
|
|
78
|
+
throw e;
|
|
79
|
+
}
|
|
56
80
|
log('Using default config (no config file found)', 2);
|
|
57
81
|
}
|
|
58
82
|
|
|
@@ -139,14 +163,14 @@ export default async (flags, log) => {
|
|
|
139
163
|
for (const [urlPath, filePath] of Object.entries(config.customRoutes)) {
|
|
140
164
|
// Check if this is a wildcard route
|
|
141
165
|
if (urlPath.includes('*')) {
|
|
142
|
-
// Resolve the file path relative to
|
|
143
|
-
const resolvedPath = path.resolve(filePath);
|
|
166
|
+
// Resolve the file path relative to rootPath if relative, otherwise use absolute path
|
|
167
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(rootPath, filePath);
|
|
144
168
|
// Store wildcard routes separately for pattern matching
|
|
145
169
|
wildcardRoutes.set(urlPath, resolvedPath);
|
|
146
170
|
log(`Wildcard route mapped: ${urlPath} -> ${resolvedPath}`, 2);
|
|
147
171
|
} else {
|
|
148
|
-
// Resolve the file path relative to
|
|
149
|
-
const resolvedPath = path.resolve(filePath);
|
|
172
|
+
// Resolve the file path relative to rootPath if relative, otherwise use absolute path
|
|
173
|
+
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(rootPath, filePath);
|
|
150
174
|
customRoutes.set(urlPath, resolvedPath);
|
|
151
175
|
log(`Custom route mapped: ${urlPath} -> ${resolvedPath}`, 2);
|
|
152
176
|
}
|
|
@@ -179,7 +203,9 @@ export default async (flags, log) => {
|
|
|
179
203
|
}
|
|
180
204
|
}
|
|
181
205
|
|
|
182
|
-
return
|
|
206
|
+
// If the path is already absolute, return it as-is
|
|
207
|
+
// If it's relative, resolve it relative to rootPath
|
|
208
|
+
return path.isAbsolute(resolvedPath) ? resolvedPath : path.resolve(rootPath, resolvedPath);
|
|
183
209
|
};
|
|
184
210
|
|
|
185
211
|
// Helper function to find matching wildcard route
|
|
@@ -317,5 +317,56 @@ export default {
|
|
|
317
317
|
process.chdir(prev);
|
|
318
318
|
}
|
|
319
319
|
});
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
'router throws error when config file is outside server root': async ({pass, fail, log}) => {
|
|
323
|
+
await withTempDir(async (dir) => {
|
|
324
|
+
// Create a config file outside the server root
|
|
325
|
+
const configDir = path.join(dir, '..', 'config-outside-root');
|
|
326
|
+
const configFilePath = await write(configDir, 'outside.config.json', '{"allowedMimes": {"test": "application/test"}}');
|
|
327
|
+
|
|
328
|
+
// Create a file in the server root to verify it doesn't start
|
|
329
|
+
await write(dir, 'index.html', '<h1>Home</h1>');
|
|
330
|
+
|
|
331
|
+
const prev = process.cwd();
|
|
332
|
+
process.chdir(dir);
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
// Try to use config file outside server root with relative path
|
|
336
|
+
const flags = {root: '.', logging: 0, scan: false, config: '../config-outside-root/outside.config.json'};
|
|
337
|
+
|
|
338
|
+
log('Test setup:');
|
|
339
|
+
log('dir: ' + dir);
|
|
340
|
+
log('configDir: ' + configDir);
|
|
341
|
+
log('configFilePath: ' + configFilePath);
|
|
342
|
+
log('flags.root: ' + flags.root);
|
|
343
|
+
log('flags.config: ' + flags.config);
|
|
344
|
+
|
|
345
|
+
// Check if file exists
|
|
346
|
+
const fs = await import('fs/promises');
|
|
347
|
+
try {
|
|
348
|
+
await fs.access(configFilePath);
|
|
349
|
+
log('Config file exists at: ' + configFilePath);
|
|
350
|
+
} catch (e) {
|
|
351
|
+
log('Config file does NOT exist at: ' + configFilePath);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// This should throw an error
|
|
355
|
+
await router(flags, log);
|
|
356
|
+
|
|
357
|
+
// If we reach here, the test failed
|
|
358
|
+
return fail('router should have thrown error for config file outside root');
|
|
359
|
+
} catch (error) {
|
|
360
|
+
log('Error caught: ' + error.message);
|
|
361
|
+
// Verify the error message contains expected text
|
|
362
|
+
if (!error.message.includes('Config file must be within the server root directory')) {
|
|
363
|
+
return fail(`unexpected error message: ${error.message}`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
pass('router correctly throws error for config file outside server root');
|
|
367
|
+
} finally {
|
|
368
|
+
process.chdir(prev);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
320
371
|
}
|
|
321
372
|
};
|
|
@@ -21,7 +21,7 @@ export default {
|
|
|
21
21
|
await mkdir(path.join(rootDir, 'src'), { recursive: true });
|
|
22
22
|
await writeFile(path.join(rootDir, 'src', 'file.txt'), 'static');
|
|
23
23
|
// Create custom file outside rootPath, matching resolved customRoute
|
|
24
|
-
const customFilePath = path.resolve(
|
|
24
|
+
const customFilePath = path.resolve(rootDir, '..', 'src', 'file.txt');
|
|
25
25
|
await mkdir(path.dirname(customFilePath), { recursive: true });
|
|
26
26
|
await writeFile(customFilePath, 'custom');
|
|
27
27
|
log('Custom file path: ' + customFilePath);
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
// Configure double asterisk wildcard route
|
|
26
26
|
const config = {
|
|
27
27
|
customRoutes: {
|
|
28
|
-
'/src/**': '
|
|
28
|
+
'/src/**': '../src/**'
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -86,7 +86,7 @@ export default {
|
|
|
86
86
|
// Configure single asterisk wildcard route
|
|
87
87
|
const config = {
|
|
88
88
|
customRoutes: {
|
|
89
|
-
'/src/*': '
|
|
89
|
+
'/src/*': '../src/*'
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
|
|
@@ -140,7 +140,7 @@ export default {
|
|
|
140
140
|
// Configure wildcard route that overrides static file
|
|
141
141
|
const config = {
|
|
142
142
|
customRoutes: {
|
|
143
|
-
'/api/**': '
|
|
143
|
+
'/api/**': '../custom/**'
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
146
|
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
// Configure double asterisk wildcard route
|
|
26
26
|
const config = {
|
|
27
27
|
customRoutes: {
|
|
28
|
-
'/src/**': '
|
|
28
|
+
'/src/**': '../src/**'
|
|
29
29
|
}
|
|
30
30
|
};
|
|
31
31
|
|
|
@@ -86,7 +86,7 @@ export default {
|
|
|
86
86
|
// Configure single asterisk wildcard route
|
|
87
87
|
const config = {
|
|
88
88
|
customRoutes: {
|
|
89
|
-
'/src/*': '
|
|
89
|
+
'/src/*': '../src/*'
|
|
90
90
|
}
|
|
91
91
|
};
|
|
92
92
|
|
|
@@ -140,7 +140,7 @@ export default {
|
|
|
140
140
|
// Configure wildcard route that overrides static file
|
|
141
141
|
const config = {
|
|
142
142
|
customRoutes: {
|
|
143
|
-
'/api/**': '
|
|
143
|
+
'/api/**': '../custom/**'
|
|
144
144
|
}
|
|
145
145
|
};
|
|
146
146
|
|