Files
BetterDesk/web-nodejs/tests/safePath.test.js
T
UNITRONIX 7b938d8c7b fix(security): harden path confinement, SSRF, and shell exec
Add shared safePath helper for file browser, i18n, and backup paths;
use validated OIDC discovery URLs; restrict terminal shells and
network monitor HTTP requests. Remove obsolete one-time i18n migration
scripts already merged into lang/*.json.
2026-06-09 02:21:43 +02:00

67 lines
2.2 KiB
JavaScript

'use strict';
const fs = require('fs');
const os = require('os');
const path = require('path');
const {
isPathInsideRoot,
resolveChildPath,
resolvePathWithinRoot,
resolvePathWithinAnyRoot,
resolveLangFilePath,
} = require('../lib/safePath');
describe('safePath', () => {
let tmpRoot;
beforeEach(() => {
tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'bd-safe-path-'));
});
afterEach(() => {
fs.rmSync(tmpRoot, { recursive: true, force: true });
});
test('isPathInsideRoot accepts descendants', () => {
const child = path.join(tmpRoot, 'a', 'b.txt');
expect(isPathInsideRoot(child, tmpRoot)).toBe(true);
});
test('isPathInsideRoot rejects parent escape', () => {
const outside = path.join(tmpRoot, '..', 'etc', 'passwd');
expect(isPathInsideRoot(outside, tmpRoot)).toBe(false);
});
test('resolveChildPath blocks traversal in segment', () => {
expect(() => resolveChildPath(tmpRoot, '../etc/passwd')).toThrow(/segment/i);
expect(() => resolveChildPath(tmpRoot, 'foo/bar')).toThrow(/segment/i);
});
test('resolveChildPath returns path under root', () => {
const p = resolveChildPath(tmpRoot, 'backup-1');
expect(p).toBe(path.resolve(tmpRoot, 'backup-1'));
});
test('resolvePathWithinRoot follows realpath for existing files', () => {
const sub = path.join(tmpRoot, 'sub');
fs.mkdirSync(sub);
const file = path.join(sub, 'f.txt');
fs.writeFileSync(file, 'ok');
const resolved = resolvePathWithinRoot(file, tmpRoot);
expect(resolved).toBe(fs.realpathSync.native(file));
});
test('resolvePathWithinAnyRoot allows configured roots', () => {
const p = resolvePathWithinAnyRoot(path.join(tmpRoot, 'x'), [tmpRoot, '/var/log']);
expect(p).toBe(path.resolve(tmpRoot, 'x'));
});
test('resolveLangFilePath confines to lang dir', () => {
const langDir = path.join(tmpRoot, 'lang');
fs.mkdirSync(langDir);
const p = resolveLangFilePath(langDir, 'en');
expect(p).toBe(path.join(langDir, 'en.json'));
expect(() => resolveLangFilePath(langDir, '../secrets')).toThrow();
});
});