Skip to content

Commit 6e22605

Browse files
zeripathlunny
andauthored
Ensure that plain files are rendered correctly even when containing ambiguous characters (#22017)
As recognised in #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 #21841 Signed-off-by: Andrew Thornton <[email protected]> Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent f3370ee commit 6e22605

File tree

4 files changed

+41
-23
lines changed

4 files changed

+41
-23
lines changed

modules/charset/escape.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package charset
99

1010
import (
11+
"bufio"
1112
"io"
1213
"strings"
1314

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

34-
// 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
35+
// 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
3536
func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
3637
outputStream := &HTMLStreamerWriter{Writer: writer}
3738
streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
@@ -43,6 +44,35 @@ func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.
4344
return streamer.escaped, err
4445
}
4546

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

routers/web/repo/view.go

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
gocontext "context"
1010
"encoding/base64"
1111
"fmt"
12-
gotemplate "html/template"
1312
"io"
1413
"net/http"
1514
"net/url"
@@ -350,15 +349,13 @@ func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelin
350349
if err != nil {
351350
log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.name, ctx.Repo.Repository, err)
352351
buf := &bytes.Buffer{}
353-
ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf, ctx.Locale)
354-
ctx.Data["FileContent"] = strings.ReplaceAll(
355-
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
356-
)
352+
ctx.Data["EscapeStatus"], _ = charset.EscapeControlStringReader(rd, buf, ctx.Locale)
353+
ctx.Data["FileContent"] = buf.String()
357354
}
358355
} else {
359-
ctx.Data["IsRenderedHTML"] = true
356+
ctx.Data["IsPlainText"] = true
360357
buf := &bytes.Buffer{}
361-
ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, &charset.BreakWriter{Writer: buf}, ctx.Locale, charset.RuneNBSP)
358+
ctx.Data["EscapeStatus"], err = charset.EscapeControlStringReader(rd, buf, ctx.Locale)
362359
if err != nil {
363360
log.Error("Read failed: %v", err)
364361
}
@@ -492,15 +489,6 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
492489
}
493490
// to prevent iframe load third-party url
494491
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'")
495-
} else if readmeExist && !shouldRenderSource {
496-
buf := &bytes.Buffer{}
497-
ctx.Data["IsRenderedHTML"] = true
498-
499-
ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf, ctx.Locale)
500-
501-
ctx.Data["FileContent"] = strings.ReplaceAll(
502-
gotemplate.HTMLEscapeString(buf.String()), "\n", `<br>`,
503-
)
504492
} else {
505493
buf, _ := io.ReadAll(rd)
506494

templates/repo/settings/lfs_file.tmpl

Lines changed: 3 additions & 3 deletions
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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@
6161
{{if not (or .IsMarkup .IsRenderedHTML)}}
6262
{{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
6363
{{end}}
64-
<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsRenderedHTML}} plain-text{{else if .IsTextSource}} code-view{{end}}">
64+
<div class="file-view{{if .IsMarkup}} markup {{.MarkupType}}{{else if .IsPlainText}} plain-text{{else if .IsTextSource}} code-view{{end}}">
6565
{{if .IsMarkup}}
6666
{{if .FileContent}}{{.FileContent | Safe}}{{end}}
67-
{{else if .IsRenderedHTML}}
68-
<pre>{{if .FileContent}}{{.FileContent | Str2html}}{{end}}</pre>
67+
{{else if .IsPlainText}}
68+
<pre>{{if .FileContent}}{{.FileContent | Safe}}{{end}}</pre>
6969
{{else if not .IsTextSource}}
7070
<div class="view-raw ui center">
7171
{{if .IsImageFile}}

0 commit comments

Comments
 (0)