Security
This page documents the security model of the Yak SDK and best practices for secure integration.
Overview
The Yak SDK is designed with security in mind. The widget runs in an iframe hosted on chat.yak.io, communicating with your application via postMessage. This architecture provides several security benefits:
- Origin isolation – The iframe is sandboxed from your application's DOM
- Controlled communication – All messages are validated against expected origins
- Allowlisted tools – Only explicitly allowed tools can be executed
Communication Flow
┌─────────────────────┐ postMessage ┌─────────────────────┐
│ Your Application │◄────────────────────────►│ Yak Chat Widget │
│ (host origin) │ (origin validated) │ (chat.yak.io) │
└─────────────────────┘ └─────────────────────┘
│
│ fetch (same-origin)
▼
┌─────────────────────┐
│ /api/yak │
│ Server Handlers │
└─────────────────────┘Origin Validation
The SDK validates all incoming postMessage events against allowed origins:
- The environment-determined iframe origin (localhost →
http://localhost:3001, dev →https://chat.yak.supply, production →https://chat.yak.io) - Your application's own origin (for same-origin messages)
Messages from unexpected origins are rejected and logged. The iframe origin is determined automatically based on your environment and cannot be configured—this ensures communication only happens with trusted Yak domains.
Redirect Protection
The SDK includes built-in protection against open redirect attacks. When the chat widget requests a redirect, the SDK validates the path:
- ✅ Relative paths (
/dashboard,/settings) - ✅ Hash and query paths (
#section,?tab=profile) - ✅ Same-origin absolute URLs
- ❌ External domain URLs (blocked)
// These redirects are allowed
"/dashboard" // ✅ Relative path
"/posts/123" // ✅ Relative path
"#comments" // ✅ Hash path
"?filter=active" // ✅ Query path
// These redirects are blocked
"https://evil.com" // ❌ External domain
"//evil.com/phish" // ❌ Protocol-relative URLIf you need custom redirect behavior, provide your own handler:
<YakProvider
appId="your-app"
onRedirect={(path) => {
// Implement your own validation if needed
if (isAllowedPath(path)) {
router.push(path);
}
}}
/>Tool Security
Allowlisting Procedures
When using the tRPC adapter, you must explicitly allowlist which procedures can be invoked as tools:
import { createTRPCToolAdapter } from "@yak-io/trpc";
const toolAdapter = createTRPCToolAdapter({
router: appRouter,
createContext,
// Only these procedures can be called
allowedProcedures: [
"orders.list",
"orders.getById",
"products.search",
],
});Never use a wildcard or allow all procedures. The allowlist ensures only safe, read-oriented operations are exposed to the AI.
Tool Execution Context
Tool calls receive the original Request object, allowing you to:
- Validate authentication via cookies/headers
- Apply rate limiting
- Log tool invocations
const toolAdapter = createTRPCToolAdapter({
router: appRouter,
createContext: async ({ req }) => {
// Your existing auth logic
const session = await getSession(req);
if (!session) throw new Error("Unauthorized");
return { user: session.user };
},
allowedProcedures: ["..."],
});Prototype Pollution Protection
The SDK includes protection against prototype pollution attacks in dynamic property access paths:
// These paths are blocked
"__proto__.polluted" // ❌ Blocked
"constructor.prototype" // ❌ Blocked
"prototype.isAdmin" // ❌ BlockedData Flow Security
What Data Flows to the Widget
| Data Type | Direction | Description |
|---|---|---|
| Page Context | Host → Widget | Current URL, page title, visible text |
| Route Manifest | Host → Widget | List of available routes |
| Tool Manifest | Host → Widget | Tool names, descriptions, schemas |
| Tool Results | Host → Widget | Results of executed tools |
| Prompts | Widget → Host | User messages (for redirect/tool calls) |
What Data Does NOT Flow
- Cookies – Not sent to the iframe
- localStorage – Not accessible from the iframe
- DOM access – The iframe cannot read your page's DOM
- Auth tokens – Never sent to the widget
Page context (visible text) is extracted from your page and sent to the AI for contextual assistance. If you have sensitive information on the page, consider what's visible to users.
Server-Side Security
Authentication
Always authenticate tool requests on the server:
// app/api/yak/route.ts
import { createNextYakHandler } from "@yak-io/nextjs/server";
import { auth } from "@clerk/nextjs/server";
export const { GET, POST } = createNextYakHandler({
routes: scanRoutes("./src/app"),
tools: createTRPCToolAdapter({
router: appRouter,
createContext: async ({ req }) => {
const { userId } = await auth();
if (!userId) throw new Error("Unauthorized");
return { userId };
},
allowedProcedures: ["..."],
}),
});Rate Limiting
Consider adding rate limiting to your tool endpoint:
// Using a rate limiter (example with upstash)
import { Ratelimit } from "@upstash/ratelimit";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
});
export async function POST(req: Request) {
const ip = req.headers.get("x-forwarded-for") ?? "anonymous";
const { success } = await ratelimit.limit(ip);
if (!success) {
return new Response("Too many requests", { status: 429 });
}
return handler.POST(req);
}Input Validation
Tool inputs are validated against the JSON Schema you define. Always validate on the server side as well:
// In your tRPC procedures
export const ordersRouter = router({
list: protectedProcedure
.input(z.object({
limit: z.number().min(1).max(100).default(10),
status: z.enum(["pending", "shipped", "delivered"]).optional(),
}))
.query(async ({ ctx, input }) => {
// Input is validated by Zod
return db.orders.findMany({
where: { userId: ctx.userId, status: input.status },
take: input.limit,
});
}),
});Production Checklist
Before deploying to production, verify:
- Tool allowlist – Only safe procedures are exposed
- Authentication – Tool requests require valid auth
- Rate limiting – Prevent abuse of tool endpoints
- HTTPS – All endpoints use HTTPS
- CSP headers – Allow
frame-src chat.yak.ioif using CSP - Logging – Tool invocations are logged for audit
Content Security Policy
If your application uses Content Security Policy headers, add the following:
frame-src https://chat.yak.io;This allows the Yak widget iframe to load while maintaining your CSP protections.
Reporting Security Issues
If you discover a security vulnerability in the Yak SDK, please report it responsibly:
- Do not create a public GitHub issue
- Email security@yak.io with details
- Include steps to reproduce if possible
We take security seriously and will respond promptly to all reports.