CVE-2023-7171: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: A vulnerability was found in Novel-Plus up to 4.2.0. It has been declared as problematic. Affected by this vulnerability is an unknown functionality of the file novel-admin/src/main/java/com/java2nb/novel/controller/FriendLinkController.java of the component Friendly Link Handler. The manipulation leads to cross site scripting. The attack can be launched remotely. The exploit has been disclosed to the public and may be used. The patch is named d6093d8182362422370d7eaf6c53afde9ee45215. It is recommended to apply a patch to fix this issue. The associated identifier of this vulnerability is VDB-249307.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 4.8
Published: 2023-12-29T18:15Z
Repository: https://github.com/201206030/novel-plus
Language: Java
Files changed: novel-admin/src/main/java/com/java2nb/novel/controller/FriendLinkController.java, novel-admin/src/main/java/com/java2nb/novel/domain/FriendLinkDO.java, novel-admin/src/main/resources/templates/novel/friendLink/add.html, novel-admin/src/main/resources/templates/novel/friendLink/edit.html
Commit date: 2023-12-29 18:54:53 +0800
Commit message: fix(novel-admin): 友情链接URL格式校验

Diff: diff --git a/novel-admin/src/main/java/com/java2nb/novel/controller/FriendLinkController.java b/novel-admin/src/main/java/com/java2nb/novel/controller/FriendLinkController.java
index a26a40a..dd86893 100644
--- a/novel-admin/src/main/java/com/java2nb/novel/controller/FriendLinkController.java
+++ b/novel-admin/src/main/java/com/java2nb/novel/controller/FriendLinkController.java
@@ -10,10 +10,11 @@ import io.swagger.annotations.ApiOperation;
 import org.apache.shiro.authz.annotation.RequiresPermissions;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.Model;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
 import java.util.Map;
 
