Skip to content

Commit d1d44ec

Browse files
committed
fix
1 parent 7bcdcc0 commit d1d44ec

File tree

13 files changed

+118
-17
lines changed

13 files changed

+118
-17
lines changed

modules/markup/internal/finalprocessor.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ package internal
55

66
import (
77
"bytes"
8+
"html/template"
89
"io"
910
)
1011

1112
type finalProcessor struct {
1213
renderInternal *RenderInternal
14+
extraHeadHTML template.HTML
1315

1416
output io.Writer
1517
buf bytes.Buffer
@@ -25,6 +27,22 @@ func (p *finalProcessor) Close() error {
2527
// because "postProcess" already does so. In the future we could optimize the code to process data on the fly.
2628
buf := p.buf.Bytes()
2729
buf = bytes.ReplaceAll(buf, []byte(` data-attr-class="`+p.renderInternal.secureIDPrefix), []byte(` class="`))
28-
_, err := p.output.Write(buf)
30+
31+
// add our extra head HTML into output
32+
headBytes := []byte("<head>")
33+
posHead := bytes.Index(buf, headBytes)
34+
var part1, part2 []byte
35+
if posHead >= 0 {
36+
part1, part2 = buf[:posHead+len(headBytes)], buf[posHead+len(headBytes):]
37+
} else {
38+
part1, part2 = nil, buf
39+
}
40+
if _, err := p.output.Write(part1); err != nil {
41+
return err
42+
}
43+
if _, err := io.WriteString(p.output, string(p.extraHeadHTML)); err != nil {
44+
return err
45+
}
46+
_, err := p.output.Write(part2)
2947
return err
3048
}

modules/markup/internal/internal_test.go

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
"github.com/stretchr/testify/assert"
1313
)
1414

