Task architecture

Sanitize Local Model Output Before Rendering or Execution

Treat generated text and structured fields as untrusted input to deterministic application code.

Review status: Fact and editorial QA complete; owner review pending · 2026-10-06 · Fact IDs: FUNCTION-01, COVE-LIFECYCLE-01

Direct answer

Sanitize local-model output at the boundary where the application sends it to a particular destination. “Sanitized” is not a universal property of a string: a value safe to show in a plain-text label may still disclose a private name in a diagnostic log, break a CSV export, or be interpreted as markup by a renderer. Start by mapping each output sink—screen, system log, app-owned diagnostic file, analytics, clipboard, database, notification, share sheet, and tool handoff—and define the purpose and data fields allowed at each one. Keep the raw generation result in memory only as long as the task needs it; do not create hidden copies in error messages, generic event payloads, or debug snapshots. This workflow is distinct from structured-output validation. A parser and schema check answer whether a response has the expected shape and types. Sink handling answers how a particular field is encoded or whether it is allowed to reach that destination. Neither step proves a claim true or authorizes a tool call. The application still constructs fixed operations, checks permission and current state, and asks for user confirmation when its policy requires it. Do not repeat schema tutorials here, use a regular expression to repair malformed proposals, or describe prompt instructions as an output filter. Classify fields before choosing a transformation. User-provided passages, names, email addresses, phone numbers, access tokens, filesystem paths, document excerpts, model-generated summaries, and internal error details have different disclosure consequences. If a field is not needed at a sink, omit it rather than partially masking it. If a short value is needed for debugging, prefer a stable non-content identifier or a bounded category such as missing_field; a hash of a short or predictable string may still reveal it by guessing. Redaction should remove the whole sensitive value when partial disclosure could be harmful. Masking preserves structure and can help correlate formats, but it may leave enough information to identify someone. The Android log guidance warns that both app-written and externally generated log messages can expose sensitive information, including through unexpected exception text. Treat rendering as a separate context. Prefer plain text for untrusted model output. Inside a WebView page, assign untrusted text to the intended DOM node with textContent instead of concatenating it into HTML; this is a DOM operation, not an Android WebView setText API. If rich markup is genuinely required, use a maintained sanitizer with a small allowed grammar and apply encoding for the exact HTML, attribute, and URL contexts; removing angle brackets is not a substitute. Keep links inert until the user selects them. Parse the URI, restrict schemes and hosts according to the feature, and reject ambiguous or opaque forms before navigation. Test control characters, bidirectional text, long unbroken strings, fake links, markdown fences, markup-looking content, and percent-encoded URI components. Platform calls need an allowlist too. Never parse model text as a serialized Intent and launch it, and never allow it to pick an arbitrary component, action, flag set, content URI, or nested Intent. Construct a fixed application-owned operation and copy only the fields that operation expects. For a filename, resolve an app-owned ID inside a controlled directory; do not join raw output onto a storage root or assume removing separators prevents traversal. For a user-selected document, carry forward the URI and grant from the picker rather than reconstructing a path. URI validation must inspect parsed scheme and authority, not only search the raw string for a trusted host substring. Diagnostics need their own design. Store stage, request ID, build, model/runtime identity when known, and a small failure category. Do not serialize request or response objects into an exception, and do not rely on their toString() implementation remaining redacted after a refactor. Use synthetic canary values to test whether a prompt, transcript, address, secret-like string, or unique test marker appears in logs, crash reports, exports, or analytics. Check debug and release builds separately, including errors thrown by downstream parsers. If the user deliberately submits a support report, show exactly which fields are included and allow review before sharing; an opt-in report is not a reason to attach the full conversation by default. Make the policy observable. For each sink, record the selected transformation, expected omissions, and what evidence the test captures. A report should distinguish “field omitted,” “field redacted,” “plain text encoded for this renderer,” and “not observable.” Retain the source output only in a controlled synthetic fixture or a user-approved workflow. If a sanitizer encounters an unknown value or an unsupported structured block, fail closed for that sink and preserve the underlying task state for a deliberate retry or review. Do not claim that redaction guarantees privacy, prevents prompt injection, or makes the remaining text accurate. The result is a testable data-handling boundary around probabilistic output, not a guarantee about all content a model could generate.

A generated resource link and filename cross platform boundaries

A local assistant summarizes an imported note and suggests a web link plus a filename for an export. The result is rendered in a WebView; a link action opens an external app; an export action creates a file in app-owned storage.

