You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by li...@apache.org on 2026/09/22 08:04:12 UTC

(answer) 02/16: fix: normalize imported content

This is an automated email from the ASF dual-hosted git repository.

LinkinStars pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/answer.git

commit 1a8627e26e9b70713b140fedae7f8606a50d2203
Author: LinkinStars <li...@foxmail.com>
AuthorDate: Fri Aug 14 16:48:53 2026 +0800

    fix: normalize imported content
---
 internal/service/importer/importer_service.go      | 29 +++++++++------
 internal/service/importer/importer_service_test.go | 41 ++++++++++++++++++++++
 2 files changed, 59 insertions(+), 11 deletions(-)

diff --git a/internal/service/importer/importer_service.go b/internal/service/importer/importer_service.go
index 9d12bf07..1842479b 100644
--- a/internal/service/importer/importer_service.go
+++ b/internal/service/importer/importer_service.go
@@ -32,6 +32,7 @@ import (
 	"github.com/apache/answer/internal/service/permission"
 	"github.com/apache/answer/internal/service/rank"
 	usercommon "github.com/apache/answer/internal/service/user_common"
+	"github.com/apache/answer/pkg/converter"
 	"github.com/apache/answer/plugin"
 	"github.com/gin-gonic/gin"
 	"github.com/segmentfault/pacman/errors"
@@ -70,7 +71,7 @@ func (ip *ImporterService) NewImporterFunc() plugin.ImporterFunc {
 }
 
 func (ip *ImporterService) ImportQuestion(ctx context.Context, questionInfo plugin.QuestionImporterInfo) (err error) {
-	req := &schema.QuestionAdd{}
+	req := newImportedQuestionRequest(questionInfo)
 	errFields := make([]*validator.FormErrorField, 0)
 	// To limit rate, remove the following code from comment: Part 1/2
 	// reject, rejectKey := ipc.rateLimitMiddleware.DuplicateRequestRejection(ctx, req)
@@ -94,16 +95,6 @@ func (ip *ImporterService) ImportQuestion(ctx context.Context, questionInfo plug
 	// 	}
 	// }()
 	req.UserID = userInfo.ID
-	req.Title = questionInfo.Title
-	req.Content = questionInfo.Content
-	req.HTML = "<p>" + questionInfo.Content + "</p>"
-	req.Tags = make([]*schema.TagItem, len(questionInfo.Tags))
-	for i, tag := range questionInfo.Tags {
-		req.Tags[i] = &schema.TagItem{
-			SlugName:    tag,
-			DisplayName: tag,
-		}
-	}
 	canList, requireRanks, err := ip.rankService.CheckOperationPermissionsForRanks(ctx, req.UserID, []string{
 		permission.QuestionAdd,
 		permission.QuestionEdit,
@@ -169,3 +160,19 @@ func (ip *ImporterService) ImportQuestion(ctx context.Context, questionInfo plug
 	log.Info("Add Question Successfully")
 	return nil
 }
+
+func newImportedQuestionRequest(questionInfo plugin.QuestionImporterInfo) *schema.QuestionAdd {
+	req := &schema.QuestionAdd{
+		Title:   questionInfo.Title,
+		Content: questionInfo.Content,
+		HTML:    converter.Markdown2HTML(questionInfo.Content),
+		Tags:    make([]*schema.TagItem, len(questionInfo.Tags)),
+	}
+	for i, tag := range questionInfo.Tags {
+		req.Tags[i] = &schema.TagItem{
+			SlugName:    tag,
+			DisplayName: tag,
+		}
+	}
+	return req
+}
diff --git a/internal/service/importer/importer_service_test.go b/internal/service/importer/importer_service_test.go
new file mode 100644
index 00000000..ff32740b
--- /dev/null
+++ b/internal/service/importer/importer_service_test.go
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package importer
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/apache/answer/plugin"
+)
+
+func TestNewImportedQuestionRequestSanitizesContent(t *testing.T) {
+	request := newImportedQuestionRequest(plugin.QuestionImporterInfo{
+		Title:   "Imported question",
+		Content: `<img src=x onerror=alert(1)><script>alert(1)</script>`,
+		Tags:    []string{"security"},
+	})
+
+	for _, unsafeContent := range []string{"onerror", "<script"} {
+		if strings.Contains(strings.ToLower(request.HTML), unsafeContent) {
+			t.Fatalf("imported question HTML contains unsafe content %q: %s", unsafeContent, request.HTML)
+		}
+	}
+}