PathrockNetwork Gno Explorer
HomeBlocksTransactionsTokensRealmsPackagesValidatorsAnalytics

PathrockNetwork Gno Explorer — an independent explorer for Gno.land Mainnet (gnoland-1), operated by PathrockNetwork. Not an official Gno.land service.

gnowebarchive RPC

gno.land/p/nt/markdown/foreign/v0

Package
Open in gnoweb ↗

Overview

Kind
Pure package
Name
v0
Namespace
nt / markdown / foreign
Files
4 (README)(gnomod.toml)
Exported functions
n/a — not supported for pure packages by the node (vm/qfuncs)
Module
gno.land/p/nt/markdown/foreign/v0
gno
0.9

Files (4)

  • README.mdmarkdown
  • gnomod.tomltoml
  • foreign.gnogno
  • foreign_test.gnogno
foreign_test.gnogno
1package foreign23import (4	"strings"5	"testing"6)78func TestForeign_BasicWrap(t *testing.T) {9	got := Foreign("hello")10	want := "\n\n<gno-foreign>\nhello\n</gno-foreign>\n\n"11	if got != want {12		t.Errorf("got %q, want %q", got, want)13	}14}1516func TestForeign_LeadingTrailingBlankLines(t *testing.T) {17	// Parser requires a blank line before the opener (CM §4.6). Verify18	// the helper emits exactly that.19	got := Foreign("body")20	if !strings.HasPrefix(got, "\n\n<gno-foreign>\n") {21		t.Errorf("missing blank line before opener: %q", got)22	}23	if !strings.HasSuffix(got, "\n</gno-foreign>\n\n") {24		t.Errorf("missing blank line after closer: %q", got)25	}26}2728func TestForeign_NormalizesCRLF(t *testing.T) {29	got := Foreign("line1\r\nline2\r\nline3")30	if strings.Contains(got, "\r") {31		t.Errorf("CRLF not normalized: %q", got)32	}33	if !strings.Contains(got, "line1\nline2\nline3") {34		t.Errorf("body content lost: %q", got)35	}36}3738func TestForeign_NormalizesBareCR(t *testing.T) {39	got := Foreign("line1\rline2")40	if strings.Contains(got, "\r") {41		t.Errorf("bare CR not normalized: %q", got)42	}43	if !strings.Contains(got, "line1\nline2") {44		t.Errorf("body content lost: %q", got)45	}46}4748func TestForeign_EscapesSentinelClose(t *testing.T) {49	// Unbalanced </gno-foreign> in body would terminate the outer50	// block early. Verify it's escaped to literal text — only the51	// leading "<" is replaced with "&lt;"; the trailing ">" stays52	// literal (rendering as visible ">" in HTML).53	got := Foreign("foo\n</gno-foreign>\nbar")54	if strings.Contains(got, "\n</gno-foreign>\nbar") {55		t.Errorf("unbalanced close not escaped: %q", got)56	}57	if !strings.Contains(got, "&lt;/gno-foreign>") {58		t.Errorf("expected escaped close: %q", got)59	}60}6162func TestForeign_EscapesSentinelOpen(t *testing.T) {63	// A bare opener inside body (without matching close in body)64	// would be picked up as a nested inner foreign. Escape it.65	got := Foreign("foo\n<gno-foreign>\nbar")66	if !strings.Contains(got, "\n&lt;gno-foreign>\n") {67		t.Errorf("expected escaped inner opener: %q", got)68	}69}7071func TestForeign_EscapesIndentedSentinel(t *testing.T) {72	// Parser strips 0-3 leading spaces before matching; helper73	// must mirror that to catch indented sentinels.74	got := Foreign("foo\n   </gno-foreign>\nbar")75	if !strings.Contains(got, "&lt;/gno-foreign>") {76		t.Errorf("indented sentinel not escaped: %q", got)77	}78}7980func TestForeign_EscapesAttributeBearingOpener(t *testing.T) {81	// `<gno-foreign label="x">` IS recognized by the parser as a82	// nested opener. If left unmangled, the parser bumps framingDepth83	// when it sees this line in body bytes, and then consumes the84	// helper's own outer-close as a fake "inner close" — capturing85	// realm content that follows the helper's output INTO the86	// sandbox. Verify the helper escapes the leading "<".87	got := Foreign(`<gno-foreign label="x">`)88	if strings.Contains(got, `<gno-foreign label="x">`) {89		t.Errorf("attribute-bearing opener was NOT mangled — escape vector:\n%q", got)90	}91	if !strings.Contains(got, `&lt;gno-foreign label="x">`) {92		t.Errorf("expected leading '<' escaped: %q", got)93	}94}9596func TestForeign_EscapesAttributeBearingCloser(t *testing.T) {97	// golang.org/x/net/html zeroes the Attr slice on end tags, so the98	// parser recognizes ANY `</gno-foreign attr…>` form as a sentinel99	// close. Leaving such a line unmangled in body bytes would close100	// the outer block early (framingDepth=0 case in the parser's101	// Continue), letting body content that follows render OUTSIDE102	// the sandbox at top level. Verify the helper escapes it.103	got := Foreign(`</gno-foreign label="x">`)104	if strings.Contains(got, `> </gno-foreign label="x"> </gno-foreign>`) {105		t.Errorf("attribute-bearing closer was NOT mangled — escape vector:\n%q", got)106	}107	if !strings.Contains(got, `&lt;/gno-foreign label="x">`) {108		t.Errorf("expected leading '<' escaped: %q", got)109	}110}111112func TestForeign_EscapesGnoForeignWithExtraContent(t *testing.T) {113	// `<gno-foreign> extra` would not be recognized as a sentinel by114	// the parser (tokenizer yields 2 tokens). But the helper is115	// over-inclusive on purpose: anything that looks like a116	// gno-foreign tag opener or closer gets mangled. Predictable117	// behavior beats narrow matching that diverges from the parser118	// on attribute-bearing forms.119	got := Foreign("<gno-foreign> extra text")120	if !strings.Contains(got, "&lt;gno-foreign> extra text") {121		t.Errorf("expected over-inclusive mangle of `<gno-foreign> extra`: %q", got)122	}123}124125func TestForeign_EscapesCaseVariantClose(t *testing.T) {126	// The renderer-side parser recognizes tags case-insensitively127	// (goldmark's tokenizer lowercases tag names). A case-variant close128	// left unescaped would terminate the outer block early and let129	// trailing body render OUTSIDE the sandbox. The helper must escape130	// `</GNO-FOREIGN>` just like `</gno-foreign>`.131	got := Foreign("foo\n</GNO-FOREIGN>\nbar")132	if strings.Contains(got, "\n</GNO-FOREIGN>\nbar") {133		t.Errorf("case-variant close not escaped — sandbox-escape vector: %q", got)134	}135	if !strings.Contains(got, "&lt;/GNO-FOREIGN>") {136		t.Errorf("expected escaped case-variant close: %q", got)137	}138}139140func TestForeign_EscapesCaseVariantOpen(t *testing.T) {141	// A case-variant opener left unescaped would be picked up by the142	// parser as a nested inner foreign (framingDepth++), causing the143	// helper's own outer-close to be consumed as a fake inner close and144	// capturing trailing realm content into the sandbox.145	got := Foreign("foo\n<GNO-FOREIGN>\nbar")146	if !strings.Contains(got, "\n&lt;GNO-FOREIGN>\n") {147		t.Errorf("case-variant opener not escaped — capture vector: %q", got)148	}149}150151func TestForeign_EscapesMixedCaseSentinel(t *testing.T) {152	// Mixed-case forms (the most likely hand-crafted evasion) must also153	// be caught, including the attribute-bearing mixed-case opener.154	for _, in := range []string{155		"<Gno-Foreign>",156		"</Gno-Foreign>",157		`<GNO-foreign label="x">`,158		"</gNo-fOrEiGn>",159	} {160		got := Foreign(in)161		if !strings.Contains(got, "&lt;") {162			t.Errorf("mixed-case sentinel %q not escaped: %q", in, got)163		}164	}165}166167func TestForeignWithLabel_StripsBidiAndZeroWidth(t *testing.T) {168	// A bidi-override (U+202E) or zero-width (U+200C) run in the label169	// is invisible-spoof payload; it must be stripped before the label170	// is spliced into the opener attribute.171	got := ForeignWithLabel("a\u202eb\u200cc", "body")172	if !strings.Contains(got, `label="abc"`) {173		t.Errorf("expected bidi/zero-width-stripped label: %q", got)174	}175}176177func TestForeignWithLabel_UnicodeSeparatorsBecomeSpaces(t *testing.T) {178	// U+2028 / U+2029 / U+0085 must not survive in the single-line179	// opener attribute; they fold to spaces like ASCII line breaks.180	got := ForeignWithLabel("a\u2028b\u2029c\u0085d", "body")181	if !strings.Contains(got, `label="a b c d"`) {182		t.Errorf("expected space-folded label: %q", got)183	}184}185186func TestForeign_EscapesUnrelatedForeignPrefix(t *testing.T) {187	// `<gno-foreignx>` is a different tag the parser would NOT treat as188	// a sentinel, but the escaper is deliberately over-inclusive (it189	// matches the `<gno-foreign` prefix alone), so it is escaped to190	// visible literal text. Harmless, and the safe side of the trade.191	got := Foreign("<gno-foreignx>")192	if !strings.Contains(got, "&lt;gno-foreignx>") {193		t.Errorf("expected over-inclusive escape of <gno-foreignx>: %q", got)194	}195}196197func TestForeign_EscapesSlashAndFormFeedClosers(t *testing.T) {198	// REGRESSION: goldmark's tokenizer ends a tag name at `/` and at199	// form-feed (both HTML tag-name terminators), so `</gno-foreign/>`200	// and `</gno-foreign\f>` are recognized as CLOSERS by the parser. A201	// body line carrying one of these, if it reached the parser202	// unescaped, would close the outer block early — a sandbox escape.203	// The over-inclusive escaper must neutralize every such variant.204	for _, in := range []string{205		"foo\n</gno-foreign/>\nbar",206		"foo\n</gno-foreign/ >\nbar",207		"foo\n</gno-foreign\f>\nbar",208		"foo\n<gno-foreign/>\nbar",209	} {210		got := Foreign(in)211		if !strings.Contains(got, "&lt;") {212			t.Errorf("variant not escaped (sandbox-escape vector): %q -> %q", in, got)213		}214	}215}216217func TestForeign_EmptyBody(t *testing.T) {218	got := Foreign("")219	want := "\n\n<gno-foreign>\n\n</gno-foreign>\n\n"220	if got != want {221		t.Errorf("got %q, want %q", got, want)222	}223}224225func TestForeign_PreservesInternalBlankLines(t *testing.T) {226	got := Foreign("para1\n\npara2")227	if !strings.Contains(got, "para1\n\npara2") {228		t.Errorf("internal blank line lost: %q", got)229	}230}231232func TestForeignWithLabel_BasicWrap(t *testing.T) {233	got := ForeignWithLabel("My Label", "body")234	want := "\n\n<gno-foreign label=\"My Label\">\nbody\n</gno-foreign>\n\n"235	if got != want {236		t.Errorf("got %q, want %q", got, want)237	}238}239240func TestForeignWithLabel_EmptyLabelFallsBack(t *testing.T) {241	// An empty label (or one that becomes empty after sanitization)242	// should be identical to Foreign — no attribute on the opener.243	got := ForeignWithLabel("", "body")244	if strings.Contains(got, "label=") {245		t.Errorf("empty label should not emit attribute: %q", got)246	}247	if got != Foreign("body") {248		t.Errorf("empty label should match Foreign: got %q vs %q", got, Foreign("body"))249	}250}251252func TestForeignWithLabel_WhitespaceOnlyLabelFallsBack(t *testing.T) {253	got := ForeignWithLabel("   ", "body")254	if strings.Contains(got, "label=") {255		t.Errorf("whitespace label should not emit attribute: %q", got)256	}257}258259func TestForeignWithLabel_EscapesAmpAndQuote(t *testing.T) {260	got := ForeignWithLabel(`Tom & "Jerry"`, "body")261	if !strings.Contains(got, `label="Tom &amp; &quot;Jerry&quot;"`) {262		t.Errorf("expected & and \" escaped: %q", got)263	}264}265266func TestForeignWithLabel_EscapesAngleBrackets(t *testing.T) {267	// An unescaped `>` would close the opener tag early. Must be268	// escaped.269	got := ForeignWithLabel(`a<b>c`, "body")270	if !strings.Contains(got, `label="a&lt;b&gt;c"`) {271		t.Errorf("expected < and > escaped: %q", got)272	}273}274275func TestForeignWithLabel_StripsNUL(t *testing.T) {276	got := ForeignWithLabel("a\x00b\x00c", "body")277	if strings.Contains(got, "\x00") {278		t.Errorf("NUL not stripped: %q", got)279	}280	if !strings.Contains(got, `label="abc"`) {281		t.Errorf("expected NUL-stripped label: %q", got)282	}283}284285func TestForeignWithLabel_ControlCharsBecomeSpaces(t *testing.T) {286	// Newlines and tabs would break the single-line opener required287	// by the parser. They must become spaces.288	got := ForeignWithLabel("line1\nline2\ttab", "body")289	if strings.Contains(got, "\nline2") || strings.Contains(got, "\t") {290		t.Errorf("control chars not converted: %q", got)291	}292	if !strings.Contains(got, "line1 line2 tab") {293		t.Errorf("expected space-separated label: %q", got)294	}295}296297func TestForeignWithLabel_TrimsLeadingTrailingSpace(t *testing.T) {298	got := ForeignWithLabel("  hello  ", "body")299	if !strings.Contains(got, `label="hello"`) {300		t.Errorf("expected trimmed label: %q", got)301	}302}303304func TestForeignWithLabel_PreservesBodyNormalization(t *testing.T) {305	// Body normalization (CRLF, sentinel escaping) must still apply306	// in the labeled variant.307	got := ForeignWithLabel("L", "a\r\n</gno-foreign>\r\nb")308	if strings.Contains(got, "\r") {309		t.Errorf("CRLF not normalized in labeled variant: %q", got)310	}311	if !strings.Contains(got, "&lt;/gno-foreign>") {312		t.Errorf("sentinel not escaped in labeled variant: %q", got)313	}314}315316func TestMaxBlocksPerRender(t *testing.T) {317	// Surfaces gnoweb's per-render foreign-block cap to realms via the318	// chain/markdown native. Pin the contract value; bump here if the319	// renderer cap changes.320	if got := MaxBlocksPerRender(); got != 256 {321		t.Errorf("MaxBlocksPerRender() = %d, want 256", got)322	}323}324

Functions

not supported for pure packages by the node (vm/qfuncs)

Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.