Jaga (named after the word for "guard" or "protect") is an ultra-lightweight, zero-dependency security engine that brings Context-Aware XSS Protection to your HTML templates. It's the invisible guardian between your user's data and your application's DOM.
"Don't audit your security. Write it."
- Smart Context Awareness: Automatically identifies if data is in a
<div>, anhref, or anonclick. - Built-in URL Sanitization: Proactively blocks
javascript:and other dangerous protocols. - HTML Sanitizer: SSR-native allowlist sanitizer (
jagajs/sanitize) — zero dependencies, works in Node.js, Bun, Deno. - Secure JSON Injection:
j.json(data)safely embeds state into<script>tags, preventing breakout attacks. - Smart Minifier: Automatically cleans up unnecessary whitespace between HTML tags (intelligently preserves
<pre>and<textarea>). - DX Guardrails: Helpful console warnings during development when a security risk or non-CSP-compliant pattern is detected.
- Nano-sized: Less than 3KB gzipped (Core + Sanitizer). No dependencies, no bloat.
Modern frameworks (React, Vue, Angular) are great at escaping data in their templates, but they have blind spots where they leave security entirely to the developer. Jaga is built to fill those gaps.
| Environment | Secure Context | Blind Spot (XSS Risk) | Jaga's Solution |
|---|---|---|---|
| SSR / Node.js | - | String Templates | j template tag ✅ Native |
| React | JSX {} |
dangerouslySetInnerHTML |
sanitize(html).toString() |
| Vue | {{ }} |
v-html |
sanitize(html).toString() |
| Angular | {{ }} |
bypassSecurityTrustHtml() |
sanitize(html).toString() |
| Vanilla JS | - | element.innerHTML |
j tag or sanitize().toString() |
Note:
sanitize()returns aJagaHTMLobject (not a raw string). This is intentional — it prevents double-escaping when used with Jaga'sjtag. When passing to React, Vue, or Angular APIs that expect a plain string, call.toString()explicitly. Jaga is most powerful in SSR and Vanilla JS environments where it works natively without ceremony.
npm install jagajsJaga uses Context-Aware Escaping. It knows where your data is going and applies the correct security rules automatically.
import { j } from "jagajs";
const userUrl = "javascript:alert(1)";
const userName = '"><img src=x onerror=alert(1)>';
// Jaga handles everything:
const html = j`
<div title="${userName}">
<a href="${userUrl}">Profile</a>
</div>
`;
// Result:
// <div title=""><img src=x onerror=alert(1)>">
// <a href="about:blank">Profile</a>
// </div>Use this for dangerouslySetInnerHTML or whenever you need to permit some HTML (like from a rich text editor) but block the dangerous parts.
import { sanitize } from "jagajs/sanitize";
// Strip dangerous tags/attrs but keep formatting
const clean = sanitize(userRichText, {
allowedTags: ["b", "i", "p", "a"],
allowedAttrs: { a: ["href"] },
});
// Works perfectly with Jaga core
const article = j`<div class="content">${clean}</div>`;Safely inject server-side state into your frontend without worrying about </script> breakouts.
const state = {
user: "Admin",
bio: "</script><script>alert('pwned')</script>",
};
const html = j`
<script>
window.__INITIAL_STATE__ = ${j.json(state)};
</script>
`;Jaga handles arrays seamlessly and securely:
const items = ["Safe", "<b>Bold</b>", "<i>Italic</i>"];
const list = j`<ul>${items.map((i) => j`<li>${i}</li>`)}</ul>`;Jaga's j tag automatically minifies your HTML by removing unnecessary whitespace, but it's smart enough to ignore <pre> and <textarea> tags.
const html = j`
<div>
<span>Compact but...</span>
<pre> This space is preserved! </pre>
</div>
`;Easy injection of CSP nonces for inline scripts:
import { j, nonce } from "jagajs";
const myNonce = nonce();
const script = j`<script nonce="${myNonce}">console.log('Safe script');</script>`;Jaga's JagaHTML wrapper is designed to integrate cleanly with the browser's Trusted Types API. When your CSP enforces require-trusted-types-for 'script', you can wrap Jaga's output with your own policy:
const policy = trustedTypes.createPolicy("jaga", {
// Best Practice: Always sanitize inside the policy itself!
createHTML: (html) => sanitize(html).toString(),
});
// Now you can safely pass even untrusted strings:
element.innerHTML = policy.createHTML(untrustedHtml);Native
TrustedTypePolicyintegration (auto-wrapping) is planned for a future release.
MIT © Dogukan Batal