fix(auth): protect local registry directory

This commit is contained in:
2026-08-16 20:11:30 +02:00
parent de8844a4ac
commit b80a2cc1e9
2 changed files with 102 additions and 18 deletions
+24
View File
@@ -2,6 +2,7 @@ import {
chmodSync,
existsSync,
lstatSync,
mkdirSync,
renameSync,
realpathSync,
symlinkSync,
@@ -44,6 +45,7 @@ function lstatMaybe(path: string): boolean {
function root(): string {
const path = mkdtempSync(join(realpathSync(tmpdir()), "thothii-local-registry-"));
chmodSync(path, 0o700);
if ((lstatSync(path).mode & 0o7777) !== 0o700) throw new Error("test root is not private");
createdRoots.push(path);
return path;
}
@@ -123,6 +125,28 @@ describe("local user registry", () => {
await expect(registry.verify(await registry.findByUsername("operator"), password)).resolves.toBe(false);
});
test("rejects a valid registry under a non-private authentication directory", async () => {
const fixture = writeRegistry(registryYaml(userYaml()));
chmodSync(fixture.root, 0o750);
expect(lstatSync(fixture.root).mode & 0o7777).toBe(0o750);
await expectInvalid(createLocalUserRegistry(fixture.path).findByUsername("admin"), ["admin", passwordHash, fixture.path]);
});
test("rejects a valid registry under a symlinked authentication directory", async () => {
const outer = root();
const realDirectory = join(outer, "real-auth");
const linkedDirectory = join(outer, "linked-auth");
mkdirSync(realDirectory, { mode: 0o700 });
chmodSync(realDirectory, 0o700);
const path = join(realDirectory, "users.yaml");
writeFileSync(path, registryYaml(userYaml()), { encoding: "utf8", mode: 0o600 });
chmodSync(path, 0o600);
symlinkSync(realDirectory, linkedDirectory);
await expectInvalid(createLocalUserRegistry(join(linkedDirectory, "users.yaml")).findByUsername("admin"), ["admin", passwordHash]);
});
test.each([
["duplicate normalized usernames", registryYaml(userYaml() + userYaml({ id: userId, username: "admin" }))],
["duplicate IDs", registryYaml(userYaml() + userYaml({ username: "operator" }))],