# Security
## The Expression Engine
Template expressions are not run through the `Function` constructor. Lune tokenizes, parses and
compiles each expression into a closure tree, caches it by source text, and reuses it on every
update. Two things follow from that: Lune runs under a Content Security Policy that forbids
`unsafe-eval`, and an expression can only do what the engine implements.
### Supported Syntax
The subset covers what bindings need:
- Literals and template literals.
- Arrays and objects, including spreads.
- Member access, optional chaining, calls, and `new`.
- The arithmetic, comparison, logical, bitwise, and assignment operators, plus `typeof`, `void`,
`in` and `instanceof`.
- Arrow functions.
- The `if`, `return` and `throw` statements, and `let`/`const`/`var` declarations.
Loops, `function` and `class` declarations, `async`/`await`, and the `delete` operator are not part
of the subset. Keep anything that needs them in a scope method, where it is ordinary JavaScript.
Arrow parameters and declarations shadow scope properties of the same name, which is worth knowing
if you are moving from a `with`-based template engine:
```html
{{ items.filter((item) => item.done).length }}
```
### Identifier Resolution
An identifier is looked up in this order:
1. Local bindings introduced by the expression (arrow parameters, `let`/`const`/`var`).
2. The current scope, including everything inherited from parent scopes.
3. The `$data` and `$el` aliases.
4. The allowed globals.
Anything else raises a `ReferenceError`. The built-in allow list is the standard library plus a few
conveniences:
`Array`, `BigInt`, `Boolean`, `Date`, `Error`, `Infinity`, `Intl`, `JSON`, `Map`, `Math`, `NaN`,
`Number`, `Object`, `Promise`, `RangeError`, `RegExp`, `Set`, `String`, `Symbol`, `TypeError`,
`WeakMap`, `WeakSet`, `alert`, `confirm`, `console`, `decodeURI`, `decodeURIComponent`, `encodeURI`,
`encodeURIComponent`, `isFinite`, `isNaN`, `parseFloat`, `parseInt`, `structuredClone`.
### Always Rejected
These are refused when the expression is compiled, whatever the scope contains:
- **Identifiers**: `Function`, `WebSocket`, `Worker`, `XMLHttpRequest`, `document`, `eval`,
`exports`, `fetch`, `global`, `globalThis`, `importScripts`, `module`, `navigator`, `process`,
`require`, `self`, `setInterval`, `setTimeout`, `top`, `window`.
- **Property keys**: `__proto__`, `constructor` and `prototype`, whether read or written.
- **The `delete` operator.**
- **Expressions longer than 1000 characters.**
Together these close the classic routes back to dynamic code evaluation and to prototype pollution.
In development, a rejected expression produces a console warning and the binding is skipped:
```console
[Lune] WARN - Potentially unsafe expression rejected: "return(document.cookie)" "document" is not available to expressions.
```
(Single expressions are compiled as `return(…)`, which is why the wrapper shows up in the message.)
## Application Globals
Applications that reference their own globals from a template register them first:
```js
import { allowGlobals } from "lune-js";
import dayjs from "dayjs";
allowGlobals({ dayjs });
```
```html
{{ dayjs(date).format('MMM D') }}
```
A few things to know about the registry:
- It belongs to the expression engine, not to an app: every app created from the same copy of Lune
resolves the registered names, and there is no way to unregister one.
- Names are resolved when a binding runs, so registration works before or after `mount()`.
- The scope still wins. A scope property shadows a registered global of the same name.
- Names on the rejected list above cannot be registered — `allowGlobals({ fetch })` throws.
It is available from every build. With a plain script tag it sits on the global `Lune` object:
```html
```
App data is the alternative: reach for `allowGlobals` when a value is shared by every region on the
page, and for `createApp({ … })` when it belongs to one app.
## What the Engine Does Not Protect Against
The engine limits what a _template_ can reach. It is not a sandbox for untrusted templates, and it
is not a substitute for sanitizing input.
- **Scope methods are ordinary JavaScript.** A method on your scope can call `fetch`, touch
`document`, or do anything else — only the attribute expression that calls it is restricted.
- **`lu-html` still renders raw HTML.** See the [warning on that page](/directives/lu-html#security-warning-xss-risks).
## XSS Vulnerabilities
Lune evaluates JavaScript expressions in the templates. This means **if** Lune is mounted on a region of the DOM that contains non-sanitized HTML from user data, it may lead to XSS attacks.
**If your page renders user-submitted HTML, you should prefer initializing Lune using [explicit mount target](/directives/lu-scope#explicit-mount-target) so that it only processes parts that are controlled by you**.
You can also sanitize any user-submitted HTML for the `lu-scope` attribute.