Anticode Open builder

Inline event handlers: why onclick and onerror never ship

An inline event handler is JavaScript written straight into an HTML attribute: onclick, onload, onerror, onmouseover and dozens more. They're the oldest way to make a page interactive and one of the commonest ways script gets smuggled into one. Anticode pages carry none, and three separate checks make sure it stays that way.

Updated · By the Anticode team

Why a handler is a script in disguise

Many filters that clean up user content look for script tags and stop there. An attacker doesn't need one. An image with a broken address and an onerror attribute runs its code the moment the browser fails to load it; a link with onmouseover runs when the pointer passes. The classic example is a comment form that lets through <img src=x onerror=…>. To the filter it's just an image. To the browser it's an instruction. That, in one line, is the whole inline event handlers security problem: any attribute that can hold code is a place to hide code.

This is also where inline handlers meet a Content-Security-Policy. A strict policy without 'unsafe-inline' for scripts blocks inline handlers, which is good. But a site that relies on them for its own buttons can't adopt that policy without breaking itself, so developers loosen the policy and the protection goes with it. The usual advice is to remove inline handlers and attach behaviour from separate script files. Anticode's pages go further, since they have no script at all.

Three checks, in order

  1. The code editor, before an edit is applied. In Pro mode you can edit a page's HTML. If what you type adds an attribute starting with on, the edit isn't applied, the line is highlighted, and the message explains that inline handlers are a common way to slip script into a page. A button offers to undo your code edits.
  2. Sentinel, after every build and edit. It checks every element on the page for attributes whose names start with on and reports “No inline event handlers” in the Report tab, or the number of elements that have one.
  3. The server, at publish. It parses the page with a real HTML tokenizer and fails any element with an on attribute, naming the tag and attribute, then scans the raw markup (outside style blocks) for anything shaped like onsomething= as a second net. A failing page isn't published.

The server step is the one that matters for safety, because it doesn't depend on the builder in your browser. The CLI, coding agents and AI assistants connected over MCP all publish through it too.

Close relatives the same checks catch

Inline handlers have cousins that run code from an attribute without being called on-anything. The code editor and the publish gate refuse these as well:

javascript: URLs
A link or form target such as href="javascript:…" runs code when followed. The server decodes character references and strips the control characters and whitespace browsers ignore before checking, so disguised versions are caught too.
data: documents
A link or source pointing at a data: URL that holds an HTML document rather than an image.
Script in styles
Old tricks such as expression(…) or a javascript: URL inside url() in a style attribute or stylesheet.

Removing inline handlers from your own sites

If you maintain other sites, a quick search of the source for on followed by letters and = finds most of them. Replace each with an event listener in a script file, then tighten your policy so inline script is refused. Often the handler isn't needed at all: a details element opens and closes without one, a link can go to a section without one, and a form can submit without one. The guide on checking a website for security issues covers this alongside other quick wins.

Limits

  • Any attribute starting with on counts. The rule is broad on purpose. A custom attribute that happens to begin with those letters will fail; rename it.
  • Pages, not apps. App builds run real JavaScript inside a sandbox and are checked by their own code gate, which looks for different risks; see the app code gate.
  • Markup, not intent. The checks block the mechanism. They can't tell whether a handler was meant well, and they don't need to.

Questions people ask

Does a Content-Security-Policy block inline event handlers?

A strict policy that doesn't allow inline script does block them. Anticode pages go further: they contain no handlers, and their policy allows no script at all.

Why did the code editor refuse my onclick?

Pages ship with script-src 'none', so the handler would never run, and inline handlers are a common route for injected code. The editor refuses them before applying the edit.

How do I make a button do something without onclick?

On an Anticode page, buttons either submit a form to the site's server or link somewhere. For live interactivity, build an app instead of a page.