func Foreign
Foreign wraps body in a `<gno-foreign>` ... `</gno-foreign>` sandbox block. The returned string is ready to concatenate into a larger markdown document.
Three normalization steps apply to body:
-
\r\n and bare \r line endings are normalized to \n. The parser uses byte-equal matching against the sentinel close tag, so mixed line endings would otherwise change the match boundary.
-
Any line whose trimmed content looks like a gno-foreign tag opener OR closer — bare (`<gno-foreign>`, `</gno-foreign>`) or attribute-bearing (`<gno-foreign label="x">`, `</gno-foreign attr="…">`, etc.) — is neutralized by HTML-escaping the leading `<` to `<`. The parser tokenizes line bytes literally, so the escaped form is seen as text and cannot terminate the outer block or open an unintended inner block.
Crucially, BOTH open-tag and close-tag attribute-bearing forms are neutralized. The parser recognizes a bare `<gno-foreign>` opener, a labeled `<gno-foreign label="x">` opener, and ANY `</gno-foreign…>` closer (golang.org/x/net/html drops attrs on end tags before our recognizer sees them, so attr-bearing closers are sentinel-equivalent). Leaving any of those forms un-neutralized in body bytes would let attacker-supplied markdown adjust the parser's framing-depth counter and either consume the helper's own close (capturing trailing realm content into the sandbox) or close the outer block early (escaping the sandbox entirely).
There is therefore NO nesting via the helper: Foreign(Foreign(x)) escapes the inner call's own `<gno-foreign>`/`</gno-foreign>` lines, so the inner block renders as visible literal text inside one sandbox, not as a nested sandbox. This is intended — wrapping foreign-built markdown that itself contains gno-foreign sentinels must neutralize them, not honor them.
-
A leading and trailing blank line are emitted around the opener / closer. CommonMark §4.6 forbids Type-7 HTML blocks from interrupting a paragraph; without the blank line, an opener following a non-blank line is absorbed into the preceding paragraph instead of opening a sandbox.
The renderer caps cross-family nesting at 4 levels and per-Convert foreign blocks at 256. Beyond those caps, the opener falls through to raw HTML and is stripped by the renderer's safe mode.