Skip to content

Commit 72524ad

Browse files
zeripathlunny
andauthored
Ensure that plain files are rendered correctly even when containing ambiguous characters (go-gitea#22017) (go-gitea#22160)
Backport go-gitea#22017 As recognised in go-gitea#21841 the rendering of plain text files is somewhat incorrect when there are ambiguous characters as the html code is double escaped. In fact there are several more problems here. We have a residual isRenderedHTML which is actually simply escaping the file - not rendering it. This is badly named and gives the wrong impression. There is also unusual behaviour whether the file is called a Readme or not and there is no way to get to the source code if the file is called README. In reality what should happen is different depending on whether the file is being rendered a README at the bottom of the directory view or not. 1. If it is rendered as a README on a directory - it should simply be escaped and rendered as `<pre>` text. 2. If it is rendered as a file then it should be rendered as source code. This PR therefore does: 1. Rename IsRenderedHTML to IsPlainText 2. Readme files rendered at the bottom of the directory are rendered without line numbers 3. Otherwise plain text files are rendered as source code. Replace go-gitea#21841 Signed-off-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
1 parent 2d4083f commit 72524ad

File tree

4 files changed

+41
-23
lines changed

4 files changed

+41
-23
lines changed

modules/charset/escape.go

+31-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
package charset
1010

1111
import (
12+
"bufio"
1213
"io"
1314
"strings"
1415

@@ -32,7 +33,7 @@ func EscapeControlHTML(text string, locale translation.Locale, allowed ...rune)
3233
return streamer.escaped, sb.String()
3334
}
3435

35-
// EscapeControlReaders escapes the unicode control sequences in a provider reader and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte
36+
// EscapeControlReaders escapes the unicode control sequences in a provided reader of HTML content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte
3637
func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
3738
outputStream := &HTMLStreamerWriter{Writer: writer}
3839
streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
@@ -44,6 +45,35 @@ func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.
4445
return streamer.escaped, err
4546
}
4647

48+
// EscapeControlStringReader escapes the unicode control sequences in a provided reader of string content and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte
49+
func EscapeControlStringReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
50+
bufRd := bufio.NewReader(reader)
51+
outputStream := &HTMLStreamerWriter{Writer: writer}
52+
streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
53+
54+
for {
55+
line, rdErr := bufRd.ReadString('\n')
56+
if len(line) > 0 {
57+
if err := streamer.Text(line); err != nil {
58+
streamer.escaped.HasError = true
59+
log.Error("Error whilst escaping: %v", err)
60+
return streamer.escaped, err
61+
}
62+
}
63+
if rdErr != nil {
64+
if rdErr != io.EOF {
65+
err = rdErr
66+
}
67+
break
68+
}
69+
if err := streamer.SelfClosingTag("br"); err != nil {
70+
streamer.escaped.HasError = true
71+
return streamer.escaped, err
72+
}
73+
}
74+
return streamer.escaped, err
75+
}
76+
4777
// EscapeControlString escapes the unicode control sequences in a provided string and returns the findings as an EscapeStatus and the escaped string
4878
func EscapeControlString(text string, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output string) {
4979
sb := &strings.Builder{}

routers/web/repo/view.go

+4-16
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
gocontext "context"
1111
"encoding/base64"
1212
"fmt"
13-
gotemplate "html/template"
1413
"io"
1514
"net/http"
1615
"net/url"
@@ -342,15 +341,13 @@ func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelin
342341
if err != nil {
343342
log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.name, ctx.Repo.Repository, err)
344343
buf := &bytes.Buffer{}
345-
ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf, ctx.Locale)
346-
ctx.Data["FileContent"] = strings.ReplaceAll(
347-
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
348-
)
344+
ctx.Data["EscapeStatus"], _ = charset.EscapeControlStringReader(rd, buf, ctx.Locale)
345+
ctx.Data["FileContent"] = buf.String()
349346
}
350347
} else {
351-
ctx.Data["IsRenderedHTML"] = true
348+
ctx.Data["IsPlainText"] = true
352349
buf := &bytes.Buffer{}
353-
ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, &charset.BreakWriter{Writer: buf}, ctx.Locale, charset.RuneNBSP)
350+
ctx.Data["EscapeStatus"], err = charset.EscapeControlStringReader(rd, buf, ctx.Locale)
354351
if err != nil {
355352
log.Error("Read failed: %v", err)
356353
}
@@ -522,15 +519,6 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
522519
}
523520
// to prevent iframe load third-party url
524521
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'")
525-
} else if readmeExist && !shouldRenderSource {
526-
buf := &bytes.Buffer{}
527-
ctx.Data["IsRenderedHTML"] = true
528-
529-
ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf, ctx.Locale)
530-
531-
ctx.Data["FileContent"] = strings.ReplaceAll(
532-
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
533-
)
534522
} else {
535523
buf, _ := io.ReadAll(rd)
536524

templates/repo/settings/lfs_file.tmpl

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
</h4>
1818
<div class="ui attached table unstackable segment">
1919
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
20-
<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsRenderedHTML}} plain-text{{else if .IsTextFile}} code-view{{end}}">
20+
<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextFile}} code-view{{end}}">
2121
{{if .IsMarkup}}
2222
{{if .FileContent}}{{.FileContent | Safe}}{{end}}
23-
{{else if .IsRenderedHTML}}
24-
<pre>{{if .FileContent}}{{.FileContent | Str2html}}{{end}}</pre>
23+
{{else if .IsPlainText}}
24+
<pre>{{if .FileContent}}{{.FileContent | Safe}}{{end}}</pre>
2525
{{else if not .IsTextFile}}
2626
<div class="view-raw ui center">
2727
{{if .IsImageFile}}

templates/repo/view_file.tmpl

+3-3
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@
8282
{{if not (or .IsMarkup .IsRenderedHTML)}}
8383
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
8484
{{end}}
85-
<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsRenderedHTML}} plain-text{{else if .IsTextSource}} code-view{{end}}">
85+
<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextSource}} code-view{{end}}">
8686
{{if .IsMarkup}}
8787
{{if .FileContent}}{{.FileContent | Safe}}{{end}}
88-
{{else if .IsRenderedHTML}}
89-
<pre>{{if .FileContent}}{{.FileContent | Str2html}}{{end}}</pre>
88+
{{else if .IsPlainText}}
89+
<pre>{{if .FileContent}}{{.FileContent | Safe}}{{end}}</pre>
9090
{{else if not .IsTextSource}}
9191
<div class="view-raw ui center">
9292
{{if .IsImageFile}}

0 commit comments

Comments
 (0)