Skip to content

fix: svg image not showing up #4152

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gno.land/cmd/gnoweb/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ func setupWeb(cfg *webCfg, _ []string, io commands.IO) (func() error, error) {

func SecureHeadersMiddleware(next http.Handler, strict bool) http.Handler {
// Build img-src CSP directive
imgSrc := "'self' data:image/svg+xml"
imgSrc := "'self' data:"

for _, host := range cspImgHost {
imgSrc += " " + host
Expand Down
21 changes: 21 additions & 0 deletions gno.land/pkg/gnoweb/markdown/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package markdown

import (
"github.com/gnolang/gno/gno.land/pkg/gnoweb/weburl"
"github.com/yuin/goldmark/parser"
)

var gUrlContextKey = parser.NewContextKey()

// NewGnoParserContext creates a new parser context with GnoURL
func NewGnoParserContext(url *weburl.GnoURL) parser.Context {
ctx := parser.NewContext()
ctx.Set(gUrlContextKey, *url)
return ctx
}

// getUrlFromContext retrieves the GnoURL from the parser context
func getUrlFromContext(ctx parser.Context) (url weburl.GnoURL, ok bool) {
url, ok = ctx.Get(gUrlContextKey).(weburl.GnoURL)
return
}
47 changes: 29 additions & 18 deletions gno.land/pkg/gnoweb/markdown/ext.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,37 +31,48 @@
package markdown

import (
"github.com/gnolang/gno/gno.land/pkg/gnoweb/weburl"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/parser"
)

var _ goldmark.Extender = (*gnoExtension)(nil)
var _ goldmark.Extender = (*GnoExtension)(nil)

// NewGnoParserContext creates a new parser context with GnoURL
func NewGnoParserContext(url *weburl.GnoURL) parser.Context {
ctx := parser.NewContext()
ctx.Set(gUrlContextKey, *url)
return ctx
type GnoExtension struct {
cfg *config
}

var gUrlContextKey = parser.NewContextKey()
// Option

// getUrlFromContext retrieves the GnoURL from the parser context
func getUrlFromContext(ctx parser.Context) (url weburl.GnoURL, ok bool) {
url, ok = ctx.Get(gUrlContextKey).(weburl.GnoURL)
return
type config struct {
imgValidatorFunc ImageValidatorFunc
}

type gnoExtension struct{}
type Option func(cfg *config)

var GnoExtension = &gnoExtension{}
func WithImageValidator(valFunc ImageValidatorFunc) Option {
return func(cfg *config) {
cfg.imgValidatorFunc = valFunc
}
}

func NewGnoExtension(opts ...Option) *GnoExtension {
var cfg config
for _, opt := range opts {
opt(&cfg)
}

return &GnoExtension{&cfg}
}

// Extend adds the Gno extension to the provided Goldmark markdown processor.
func (e *gnoExtension) Extend(m goldmark.Markdown) {
func (e *GnoExtension) Extend(m goldmark.Markdown) {
// Add column extension
Columns.Extend(m)
ExtColumns.Extend(m)

// Add link extension with context
Links.Extend(m)
ExtLinks.Extend(m)

// If set, setup images filter
if e.cfg.imgValidatorFunc != nil {
ExtImageValidator.Extend(m, e.cfg.imgValidatorFunc)
}
}
4 changes: 2 additions & 2 deletions gno.land/pkg/gnoweb/markdown/ext_columns.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,8 @@ func renderGnoColumns(w util.BufWriter, _ []byte, node ast.Node, entering bool)

type columns struct{}

// Columns instance for extending markdown with column functionality.
var Columns = &columns{}
// ExtColumns instance for extending markdown with column functionality.
var ExtColumns = &columns{}

// Extend adds column functionality to the markdown processor.
// XXX: Use 500 for priority for now; we will rework these numbers once another extension is implemented.
Expand Down
53 changes: 53 additions & 0 deletions gno.land/pkg/gnoweb/markdown/ext_imgvalidator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package markdown

import (
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/text"
"github.com/yuin/goldmark/util"
)

// ImageValidatorFunc validates image URLs. It should return `true` for any valid image URL.
type ImageValidatorFunc func(uri string) (ok bool)

// imgValidatorTransformer implements ASTTransformer
type imgValidatorTransformer struct {
valFunc ImageValidatorFunc
}

// Transform iterate on `ast.Image` nodes and validate images URLs.
func (t *imgValidatorTransformer) Transform(doc *ast.Document, reader text.Reader, pc parser.Context) {
if t.valFunc == nil {
return
}

Check warning on line 23 in gno.land/pkg/gnoweb/markdown/ext_imgvalidator.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoweb/markdown/ext_imgvalidator.go#L22-L23

Added lines #L22 - L23 were not covered by tests

ast.Walk(doc, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}

img, ok := node.(*ast.Image)
if !ok {
return ast.WalkContinue, nil
}

if !t.valFunc(string(img.Destination)) {
img.Destination = []byte{} // Erase destination
}

return ast.WalkContinue, nil
})
}

type imgValidatorExtension struct{}

// ExtImageValidator is a Goldmark extension that pre validation on image URLs.
var ExtImageValidator = &imgValidatorExtension{}

// Extend adds the ExtImageValidator to the provided Goldmark markdown processor
func (l *imgValidatorExtension) Extend(m goldmark.Markdown, valFunc ImageValidatorFunc) {
m.Parser().AddOptions(parser.WithASTTransformers(
util.Prioritized(&imgValidatorTransformer{valFunc}, 500),
))
}
4 changes: 2 additions & 2 deletions gno.land/pkg/gnoweb/markdown/ext_links.go
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,8 @@ func (r *linkRenderer) renderGnoLink(w util.BufWriter, source []byte, node ast.N
// for external, internal, and same-package links.
type linkExtension struct{}

// Links instance for extending markdown with link functionality
var Links = &linkExtension{}
// ExtLinks instance for extending markdown with link functionality
var ExtLinks = &linkExtension{}

// Extend adds the LinkExtension to the provided Goldmark markdown processor
func (l *linkExtension) Extend(m goldmark.Markdown) {
Expand Down
6 changes: 5 additions & 1 deletion gno.land/pkg/gnoweb/markdown/ext_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,13 @@ func testGoldmarkOutput(t *testing.T, nameIn string, input []byte) (string, []by
// Create parser context with the test URL
ctxOpts := parser.WithContext(NewGnoParserContext(gnourl))

ext := NewGnoExtension(WithImageValidator(func(uri string) bool {
return !strings.HasPrefix(uri, "https://") // disallow https
}))

// Create markdown processor with extensions and renderer options
m := goldmark.New()
GnoExtension.Extend(m)
ext.Extend(m)

// Parse markdown input with context
node := m.Parser().Parse(text.NewReader(input), ctxOpts)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- input.md --
## Normal image link
![img1](http://gno.land)

## Data iamge uri
![svg image](data:image/svg+xml;base64,AAA==)


## Empty image uri
![empty img]()

## Filter any image starting by `https://`, see `ext_text.go`
![invalid image](https://invalid.land)

-- output.html --
<h2>Normal image link</h2>
<p><img src="http://gno.land" alt="img1"></p>
<h2>Data iamge uri</h2>
<p><img src="data:image/svg+xml;base64,AAA==" alt="svg image"></p>
<h2>Empty image uri</h2>
<p><img src="" alt="empty img"></p>
<h2>Filter any image starting by <code>https://</code>, see <code>ext_text.go</code></h2>
<p><img src="" alt="invalid image"></p>
15 changes: 12 additions & 3 deletions gno.land/pkg/gnoweb/webclient_html.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,25 @@
chromahtml.ClassPrefix("chroma-"),
}

// Only allow svg data image
allowSvgDataImage := func(uri string) bool {
const svgdata = "image/svg+xml"
return !strings.HasPrefix(uri, "data:") || strings.HasPrefix(uri, "data:"+svgdata)
}

Check warning on line 52 in gno.land/pkg/gnoweb/webclient_html.go

View check run for this annotation

Codecov / codecov/patch

gno.land/pkg/gnoweb/webclient_html.go#L50-L52

Added lines #L50 - L52 were not covered by tests

goldmarkOptions := []goldmark.Option{
goldmark.WithParserOptions(parser.WithAutoHeadingID()),
goldmark.WithExtensions(
markdown.NewHighlighting(
markdown.WithFormatOptions(chromaOptions...),
),

extension.Strikethrough,
extension.Table,

md.GnoExtension,
md.NewGnoExtension(
md.WithImageValidator(allowSvgDataImage),
),
),
}

Expand Down Expand Up @@ -197,10 +206,10 @@
return nil, err
}

ctxOpts := parser.WithContext(md.NewGnoParserContext(u))
ctx := md.NewGnoParserContext(u)

// Use Goldmark for Markdown parsing
doc := s.Markdown.Parser().Parse(text.NewReader(rawres), ctxOpts)
doc := s.Markdown.Parser().Parse(text.NewReader(rawres), parser.WithContext(ctx))
if err := s.Markdown.Renderer().Render(w, rawres, doc); err != nil {
return nil, fmt.Errorf("unable to render realm %q: %w", data, err)
}
Expand Down
Loading