harden browser routes with csrf checks and lock internal/dev endpoints

This commit is contained in:
Codex
2026-02-18 15:27:47 +00:00
parent 4814342156
commit f672677d4f
7 changed files with 200 additions and 30 deletions

View File

@@ -76,7 +76,7 @@ function createApp(options = {}) {
betterAuthBasePath: "/api/auth",
xOAuthClientId: "x-client-id",
xOAuthClientSecret: "x-client-secret",
internalApiToken: "",
internalApiToken: "internal-token",
convexDeploymentUrl: "",
convexAuthToken: "",
convexStateQuery: "state:getLatestSnapshot",
@@ -381,6 +381,7 @@ test("/api/x/mentions returns upstream mentions when configured", async () => {
const response = await call(app, {
method: "GET",
path: "/api/x/mentions",
headers: { "x-internal-token": "internal-token" },
query: { sinceId: "100" },
});
@@ -390,6 +391,61 @@ test("/api/x/mentions returns upstream mentions when configured", async () => {
assert.equal(body.mentions[0].id, "m1");
});
test("/api/x/mentions requires internal token", async () => {
const app = createApp({
xAdapter: {
isConfigured() {
return true;
},
async listMentions() {
return [];
},
},
});
const response = await call(app, {
method: "GET",
path: "/api/x/mentions",
query: { sinceId: "1" },
});
assert.equal(response.status, 401);
});
test("cross-site browser posts are blocked", async () => {
const app = createApp();
const response = await call(app, {
method: "POST",
path: "/app/actions/topup",
headers: {
cookie: "xartaudio_user=alice",
origin: "https://evil.example",
"sec-fetch-site": "cross-site",
},
body: "amount=5",
});
assert.equal(response.status, 403);
assert.match(response.body, /csrf_blocked|invalid_origin/);
});
test("dev dashboard routes can be disabled", async () => {
const app = createApp({
config: {
enableDevRoutes: false,
},
});
const response = await call(app, {
method: "POST",
path: "/app/actions/topup",
headers: { cookie: "xartaudio_user=alice" },
body: "amount=5",
});
assert.equal(response.status, 404);
});
test("simulate mention schedules background audio generation when service is configured", async () => {
const queued = [];
const app = createApp({
@@ -573,7 +629,11 @@ test("internal retention endpoint prunes stale content and assets", async () =>
});
test("internal endpoints are disabled when no token configured", async () => {
const app = createApp();
const app = createApp({
config: {
internalApiToken: "",
},
});
const response = await call(app, {
method: "POST",
path: "/internal/retention/run",