fix: harden pi management verification
This commit is contained in:
@@ -85,6 +85,51 @@ test("loopback-only AUTH_MODE=none may read the sanitized Pi status", async () =
|
||||
}
|
||||
});
|
||||
|
||||
// Catches an arbitrary website using browser CORS to mutate a loopback-only installation's Pi
|
||||
// defaults or trigger provider work with the local user's authority.
|
||||
test("loopback-only management rejects cross-origin writes", async () => {
|
||||
const service = fakeService();
|
||||
const app = appWith(service);
|
||||
try {
|
||||
const configured = await app.inject({
|
||||
method: "PUT", url: "/pi-management/config",
|
||||
headers: { host: "127.0.0.1:8080", origin: "https://evil.example" },
|
||||
payload: { provider: "zai", model: "glm-5.2", reasoning: "high" },
|
||||
});
|
||||
const smoke = await app.inject({
|
||||
method: "POST", url: "/pi-management/test",
|
||||
headers: { host: "127.0.0.1:8080", origin: "https://evil.example" },
|
||||
});
|
||||
|
||||
expect(configured.statusCode).toBe(403);
|
||||
expect(smoke.statusCode).toBe(403);
|
||||
expect(service.configure).not.toHaveBeenCalled();
|
||||
expect(service.test).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
await app.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Catches an origin guard that also blocks the same-origin Docker frontend or non-browser local
|
||||
// lifecycle clients that do not send Origin.
|
||||
test("loopback-only management preserves same-origin frontend and origin-less local writes", async () => {
|
||||
const service = fakeService();
|
||||
const app = appWith(service);
|
||||
try {
|
||||
const sameOrigin = await app.inject({
|
||||
method: "PUT", url: "/pi-management/config",
|
||||
headers: { host: "127.0.0.1:8080", origin: "http://127.0.0.1:8080" },
|
||||
payload: { provider: "zai", model: "glm-5.2", reasoning: "high" },
|
||||
});
|
||||
const lifecycleClient = await app.inject({ method: "POST", url: "/pi-management/test" });
|
||||
|
||||
expect(sameOrigin.statusCode).toBe(200);
|
||||
expect(lifecycleClient.statusCode).toBe(200);
|
||||
} finally {
|
||||
await app.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Catches route wiring that bypasses closed service validation or gives the browser a Docker/image
|
||||
// lifecycle endpoint rather than only installation-default configuration and diagnostics.
|
||||
test("trusted admins receive only configuration, smoke, options, and log endpoints", async () => {
|
||||
|
||||
Reference in New Issue
Block a user