15-
func TestRenderInternal(t *testing.T) {
15+
func TestRenderInternalAttrs(t *testing.T) {
1616
cases := []struct {
1717
input, protected, recovered string
1818
}{
@@ -30,7 +30,7 @@ func TestRenderInternal(t *testing.T) {
3030
for _, c := range cases {
3131
var r RenderInternal
3232
out := &bytes.Buffer{}
33-
in := r.init("sec", out)
33+
in := r.init("sec", out, "")
3434
protected := r.ProtectSafeAttrs(template.HTML(c.input))
3535
assert.EqualValues(t, c.protected, protected)
3636
_, _ = io.WriteString(in, string(protected))
@@ -41,7 +41,7 @@ func TestRenderInternal(t *testing.T) {
4141
var r1, r2 RenderInternal
4242
protected := r1.ProtectSafeAttrs(`<div class="test"></div>`)
4343
assert.EqualValues(t, `<div class="test"></div>`, protected, "non-initialized RenderInternal should not protect any attributes")
44-
_ = r1.init("sec", nil)
44+
_ = r1.init("sec", nil, "")
4545
protected = r1.ProtectSafeAttrs(`<div class="test"></div>`)
4646
assert.EqualValues(t, `<div data-attr-class="sec:test"></div>`, protected)
4747
assert.Equal(t, "data-attr-class", r1.SafeAttr("class"))
@@ -54,8 +54,28 @@ func TestRenderInternal(t *testing.T) {
5454
assert.Empty(t, recovered)
5555

5656
out2 := &bytes.Buffer{}
57-
in2 := r2.init("sec-other", out2)
57+
in2 := r2.init("sec-other", out2, "")
5858
_, _ = io.WriteString(in2, string(protected))
5959
_ = in2.Close()
6060
assert.Equal(t, `<div data-attr-class="sec:test"></div>`, out2.String(), "different secureID should not recover the value")
6161
}
62+
63+
func TestRenderInternalExtraHead(t *testing.T) {
64+
t.Run("HeadExists", func(t *testing.T) {
65+
out := &bytes.Buffer{}
66+
var r RenderInternal
67+
in := r.init("sec", out, `<MY-TAG>`)
68+
_, _ = io.WriteString(in, `<head></head>`)
69+
_ = in.Close()
70+
assert.Equal(t, `<head><MY-TAG></head>`, out.String())
71+
})
72+
73+
t.Run("HeadNotExists", func(t *testing.T) {
74+
out := &bytes.Buffer{}
75+
var r RenderInternal
76+
in := r.init("sec", out, `<MY-TAG>`)
77+
_, _ = io.WriteString(in, `<div></div>`)
78+
_ = in.Close()
79+
assert.Equal(t, `<MY-TAG><div></div>`, out.String())
80+
})
81+
}

modules/markup/internal/renderinternal.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,19 @@ type RenderInternal struct {
2929
secureIDPrefix string
3030
}
3131

32-
func (r *RenderInternal) Init(output io.Writer) io.WriteCloser {
32+
func (r *RenderInternal) Init(output io.Writer, extraHeadHTML template.HTML) io.WriteCloser {
3333
buf := make([]byte, 12)
3434
_, err := rand.Read(buf)
3535
if err != nil {
3636
panic("unable to generate secure id")
3737
}
38-
return r.init(base64.URLEncoding.EncodeToString(buf), output)
38+
return r.init(base64.URLEncoding.EncodeToString(buf), output, extraHeadHTML)
3939
}
4040

41-
func (r *RenderInternal) init(secID string, output io.Writer) io.WriteCloser {
41+
func (r *RenderInternal) init(secID string, output io.Writer, extraHeadHTML template.HTML) io.WriteCloser {
4242
r.secureID = secID
4343
r.secureIDPrefix = r.secureID + ":"
44-
return &finalProcessor{renderInternal: r, output: output}
44+
return &finalProcessor{renderInternal: r, output: output, extraHeadHTML: extraHeadHTML}
4545
}
4646

4747
func (r *RenderInternal) RecoverProtectedValue(v string) (string, bool) {

modules/markup/render.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package markup
66
import (
77
"context"
88
"fmt"
9+
"html/template"
910
"io"
1011
"net/url"
1112
"strconv"
@@ -180,7 +181,7 @@ func renderIFrame(ctx *RenderContext, output io.Writer) error {
180181
iframe := htmlutil.HTMLFormat(`
181182
<iframe data-src="%s"
182183
class="external-render-iframe"
183-
sandbox="allow-scripts"
184+
sandbox="allow-scripts allow-popups"
184185
width="%s" height="%s"
185186
></iframe>
186187
`,
@@ -199,21 +200,26 @@ func pipes() (io.ReadCloser, io.WriteCloser, func()) {
199200
}
200201

201202
func RenderWithRenderer(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
203+
var extraHeadHTML template.HTML
202204
if externalRender, ok := renderer.(ExternalRenderer); ok && externalRender.DisplayInIFrame() {
203205
if !ctx.RenderOptions.InStandalonePage {
204206
// for an external "DisplayInIFrame" render, it could only output its content in a standalone page
205207
// otherwise, a <iframe> should be outputted to embed the external rendered page
206208
return renderIFrame(ctx, output)
207209
}
208210
// else: this is a standalone page, fallthrough to the real rendering
211+
extraStyleHref := fmt.Sprintf("%s/assets/css/external-render-iframe.css", setting.AppSubURL)
212+
extraScriptSrc := fmt.Sprintf("%s/assets/js/external-render-iframe.js", setting.AppSubURL)
213+
// "<script>" must go before "<link>", to make Golang's http.DetectContentType() can still recognize the content as "text/html"
214+
extraHeadHTML = htmlutil.HTMLFormat(`<script src="%s"></script><link rel="stylesheet" href="%s">`, extraScriptSrc, extraStyleHref)
209215
}
210216

211217
ctx.usedByRender = true
212218
if ctx.RenderHelper != nil {
213219
defer ctx.RenderHelper.CleanUp()
214220
}
215221

216-
finalProcessor := ctx.RenderInternal.Init(output)
222+
finalProcessor := ctx.RenderInternal.Init(output, extraHeadHTML)
217223
defer finalProcessor.Close()
218224

219225
// input -> (pw1=pr1) -> renderer -> (pw2=pr2) -> SanitizeReader -> finalProcessor -> output

routers/web/repo/render.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ func RenderFile(ctx *context.Context) {
5252
isTextFile := st.IsText()
5353

5454
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
55-
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
55+
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts allow-popups")
5656

5757
if markupType := markup.DetectMarkupTypeByFileName(blob.Name()); markupType == "" {
5858
if isTextFile {

tests/integration/markup_external_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ func TestExternalMarkupRenderer(t *testing.T) {
7575
assert.Equal(t, "text/html; charset=utf-8", resp.Header().Get("Content-Type"))
7676
bs, err := io.ReadAll(resp.Body)
7777
assert.NoError(t, err)
78-
assert.Equal(t, "frame-src 'self'; sandbox allow-scripts", resp.Header().Get("Content-Security-Policy"))
78+
assert.Equal(t, "frame-src 'self'; sandbox allow-scripts allow-popups", resp.Header().Get("Content-Security-Policy"))
7979
assert.Equal(t, "<div>\n\ttest external renderer\n</div>\n", string(bs))
8080
})
8181
}

web_src/css/index.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
@import "./features/console.css";
4545

4646
@import "./markup/content.css";
47-
@import "./markup/render.css";
4847
@import "./markup/codecopy.css";
4948
@import "./markup/codepreview.css";
5049
@import "./markup/asciicast.css";

web_src/css/markup/render.css

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/* dummy */

web_src/js/markup/content.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {initMarkupCodeCopy} from './codecopy.ts';
44
import {initMarkupRenderAsciicast} from './asciicast.ts';
55
import {initMarkupTasklist} from './tasklist.ts';
66
import {registerGlobalSelectorFunc} from '../modules/observer.ts';
7+
import {initMarkupRenderIframe} from './render-iframe.ts';
78

89
// code that runs for all markup content
910
export function initMarkupContent(): void {
@@ -13,5 +14,6 @@ export function initMarkupContent(): void {
1314
initMarkupCodeMermaid(el);
1415
initMarkupCodeMath(el);
1516
initMarkupRenderAsciicast(el);
17+
initMarkupRenderIframe(el);
1618
});
1719
}

0 commit comments

Comments
 (0)