CVE-2018-20594: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: An issue was discovered in hsweb 3.0.4. It is a reflected XSS vulnerability due to the absence of type parameter checking in FlowableModelManagerController.java.
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: 6.1
Published: 2018-12-30T18:29Z
Repository: https://github.com/hs-web/hsweb-framework
Language: Java
Files changed: hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/enums/ModelType.java, hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/web/FlowableModelManagerController.java
Commit date: 2018-12-29 11:10:11 +0800
Commit message: fix #107  修复反射型xss

Diff: diff --git a/hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/enums/ModelType.java b/hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/enums/ModelType.java
new file mode 100644
index 00000000..355465e7
--- /dev/null
+++ b/hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/enums/ModelType.java
@@ -0,0 +1,9 @@
+package org.hswebframework.web.workflow.enums;
+
+/**
+ * @author zhouhao
+ * @since 3.0.5
+ */
+public enum ModelType {
+    bpmn, json
+}
diff --git a/hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/web/FlowableModelManagerController.java b/hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/web/FlowableModelManagerController.java
index 72202a2c..319c637c 100644
--- a/hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/web/FlowableModelManagerController.java
+++ b/hsweb-system/hsweb-system-workflow/hsweb-system-workflow-local/src/main/java/org/hswebframework/web/workflow/web/FlowableModelManagerController.java
@@ -6,10 +6,11 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import lombok.SneakyThrows;
 import lombok.extern.slf4j.Slf4j;
 import org.activiti.bpmn.converter.BpmnXMLConverter;
 import org.activiti.bpmn.model.BpmnModel;
 import org.activiti.editor.language.json.converter.BpmnJsonConverter;
 import org.activiti.engine.RepositoryService;
@@ -31,10 +32,11 @@ import org.hswebframework.web.authorization.Permission;
 import org.hswebframework.web.authorization.annotation.Authorize;
 import org.hswebframework.web.bean.FastBeanCopier;
 import org.hswebframework.web.commons.entity.PagerResult;
 import org.hswebframework.web.commons.entity.param.QueryParamEntity;
 import org.hswebframework.web.controller.message.ResponseMessage;
+import org.hswebframework.web.workflow.enums.ModelType;
 import org.hswebframework.web.workflow.util.QueryUtils;
 import org.hswebframework.web.workflow.web.request.ModelCreateRequest;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
@@ -130,62 +132,55 @@ public class FlowableModelManagerController {
      * @param type    导出文件类型(bpmn\json)
      */
     @GetMapping(value = "export/{modelId}/{type}")
     @ApiOperation("导出模型")
     @Authorize(action = "export")
+    @SneakyThrows
     public void export(@PathVariable("modelId") @ApiParam("模型ID") String modelId,
-                       @PathVariable("type") @ApiParam(value = "类型", allowableValues = "bpmn,json", example = "json") String type,
+                       @PathVariable("type") @ApiParam(value = "类型", allowableValues = "bpmn,json", example = "json")
+                               ModelType type,
                        @ApiParam(hidden = true) HttpServletResponse response) {
-        try {
-            Model modelData = repositoryService.getModel(modelId);
-            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
-            byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());
-
-            JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);
-            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
-
-            // 处理异常
-            if (bpmnModel.getMainProcess() == null) {
-                response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value());
-                response.getOutputStream().println("no main process, can't export for dimension: " + type);
-                response.flushBuffer();
-                return;
-            }
-
-            String filename = "";
-            byte[] exportBytes = null;
-
-            String mainProcessId = bpmnModel.getMainProcess().getId();
+        Model modelData = repositoryService.getModel(modelId);
+        if (modelData == null) {
+            throw new NotFoundException("模型不存在");
+        }
+        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
+        byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());
 
-            if ("bpmn".equals(type)) {
+        JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);
+        BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
 
-                BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
-                exportBytes = xmlConverter.convertToXML(bpmnModel);
+        // 处理异常
+        if (bpmnModel.getMainProcess() == null) {
+            throw new UnsupportedOperationException("无法导出模型文件:" + type);
+        }
 
