Skip to content

Commit 1d37c52

Browse files
jmcmenamyjba
authored andcommitted
internal/sanitizer: allow picture and source elements in readme
The existing implementation sanitizes picture and source elements, causing readmes with images for light or dark mode to display incorrectly Fixes golang/go#74276 Change-Id: I8b9a34a4247e957ebbb505a76c75c49e68287eee GitHub-Last-Rev: aa2bf4d GitHub-Pull-Request: #111 Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/688015 kokoro-CI: kokoro <[email protected]> Reviewed-by: Michael Knyszek <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Jonathan Amsterdam <[email protected]>
1 parent 01b046e commit 1d37c52

File tree

5 files changed

+61
-6
lines changed

5 files changed

+61
-6
lines changed

internal/frontend/markdown.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ func processReadme(ctx context.Context, readme *internal.Readme, info *source.In
7575
}, nil
7676
}
7777

78-
// rewriteImgSrc rewrites the HTML in the markdown document to replace img
79-
// src keys with a value that properly represents the source of the image
78+
// rewriteImgSrc rewrites the HTML in the markdown document to replace img and source
79+
// src and srcset keys with a value that properly represents the source of the image
8080
// from the repo.
8181
func rewriteImgSrc(doc *markdown.Document, info *source.Info, readme *internal.Readme) {
8282
walkBlocks(doc.Blocks, func(b markdown.Block) error {

internal/frontend/overview.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,10 @@ func translateHTML(htmlText []byte, info *source.Info, readme *internal.Readme)
129129
// It reports whether it made a change.
130130
func walkHTML(n *html.Node, info *source.Info, readme *internal.Readme) bool {
131131
changed := false
132-
if n.Type == html.ElementNode && n.DataAtom == atom.Img {
132+
if n.Type == html.ElementNode && (n.DataAtom == atom.Img || n.DataAtom == atom.Source) {
133133
var attrs []html.Attribute
134134
for _, a := range n.Attr {
135-
if a.Key == "src" {
135+
if a.Key == "src" || a.Key == "srcset" {
136136
if v := translateLink(a.Val, info, true, readme); v != "" {
137137
a.Val = v
138138
changed = true

internal/frontend/readme_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,42 @@ func TestReadme(t *testing.T) {
479479
wantHTML: `<p><strong><img src="https://github.com/gobuffalo/buffalo/raw/master/logo.svg" alt="alt"/></strong></p>`,
480480
wantOutline: nil,
481481
},
482+
{
483+
name: "picture and source elements with escaped image source",
484+
unit: unit,
485+
readme: &internal.Readme{
486+
Filepath: "README.md",
487+
Contents: `<picture>` + "\n" +
488+
`<source media="(prefers-color-scheme: dark)" srcset=".images/dark.svg">` + "\n" +
489+
`<source media="(prefers-color-scheme: light)" srcset=".images/light.svg">` + "\n" +
490+
`<img alt="GoVector Logo" src=".images/light.svg">` + "\n" +
491+
`</picture>`,
492+
},
493+
wantHTML: `<picture>` + "\n" +
494+
`<source media="(prefers-color-scheme: dark)" srcset="https://github.com/valid/module_name/raw/v1.0.0/.images/dark.svg"/>` + "\n" +
495+
`<source media="(prefers-color-scheme: light)" srcset="https://github.com/valid/module_name/raw/v1.0.0/.images/light.svg"/>` + "\n" +
496+
`<img alt="GoVector Logo" src="https://github.com/valid/module_name/raw/v1.0.0/.images/light.svg"/>` + "\n" +
497+
`</picture>`,
498+
wantOutline: nil,
499+
},
500+
{
501+
name: "picture and source elements with invalid media query",
502+
unit: unit,
503+
readme: &internal.Readme{
504+
Filepath: "README.md",
505+
Contents: `<picture>` + "\n" +
506+
`<source media="<script>malicious code</script>" srcset=".images/dark.svg">` + "\n" +
507+
`<source media="(prefers-color-scheme: light)" srcset=".images/light.svg">` + "\n" +
508+
`<img alt="GoVector Logo" src=".images/light.svg">` + "\n" +
509+
`</picture>`,
510+
},
511+
wantHTML: `<picture>` + "\n" +
512+
`<source srcset="https://github.com/valid/module_name/raw/v1.0.0/.images/dark.svg"/>` + "\n" +
513+
`<source media="(prefers-color-scheme: light)" srcset="https://github.com/valid/module_name/raw/v1.0.0/.images/light.svg"/>` + "\n" +
514+
`<img alt="GoVector Logo" src="https://github.com/valid/module_name/raw/v1.0.0/.images/light.svg"/>` + "\n" +
515+
`</picture>`,
516+
wantOutline: nil,
517+
},
482518
} {
483519
t.Run(test.name, func(t *testing.T) {
484520
test.unit.Readme = test.readme

internal/sanitizer/sanitizer.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ var allowElems = []string{
225225
"mark",
226226
"ol",
227227
"p",
228+
"picture",
228229
"pre",
229230
"q",
230231
"rp",
@@ -234,6 +235,7 @@ var allowElems = []string{
234235
"samp",
235236
"section",
236237
"small",
238+
"source",
237239
"span",
238240
"strike",
239241
"strong",
@@ -292,6 +294,8 @@ var allowAttrs = []allowAttr{
292294
{"p", "width", flexiblewidth}, // pkgsite allows all values
293295
{"q", "cite", validURL},
294296
{"time", "datetime", iso8601},
297+
{"source", "media", mediaQuery},
298+
{"source", "srcset", validURL},
295299
{"ol", "type", re(`(?i)^(circle|disc|square|a|A|i|I|1)$`)},
296300
{"ul", "type", re(`(?i)^(circle|disc|square|a|A|i|I|1)$`)},
297301
{"li", "type", re(`(?i)^(circle|disc|square|a|A|i|I|1)$`)},
@@ -352,8 +356,9 @@ var allowAttrs = []allowAttr{
352356
// roundtripAttrs is a map from attribute keys which should be checked
353357
// against roundtripURL to maps of tags which are allowed to have them.
354358
var roundtripAttrs = map[string]map[string]bool{
355-
"src": {"img": true},
356-
"href": {"a": true},
359+
"src": {"img": true},
360+
"srcset": {"source": true},
361+
"href": {"a": true},
357362
"cite": {
358363
"blockquote": true,
359364
"del": true,
@@ -385,6 +390,8 @@ var integer = re(`^[0-9]+$`)
385390
var iso8601 = re(`^[0-9]{4}(-[0-9]{2}(-[0-9]{2}([ T][0-9]{2}(:[0-9]{2}){1,2}(.[0-9]{1,6})` +
386391
`?Z?([\+-][0-9]{2}:[0-9]{2})?)?)?)?$`)
387392

393+
var mediaQuery = re(`^[\(\)\s\w\-:./]+$`)
394+
388395
func re(rx string) func(string) bool {
389396
return regexp.MustCompile(rx).MatchString
390397
}

internal/sanitizer/sanitizer_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ func TestSanitizeBytes(t *testing.T) {
146146
},
147147
{`<p><bad><bad2><bad3><bad4>hello<bad5><bad6><p> middle</p>goodbye`,
148148
`<p>hello</p><p> middle</p>goodbye`},
149+
{
150+
`<picture>
151+
<source media="(prefers-color-scheme: dark)" srcset="dark.svg">
152+
<source media="(prefers-color-scheme: light)" srcset="light.svg">
153+
<img src="light.svg" alt="Logo">
154+
</picture>`,
155+
`<picture>
156+
<source media="(prefers-color-scheme: dark)" srcset="dark.svg"/>
157+
<source media="(prefers-color-scheme: light)" srcset="light.svg"/>
158+
<img src="light.svg" alt="Logo"/>
159+
</picture>`,
160+
},
149161
}
150162

151163
for _, tc := range testCases {

0 commit comments

Comments
 (0)