@@ -81,11 +82,11 @@ public class FriendLinkController {
      */
     @ApiOperation(value = "新增", notes = "新增")
     @ResponseBody
     @PostMapping("/save")
     @RequiresPermissions("novel:friendLink:add")
-    public R save(FriendLinkDO friendLink) {
+    public R save(@Validated FriendLinkDO friendLink) {
         if (friendLinkService.save(friendLink) > 0) {
             redisTemplate.delete(CacheKey.INDEX_LINK_KEY);
             return R.ok();
         }
         return R.error();
@@ -96,11 +97,11 @@ public class FriendLinkController {
      */
     @ApiOperation(value = "修改", notes = "修改")
     @ResponseBody
     @RequestMapping("/update")
     @RequiresPermissions("novel:friendLink:edit")
-    public R update(FriendLinkDO friendLink) {
+    public R update(@Validated FriendLinkDO friendLink) {
         friendLinkService.update(friendLink);
         redisTemplate.delete(CacheKey.INDEX_LINK_KEY);
         return R.ok();
     }
 
diff --git a/novel-admin/src/main/java/com/java2nb/novel/domain/FriendLinkDO.java b/novel-admin/src/main/java/com/java2nb/novel/domain/FriendLinkDO.java
index b0b9d73..d026566 100644
--- a/novel-admin/src/main/java/com/java2nb/novel/domain/FriendLinkDO.java
+++ b/novel-admin/src/main/java/com/java2nb/novel/domain/FriendLinkDO.java
@@ -1,163 +1,175 @@
 package com.java2nb.novel.domain;
 
-import java.io.Serializable;
-
-
-import java.math.BigDecimal;
-
 import com.fasterxml.jackson.databind.annotation.JsonSerialize;
 import com.java2nb.common.jsonserializer.LongToStringSerializer;
-
-
+import org.hibernate.validator.constraints.URL;
 import org.springframework.format.annotation.DateTimeFormat;
-import java.util.Date;
 
+import java.io.Serializable;
+import java.util.Date;
 
 
 /**
- * 
- * 
  * @author xiongxy
  * @email 1179705413@qq.com
  * @date 2023-04-14 15:12:25
  */
 public class FriendLinkDO implements Serializable {
-	private static final long serialVersionUID = 1L;
-
-	
-	//主键
-			private Integer id;
-	//链接名
-			private String linkName;
-	//链接url
-			private String linkUrl;
-	//排序号
-			private Integer sort;
-	//是否开启，0：不开启，1：开启
-			private Integer isOpen;
-	//创建人id
-		//java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
-	//所以通过序列化成字符串来解决
-	@JsonSerialize(using = LongToStringSerializer.class)
-			private Long createUserId;
-	//创建时间
-			@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-		private Date createTime;
-	//更新者用户id
-		//java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
-	//所以通过序列化成字符串来解决
-	@JsonSerialize(using = LongToStringSerializer.class)
-			private Long updateUserId;
-	//更新时间
-			@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
-		private Date updateTime;
-
-	/**
-	 * 设置：主键
-	 */
-	public void setId(Integer id) {
-		this.id = id;
-	}
-	/**
-	 * 获取：主键
-	 */
-	public Integer getId() {
-		return id;
-	}
-	/**
-	 * 设置：链接名
-	 */
-	public void setLinkName(String linkName) {
-		this.linkName = linkName;
-	}
-	/**
-	 * 获取：链接名
-	 */
-	public String getLinkName() {
-		return linkName;
-	}
-	/**
-	 * 设置：链接url
-	 */
-	public void setLinkUrl(String linkUrl) {
-		this.linkUrl = linkUrl;
-	}
-	/**
-	 * 获取：链接url
-	 */
-	public String getLinkUrl() {
-		return linkUrl;
-	}
-	/**
-	 * 设置：排序号
-	 */
-	public void setSort(Integer sort) {
-		this.sort = sort;
-	}
-	/**
-	 * 获取：排序号
-	 */
-	public Integer getSort() {
-		return sort;
-	}
-	/**
-	 * 设置：是否开启，0：不开启，1：开启
-	 */
-	public void setIsOpen(Integer isOpen) {
-		this.isOpen = isOpen;
-	}
-	/**
-	 * 获取：是否开启，0：不开启，1：开启
-	 */
-	public Integer getIsOpen() {
-		return isOpen;
-	}
-	/**
-	 * 设置：创建人id
-	 */
-	public void setCreateUserId(Long createUserId) {
-		this.createUserId = createUserId;
-	}
-	/**
-	 * 获取：创建人id
-	 */
-	public Long getCreateUserId() {
-		return createUserId;
-	}
-	/**
-	 * 设置：创建时间
-	 */
-	public void setCreateTime(Date createTime) {
-		this.createTime = createTime;
-	}
-	/**
-	 * 获取：创建时间
-	 */
-	public Date getCreateTime() {
-		return createTime;
-	}
-	/**
-	 * 设置：更新者用户id
-	 */
-	public void setUpdateUserId(Long updateUserId) {
-		this.updateUserId = updateUserId;
-	}
-	/**
-	 * 获取：更新者用户id
-	 */
-	public Long getUpdateUserId() {
-		return updateUserId;
-	}
-	/**
-	 * 设置：更新时间
-	 */
-	public void setUpdateTime(Date updateTime) {
-		this.updateTime = updateTime;
-	}
-	/**
-	 * 获取：更新时间
-	 */
-	public Date getUpdateTime() {
-		return updateTime;
-	}
+
+    private static final long serialVersionUID = 1L;
+
+
+    //主键
+    private Integer id;
+    //链接名
+    private String linkName;
+    //链接url
+    @URL
+    private String linkUrl;
+    //排序号
+    private Integer sort;
+    //是否开启，0：不开启，1：开启
+    private Integer isOpen;
+    //创建人id
+    //java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
+    //所以通过序列化成字符串来解决
+    @JsonSerialize(using = LongToStringSerializer.class)
+    private Long createUserId;
+    //创建时间
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+    //更新者用户id
+    //java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
+    //所以通过序列化成字符串来解决
+    @JsonSerialize(using = LongToStringSerializer.class)
+    private Long updateUserId;
+    //更新时间
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    /**
+     * 设置：主键
+     */
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    /**
+     * 获取：主键
+     */
+    public Integer getId() {
+        return id;
+    }
+
+    /**
+     * 设置：链接名
+     */
+    public void setLinkName(String linkName) {
+        this.linkName = linkName;
+    }
+
+    /**
+     * 获取：链接名
+     */
+    public String getLinkName() {
+        return linkName;
+    }
+
+    /**
+     * 设置：链接url
+     */
+    public void setLinkUrl(String linkUrl) {
+        this.linkUrl = linkUrl;
+    }
+
+    /**
+     * 获取：链接url
+     */
+    public String getLinkUrl() {
+        return linkUrl;
+    }
+
+    /**
+     * 设置：排序号
+     */
+    public void setSort(Integer sort) {
+        this.sort = sort;
+    }
+
+    /**
+     * 获取：排序号
+     */
+    public Integer getSort() {
+        return sort;
+    }
+
+    /**
+     * 设置：是否开启，0：不开启，1：开启
+     */
+    public void setIsOpen(Integer isOpen) {
+        this.isOpen = isOpen;
+    }
+
+    /**
+     * 获取：是否开启，0：不开启，1：开启
+     */
+    public Integer getIsOpen() {
+        return isOpen;
+    }
+
+    /**
+     * 设置：创建人id
+     */
+    public void setCreateUserId(Long createUserId) {
+        this.createUserId = createUserId;
+    }
+
+    /**
+     * 获取：创建人id
+     */
+    public Long getCreateUserId() {
+        return createUserId;
+    }
+
+    /**
+     * 设置：创建时间
+     */
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    /**
+     * 获取：创建时间
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * 设置：更新者用户id
+     */
+    public void setUpdateUserId(Long updateUserId) {
+        this.updateUserId = updateUserId;
+    }
+
+    /**
+     * 获取：更新者用户id
+     */
+    public Long getUpdateUserId() {
+        return updateUserId;
+    }
+
+    /**
+     * 设置：更新时间
+     */
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    /**
+     * 获取：更新时间
+     */
+    public Date getUpdateTime() {
+        return updateTime;
+    }
 }
diff --git a/novel-admin/src/main/resources/templates/novel/friendLink/add.html b/novel-admin/src/main/resources/templates/novel/friendLink/add.html
index 66e3d11..10096e1 100644
--- a/novel-admin/src/main/resources/templates/novel/friendLink/add.html
+++ b/novel-admin/src/main/resources/templates/novel/friendLink/add.html
@@ -21,11 +21,11 @@
                         <div class="form-group">
                             <label class="col-sm-3 control-label">链接url：</label>
                             <div class="col-sm-8">
                                 <input id="linkUrl" name="linkUrl"
                                        class="form-control"
-                                       type="text" required>
+                                       type="url" required>
 
                             </div>
                         </div>
                         <div class="form-group">
                             <label class="col-sm-3 control-label">排序号：</label>
diff --git a/novel-admin/src/main/resources/templates/novel/friendLink/edit.html b/novel-admin/src/main/resources/templates/novel/friendLink/edit.html
index cef908d..f04ff49 100644
--- a/novel-admin/src/main/resources/templates/novel/friendLink/edit.html
+++ b/novel-admin/src/main/resources/templates/novel/friendLink/edit.html
@@ -24,11 +24,11 @@
                             <label class="col-sm-3 control-label">链接url：</label>
                             <div class="col-sm-8">
                                 <input id="linkUrl" name="linkUrl"
                                        th:value="${friendLink.linkUrl}"
                                        class="form-control"
-                                       type="text" required>
+                                       type="url" required>
                             </div>
                         </div>
                         <div class="form-group">
                             <label class="col-sm-3 control-label">排序号：</label>
                             <div class="col-sm-8">

Vulnerable code:     public R save(FriendLinkDO friendLink) {
    public R update(FriendLinkDO friendLink) {
import java.io.Serializable;


import java.math.BigDecimal;



import java.util.Date;
 * 
 * 
	private static final long serialVersionUID = 1L;

	
	//主键
			private Integer id;
	//链接名
			private String linkName;
	//链接url
			private String linkUrl;
	//排序号
			private Integer sort;
	//是否开启，0：不开启，1：开启
			private Integer isOpen;
	//创建人id
		//java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
	//所以通过序列化成字符串来解决
	@JsonSerialize(using = LongToStringSerializer.class)
			private Long createUserId;
	//创建时间
			@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
		private Date createTime;
	//更新者用户id
		//java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
	//所以通过序列化成字符串来解决
	@JsonSerialize(using = LongToStringSerializer.class)
			private Long updateUserId;
	//更新时间
			@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
		private Date updateTime;

	/**
	 * 设置：主键
	 */
	public void setId(Integer id) {
		this.id = id;
	}
	/**
	 * 获取：主键
	 */
	public Integer getId() {
		return id;
	}
	/**
	 * 设置：链接名
	 */
	public void setLinkName(String linkName) {
		this.linkName = linkName;
	}
	/**
	 * 获取：链接名
	 */
	public String getLinkName() {
		return linkName;
	}
	/**
	 * 设置：链接url
	 */
	public void setLinkUrl(String linkUrl) {
		this.linkUrl = linkUrl;
	}
	/**
	 * 获取：链接url
	 */
	public String getLinkUrl() {
		return linkUrl;
	}
	/**
	 * 设置：排序号
	 */
	public void setSort(Integer sort) {
		this.sort = sort;
	}
	/**
	 * 获取：排序号
	 */
	public Integer getSort() {
		return sort;
	}
	/**
	 * 设置：是否开启，0：不开启，1：开启
	 */
	public void setIsOpen(Integer isOpen) {
		this.isOpen = isOpen;
	}
	/**
	 * 获取：是否开启，0：不开启，1：开启
	 */
	public Integer getIsOpen() {
		return isOpen;
	}
	/**
	 * 设置：创建人id
	 */
	public void setCreateUserId(Long createUserId) {
		this.createUserId = createUserId;
	}
	/**
	 * 获取：创建人id
	 */
	public Long getCreateUserId() {
		return createUserId;
	}
	/**
	 * 设置：创建时间
	 */
	public void setCreateTime(Date createTime) {
		this.createTime = createTime;
	}
	/**
	 * 获取：创建时间
	 */
	public Date getCreateTime() {
		return createTime;
	}
	/**
	 * 设置：更新者用户id
	 */
	public void setUpdateUserId(Long updateUserId) {
		this.updateUserId = updateUserId;
	}
	/**
	 * 获取：更新者用户id
	 */
	public Long getUpdateUserId() {
		return updateUserId;
	}
	/**
	 * 设置：更新时间
	 */
	public void setUpdateTime(Date updateTime) {
		this.updateTime = updateTime;
	}
	/**
	 * 获取：更新时间
	 */
	public Date getUpdateTime() {
		return updateTime;
	}
                                       type="text" required>
                                       type="text" required>
Fixed code: import org.springframework.validation.annotation.Validated;
    public R save(@Validated FriendLinkDO friendLink) {
    public R update(@Validated FriendLinkDO friendLink) {
import org.hibernate.validator.constraints.URL;
import java.io.Serializable;
import java.util.Date;

    private static final long serialVersionUID = 1L;


    //主键
    private Integer id;
    //链接名
    private String linkName;
    //链接url
    @URL
    private String linkUrl;
    //排序号
    private Integer sort;
    //是否开启，0：不开启，1：开启
    private Integer isOpen;
    //创建人id
    //java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
    //所以通过序列化成字符串来解决
    @JsonSerialize(using = LongToStringSerializer.class)
    private Long createUserId;
    //创建时间
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;
    //更新者用户id
    //java中的long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值)
    //所以通过序列化成字符串来解决
    @JsonSerialize(using = LongToStringSerializer.class)
    private Long updateUserId;
    //更新时间
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

    /**
     * 设置：主键
     */
    public void setId(Integer id) {
        this.id = id;
    }

    /**
     * 获取：主键
     */
    public Integer getId() {
        return id;
    }

    /**
     * 设置：链接名
     */
    public void setLinkName(String linkName) {
        this.linkName = linkName;
    }

    /**
     * 获取：链接名
     */
    public String getLinkName() {
        return linkName;
    }

    /**
     * 设置：链接url
     */
    public void setLinkUrl(String linkUrl) {
        this.linkUrl = linkUrl;
    }

    /**
     * 获取：链接url
     */
    public String getLinkUrl() {
        return linkUrl;
    }

    /**
     * 设置：排序号
     */
    public void setSort(Integer sort) {
        this.sort = sort;
    }

    /**
     * 获取：排序号
     */
    public Integer getSort() {
        return sort;
    }

    /**
     * 设置：是否开启，0：不开启，1：开启
     */
    public void setIsOpen(Integer isOpen) {
        this.isOpen = isOpen;
    }

    /**
     * 获取：是否开启，0：不开启，1：开启
     */
    public Integer getIsOpen() {
        return isOpen;
    }

    /**
     * 设置：创建人id
     */
    public void setCreateUserId(Long createUserId) {
        this.createUserId = createUserId;
    }

    /**
     * 获取：创建人id
     */
    public Long getCreateUserId() {
        return createUserId;
    }

    /**
     * 设置：创建时间
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    /**
     * 获取：创建时间
     */
    public Date getCreateTime() {
        return createTime;
    }

    /**
     * 设置：更新者用户id
     */
    public void setUpdateUserId(Long updateUserId) {
        this.updateUserId = updateUserId;
    }

    /**
     * 获取：更新者用户id
     */
    public Long getUpdateUserId() {
        return updateUserId;
    }

    /**
     * 设置：更新时间
     */
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    /**
     * 获取：更新时间
     */
    public Date getUpdateTime() {
        return updateTime;
    }
                                       type="url" required>
                                       type="url" required>