mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
@@ -3,9 +3,9 @@
|
||||
* the code/data path resolution (both the env-override and default branches).
|
||||
*/
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
import { isKnownPermission, METHOD_PERMISSION, KNOWN_METHODS } from '../../../src/nest/plugins/protocol/envelope';
|
||||
import { isKnownPermission, METHOD_PERMISSION, KNOWN_METHODS, HOOK_PERMISSION } from '../../../src/nest/plugins/protocol/envelope';
|
||||
import path from 'node:path';
|
||||
import { pluginsCodeRoot, pluginsDataRoot, pluginCodeDir, pluginDbFile, resolveChildEntry, serverCodeRoot, pluginPermissionArgs, pluginRealCodeDir, ensurePluginModuleType } from '../../../src/nest/plugins/paths';
|
||||
import { pluginsCodeRoot, pluginsDataRoot, pluginCodeDir, pluginDataDir, pluginDbFile, resolveChildEntry, serverCodeRoot, pluginPermissionArgs, pluginRealCodeDir, ensurePluginModuleType } from '../../../src/nest/plugins/paths';
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.TREK_PLUGINS_DIR;
|
||||
@@ -21,9 +21,19 @@ describe('envelope helpers', () => {
|
||||
expect(isKnownPermission('')).toBe(false);
|
||||
});
|
||||
|
||||
it('every known method maps to a permission', () => {
|
||||
// The compile side of this is `as const satisfies Record<KnownMethod, KnownPermission>`
|
||||
// on METHOD_PERMISSION. These are the runtime mirror, and they also cover what the type
|
||||
// system cannot: isKnownPermission's http:outbound: prefix branch.
|
||||
it('every known method maps to a permission the host actually knows', () => {
|
||||
for (const m of KNOWN_METHODS) {
|
||||
expect(METHOD_PERMISSION[m]).toBeTruthy();
|
||||
const perm = METHOD_PERMISSION[m];
|
||||
expect(isKnownPermission(perm), `${m} -> ${perm}`).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('every hook permission is a known permission', () => {
|
||||
for (const [hook, perm] of Object.entries(HOOK_PERMISSION)) {
|
||||
expect(isKnownPermission(perm), `${hook} -> ${perm}`).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -91,3 +101,44 @@ describe('paths', () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* The id these helpers get comes off a route parameter — POST
|
||||
* /api/admin/plugins/:id/uninstall walks it into pluginCodeDir and hands the result
|
||||
* to a recursive remove. Express decodes %2F only AFTER routing, so a traversal
|
||||
* still matches the route and arrives here decoded.
|
||||
*/
|
||||
describe('plugin id containment', () => {
|
||||
const traversals = [
|
||||
'../../uploads',
|
||||
'../..',
|
||||
'..',
|
||||
'.',
|
||||
'a/b',
|
||||
'a\b',
|
||||
'C:\Windows',
|
||||
'.hidden',
|
||||
'',
|
||||
];
|
||||
|
||||
it('refuses any id that could point outside its parent', () => {
|
||||
for (const id of traversals) {
|
||||
expect(() => pluginCodeDir(id), id).toThrow(/invalid plugin id/);
|
||||
expect(() => pluginDataDir(id), id).toThrow(/invalid plugin id/);
|
||||
}
|
||||
});
|
||||
|
||||
it('still accepts the ids plugins actually use', () => {
|
||||
for (const id of ['x', 'a1', '001', 'flight-tracker', 'Bad-Id', 'trip.todos', 'koffi_and_friends']) {
|
||||
expect(path.basename(pluginCodeDir(id))).toBe(id);
|
||||
expect(path.basename(pluginDataDir(id))).toBe(id);
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps every accepted id inside the plugin roots', () => {
|
||||
for (const id of ['x', 'flight-tracker', 'a.b', 'a-b_c']) {
|
||||
expect(pluginCodeDir(id).startsWith(pluginsCodeRoot() + path.sep)).toBe(true);
|
||||
expect(pluginDataDir(id).startsWith(pluginsDataRoot() + path.sep)).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user