-                filename = mainProcessId + ".bpmn20.xml";
-            } else if ("json".equals(type)) {
+        String filename = "";
+        byte[] exportBytes = null;
 
-                exportBytes = modelEditorSource;
-                filename = mainProcessId + ".json";
+        String mainProcessId = bpmnModel.getMainProcess().getId();
 
-            } else {
-                throw new UnsupportedOperationException("不支持的格式:" + type);
-            }
+        if (type == ModelType.bpmn) {
+            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
+            exportBytes = xmlConverter.convertToXML(bpmnModel);
+            filename = mainProcessId + ".bpmn20.xml";
+        } else if (type == ModelType.json) {
+            exportBytes = modelEditorSource;
+            filename = mainProcessId + ".json";
 
-            response.setCharacterEncoding("UTF-8");
-            response.setContentType("application/octet-stream");
-            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
+        } else {
+            throw new UnsupportedOperationException("不支持的格式:" + type);
+        }
 
-            /*创建输入流*/
-            try (ByteArrayInputStream in = new ByteArrayInputStream(exportBytes)) {
-                IOUtils.copy(in, response.getOutputStream());
-                response.flushBuffer();
-                in.close();
-            }
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/octet-stream");
+        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
 
-        } catch (Exception e) {
-            log.error("导出model的xml文件失败：modelId={}, type={}", modelId, type, e);
+        /*创建输入流*/
+        try (ByteArrayInputStream in = new ByteArrayInputStream(exportBytes)) {
+            IOUtils.copy(in, response.getOutputStream());
+            response.flushBuffer();
         }
     }
 
     @GetMapping(value = "/{modelId}/json")
     @Authorize(action = Permission.ACTION_GET)

Vulnerable code:                        @PathVariable("type") @ApiParam(value = "类型", allowableValues = "bpmn,json", example = "json") String type,
        try {
            Model modelData = repositoryService.getModel(modelId);
            BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
            byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());

            JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);
            BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);

            // 处理异常
            if (bpmnModel.getMainProcess() == null) {
                response.setStatus(HttpStatus.UNPROCESSABLE_ENTITY.value());
                response.getOutputStream().println("no main process, can't export for dimension: " + type);
                response.flushBuffer();
                return;
            }

            String filename = "";
            byte[] exportBytes = null;

            String mainProcessId = bpmnModel.getMainProcess().getId();
            if ("bpmn".equals(type)) {
                BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
                exportBytes = xmlConverter.convertToXML(bpmnModel);
                filename = mainProcessId + ".bpmn20.xml";
            } else if ("json".equals(type)) {
                exportBytes = modelEditorSource;
                filename = mainProcessId + ".json";
            } else {
                throw new UnsupportedOperationException("不支持的格式:" + type);
            }
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
            /*创建输入流*/
            try (ByteArrayInputStream in = new ByteArrayInputStream(exportBytes)) {
                IOUtils.copy(in, response.getOutputStream());
                response.flushBuffer();
                in.close();
            }
        } catch (Exception e) {
            log.error("导出model的xml文件失败：modelId={}, type={}", modelId, type, e);
Fixed code: package org.hswebframework.web.workflow.enums;

/**
 * @author zhouhao
 * @since 3.0.5
 */
public enum ModelType {
    bpmn, json
}
import lombok.SneakyThrows;
import org.hswebframework.web.workflow.enums.ModelType;
    @SneakyThrows
                       @PathVariable("type") @ApiParam(value = "类型", allowableValues = "bpmn,json", example = "json")
                               ModelType type,
        Model modelData = repositoryService.getModel(modelId);
        if (modelData == null) {
            throw new NotFoundException("模型不存在");
        }
        BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
        byte[] modelEditorSource = repositoryService.getModelEditorSource(modelData.getId());
        JsonNode editorNode = new ObjectMapper().readTree(modelEditorSource);
        BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
        // 处理异常
        if (bpmnModel.getMainProcess() == null) {
            throw new UnsupportedOperationException("无法导出模型文件:" + type);
        }
        String filename = "";
        byte[] exportBytes = null;
        String mainProcessId = bpmnModel.getMainProcess().getId();
        if (type == ModelType.bpmn) {
            BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
            exportBytes = xmlConverter.convertToXML(bpmnModel);
            filename = mainProcessId + ".bpmn20.xml";
        } else if (type == ModelType.json) {
            exportBytes = modelEditorSource;
            filename = mainProcessId + ".json";
        } else {
            throw new UnsupportedOperationException("不支持的格式:" + type);
        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
        /*创建输入流*/
        try (ByteArrayInputStream in = new ByteArrayInputStream(exportBytes)) {
            IOUtils.copy(in, response.getOutputStream());
            response.flushBuffer();