mirror of
https://github.com/Eggbertx/gochan.git
synced 2025-08-30 09:56:23 -07:00
Check for NSFW tags and show an error if they are not allowed on the board (or site)
This commit is contained in:
parent
a8207b77d7
commit
8fe4c25938
7 changed files with 81 additions and 23 deletions
|
@ -249,7 +249,7 @@ func editPost(checkedPosts []int, editBtn string, doEdit string, writer http.Res
|
|||
return
|
||||
}
|
||||
|
||||
formatted, err := posting.FormatMessage(formattedStr, board.Dir)
|
||||
formatted, err := posting.FormatMessage(formattedStr, board.Dir, warnEv, errEv)
|
||||
if err != nil {
|
||||
errEv.Err(err).Caller().Send()
|
||||
server.ServeError(writer, err.Error(), wantsJSON, nil)
|
||||
|
|
|
@ -428,6 +428,7 @@ type BoardConfig struct {
|
|||
EnableSpoileredThreads bool
|
||||
|
||||
// Worksafe determines whether the board is worksafe or not. If it is set to true, threads cannot be marked NSFW
|
||||
// (given a hashtag with the text NSFW, case insensitive).
|
||||
// Default: true
|
||||
Worksafe bool
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -38,3 +40,9 @@ func GoToGochanRoot(t *testing.T) (string, error) {
|
|||
}
|
||||
return dir, errors.New("test running from unexpected dir, should be in gochan root or the current testing dir")
|
||||
}
|
||||
|
||||
// GetTestLogs returns logs with info, warn, and error levels respectively for testing
|
||||
func GetTestLogs(t *testing.T) (*zerolog.Event, *zerolog.Event, *zerolog.Event) {
|
||||
logger := zerolog.New(zerolog.NewTestWriter(t))
|
||||
return logger.Info(), logger.Warn(), logger.Error()
|
||||
}
|
||||
|
|
|
@ -600,8 +600,10 @@ func rebuildBoardsCallback(_ http.ResponseWriter, _ *http.Request, _ *gcsql.Staf
|
|||
return "Boards built successfully", nil
|
||||
}
|
||||
|
||||
func reparseHTMLCallback(_ http.ResponseWriter, _ *http.Request, _ *gcsql.Staff, _ bool, _ *zerolog.Event, errEv *zerolog.Event) (output any, err error) {
|
||||
func reparseHTMLCallback(_ http.ResponseWriter, request *http.Request, _ *gcsql.Staff, _ bool, _ *zerolog.Event, errEv *zerolog.Event) (output any, err error) {
|
||||
var outputStr string
|
||||
_, warnEv, _ := gcutil.LogRequest(request)
|
||||
defer warnEv.Discard()
|
||||
tx, err := gcsql.BeginTx()
|
||||
if err != nil {
|
||||
errEv.Err(err).Msg("Unable to begin transaction")
|
||||
|
@ -635,7 +637,7 @@ func reparseHTMLCallback(_ http.ResponseWriter, _ *http.Request, _ *gcsql.Staff,
|
|||
errEv.Err(err).Caller().Msg("Unable to scan SQL row")
|
||||
return "", err
|
||||
}
|
||||
if formatted, err := posting.FormatMessage(messageRaw, boardDir); err != nil {
|
||||
if formatted, err := posting.FormatMessage(messageRaw, boardDir, warnEv, errEv); err != nil {
|
||||
errEv.Err(err).Caller().Msg("Unable to format message")
|
||||
return "", err
|
||||
} else {
|
||||
|
|
|
@ -12,15 +12,17 @@ import (
|
|||
"github.com/frustra/bbcode"
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil"
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
var (
|
||||
msgfmtr MessageFormatter
|
||||
urlRE = regexp.MustCompile(`https?://(\S+)`)
|
||||
unsetBBcodeTags = []string{"center", "color", "img", "quote", "size"}
|
||||
diceRollRE = regexp.MustCompile(`\[(\d*)d(\d+)(?:([+-])(\d+))?\]`)
|
||||
hashTagRE = regexp.MustCompile(`\[#(.+)\]`)
|
||||
msgfmtr MessageFormatter
|
||||
urlRE = regexp.MustCompile(`https?://(\S+)`)
|
||||
unsetBBcodeTags = []string{"center", "color", "img", "quote", "size"}
|
||||
diceRollRE = regexp.MustCompile(`\[(\d*)d(\d+)(?:([+-])(\d+))?\]`)
|
||||
hashTagRE = regexp.MustCompile(`\[#([^\]]+)\]`)
|
||||
brRE = regexp.MustCompile(`<br\s*/?>`)
|
||||
ErrWorksafeBoard = errors.New("this board does not allow NSFW content")
|
||||
)
|
||||
|
||||
// InitPosting prepares the formatter and the temp post pruner
|
||||
|
@ -89,14 +91,16 @@ func wrapLinksInURL(urlStr string) string {
|
|||
return "[url]" + urlStr + "[/url]"
|
||||
}
|
||||
|
||||
func FormatMessage(message string, boardDir string) (template.HTML, error) {
|
||||
func FormatMessage(message string, boardDir string, warnEv, errEv *zerolog.Event) (template.HTML, error) {
|
||||
if config.GetBoardConfig(boardDir).RenderURLsAsLinks {
|
||||
message = urlRE.ReplaceAllStringFunc(message, wrapLinksInURL)
|
||||
message = msgfmtr.linkFixer.Replace(message)
|
||||
}
|
||||
message = msgfmtr.Compile(message, boardDir)
|
||||
// prepare each line to be formatted
|
||||
postLines := strings.Split(message, "<br>")
|
||||
postLines := brRE.Split(message, -1)
|
||||
boardConfig := config.GetBoardConfig(boardDir)
|
||||
var err error
|
||||
for i, line := range postLines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
lineWords := strings.Split(trimmedLine, " ")
|
||||
|
@ -110,7 +114,7 @@ func FormatMessage(message string, boardDir string) (template.HTML, error) {
|
|||
var boardDir string
|
||||
var linkParent int
|
||||
if linkParent, boardDir, err = gcsql.GetTopPostAndBoardDirFromPostID(postID); err != nil {
|
||||
gcutil.LogError(err).Caller().Int("childPostID", postID).Msg("Unable to get top post and board")
|
||||
errEv.Caller().Int("childPostID", postID).Msg("Unable to get top post and board")
|
||||
return "", fmt.Errorf("unable to get top post and board for post #%d", postID)
|
||||
}
|
||||
|
||||
|
@ -131,9 +135,28 @@ func FormatMessage(message string, boardDir string) (template.HTML, error) {
|
|||
if isGreentext {
|
||||
line += "</span>"
|
||||
}
|
||||
|
||||
err = nil
|
||||
var classList string
|
||||
line = hashTagRE.ReplaceAllStringFunc(line, func(tag string) string {
|
||||
return fmt.Sprintf(`<span class="hashtag">%s</span>`, tag[1:len(tag)-1])
|
||||
if err != nil {
|
||||
return tag // don't bother processing if there's already an error
|
||||
}
|
||||
tagNoBrackets := tag[1 : len(tag)-1]
|
||||
classList = "hashtag"
|
||||
if strings.ToLower(tagNoBrackets) == "#nsfw" {
|
||||
if boardConfig.Worksafe {
|
||||
err = ErrWorksafeBoard
|
||||
return ""
|
||||
}
|
||||
classList += " nsfw"
|
||||
}
|
||||
return fmt.Sprintf(`<span class="%s">%s</span>`, classList, tagNoBrackets)
|
||||
})
|
||||
if err != nil {
|
||||
warnEv.Str("board", boardDir).Msg("NSFW tag found on worksafe board")
|
||||
return "", err
|
||||
}
|
||||
postLines[i] = line
|
||||
}
|
||||
return template.HTML(strings.Join(postLines, "<br />")), nil // skipcq: GSC-G203
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
"github.com/gochan-org/gochan/pkg/config"
|
||||
"github.com/gochan-org/gochan/pkg/gcsql"
|
||||
"github.com/gochan-org/gochan/pkg/gcutil/testutil"
|
||||
"github.com/stretchr/testify/assert"
|
||||
lua "github.com/yuin/gopher-lua"
|
||||
)
|
||||
|
@ -164,7 +165,8 @@ func TestLinks(t *testing.T) {
|
|||
func TestNoDoubleTags(t *testing.T) {
|
||||
config.SetVersion(versionStr)
|
||||
msgfmtr.Init()
|
||||
rendered, err := FormatMessage(doubleTagPreRender, "")
|
||||
_, warnEv, errEv := testutil.GetTestLogs(t)
|
||||
rendered, err := FormatMessage(doubleTagPreRender, "", warnEv, errEv)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, doubleTagExpected, rendered)
|
||||
}
|
||||
|
@ -185,7 +187,8 @@ func TestLuaBBCode(t *testing.T) {
|
|||
|
||||
func diceRollRunner(t *testing.T, tC *diceRollerTestCase) {
|
||||
var err error
|
||||
tC.post.Message, err = FormatMessage(tC.post.MessageRaw, "")
|
||||
_, warnEv, errEv := testutil.GetTestLogs(t)
|
||||
tC.post.Message, err = FormatMessage(tC.post.MessageRaw, "", warnEv, errEv)
|
||||
assert.NoError(t, err)
|
||||
err = ApplyDiceRoll(&tC.post)
|
||||
if tC.expectError {
|
||||
|
@ -215,6 +218,7 @@ func TestDiceRoll(t *testing.T) {
|
|||
func TestHashTags(t *testing.T) {
|
||||
config.SetVersion(versionStr)
|
||||
msgfmtr.Init()
|
||||
_, warnEv, errEv := testutil.GetTestLogs(t)
|
||||
msg := `[#tag]
|
||||
[#t a g]
|
||||
[ #tag]
|
||||
|
@ -223,8 +227,9 @@ func TestHashTags(t *testing.T) {
|
|||
>greentext [#tag]
|
||||
[#line
|
||||
test]
|
||||
[#single] [#line] [#tags]
|
||||
[#js<script>alert("lol")</script>injection]`
|
||||
msgHTML, err := FormatMessage(msg, "test")
|
||||
msgHTML, err := FormatMessage(msg, "test", warnEv, errEv)
|
||||
if !assert.NoError(t, err) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
@ -237,6 +242,25 @@ test]
|
|||
`<span class="greentext">>greentext <span class="hashtag">#tag</span></span><br />`+
|
||||
`[#line<br />`+
|
||||
`test]<br />`+
|
||||
`<span class="hashtag">#single</span> <span class="hashtag">#line</span> <span class="hashtag">#tags</span><br />`+
|
||||
`<span class="hashtag">#js<script>alert("lol")</script>injection</span>`,
|
||||
), msgHTML)
|
||||
}
|
||||
|
||||
func TestWorksafe(t *testing.T) {
|
||||
config.SetVersion(versionStr)
|
||||
msgfmtr.Init()
|
||||
_, warnEv, errEv := testutil.GetTestLogs(t)
|
||||
boardConfig := config.GetBoardConfig("test")
|
||||
boardConfig.Worksafe = true
|
||||
config.SetBoardConfig("test", boardConfig)
|
||||
_, err := FormatMessage("[#nsfw] [#tag2]", "test", warnEv, errEv)
|
||||
if !assert.ErrorIs(t, err, ErrWorksafeBoard) {
|
||||
t.FailNow()
|
||||
}
|
||||
boardConfig.Worksafe = false
|
||||
config.SetBoardConfig("test", boardConfig)
|
||||
msgHTML, err := FormatMessage("[#nsfw]", "test", warnEv, errEv)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, template.HTML(`<span class="hashtag nsfw">#nsfw</span>`), msgHTML)
|
||||
}
|
||||
|
|
|
@ -208,11 +208,9 @@ func getPostFromRequest(request *http.Request, infoEv, errEv *zerolog.Event) (po
|
|||
return
|
||||
}
|
||||
|
||||
func doFormatting(post *gcsql.Post, board *gcsql.Board, request *http.Request, errEv *zerolog.Event) (err error) {
|
||||
func doFormatting(post *gcsql.Post, board *gcsql.Board, request *http.Request, warnEv, errEv *zerolog.Event) (err error) {
|
||||
if len(post.MessageRaw) > board.MaxMessageLength {
|
||||
errEv.Caller().
|
||||
Int("messageLength", len(post.MessageRaw)).
|
||||
Int("maxMessageLength", board.MaxMessageLength).Send()
|
||||
warnEv.Int("messageLength", len(post.MessageRaw)).Int("maxMessageLength", board.MaxMessageLength).Send()
|
||||
return errors.New("message is too long")
|
||||
}
|
||||
|
||||
|
@ -232,8 +230,10 @@ func doFormatting(post *gcsql.Post, board *gcsql.Board, request *http.Request, e
|
|||
return err
|
||||
}
|
||||
|
||||
if post.Message, err = FormatMessage(post.MessageRaw, board.Dir); err != nil {
|
||||
errEv.Err(err).Caller().Msg("Unable to format message")
|
||||
post.Message, err = FormatMessage(post.MessageRaw, board.Dir, warnEv, errEv)
|
||||
if errors.Is(err, ErrWorksafeBoard) {
|
||||
return err
|
||||
} else if err != nil {
|
||||
return errors.New("unable to format message")
|
||||
}
|
||||
if err = ApplyDiceRoll(post); err != nil {
|
||||
|
@ -345,7 +345,7 @@ func MakePost(writer http.ResponseWriter, request *http.Request) {
|
|||
}
|
||||
|
||||
// do formatting and apply wordfilters
|
||||
if err = doFormatting(post, board, request, errEv); err != nil {
|
||||
if err = doFormatting(post, board, request, warnEv, errEv); err != nil {
|
||||
server.ServeError(writer, err.Error(), wantsJSON, nil)
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue