Sanitize HTML
web.sanitize_html
Overview
You send HTML and we rebuild it from an allowlist, so anything not explicitly permitted disappears: script and style elements, iframes, forms, on-event handlers, and links using a scheme outside ftp, http, https, and mailto. Four ready-made policies run from plain text through to tables and headings, or you can name your own tags and attributes — a list of your own can narrow what survives but never re-admit active markup. Surviving anchors can be given target and rel attributes for you. The cleaned markup comes back as sanitizedHtml, so nothing overwrites what you sent. Accepts file input via file_ref — no need to extract file content first.
Markup you did not write can carry an attack, and an allowlist that only keeps what it recognises is far safer than a blocklist that tries to name every trick.
Reliability
Same markup and same policy always give the same result, and nothing leaves the platform. Four behaviours matter before you rely on this. Under every policy, your own included, an anchor may only use ftp, http, https, or mailto, and an image only http or https, so a javascript: or data: link loses its href while the element itself stays; because we clean without a base address, a relative href like "/pricing" and a fragment href like "#top" are dropped the same way, so resolve links before cleaning if you need them. A custom policy can only narrow what survives, never widen it: asking for an active element such as script or iframe, for an on-event handler, or for style, srcdoc or formaction is refused as invalid input, so the answer is never markup you believed was screened but was not. The counts under removed are before-and-after differences rather than an audit trail, and unsafeUrls is a plain text scan, so it also counts the word "data:" in ordinary prose. Bad input is rejected outright and retrying will not help — sending neither html nor file_ref, an unrecognised policy, policy "custom" with no customPolicy object or one that asks for a refused element or attribute, a file reference we cannot read for you, or content beyond 2097152 bytes.
Context footprint
The reply is roughly the size of your markup, or smaller once the unwanted parts are gone, plus three counts. A successful call costs 1 execution credit.
When to use it
Before you display or store HTML that came from a person, a scraper, or a model.
When scripts, forms, or javascript: links have to survive, or when you only want the markup smaller rather than safer.
Runs on jsoup's allowlist sanitizer, the long-standing Java answer to this job.
Scope
Non-goals
- Does not render the markup or run anything in it
- Does not fetch images, stylesheets, or any other linked resource
- Does not keep scripts, styles, forms, or inline event handlers
- Does not resolve relative links, which lose their href instead
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
File limits
Constraints this skill applies to your input and generated files. The “Limited by” column shows the static source of each cap; none of these can be raised by upgrading your plan.
| Limit | Value | Limited by |
|---|---|---|
| Maximum size per input file | 2 MiB | Skill maximum |
How we handle your data
What this means
This skill may receive sensitive content and may return submitted, transformed, extracted, or reformatted content.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response may contain submitted or transformed content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| html | string | No | Bounded inline source. Required unless file_ref is provided. |
| file_ref | string | No | Tenant-scoped opaque file reference. Omit it when inline source is supplied. |
| policy | string | No | Sanitization policy; omit it for basic_formatting. |
| customPolicy | object | No | Purpose-bound custom sanitizer policy object; omit it unless policy is custom. It contains from 0 through 256 purpose-bound properties — unconstrained RFC 8259 JSON items. |
| rewriteLinks | boolean | No | Whether unsafe link targets are rewritten; omit it for true. |
| addNoopener | boolean | No | Whether external links receive noopener; omit it for true. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| ok | boolean | Yes | Whether sanitization completed successfully. |
| sanitizedHtml | string | Yes | Sanitized HTML with active content removed. |
| removed | object | Yes | Closed removal-count receipt for elements, attributes, URLs, and comments. Its documented web receipt properties are bounded to 128 entries — unconstrained RFC 8259 JSON items. |
| warnings | array | No | Bounded array of non-fatal safe warning strings. The item shape is defined by the nested schema and the array contains from 0 through 20000 items. |
| metadata | object | Yes | Closed sanitizer metadata receipt with applied policy and transformation counts. Its documented web receipt properties are bounded to 128 entries — unconstrained RFC 8259 JSON items. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"web.sanitize_html","input":{"html":"<p onclick=\"steal()\">Welcome</p>"}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("web.sanitize_html", {"html":"<p onclick=\"steal()\">Welcome<script>alert(1)</script></p>"});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not render the markup or run anything in it
- Does not fetch images, stylesheets, or any other linked resource
- Does not keep scripts, styles, forms, or inline event handlers
- Does not resolve relative links, which lose their href instead
- Keeping scripts, forms, or anything else that has to stay active
- Shrinking markup for transfer, which web.minify_html does
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "web.sanitize_html",
"input": {
"html": "<p onclick=\"steal()\">Welcome<script>alert(1)</script></p>"
}
}Examples
Script and inline handler removed
{
"html": "<p onclick=\"steal()\">Welcome<script>alert(1)</script></p>"
}{
"ok": true,
"sanitizedHtml": "<p>Welcome</p>",
"metadata": {
"policy": "basic_formatting"
},
"removed": {
"elements": 1,
"attributes": 1,
"unsafeUrls": 0
}
}plain_text policy leaves only the words
{
"policy": "plain_text",
"html": "<p><b>Welcome</b> aboard</p>"
}{
"ok": true,
"sanitizedHtml": "Welcome aboard",
"metadata": {
"policy": "plain_text"
},
"removed": {
"elements": 2,
"attributes": 0,
"unsafeUrls": 0
}
}Recommended use cases
- Clean HTML from a person, a scraper, or a model before you show it
- Strip scripts and onclick handlers out of rich-text submissions
- Reduce markup to its visible text with the plain_text policy
Avoid for
- Keeping scripts, forms, or anything else that has to stay active
- Shrinking markup for transfer, which web.minify_html does
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.