security: remove dead free signup route (abuse vector) + add test
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 9m39s

The /v1/signup/free endpoint was still mounted despite free tier
being removed in v0.3.0. Anyone could generate unlimited free API keys.

- Removed signup route registration from index.ts
- Deleted src/routes/signup.ts (dead code)
- Added signup-removed.test.ts verifying 404 on signup endpoints
- Cleaned up leaked test key from production DB

334 tests passing.
This commit is contained in:
SnapAPI CEO 2026-03-03 21:02:18 +01:00
parent 740c70f905
commit f3a363fb17
3 changed files with 44 additions and 31 deletions

View file

@ -16,7 +16,7 @@ import { loadKeys, getAllKeys } from "./services/keys.js";
import { initDatabase, pool } from "./services/db.js";
import { billingRouter } from "./routes/billing.js";
import { statusRouter } from "./routes/status.js";
import { signupRouter } from "./routes/signup.js";
import { usageRouter } from "./routes/usage.js";
import { openapiSpec } from "./docs/openapi.js";
@ -96,7 +96,7 @@ app.use("/health", healthRouter);
app.use("/v1/billing", billingRouter);
app.use("/status", statusRouter);
app.use("/v1/playground", playgroundRouter);
app.use("/v1/signup", signupRouter);
// Authenticated routes
app.use("/v1/usage", usageRouter);

View file

@ -0,0 +1,42 @@
import { describe, it, expect, vi, beforeAll } from "vitest";
import request from "supertest";
// Mock dependencies before importing app
vi.mock("../../services/db.js", () => ({
initDatabase: vi.fn(),
pool: { query: vi.fn(), end: vi.fn() },
}));
vi.mock("../../services/browser.js", () => ({
initBrowser: vi.fn(),
closeBrowser: vi.fn(),
}));
vi.mock("../../services/keys.js", () => ({
loadKeys: vi.fn(),
getAllKeys: vi.fn().mockReturnValue([]),
findKey: vi.fn(),
createKey: vi.fn(),
}));
vi.mock("../../middleware/usage.js", () => ({
loadUsageData: vi.fn(),
usageMiddleware: vi.fn((_req: any, _res: any, next: any) => next()),
}));
let app: any;
beforeAll(async () => {
const mod = await import("../../index.js");
app = mod.app;
});
describe("Free signup removal (v0.3.0)", () => {
it("POST /v1/signup/free should return 404 — free tier removed", async () => {
const res = await request(app)
.post("/v1/signup/free")
.send({ email: "test@example.com" });
expect(res.status).toBe(404);
});
it("GET /v1/signup should return 404", async () => {
const res = await request(app).get("/v1/signup");
expect(res.status).toBe(404);
});
});

View file

@ -1,29 +0,0 @@
import { Router } from "express";
import { createKey } from "../services/keys.js";
import logger from "../services/logger.js";
export const signupRouter = Router();
// Simple signup: email → instant API key (no verification for now)
signupRouter.post("/free", async (req, res) => {
const { email } = req.body;
if (!email || typeof email !== "string" || !email.includes("@")) {
res.status(400).json({ error: "Valid email required" });
return;
}
try {
const key = await createKey(email.toLowerCase().trim(), "free");
logger.info({ email: email.slice(0, 3) + "***" }, "Free signup");
res.json({
apiKey: key.key,
tier: "free",
limit: 100,
message: "Your API key is ready! 100 free screenshots/month.",
});
} catch (err: any) {
logger.error({ err }, "Signup failed");
res.status(500).json({ error: "Signup failed" });
}
});