forked from mirrors/forgejo
[v14.0/forgejo] Add to html button in markdown type="button" (#10527)
**Backport:** https://codeberg.org/forgejo/forgejo/pulls/10520 This is for preventing that a markdown button is recognized as button for submission in a html form. Buttons can't be stripped from the markdown due to: https://codeberg.org/forgejo/forgejo/pulls/7670#issuecomment-4086608 There is no issue with buttons if they always have `type="button"`, so this should be fine. This is a "follow-up" to !7670. Fixes #7656 Co-authored-by: Beowulf <beowulf@beocode.eu> Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10527 Reviewed-by: Beowulf <beowulf@beocode.eu> Co-authored-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org> Co-committed-by: forgejo-backport-action <forgejo-backport-action@noreply.codeberg.org>
This commit is contained in:
parent
cd0afc4f90
commit
650252f851
5 changed files with 97 additions and 1 deletions
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package markdown
|
||||
|
|
@ -87,6 +88,8 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
|
|||
if scope, found := ctx.Metas["scope"]; found {
|
||||
v.Name = fmt.Appendf(v.Name, "-%s", scope)
|
||||
}
|
||||
case *ast.RawHTML:
|
||||
g.transformRawHTML(ctx, v, reader)
|
||||
}
|
||||
return ast.WalkContinue, nil
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package markdown_test
|
||||
|
|
@ -125,6 +126,32 @@ func TestRender_Images(t *testing.T) {
|
|||
`<p><a href="`+href+`" rel="nofollow"><img src="`+result+`" alt="`+title+`"/></a></p>`)
|
||||
}
|
||||
|
||||
func TestRender_Buttons(t *testing.T) {
|
||||
setting.AppURL = AppURL
|
||||
|
||||
test := func(input, expected string) {
|
||||
buffer, err := markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: git.DefaultContext,
|
||||
Links: markup.Links{
|
||||
Base: FullURL,
|
||||
},
|
||||
}, input)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(string(buffer)))
|
||||
}
|
||||
|
||||
test(
|
||||
"<button>Test</button>",
|
||||
`<p><button type="button">Test</button></p>`)
|
||||
|
||||
test(
|
||||
`<button class="toggle-escape-button btn interact-bg">Test</button>`,
|
||||
`<p><button type="button" class="toggle-escape-button btn interact-bg">Test</button></p>`)
|
||||
test(
|
||||
`<button type="submit" class="toggle-escape-button btn interact-bg">Test</button>`,
|
||||
`<p><button type="button" class="toggle-escape-button btn interact-bg">Test</button></p>`)
|
||||
}
|
||||
|
||||
func testAnswers(baseURLContent, baseURLImages string) []string {
|
||||
return []string{
|
||||
`<p>Wiki! Enjoy :)</p>
|
||||
|
|
|
|||
28
modules/markup/markdown/transform_html.go
Normal file
28
modules/markup/markdown/transform_html.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package markdown
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"forgejo.org/modules/markup"
|
||||
|
||||
"github.com/yuin/goldmark/ast"
|
||||
"github.com/yuin/goldmark/text"
|
||||
)
|
||||
|
||||
func (g *ASTTransformer) addTypeToButton(v *ast.RawHTML, segment string) {
|
||||
segment = strings.TrimPrefix(segment, "<button")
|
||||
newTag := ast.NewString([]byte(`<button type="button"` + segment))
|
||||
newTag.SetCode(true)
|
||||
v.Parent().ReplaceChild(v.Parent(), v, newTag)
|
||||
}
|
||||
|
||||
func (g *ASTTransformer) transformRawHTML(_ *markup.RenderContext, v *ast.RawHTML, reader text.Reader) {
|
||||
segment := string(v.Segments.Value(reader.Source()))
|
||||
|
||||
if strings.HasPrefix(segment, "<button") {
|
||||
g.addTypeToButton(v, segment)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2017 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2017 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package markup
|
||||
|
|
@ -78,6 +79,9 @@ func createDefaultPolicy() *bluemonday.Policy {
|
|||
policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
|
||||
policy.AllowAttrs("checked", "disabled", "data-source-position").OnElements("input")
|
||||
|
||||
// Buttons
|
||||
policy.AllowAttrs("type").Matching(regexp.MustCompile(`^button$`)).OnElements("button")
|
||||
|
||||
// Custom URL-Schemes
|
||||
if len(setting.Markdown.CustomURLSchemes) > 0 {
|
||||
policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
|
||||
|
|
|
|||
34
tests/e2e/issue-comment-file-preview.test.e2e.ts
Normal file
34
tests/e2e/issue-comment-file-preview.test.e2e.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// @watch start
|
||||
// modules/markup/**
|
||||
// web_src/js/features/repo-unicode-escape.js
|
||||
// @watch end
|
||||
|
||||
import {expect} from '@playwright/test';
|
||||
import {dynamic_id, test} from './utils_e2e.ts';
|
||||
|
||||
test.use({user: 'user2'});
|
||||
|
||||
test('Escape button in file preview', async ({page}) => {
|
||||
await page.goto('/user2/unicode-escaping/src/branch/main/a-file');
|
||||
|
||||
const url = await page.getByRole('link', {name: 'Permalink'}).getAttribute('href');
|
||||
|
||||
const response = await page.goto('/user2/repo1/issues/new');
|
||||
expect(response?.status()).toBe(200);
|
||||
|
||||
// Create a new issue
|
||||
await page.getByPlaceholder('Title').fill(dynamic_id());
|
||||
await page.getByPlaceholder('Leave a comment').fill(`http://localhost:3003${url}#L1`);
|
||||
await page.getByRole('button', {name: 'Create issue'}).click();
|
||||
|
||||
await expect(page).toHaveURL(/\/user2\/repo1\/issues\/\d+$/);
|
||||
|
||||
await expect(page.locator('table.file-preview.unicode-escaped')).toHaveCount(0);
|
||||
await expect(async () => {
|
||||
await page.locator('button.toggle-escape-button').click();
|
||||
await expect(page.locator('table.file-preview.unicode-escaped')).toHaveCount(1);
|
||||
}).toPass();
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue