Files
2026-07-13 13:39:12 +08:00

53 lines
2.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import { resolveSafeI18nSectionDir } from "../../../src/lib/docsI18nPath.ts";
const DOCS_ROOT = path.resolve("/srv/app", "docs");
const I18N_ROOT = path.join(DOCS_ROOT, "i18n");
test("resolveSafeI18nSectionDir rejects traversal in the locale (cookie-controlled)", () => {
const badLocales = ["..", "../..", "/etc", "es/../../etc", "es\0", "..%2f", "en.", "es/x"];
for (const locale of badLocales) {
assert.strictEqual(
resolveSafeI18nSectionDir(DOCS_ROOT, locale, ["overview"]),
null,
`Should reject bad locale: ${JSON.stringify(locale)}`
);
}
});
test("resolveSafeI18nSectionDir rejects traversal in slug segments", () => {
const badSlugs = [[".."], ["..", "foo"], ["/etc", "passwd"], ["foo", "..", "bar"], ["a/b"]];
for (const slug of badSlugs) {
assert.strictEqual(
resolveSafeI18nSectionDir(DOCS_ROOT, "pt-BR", slug),
null,
`Should reject bad slug: ${slug.join("/")}`
);
}
});
test("resolveSafeI18nSectionDir confines the resolved dir to docs/i18n", () => {
// Valid input → a dir strictly under i18nRoot.
const ok = resolveSafeI18nSectionDir(DOCS_ROOT, "pt-BR", ["architecture", "overview"]);
assert.ok(ok, "valid locale+slug should resolve");
assert.ok(
ok === I18N_ROOT || ok.startsWith(I18N_ROOT + path.sep),
`resolved dir must stay under i18nRoot, got ${ok}`
);
assert.strictEqual(ok, path.join(I18N_ROOT, "pt-BR", "docs", "architecture"));
});
test("resolveSafeI18nSectionDir allows valid locale/slug patterns", () => {
const goodLocales = ["en", "pt-BR", "zh-CN", "es"];
for (const locale of goodLocales) {
assert.ok(
resolveSafeI18nSectionDir(DOCS_ROOT, locale, ["getting-started"]),
`Should allow good locale: ${locale}`
);
}
assert.ok(resolveSafeI18nSectionDir(DOCS_ROOT, "es", ["v1-migration"]));
assert.strictEqual(resolveSafeI18nSectionDir(DOCS_ROOT, "es", []), null, "empty slug → null");
});