Przeglądaj źródła

Merge branch 'hotfix/jinbo_test_0329' of eta_server/eta_api into master

xyxie 11 miesięcy temu
rodzic
commit
d6047c92c2

+ 34 - 30
controllers/banner.go

@@ -4,6 +4,8 @@ import (
 	"eta/eta_api/models"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
+	"github.com/h2non/filetype"
+	"io/ioutil"
 	"os"
 	"path"
 	"time"
@@ -22,15 +24,42 @@ type BannerController struct {
 func (this *BannerController) Upload() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
 	f, h, err := this.GetFile("file")
 	if err != nil {
 		br.Msg = "获取资源信息失败"
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+	defer f.Close() //关闭上传文件
+
+	// 不依赖于文件扩展名检查文件格式
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	pass := filetype.IsImage(fileData)
+	if !pass {
+		br.Msg = "文件格式有误"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
+
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
@@ -43,22 +72,18 @@ func (this *BannerController) Upload() {
 	randStr := utils.GetRandStringNoSpecialChar(28)
 	fileName := randStr + ext
 	fpath := uploadDir + "/" + fileName
-	defer f.Close() //关闭上传文件
 	err = this.SaveToFile("file", fpath)
 	if err != nil {
 		br.Msg = "文件上传失败"
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
 		return
 	}
-	resourceUrl := ``
-
-	//resourceUrl, err = services.UploadImgToMinIoTest(fileName, fpath)
-	//if err != nil {
-	//	br.Msg = "文件上传失败"
-	//	br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//	return
-	//}
+	defer func() {
+		os.Remove(fpath)
+	}()
 
+	// 上传文件
+	resourceUrl := ``
 	ossClient := services.NewOssClient()
 	if ossClient == nil {
 		br.Msg = "上传失败"
@@ -72,27 +97,6 @@ func (this *BannerController) Upload() {
 		return
 	}
 
-	////上传到阿里云 和 minio
-	//if utils.ObjectStorageClient == "minio" {
-	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//} else {
-	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
-	//	if err != nil {
-	//		br.Msg = "文件上传失败"
-	//		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-	//		return
-	//	}
-	//}
-
-	defer func() {
-		os.Remove(fpath)
-	}()
-
 	item := new(models.Resource)
 	item.ResourceUrl = resourceUrl
 	item.ResourceType = 1

+ 4 - 0
controllers/base_auth.go

@@ -308,6 +308,10 @@ func (c *BaseAuthController) JSON(data interface{}, hasIndent bool, coding bool)
 	c.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
 	desEncrypt := utils.DesBase64Encrypt([]byte(utils.DesKey), utils.DesKeySalt)
 	c.Ctx.Output.Header("Dk", string(desEncrypt)) // des3加解密key
+	// 设置Cookie为HTTPOnly
+	c.Ctx.SetCookie("", "", -1, "/", "", false, true, "")
+	//fmt.Println(c.Ctx.ResponseWriter.Header().Get("Set-Cookie"))
+
 	var content []byte
 	var err error
 	if hasIndent {

+ 3 - 0
controllers/base_common.go

@@ -87,6 +87,9 @@ func (c *BaseCommonController) JSON(data interface{}, hasIndent bool, coding boo
 	c.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
 	desEncrypt := utils.DesBase64Encrypt([]byte(utils.DesKey), utils.DesKeySalt)
 	c.Ctx.Output.Header("Dk", string(desEncrypt)) // des3加解密key
+	// 设置Cookie为HTTPOnly
+	c.Ctx.SetCookie("", "", -1, "/", "", false, true, "")
+
 	var content []byte
 	var err error
 	if hasIndent {

+ 28 - 2
controllers/cloud_disk.go

@@ -8,7 +8,9 @@ import (
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/h2non/filetype"
 	"github.com/rdlucklib/rdluck_tools/http"
+	"io/ioutil"
 	"os"
 	"path"
 	"strconv"
@@ -517,9 +519,20 @@ func (this *CloudDiskController) CheckResourceRepeat() {
 func (this *CloudDiskController) ResourceUpload() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
 	menuId, _ := this.GetInt("MenuId", 0)
 	if menuId <= 0 {
 		br.Msg = "不允许上传文件至根目录"
@@ -543,10 +556,22 @@ func (this *CloudDiskController) ResourceUpload() {
 	defer func() {
 		_ = f.Close()
 	}()
-
 	extIndex := strings.LastIndex(originName, ".")
 	fileName := originName[:extIndex]
-	ext := path.Ext(h.Filename)
+
+	// 不依赖于文件扩展名检查文件格式
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	// 允许上传图片/音视频/文档/压缩包
+	if !filetype.IsImage(fileData) && !filetype.IsVideo(fileData) && !filetype.IsAudio(fileData) && !filetype.IsDocument(fileData) && !filetype.IsArchive(fileData) {
+		br.Msg = "不允许上传该格式文件"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
 
 	// 重名校验
 	existItem := new(models.CloudDiskResource)
@@ -569,6 +594,7 @@ func (this *CloudDiskController) ResourceUpload() {
 		br.ErrMsg = "存储目录创建失败, Err:" + e.Error()
 		return
 	}
+	ext := path.Ext(h.Filename)
 	ossFileName := utils.GetRandStringNoSpecialChar(28) + ext
 	filePath := uploadDir + "/" + ossFileName
 	if e = this.SaveToFile("file", filePath); e != nil {

+ 19 - 1
controllers/english_report/report.go

@@ -65,6 +65,12 @@ func (this *EnglishReportController) Add() {
 
 	var contentSub string
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
 		content, e := services.FilterReportContentBr(req.Content)
 		if e != nil {
 			br.Msg = "内容去除前后空格失败"
@@ -182,6 +188,12 @@ func (this *EnglishReportController) Edit() {
 	}
 	var contentSub string
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
 		content, e := services.FilterReportContentBr(req.Content)
 		if e != nil {
 			br.Msg = "内容去除前后空格失败"
@@ -956,7 +968,7 @@ func (this *EnglishReportController) Delete() {
 	go func() {
 		_ = services.ResetPPTReport(req.ReportIds, true)
 	}()
-	
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "删除成功"
@@ -1013,6 +1025,12 @@ func (this *EnglishReportController) SaveReportContent() {
 			content = this.GetString("Content")
 		}
 		if content != "" {
+			e := utils.ContentXssCheck(req.Content)
+			if e != nil {
+				br.Msg = "存在非法标签"
+				br.ErrMsg = "存在非法标签, Err: " + e.Error()
+				return
+			}
 			contentClean, e := services.FilterReportContentBr(req.Content)
 			if e != nil {
 				br.Msg = "内容去除前后空格失败"

+ 65 - 17
controllers/report.go

@@ -10,9 +10,11 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/server/web"
+	"github.com/h2non/filetype"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"github.com/tealeg/xlsx"
 	"html"
+	"io/ioutil"
 	"os"
 	"path"
 	"path/filepath"
@@ -523,6 +525,12 @@ func (this *ReportController) Add() {
 	}
 	var contentSub string
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
 		content, e := services.FilterReportContentBr(req.Content)
 		if e != nil {
 			br.Msg = "内容去除前后空格失败"
@@ -657,6 +665,12 @@ func (this *ReportController) Edit() {
 	}
 	var contentSub string
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
 		content, e := services.FilterReportContentBr(req.Content)
 		if e != nil {
 			br.Msg = "内容去除前后空格失败"
@@ -911,6 +925,20 @@ func (this *ReportController) Upload() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	pass := filetype.IsImage(fileData)
+	if !pass {
+		br.Msg = "文件格式有误"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
+
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
@@ -1191,6 +1219,12 @@ func (this *ReportController) SaveReportContent() {
 			content = this.GetString("Content")
 		}
 		if content != "" {
+			e := utils.ContentXssCheck(req.Content)
+			if e != nil {
+				br.Msg = "存在非法标签"
+				br.ErrMsg = "存在非法标签, Err: " + e.Error()
+				return
+			}
 			contentClean, e := services.FilterReportContentBr(req.Content)
 			if e != nil {
 				br.Msg = "内容去除前后空格失败"
@@ -1242,6 +1276,20 @@ func (this *ReportUploadCommonController) UploadImg() {
 	if err != nil {
 		return
 	}
+	defer f.Close() //关闭上传文件
+
+	// 不依赖于文件扩展名检查文件格式
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		err = fmt.Errorf("读取文件失败, Err: %s", e.Error())
+		return
+	}
+	pass := filetype.IsImage(fileData)
+	if !pass {
+		err = fmt.Errorf("文件格式有误")
+		return
+	}
+
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
@@ -1252,24 +1300,16 @@ func (this *ReportUploadCommonController) UploadImg() {
 	randStr := utils.GetRandStringNoSpecialChar(28)
 	fileName := randStr + ext
 	fpath := uploadDir + "/" + fileName
-	defer f.Close() //关闭上传文件
 	err = this.SaveToFile("file", fpath)
 	if err != nil {
 		return
 	}
+	defer func() {
+		os.Remove(fpath)
+	}()
+
+	// 上传至对象存储
 	resourceUrl := ``
-	//上传到阿里云 和 minio
-	//if utils.ObjectStorageClient == "minio" {
-	//	resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
-	//	if err != nil {
-	//		return
-	//	}
-	//} else {
-	//	resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
-	//	if err != nil {
-	//		return
-	//	}
-	//}
 	ossClient := services.NewOssClient()
 	if ossClient == nil {
 		err = fmt.Errorf("初始化OSS服务失败")
@@ -1281,10 +1321,6 @@ func (this *ReportUploadCommonController) UploadImg() {
 		return
 	}
 
-	defer func() {
-		os.Remove(fpath)
-	}()
-
 	item := new(models.Resource)
 	item.ResourceUrl = resourceUrl
 	item.ResourceType = 1
@@ -2423,6 +2459,12 @@ func (this *ReportController) EditDayWeekChapter() {
 	// 更新章节及指标
 	contentSub := ""
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
 		contentClean, e := services.FilterReportContentBr(req.Content)
 		if e != nil {
 			br.Msg = "内容去除前后空格失败"
@@ -2894,6 +2936,12 @@ func (this *ReportController) PublishDayWeekReportChapter() {
 	// 更新章节信息
 	contentSub := ""
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			br.Msg = "存在非法标签"
+			br.ErrMsg = "存在非法标签, Err: " + e.Error()
+			return
+		}
 		contentClean, e := services.FilterReportContentBr(req.Content)
 		if e != nil {
 			br.Msg = "内容去除前后空格失败"

+ 176 - 0
controllers/resource.go

@@ -2,12 +2,16 @@ package controllers
 
 import (
 	"bufio"
+	"encoding/base64"
 	"eta/eta_api/models"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/h2non/filetype"
 	"github.com/kgiannakakis/mp3duration/src/mp3duration"
 	"io"
+	"io/ioutil"
+	"net/http"
 	"os"
 	"path"
 	"regexp"
@@ -21,6 +25,11 @@ type ResourceController struct {
 	BaseCommonController
 }
 
+// ResourceAuthController 文件资源
+type ResourceAuthController struct {
+	BaseAuthController
+}
+
 // @Title 图片上传
 // @Description 图片上传接口
 // @Param   file   query   file  true       "文件"
@@ -38,6 +47,20 @@ func (this *ResourceController) Upload() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	pass := filetype.IsImage(fileData)
+	if !pass {
+		br.Msg = "文件格式有误"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
+
 	uploadFileName := h.Filename //上传的文件名
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
@@ -135,6 +158,20 @@ func (this *ResourceController) VideoUpload() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	pass := filetype.IsVideo(fileData)
+	if !pass {
+		br.Msg = "文件格式有误"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
+
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
@@ -347,6 +384,19 @@ func (this *ResourceController) VoiceUpload() {
 	}
 	uploadFileName := h.Filename //上传的文件名
 
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	pass := filetype.IsAudio(fileData)
+	if !pass {
+		br.Msg = "文件格式有误"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
+
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
@@ -605,6 +655,20 @@ func (this *ResourceController) UploadImageBase64() {
 			br.ErrMsg = "获取资源信息失败,Err:" + e.Error()
 			return
 		}
+
+		fileData, e := ioutil.ReadAll(f)
+		if e != nil {
+			br.Msg = "上传失败"
+			br.ErrMsg = "读取文件失败, Err: " + e.Error()
+			return
+		}
+		pass := filetype.IsImage(fileData)
+		if !pass {
+			br.Msg = "文件格式有误"
+			br.ErrMsg = "文件格式有误"
+			return
+		}
+
 		ext = path.Ext(h.Filename)
 		dateDir := time.Now().Format("20060102")
 		uploadDir = utils.STATIC_DIR + "hongze/" + dateDir
@@ -785,6 +849,20 @@ func (this *ResourceController) UploadV2() {
 		br.ErrMsg = "获取资源信息失败,Err:" + err.Error()
 		return
 	}
+
+	fileData, e := ioutil.ReadAll(f)
+	if e != nil {
+		br.Msg = "上传失败"
+		br.ErrMsg = "读取文件失败, Err: " + e.Error()
+		return
+	}
+	pass := filetype.IsImage(fileData)
+	if !pass {
+		br.Msg = "文件格式有误"
+		br.ErrMsg = "文件格式有误"
+		return
+	}
+
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
@@ -914,3 +992,101 @@ func (this *ResourceController) OssSTSToken() {
 	//	br.Success = true
 	//}
 }
+
+// FileDownload
+// @Title 文件下载
+// @Description 文件下载
+// @Param   FileUrl  query  string  true  "文件路径"
+// @Success 200 Ret=200 操作成功
+// @router /file/download [get]
+func (this *ResourceAuthController) FileDownload() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	//fileName := this.GetString("FileName")
+	//fileName = strings.TrimSpace(fileName)
+	//if fileName == "" {
+	//	br.Msg = "参数有误"
+	//	return
+	//}
+	fileEncode := this.GetString("FileUrl")
+	fileEncode = strings.TrimSpace(fileEncode)
+	if fileEncode == "" {
+		br.Msg = "参数有误"
+		return
+	}
+	fileByte, e := base64.StdEncoding.DecodeString(fileEncode)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "文件地址解析失败, Err: " + e.Error()
+		return
+	}
+	fileUrl := string(fileByte)
+	fileArr := strings.Split(fileUrl, "/")
+	if len(fileArr) == 0 {
+		br.Msg = "文件地址有误"
+		return
+	}
+	fileName := fileArr[len(fileArr)-1]
+	//fmt.Println(fileName)
+
+	// 获取文件
+	down, e := http.Get(fileUrl)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "文件下载失败, http get: " + e.Error()
+		return
+	}
+	defer down.Body.Close()
+	if down.StatusCode != http.StatusOK {
+		br.Msg = "下载失败"
+		br.ErrMsg = fmt.Sprintf("文件下载失败, http status: %d", down.StatusCode)
+		return
+	}
+
+	// 生成本地文件
+	localFilePath := fmt.Sprintf("%s%s", utils.GetRandStringNoSpecialChar(6), fileName)
+	localFile, e := os.Create(localFilePath)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "生成本地文件失败, Err: " + e.Error()
+		return
+	}
+	defer func() {
+		if e = localFile.Close(); e != nil {
+			fmt.Println("local file close err: ", e.Error())
+		}
+		if e = os.Remove(localFilePath); e != nil {
+			fmt.Println("local file remove err: ", e.Error())
+		}
+	}()
+
+	// 写入响应流
+	//_, e = io.Copy(this.Ctx.ResponseWriter, down.Body)
+	_, e = io.Copy(localFile, down.Body)
+	if e != nil {
+		br.Msg = "下载失败"
+		br.ErrMsg = "复制文件资源失败, Err: " + e.Error()
+		return
+	}
+	// 设置响应头
+	//this.Ctx.ResponseWriter.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
+	//this.Ctx.ResponseWriter.Header().Set("Content-Type", "application/octet-stream")
+
+	br.Ret = 200
+	br.Msg = "下载成功"
+	br.Success = true
+	this.Ctx.Output.Download(localFilePath, fileName)
+}

+ 5 - 0
go.mod

@@ -25,8 +25,10 @@ require (
 	github.com/go-xorm/xorm v0.7.9
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
 	github.com/gorilla/websocket v1.5.1
+	github.com/h2non/filetype v1.1.3
 	github.com/jung-kurt/gofpdf v1.16.2
 	github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53
+	github.com/microcosm-cc/bluemonday v1.0.26
 	github.com/minio/minio-go/v7 v7.0.69
 	github.com/mojocn/base64Captcha v1.3.6
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
@@ -40,6 +42,7 @@ require (
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.880
 	github.com/xuri/excelize/v2 v2.8.1
 	github.com/yidane/formula v0.0.0-20220322063702-c9da84ba3476
+	golang.org/x/net v0.21.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 )
 
@@ -57,6 +60,7 @@ require (
 	github.com/aliyun/credentials-go v1.3.1 // indirect
 	github.com/andybalholm/cascadia v1.3.2 // indirect
 	github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02 // indirect
+	github.com/aymerick/douceur v0.2.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
@@ -74,6 +78,7 @@ require (
 	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
 	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
 	github.com/google/uuid v1.6.0 // indirect
+	github.com/gorilla/css v1.0.0 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect

+ 8 - 0
go.sum

@@ -91,6 +91,8 @@ github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoU
 github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
 github.com/aws/aws-sdk-go v1.51.2 h1:Ruwgz5aqIXin5Yfcgc+PCzoqW5tEGb9aDL/JWDsre7k=
 github.com/aws/aws-sdk-go v1.51.2/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
+github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 github.com/beego/bee/v2 v2.1.0 h1:4WngbAnkvVOyKy74WXcRH3clon76wkjhuzrV2mx2fQU=
 github.com/beego/bee/v2 v2.1.0/go.mod h1:wDhKy5TNxv46LHKsK2gyxo38ObCOm9PbCN89lWHK3EU=
 github.com/beego/beego/v2 v2.1.0 h1:Lk0FtQGvDQCx5V5yEu4XwDsIgt+QOlNjt5emUa3/ZmA=
@@ -238,9 +240,13 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
+github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
+github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
 github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
+github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
+github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -300,6 +306,8 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
+github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
 github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
 github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
 github.com/minio/minio-go/v7 v7.0.69 h1:l8AnsQFyY1xiwa/DaQskY4NXSLA2yrGsW5iD9nRPVS0=

+ 9 - 0
routers/commentsRouter.go

@@ -7927,6 +7927,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceAuthController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceAuthController"],
+        beego.ControllerComments{
+            Method: "FileDownload",
+            Router: `/file/download`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ResourceController"],
         beego.ControllerComments{
             Method: "Upload",

+ 1 - 0
routers/router.go

@@ -148,6 +148,7 @@ func init() {
 		web.NSNamespace("/resource",
 			web.NSInclude(
 				&controllers.ResourceController{},
+				&controllers.ResourceAuthController{},
 			),
 		),
 		web.NSNamespace("/datamanage",

+ 6 - 0
services/report.go

@@ -937,6 +937,12 @@ func PcCreateAndUploadSunCode(scene, page string) (imgUrl string, err error) {
 func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId int64, reportCode, errMsg string, err error) {
 	contentSub := ""
 	if req.Content != "" {
+		e := utils.ContentXssCheck(req.Content)
+		if e != nil {
+			errMsg = "存在非法标签"
+			err = errors.New("存在非法标签, Err: " + e.Error())
+			return
+		}
 		contentClean, e := FilterReportContentBr(req.Content)
 		if e != nil {
 			errMsg = "内容去除前后空格失败"

+ 101 - 0
utils/common.go

@@ -13,7 +13,9 @@ import (
 	"errors"
 	"fmt"
 	"github.com/PuerkitoBio/goquery"
+	"github.com/microcosm-cc/bluemonday"
 	"github.com/shopspring/decimal"
+	xhtml "golang.org/x/net/html"
 	"html"
 	"image"
 	"image/png"
@@ -22,6 +24,7 @@ import (
 	"math/rand"
 	"net"
 	"net/http"
+	"net/url"
 	"os"
 	"os/exec"
 	"path"
@@ -2298,6 +2301,104 @@ func GetColorMap() map[int]string {
 	return colorMap
 }
 
+// 检查src属性是否以http或data:image开头
+func isValidSrc(src string) bool {
+	// 使用Parse函数解析URL
+	parsedURL, err := url.Parse(src)
+	if err != nil {
+		validSchemes := regexp.MustCompile(`^data:image\/.*;base64,.*$`)
+		return validSchemes.MatchString(src)
+	}
+	if parsedURL.Host == "" || (parsedURL.Scheme != "http" && parsedURL.Scheme != "https") {
+		validSchemes := regexp.MustCompile(`^data:image\/.*;base64,.*$`)
+		return validSchemes.MatchString(src)
+	}
+	return true
+}
+
+// ContentXssCheck 校验文本中的JS代码
+func ContentXssCheck(content string) (err error) {
+	// 解析HTML内容
+	node, err := xhtml.Parse(strings.NewReader(content))
+	if err != nil {
+		err = fmt.Errorf(" html.Parse Err: %v", err)
+		return
+	}
+
+	// 遍历解析后的节点树,查找特定标签
+	var visit func(n *xhtml.Node) error
+	visit = func(n *xhtml.Node) error {
+		if n.Type == xhtml.ElementNode {
+			lowerData := strings.ToLower(n.Data)
+			switch lowerData {
+			case "script", "javascript":
+				err = fmt.Errorf(" script is forbidden")
+				return err
+			default:
+				for _, attr := range n.Attr { //判断事件
+					lowerKey := strings.ToLower(attr.Key)
+					lowerVal := strings.ToLower(attr.Val)
+					if lowerKey == "src" || lowerKey == "dynsrc" || lowerKey == "background" || lowerKey == "lowsrc" {
+						if !isValidSrc(lowerVal) {
+							err = fmt.Errorf("invalid src attribute value: %s", attr.Val)
+							return err
+						}
+					}
+					if strings.HasPrefix(lowerKey, "on") {
+						err = fmt.Errorf("the event is forbidden: %s:%s", attr.Key, attr.Val)
+						return err
+					}
+					if lowerKey == "style" {
+						if strings.Contains(lowerVal, "javascript:") || strings.Contains(lowerVal, "script:") {
+							err = fmt.Errorf("invalid style attribute value: %s", attr.Val)
+							return err
+						}
+					}
+				}
+				/*	case "src":
+					// 如果<src>是某个标签的属性,你可能需要递归检查其父节点
+					// 这里简单起见,我们假设<src>不是有效的HTML标签,并忽略它
+					// 在实际中,你可能需要更复杂的逻辑来处理这种情况
+					fmt.Println("Warning: Unexpected 'src' tag found.")*/
+			}
+		}
+		for c := n.FirstChild; c != nil; c = c.NextSibling {
+			if err = visit(c); err != nil {
+				return err
+			}
+		}
+		return nil
+	}
+	// 检查HTML文档中的事件
+	if err = visit(node); err != nil {
+		return
+	}
+	return
+}
+
+func ContentXssFilter(content string) (cleanContent string) {
+	p := customXssPolicy()
+	// The policy can then be used to sanitize lots of input and it is safe to use the policy in multiple goroutines
+	cleanContent = p.Sanitize(
+		content,
+	)
+	return
+}
+
+func customXssPolicy() (p *bluemonday.Policy) {
+	p = bluemonday.UGCPolicy()
+
+	// iframe
+	p.AllowElements("iframe")
+	p.AllowAttrs("width").OnElements("iframe")
+	p.AllowAttrs("height").OnElements("iframe")
+	p.AllowAttrs("src").OnElements("iframe")
+	p.AllowAttrs("frameborder").Matching(bluemonday.Number).OnElements("iframe")
+	p.AllowAttrs("allow").Matching(regexp.MustCompile(`[a-z; -]*`)).OnElements("iframe")
+	p.AllowAttrs("allowfullscreen").OnElements("iframe")
+	return
+}
+
 // SecondsToHHMMSS 秒转HH:MM:SS
 func SecondsToHHMMSS(seconds int) string {
 	duration := time.Duration(seconds) * time.Second