Theme
Version
GitHub PyPI Discord
On this page

Security and CSP

Your site sends a Content Security Policy, and the browser blocks every script and style that the policy does not allow. Or the output must carry no JavaScript at all, such as an HTML email. Or Citry refused to render a template because a Vue directive came from c-bind, and you want to know why. This page covers all three.

For the rest of Citry's security model, such as State, CSRF, and the template sandbox, see Security.

Add a CSP nonce

A nonce is a random value, new for each response, that the policy lists and that every trusted <script> and <style> tag carries. Pass the request's nonce when you serialize the page:

page = Page()
html = page.render().serialize(csp_nonce=request_nonce)

Citry adds the nonce to the scripts and styles it places, including those that extensions and component hooks add. Your application still generates the nonce and sends the matching response header. A <script> or <style> tag written directly in a template does not get the nonce. See Use a CSP nonce.

Serialize an interactive HTML fragment with the same nonce as the page. Before starting a fragment, Citry checks that its CSP nonce, runtime, components, and assets match the page. If they do not, the fragment does not start, and the browser console shows an error.

On a page with a Content Security Policy, Vue builds the page again in the browser when it starts, so focus and text typed before then are lost. That applies when you pass csp_nonce, or when security_csp is "warn" or "strict" on Citry or on serialize(). See Replaced pages.

Inline style attributes

When the policy's style-src does not allow inline styles, the browser blocks each style attribute in the served HTML and reports it. Vue then applies the same styles through the DOM, which the policy allows. To avoid the reports, allow 'unsafe-hashes' with the style hashes, or move the styles to component CSS.

Send no JavaScript

Set security_javascript="omit" for output that should stay static, such as an email. Citry keeps the server-rendered HTML and CSS, and sends no Vue runtime, component JavaScript, Events client, or app data. Vue attributes stay in the HTML, where the browser ignores them.

Set security_javascript="forbid" to make serialization fail when the output needs browser behavior. See Limit page JavaScript.

No Vue code in c-bind

You build a dict of attributes in Python and spread it with c-bind. A plain HTML attribute such as title works. A Vue directive, : binding, or @ listener in that dict makes the page fail to render:

# Wrong: Python puts Vue code into the attributes
def template_data(self, kwargs, slots):
    return {"attrs": {"@click": "save", ":title": "hint"}}
<button c-bind="attrs">Save</button>

The error names the attribute, such as '@click'. A value that Python works out at render time may contain user input, so Citry never lets it become code the browser runs. Write the directive directly in the template:

<!-- Right: Vue code written in the template -->
<button
  :title="hint"
  @click="save"
>
  Save
</button>

Citry rejects a key that Vue would read as code: a name that starts with v-, :, @, #, ., or ^, in any letter case. The key fails whatever its value, even None: the name itself is Python data choosing Vue code. Citry's own @c- and :c- Events bindings are allowed. The same check applies to a directive written with a c- prefix, such as c-v-if, c-@click, or c-:title, because its value is also a Python expression, and to a name that an extension's on_attrs_resolved hook adds or changes.

The check runs while the template renders, so render() raises citry.VueCodeError. It fails the same way on HTML elements and component tags, and on every page, even one that never starts Vue. VueCodeError is a TypeError, so code that already catches TypeError keeps working. A c- spelling of a directive that a component tag never carries, such as c-#item, c-.prop, or c-^title on <c-Card>, fails earlier, when the template loads.

Keep Vue in templates covers the attribute mapping side of this rule.

See also