Decision

Render output as text or tightly allowed markup, validate the parsed URI against the feature allowlist, and map a user-approved file label to a generated app-owned filename rather than a raw path.

Expected outcome

Model text cannot inject markup, choose an arbitrary component, open an unsupported URI scheme, or escape the intended export directory.

Evidence to collect

The claim becomes reviewable only when the following evidence is attached to the same artifact and test run:

  • Parser and renderer tests
  • Adversarial output fixtures
  • Rejected-output behavior

Task quality needs a versioned corpus and acceptance rules. Fluent sample output is not an accuracy result.

Implementation workflow

  1. Trace fields to interpretation sinks. List display, WebView, URI navigation, Android intents, file operations, clipboard, logs, and exports. Record whether each destination interprets text as markup, a URI, a component, or a path.
  2. Keep display content inert. Prefer a plain-text widget or the WebView text API. For required formatting, use a maintained allowlist sanitizer and context-specific output encoding; avoid script, event, and style contexts.
  3. Constrain URI and intent behavior. Parse destinations, allow only schemes and hosts the feature expects, create fixed application-owned intents, and copy only documented extras. Never launch model-produced serialized or nested intents.
  4. Map file labels to owned paths. Accept a constrained display name or app-owned ID, create the final filename yourself, and resolve it beneath the selected application directory. Preserve picker-granted document URIs instead of constructing raw paths.
  5. Keep trust and permissions separate. Perform schema and domain validation in its own stage. Apply app policy, Android permission checks, current-task identity, and user confirmation before a platform action occurs.
  6. Test parser and platform boundaries. Use synthetic markup, URL schemes, encoded separators, path traversal strings, control characters, and unexpected intent fields. Confirm the exact rendered output, navigation target, resulting path, and excluded diagnostic fields.

Keep each transition observable. A failure should identify the stage, artifact, runtime, and recovery action without logging private user content.

Acceptance criteria

  • Every renderer and platform sink has a context-specific rule and a fixed destination or operation allowlist.
  • Synthetic URI, markup, intent, and path probes cannot select an unapproved destination or escape an app-owned directory.
  • Encoding remains separate from schema checks, domain policy, Android permissions, and required user confirmation.

Decision table

DimensionChoice or evidenceHow to interpret it
Schema validationCheck shape, types, and domain rulesRejects invalid or unsupported data; does not decide which valid fields may be disclosed to each sink.
Contextual encodingUse a safe text API or the output encoder for this rendererKeeps a value as data in that context; it does not validate a URL or authorize a platform action.
Platform allowlistConstruct a known Intent, URI route, or app-owned pathLimits where an operation may go; escaped model text alone is not authorization.

Failure patterns to prevent

  • Rendering arbitrary markup
  • Executing unvalidated arguments

Also prevent silent fallback, unpinned artifacts, missing cancellation, and conclusions that combine unlike configurations. Store unsuccessful runs alongside successful ones.

Minimum reproducibility record

LayerRecord
DeviceManufacturer, model, chipset, RAM class, operating-system build
SoftwareApplication version and git commit
ModelFamily, variant, revision, format, file length, hash, quantization
RuntimeName, revision, requested backend, observed backend evidence
WorkloadFixture revision, input hash, prompt hash, output policy
OutcomeCompleted, failed, cancelled, fallback, and privacy-safe diagnostics

Release checklist

  • ☐ The primary query is answered without an unsupported number.
  • ☐ Every artifact and runtime is pinned.
  • ☐ The representative task and failure policy are explicit.
  • ☐ Lifecycle, cancellation, cleanup, and fallback are tested.
  • ☐ User-content and network boundaries are documented.
  • ☐ Result wording applies only to the recorded configuration.
  • ☐ The page links to raw method or evidence when results are added.

Sources and related evidence

Chinese deployment and troubleshooting content is organized in the .奇连 AI 端侧专题.

Frequently asked questions

Can I place generated HTML directly into a WebView?

Use textContent on the intended DOM node inside the WebView page when markup is unnecessary; Android WebView has no native setText method. If rich text is required, use a maintained allowlist sanitizer and encoding for the exact output context.

Is escaping enough before opening a generated URL?

No. Parse the URI, allow only schemes and hosts the feature expects, construct a known operation, and require the intended user action. Encoding does not authorize a destination.

Can the model choose a filename or Android component?

Do not pass raw model text into a file path, component name, or nested Intent. Resolve a constrained ID to an app-owned operation and check policy and user intent separately.