feat: publish the Sandwich Hime source preview
Signed-off-by: Cole Speelman <gamertan@noreply.localhost>
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func Badge(label string)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<strong class="badge"><?= label ?></strong>
|
||||
@@ -0,0 +1,37 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 2c0063ed8724520f146d6717bb7d828ce69be17b41cbfb8f4866664ba09a58a8
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func Badge(label string) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/badge.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/badge.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<strong class=\"badge\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/badge.sando:7:27
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (label)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/badge.sando:7:35
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</strong>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func BrowseResults(view BrowseView)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<section aria-label="Browse results" data-query="<?= view.Query ?>">
|
||||
<ul>
|
||||
<? for _, record := range view.Records { ?>
|
||||
<li>
|
||||
<a href="<?= record.URL ?>"><?= record.Title ?></a>
|
||||
<span class="kind"><?= record.Kind ?></span>
|
||||
<? if record.Featured { ?><?~ Badge("featured") ?><? } ?>
|
||||
</li>
|
||||
<? } ?>
|
||||
</ul>
|
||||
</section>
|
||||
@@ -0,0 +1,85 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 c5b402971618747c906c84508d5af9fd00aa33391b9b59f093b001a3cb0b2164
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func BrowseResults(view BrowseView) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/browse_results.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<section aria-label=\"Browse results\" data-query=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:7:54
|
||||
if __himesan_error := __himesan_sando.WriteAttr(__himesan_writer, (view.Query)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:7:67
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\">\n <ul>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:9:6
|
||||
for _, record := range view.Records {
|
||||
//line views/browse_results.sando:9:46
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n <li>\n <a href=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:20
|
||||
if __himesan_error := __himesan_sando.WriteURL(__himesan_writer, (record.URL)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:33
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:39
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (record.Title)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:11:54
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</a>\n <span class=\"kind\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:12:30
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (record.Kind)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:12:44
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</span>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:13:10
|
||||
if record.Featured {
|
||||
//line views/browse_results.sando:13:37
|
||||
if __himesan_error := __himesan_sando.Render(__himesan_render_context, __himesan_writer, (Badge("featured"))); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:13:60
|
||||
}
|
||||
//line views/browse_results.sando:13:64
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n </li>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/browse_results.sando:15:6
|
||||
}
|
||||
//line views/browse_results.sando:15:10
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n </ul>\n</section>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func Home(view HomeView)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<section aria-labelledby="home-heading">
|
||||
<h1 id="home-heading"><?= view.Heading ?></h1>
|
||||
<p><?= view.Intro ?></p>
|
||||
<?~ BrowseResults(view.Browse) ?>
|
||||
</section>
|
||||
@@ -0,0 +1,53 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 696c391538cdc50c5ab1abea23b561fa354e56eaf73b7ad6aabe130389715a8c
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func Home(view HomeView) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/home.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<section aria-labelledby=\"home-heading\">\n <h1 id=\"home-heading\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:8:29
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (view.Heading)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:8:44
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</h1>\n <p>"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:9:10
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (view.Intro)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:9:23
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</p>\n "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:10:7
|
||||
if __himesan_error := __himesan_sando.Render(__himesan_render_context, __himesan_writer, (BrowseResults(view.Browse))); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/home.sando:10:36
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n</section>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func Layout(view LayoutView)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title><?= view.Title ?> · <?= view.SiteName ?></title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main">Skip to content</a>
|
||||
<header><a href="/"><?= view.SiteName ?></a></header>
|
||||
<main id="main"><?~ view.Body ?></main>
|
||||
<footer>Built with Sandwich Hime.</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 c9a8102588cb94f946a36414291d12cb681e605acc93c2b5dc8d74fd33ae5603
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func Layout(view LayoutView) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/layout.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:14
|
||||
if __himesan_error := __himesan_sando.WriteRCDATA(__himesan_writer, (view.Title)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:27
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, " · "); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:35
|
||||
if __himesan_error := __himesan_sando.WriteRCDATA(__himesan_writer, (view.SiteName)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:12:51
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</title>\n</head>\n<body>\n <a href=\"#main\">Skip to content</a>\n <header><a href=\"/\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:16:27
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (view.SiteName)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:16:43
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</a></header>\n <main id=\"main\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:17:23
|
||||
if __himesan_error := __himesan_sando.Render(__himesan_render_context, __himesan_writer, (view.Body)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/layout.sando:17:35
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</main>\n <footer>Built with Sandwich Hime.</footer>\n</body>\n</html>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
|
||||
package views
|
||||
|
||||
import "gamertan.com/sandwich-hime/sando"
|
||||
|
||||
type LayoutView struct {
|
||||
SiteName string
|
||||
Title string
|
||||
Body sando.Component
|
||||
}
|
||||
|
||||
type HomeView struct {
|
||||
Heading string
|
||||
Intro string
|
||||
Browse BrowseView
|
||||
}
|
||||
|
||||
type BrowseView struct {
|
||||
Query string
|
||||
Records []RecordView
|
||||
}
|
||||
|
||||
type RecordView struct {
|
||||
URL string
|
||||
Title string
|
||||
Kind string
|
||||
Featured bool
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// SPDX-License-Identifier: 0BSD
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gamertan.com/sandwich-hime/sando"
|
||||
)
|
||||
|
||||
func TestBrowseResultsEscapesUntrustedValues(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
component := BrowseResults(BrowseView{
|
||||
Query: `"><script>alert(1)</script>`,
|
||||
Records: []RecordView{{
|
||||
URL: `/item?q=" onclick="alert(1)`,
|
||||
Title: `<img src=x onerror=alert(1)>`,
|
||||
Kind: `" aria-label="injected`,
|
||||
}},
|
||||
})
|
||||
|
||||
var output bytes.Buffer
|
||||
if err := sando.Render(context.Background(), &output, component); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got := output.String()
|
||||
if strings.Contains(got, "<script>") || strings.Contains(got, "<img") || strings.Contains(got, `" onclick=`) {
|
||||
t.Fatalf("untrusted markup survived escaping: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBrowseResultsRejectsDangerousURL(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
component := BrowseResults(BrowseView{Records: []RecordView{{
|
||||
URL: " JaVaScRiPt:\talert(1)",
|
||||
Title: "unsafe",
|
||||
}}})
|
||||
var output bytes.Buffer
|
||||
err := sando.Render(context.Background(), &output, component)
|
||||
if !errors.Is(err, sando.ErrUnsafeURL) {
|
||||
t.Fatalf("expected unsafe URL error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGeneratedComponentPropagatesWriterFailures(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
component := Badge("proof")
|
||||
if err := sando.Render(context.Background(), shortWriter{}, component); !errors.Is(err, io.ErrShortWrite) {
|
||||
t.Fatalf("short writer error = %v, want io.ErrShortWrite", err)
|
||||
}
|
||||
|
||||
want := errors.New("connection closed")
|
||||
if err := sando.Render(context.Background(), errorWriter{err: want}, component); !errors.Is(err, want) {
|
||||
t.Fatalf("writer error = %v, want %v", err, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestContextEscapingAgainstHTMLTemplateBaseline(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
baseline := template.Must(template.New("probe").Parse(`<a href="{{ .URL }}" title="{{ .Title }}">{{ .Text }}</a>`))
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
title string
|
||||
text string
|
||||
}{
|
||||
{name: "entities", url: "/items?q=one&kind=book", title: `say "hello" & goodbye`, text: `<strong>not markup</strong>`},
|
||||
{name: "unicode", url: "/items/cole", title: "Café & 雪", text: "Cole's archive ♥"},
|
||||
}
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
data := struct{ URL, Title, Text string }{test.url, test.title, test.text}
|
||||
var legacy bytes.Buffer
|
||||
if err := baseline.Execute(&legacy, data); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var generated bytes.Buffer
|
||||
if err := sando.Render(context.Background(), &generated, SecurityProbe(test.url, test.title, test.text)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := strings.TrimSpace(generated.String()), legacy.String(); got != want {
|
||||
t.Fatalf("generated escaping differs from html/template baseline\n got: %s\nwant: %s", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDangerousURLIsBlockedAtLeastAsStrictlyAsHTMLTemplate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
const dangerous = " JaVaScRiPt:\talert(1)"
|
||||
baseline := template.Must(template.New("probe").Parse(`<a href="{{ . }}">link</a>`))
|
||||
var legacy bytes.Buffer
|
||||
if err := baseline.Execute(&legacy, dangerous); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(legacy.String(), "#ZgotmplZ") {
|
||||
t.Fatalf("html/template baseline unexpectedly accepted dangerous URL: %s", legacy.String())
|
||||
}
|
||||
|
||||
var generated bytes.Buffer
|
||||
err := sando.Render(context.Background(), &generated, SecurityProbe(dangerous, "title", "link"))
|
||||
if !errors.Is(err, sando.ErrUnsafeURL) {
|
||||
t.Fatalf("generated renderer error = %v, want ErrUnsafeURL", err)
|
||||
}
|
||||
}
|
||||
|
||||
type shortWriter struct{}
|
||||
|
||||
func (shortWriter) Write(value []byte) (int, error) {
|
||||
if len(value) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return len(value) - 1, nil
|
||||
}
|
||||
|
||||
type errorWriter struct{ err error }
|
||||
|
||||
func (writer errorWriter) Write([]byte) (int, error) { return 0, writer.err }
|
||||
@@ -0,0 +1,7 @@
|
||||
<?sando go
|
||||
package views
|
||||
|
||||
func SecurityProbe(url string, title string, text string)
|
||||
?>
|
||||
<?# SPDX-License-Identifier: 0BSD ?>
|
||||
<a href="<?= url ?>" title="<?= title ?>"><?= text ?></a>
|
||||
@@ -0,0 +1,53 @@
|
||||
// Code generated by himesan; DO NOT EDIT.
|
||||
// himesan:compiler 0.1.0-dev
|
||||
// himesan:runtime-abi sando.v1
|
||||
// himesan:source-sha256 8d59b45799d0ed2e9e6997f286315f2cb794ea660940978ec39efa3b9a3b94ab
|
||||
|
||||
package views
|
||||
|
||||
import (
|
||||
__himesan_context "context"
|
||||
__himesan_sando "gamertan.com/sandwich-hime/sando"
|
||||
__himesan_io "io"
|
||||
)
|
||||
|
||||
var _ = __himesan_sando.ABI
|
||||
|
||||
func SecurityProbe(url string, title string, text string) __himesan_sando.Component {
|
||||
return __himesan_sando.ComponentFunc(func(__himesan_render_context __himesan_context.Context, __himesan_writer __himesan_io.Writer) error {
|
||||
_ = __himesan_render_context
|
||||
//line views/security_probe.sando:5:3
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:6:37
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\n<a href=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:14
|
||||
if __himesan_error := __himesan_sando.WriteURL(__himesan_writer, (url)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:20
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\" title=\""); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:33
|
||||
if __himesan_error := __himesan_sando.WriteAttr(__himesan_writer, (title)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:41
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "\">"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:47
|
||||
if __himesan_error := __himesan_sando.WriteText(__himesan_writer, (text)); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
//line views/security_probe.sando:7:54
|
||||
if __himesan_error := __himesan_sando.WriteString(__himesan_writer, "</a>\n"); __himesan_error != nil {
|
||||
return __himesan_error
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user