mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-16 14:16:16 -08:00
Follow-up to #1278, which split four of the files the electron-backend lint target had been silently skipping. Four were left over; this splits them, so no file in the project sits above the 400-line cap outside the baseline. None are added to tools/eslint/max-lines-baseline.mjs — the baseline only shrinks. Shared setup moves to *.test-helpers.ts, the suffix tsconfig.app.json already excludes, so the production build never sees jest globals. - remote-control.events.spec.ts 588 -> 188, plus remote-control-http.spec.ts. The mock registry moves to remote-control.test-helpers.ts; each spec keeps its own jest.mock() factories, which resolve the mock-prefixed exports. - downloads.events.spec.ts 530 -> 182, plus downloads-actions.spec.ts. The jest.doMock setup is not hoisted, so the whole harness moves to downloads.test-helpers.ts behind setupDownloadsEventsHarness(). - http-server.spec.ts 448 -> 377, plus resolve-static-file-path.spec.ts. The resolveStaticFilePath cases were already self-contained. - worker-performance-capture.spec.ts 408 -> 149, plus worker-performance-capture.resilience.spec.ts, matching the .concurrency and .histogram siblings. Test bodies are unchanged; the helpers keep the same identifiers in scope so the splits are a move, not a rewrite. Verified with the glob quoted locally (that quoting is #1176's, not part of this change): 245 files, 0 max-lines errors, and the only remaining lint errors are the five #1176 fixes. Both tsconfig.app.json and tsconfig.spec.json typecheck clean. Note: worker-performance-capture.concurrency.spec.ts is flaky on master independently of this change — it asserts a real 20ms event-loop block and failed 4/4 clean-master runs here. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
75 lines
2.1 KiB
TypeScript
75 lines
2.1 KiB
TypeScript
import { posix, win32 } from 'node:path';
|
|
|
|
import { resolveStaticFilePath } from './http-server';
|
|
|
|
describe('resolveStaticFilePath', () => {
|
|
const POSIX_STATIC_ROOT = '/opt/iptvnator/remote-control';
|
|
const WINDOWS_STATIC_ROOT = 'C:\\iptvnator\\remote-control';
|
|
|
|
it('rejects a Windows double-leading-slash traversal', () => {
|
|
expect(
|
|
resolveStaticFilePath(
|
|
WINDOWS_STATIC_ROOT,
|
|
'//../../outside-secret.txt',
|
|
win32
|
|
)
|
|
).toBeNull();
|
|
});
|
|
|
|
it('rejects encoded traversal segments after one decode', () => {
|
|
expect(
|
|
resolveStaticFilePath(
|
|
POSIX_STATIC_ROOT,
|
|
'/%2e%2e/outside-secret.txt',
|
|
posix
|
|
)
|
|
).toBeNull();
|
|
});
|
|
|
|
it('fails closed for malformed percent encoding without throwing', () => {
|
|
expect(
|
|
resolveStaticFilePath(POSIX_STATIC_ROOT, '/%E0%A4%A', posix)
|
|
).toBeNull();
|
|
});
|
|
|
|
it('rejects decoded NUL bytes', () => {
|
|
expect(
|
|
resolveStaticFilePath(
|
|
POSIX_STATIC_ROOT,
|
|
'/assets/%00secret.js',
|
|
posix
|
|
)
|
|
).toBeNull();
|
|
});
|
|
|
|
it('resolves a valid Windows-style asset inside the static root', () => {
|
|
expect(
|
|
resolveStaticFilePath(
|
|
WINDOWS_STATIC_ROOT,
|
|
'/assets\\app.js?version=1',
|
|
win32
|
|
)
|
|
).toBe('C:\\iptvnator\\remote-control\\assets\\app.js');
|
|
});
|
|
|
|
it('ignores query and fragment data for filesystem resolution', () => {
|
|
expect(
|
|
resolveStaticFilePath(
|
|
POSIX_STATIC_ROOT,
|
|
'/data.json?cache=1#ignored',
|
|
posix
|
|
)
|
|
).toBe('/opt/iptvnator/remote-control/data.json');
|
|
});
|
|
|
|
it('decodes the URL pathname exactly once', () => {
|
|
expect(
|
|
resolveStaticFilePath(
|
|
POSIX_STATIC_ROOT,
|
|
'/%252e%252e/asset.js',
|
|
posix
|
|
)
|
|
).toBe('/opt/iptvnator/remote-control/%2e%2e/asset.js');
|
|
});
|
|
});
|