Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tech-dipesh/yeti-Jobs/llms.txt
Use this file to discover all available pages before exploring further.
Yeti Jobs layers several security measures on top of its JWT authentication system. Rate limiting prevents brute-force and abuse at the IP level. Helmet sets hardened HTTP response headers on every response. CORS locks the API down to a single trusted front-end origin. Zod schema validation and UUID pre-screening reject malformed input before it ever reaches business logic or the database.
Rate Limiting
Global Rate Limit
A single express-rate-limit instance is created in src/app.js and applied globally with app.use. Every IP address is allowed a maximum of 200 requests per 60-second window across all endpoints.
import rateLimit from "express-rate-limit";
const limitUser = rateLimit({
windowMs: 1000 * 60, // 60 seconds
limit: 200,
});
app.set('trust proxy', 1);
app.use(limitUser);
app.set('trust proxy', 1) is required because the API is deployed on Render, which sits behind a reverse proxy. Without this setting, express-rate-limit would see every request as coming from the proxy’s IP address and rate-limit all users as one. Setting trust proxy to 1 tells Express to read the real client IP from the X-Forwarded-For header.
When the limit is exceeded, express-rate-limit returns a 429 Too Many Requests response with the default error body.
Password Reset and Verification Rate Limit
A separate, stricter rateLimit instance is defined in src/routes/users.routes.js and applied individually to the four most abuse-prone endpoints:
const limitUser = rateLimit({
windowMs: 1000 * 60, // 60 seconds
limit: 2,
message: { message: "You only can send resend request twice per minute" },
});
router.post("/forget-password", limitUser, forgetEmailPassword);
router.post("/forget-password/verify", limitUser, verifyForgetPassword);
router.post("/verify", limitUser, isUnverifiedUser, verifyEmailConfirmation);
router.post("/verify/resend", limitUser, isUnverifiedUser, resendVerificationCode);
| Endpoint | Window | Max requests | Purpose of limit |
|---|
POST /api/v1/users/forget-password | 60 s | 2 | Stops email flooding and account enumeration |
POST /api/v1/users/forget-password/verify | 60 s | 2 | Limits brute-force guessing of 6-digit reset codes |
POST /api/v1/users/verify | 60 s | 2 | Prevents rapid-fire verification code guessing |
POST /api/v1/users/verify/resend | 60 s | 2 | Prevents abuse of the Nodemailer sending quota |
In a distributed deployment (multiple API instances behind a load balancer), the in-memory store used by express-rate-limit tracks state per process. To share rate limit counters across instances, swap the default store for a Redis-backed one using the rate-limit-redis package:import { RedisStore } from "rate-limit-redis";
import { createClient } from "redis";
const redisClient = createClient({ url: process.env.REDIS_URL });
await redisClient.connect();
const limitUser = rateLimit({
windowMs: 1000 * 60,
limit: 200,
store: new RedisStore({ sendCommand: (...args) => redisClient.sendCommand(args) }),
});
helmet() is applied as global middleware immediately after the rate limiter:
import helmet from "helmet";
app.use(limitUser);
app.use(helmet());
Helmet bundles a set of smaller middleware functions, each of which sets or removes a specific HTTP header. With the default configuration, calling helmet() activates all of them.
| Header set / removed by Helmet | Effect |
|---|
X-Powered-By removed | Hides the fact that the server runs Express, reducing fingerprinting surface |
Content-Security-Policy | Restricts the sources from which scripts, styles, and other resources can be loaded |
Cross-Origin-Embedder-Policy | Controls cross-origin resource embedding (COEP) |
Cross-Origin-Opener-Policy | Isolates browsing context to prevent cross-origin attacks |
Cross-Origin-Resource-Policy | Prevents other origins from reading the API responses |
Origin-Agent-Cluster | Requests a dedicated process for the origin to improve isolation |
Referrer-Policy | Controls how much referrer information is included in requests |
Strict-Transport-Security (HSTS) | Tells browsers to only access the site over HTTPS for the configured duration |
X-Content-Type-Options | Prevents MIME-type sniffing (nosniff) |
X-DNS-Prefetch-Control | Disables DNS prefetching to avoid information leakage |
X-Download-Options | Prevents IE from executing downloads in the site’s context |
X-Frame-Options | Blocks the API responses from being embedded in <iframe> elements (clickjacking) |
X-Permitted-Cross-Domain-Policies | Restricts Adobe Flash and PDF cross-domain access |
X-XSS-Protection | Enables the browser’s built-in XSS filter (legacy browsers) |
CORS
CORS is configured in src/app.js to allow requests only from the single trusted front-end origin defined by CLIENT_BASE_URL:
app.use(cors({
origin: process.env.CLIENT_BASE_URL,
credentials: true,
exposedHeaders: ['Content-Length'],
}));
| Option | Value | Purpose |
|---|
origin | process.env.CLIENT_BASE_URL | Whitelists one origin; all others receive a CORS error |
credentials | true | Required to allow the browser to send and receive the JWT cookie |
exposedHeaders | ['Content-Length'] | Allows the client to read the Content-Length response header |
Any request from an origin other than CLIENT_BASE_URL will be rejected by the browser’s CORS pre-flight check before it can interact with the API.
All major write endpoints parse and validate their request bodies using Zod schemas defined in src/Models/. Validation runs as the first step inside each controller, before any database query.
// Inside postSignupUserController
const validateuser = userSchema.safeParse(allUser);
if (!validateuser.success) {
const message = validateuser.error.issues[0].message;
return res.status(422).json({ message });
}
Validation schemas are applied on these endpoints:
| Endpoint | Zod Schema |
|---|
POST /api/v1/users/signup | userSchema |
POST /api/v1/users/login | loginUserSchema |
PUT /api/v1/users/:id | updateUserSchema |
POST /api/v1/users/add-education | EducationUserSchema |
Validation failures return 422 Unprocessable Entity with the first Zod error message, never a raw stack trace. This gives Yeti Jobs a three-layer validation model:
Client-side validation
↓
Server-side Zod schema (422 on failure)
↓
PostgreSQL column constraints (500 on unhandled edge cases)
UUID Validation
The validateCorrectUid middleware (src/Middleware/validateCorrectUid.js) is placed before isLoggedIn and isOwner on every route containing an :id path parameter. It validates the value against the standard UUID format regex before the request proceeds any further.
export default function validateCorrectUid(req, res, next) {
const { id } = req.params;
if (!id) {
return res.status(404).json({ message: "Please Enter a Id" });
}
const regex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
if (!regex.test(id)) {
return res.status(400).json({ message: "Please Enter Correct Id:" });
}
next();
}
This ensures that malformed or deliberately crafted :id values (SQL injection attempts, oversized strings, non-UUID formats) are rejected with 400 Bad Request before any database query or ownership check runs. Routes that use validateCorrectUid include:
GET / PUT / PATCH /api/v1/users/:id
POST /api/v1/users/:id/skills
- Any route on the jobs, companies, and applications routers that accept a UUID
:id param
Security Measures at a Glance
| Layer | Mechanism | Scope |
|---|
| Rate limiting (global) | express-rate-limit 200 req/min | All endpoints |
| Rate limiting (strict) | express-rate-limit 2 req/min | /verify, /forget-password routes |
| Security headers | helmet() (default config) | All responses |
| Origin restriction | CORS CLIENT_BASE_URL allowlist | All cross-origin requests |
| Body validation | Zod schemas | Signup, login, update, education |
| Param validation | validateCorrectUid UUID regex | All :id route params |
| Auth enforcement | isLoggedIn JWT middleware | Companies, applications, admin, notifications |
| Cookie hardening | httpOnly, secure, sameSite | JWT cookie on every auth response |