This commit is contained in:
even 2025-07-30 17:44:32 +08:00
commit f940d68ba5
18 changed files with 1072 additions and 6 deletions

View File

@ -0,0 +1,40 @@
package cd.casic.ci.api;
import cd.casic.ci.process.dto.req.localAflCrashes.LocalAflCrashesQueryReq;
import cd.casic.ci.process.dto.req.localAflCrashes.LocalAflCrashesSaveReq;
import cd.casic.ci.process.dto.resp.localAflCrashes.LocalAflCrashesResp;
import cd.casic.ci.process.process.service.localAflCrashes.LocalAflCrashesService;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.pojo.CommonResult;
import cd.casic.framework.commons.pojo.PageResult;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/LocalAflCrashes")
public class LocalAflCrashesController {
@Resource
private LocalAflCrashesService localAflCrashesService;
@PostMapping(path="/saveLocalAflCrashesInfo")
public CommonResult<Void> saveLocalAflCrashesInfo(@RequestBody @Valid LocalAflCrashesSaveReq req) throws SftpUploadUtil.SftpUploadException {
localAflCrashesService.saveLocalAflCrashesInfo(req);
return CommonResult.success();
}
/**
*分页模糊查询漏洞信息
*/
@PostMapping("/search")
public CommonResult<PageResult<LocalAflCrashesResp>> getLocalAflCrashesSearch(@RequestBody LocalAflCrashesQueryReq req) {
PageResult<LocalAflCrashesResp> search = localAflCrashesService.getLocalAflCrashesSearch(req);
return CommonResult.success(search);
}
}

View File

@ -0,0 +1,37 @@
package cd.casic.ci.api;
import cd.casic.ci.process.dto.req.vulInfo.SearchDto;
import cd.casic.ci.process.dto.req.vulInfo.VulInfoDto;
import cd.casic.ci.process.process.dataObject.volumnInfo.VulInfo;
import cd.casic.ci.process.process.service.vulInfo.VulInfoService;
import cd.casic.framework.commons.pojo.CommonResult;
import cd.casic.framework.commons.pojo.PageResult;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/vulInfo")
public class VulInfoController {
@Resource
private VulInfoService vulInfoService;
/**
*上传漏洞信息
*/
@PostMapping("/upload")
public CommonResult<Void> uploadVulInfo(@RequestBody VulInfo vulInfo) {
vulInfoService.upload(vulInfo);
return CommonResult.success();
}
/**
*分页模糊查询漏洞信息
*/
@PostMapping("/search")
public CommonResult<PageResult<VulInfoDto>> getVulInfoSearch(@RequestBody SearchDto searchDto) {
return CommonResult.success(vulInfoService.getSearch(searchDto));
}
}

View File

@ -0,0 +1,72 @@
package cd.casic.ci.process.dto.req.localAflCrashes;
import cd.casic.framework.commons.pojo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @ClassName PipelineQueryReq
* @Author hopeli
* @Date 2025/5/10 9:54
* @Version 1.0
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class LocalAflCrashesQueryReq extends PageParam {
private String id;
/**
* 流水线id
*/
private String pipelineId;
/**
* 流水线历史记录id
*/
private String pipelineHistoryId;
/**
* 任务节点id
*/
private String taskId;
/**
* 流水线节点类型
*/
private String taskType;
/**
* 分组标识
*/
private String groupIdentifier;
/**
* 文件信息
*/
private byte[] fileContent;
/**
* 流水线执行记录id
*/
private String instanceId;
/**
* 发现的目标漏洞类型源码二进制固件
*/
private String targetType;
/**
* 目标名称
*/
private String targetName;
/**
* 城市
*/
private String city;
/**
* 崩溃文件名称
*/
private String crashesName;
}

View File

@ -0,0 +1,76 @@
package cd.casic.ci.process.dto.req.localAflCrashes;
import lombok.Data;
@Data
public class LocalAflCrashesSaveReq {
/**
* 流水线id
*/
private String pipelineId;
/**
* 流水线历史记录id
*/
private String pipelineHistoryId;
/**
* 任务节点id
*/
private String taskId;
/**
* 流水线节点类型
*/
private String taskType;
/**
* 分组标识
*/
private String groupIdentifier;
/**
* 文件信息
*/
private byte[] fileContent;
/**
* 流水线执行记录id
*/
private String instanceId;
/**
* 发现的目标漏洞类型源码二进制固件
*/
private String targetType;
/**
* 目标名称
*/
private String targetName;
/**
* 城市
*/
private String city;
/**
* 崩溃文件名称
*/
private String crashesName;
/**
* 崩溃文件生成时间
*/
private String crashesCreateTime;
/**
* 崩溃文件大小
*/
private Integer crashesLength;
/**
* 崩溃文件描述
*/
private String crashesRemark;
}

View File

@ -0,0 +1,29 @@
package cd.casic.ci.process.dto.req.vulInfo;
import cd.casic.framework.commons.pojo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class SearchDto extends PageParam {
private String vulId;
private String vulTitle;
private List<String> cwes;
private String affectProjectId;
private String verifyStatus;
private String targetName;
private String unaffectedVersions;
private String affectProductName;
private String localAflCrashesId;
}

View File

@ -0,0 +1,113 @@
package cd.casic.ci.process.dto.req.vulInfo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class VulInfoDto {
/**
* 记录的唯一标识 ID数据库中为主键自增长
*/
private Long id;
/**
* 创建时间自动生成
*/
private Date createTime;
/**
* 修改时间自动生成修改时间就是审核时间
*/
private Date modifyTime;
/**
* 漏洞状态未审核审核中审核完
*/
private String verifyStatus;
/**
* 内部漏洞id不需要输入根据类型+日期随机生成类型分为源码固件二进制 根据target_type 漏洞目标相关
*/
private String vulId;
/**
* 是否开源选项框对勾源码时候才会有这个概念
*/
private String isOpenSource;
/**
* 中文标题
*/
private String vulTitle;
/**
* 中文描述
*/
private String vulDescription;
/**
* 危险等级,高中低三种
*/
private String severity;
/**
* 中文修复方案
*/
private String solution;
/**
* 暂时修复的缓解措施
*/
private String relieve;
/**
* 目标的名称
*/
private String targetName;
/**
* 漏洞类型
*/
private List<String> cwes;
/**
* 利用级别可利用和不可利用默认不要利用
*/
private String level;
/**
* 发现的漏洞目标类型源码二进制固件三种
*/
private String targetType;
/**
* 目标的链接来自于人机协同系统就是一个下载地址的url
*/
private String targetUrl;
/**
* 受影响的目标用户自己输入
*/
private String affectProjectId;
/**
* 类型是源码的情况才有这个概念安全分支列表,如果类型是源码的这个是可选项比如master分支dev分支等
*/
private String unaffectedVersions;
/**
* 受影响产品名
*/
private String affectProductName;
private String localAflCrashesId;
}

View File

@ -0,0 +1,106 @@
package cd.casic.ci.process.dto.resp.localAflCrashes;
import lombok.Data;
import java.time.LocalDateTime;
/**
* @ClassName ReportResp
* @Author hopeli
* @Date 2025/5/10 10:53
* @Version 1.0
*/
@Data
public class LocalAflCrashesResp {
private String id;
/**
* 流水线id
*/
private String pipelineId;
/**
* 流水线历史记录id
*/
private String pipelineHistoryId;
/**
* 任务节点id
*/
private String taskId;
/**
* 流水线节点类型
*/
private String taskType;
/**
* 分组标识
*/
private String groupIdentifier;
/**
* 文件信息
*/
private byte[] fileContent;
/**
* 流水线执行记录id
*/
private String instanceId;
/**
* 发现的目标漏洞类型源码二进制固件
*/
private String targetType;
/**
* 目标名称
*/
private String targetName;
/**
* 城市
*/
private String city;
/**
* 崩溃文件名称
*/
private String crashesName;
/**
* 崩溃文件生成时间
*/
private String crashesCreateTime;
/**
* 崩溃文件大小
*/
private Integer crashesLength;
/**
* 崩溃文件描述
*/
private String crashesRemark;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 最后更新时间
*/
private LocalDateTime updateTime;
/**
* 创建者目前使用 SysUser id 编号
*
* 使用 String 类型的原因是未来可能会存在非数值的情况留好拓展性
*/
private String creator;
/**
* 更新者目前使用 SysUser id 编号
*
* 使用 String 类型的原因是未来可能会存在非数值的情况留好拓展性
*/
private String updater;
}

View File

@ -0,0 +1,22 @@
package cd.casic.ci.process.process.converter;
import cd.casic.ci.process.dto.resp.localAflCrashes.LocalAflCrashesResp;
import cd.casic.ci.process.process.dataObject.localAflCrashes.LocalAflCrashesInfo;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
/**
* @author HopeLi
* @version v1.0
* @ClassName PipelineConverter
* @Date: 2025/5/13 14:39
* @Description:
*/
@Mapper(componentModel = "spring")
public interface LocalAflCrashesConverter {
LocalAflCrashesConverter INSTANCE = Mappers.getMapper(LocalAflCrashesConverter.class);
List<LocalAflCrashesResp> toRespList(List<LocalAflCrashesInfo> records);
}

View File

@ -0,0 +1,14 @@
package cd.casic.ci.process.process.dao.localAflCrashes;
import cd.casic.ci.process.process.dataObject.localAflCrashes.LocalAflCrashesInfo;
import cd.casic.framework.mybatis.core.mapper.BaseMapperX;
/**
* @author HopeLi
* @version v1.0
* @ClassName LocalAflCrashesDao
* @Date: 2025/5/13 14:39
* @Description:
*/
public interface LocalAflCrashesDao extends BaseMapperX<LocalAflCrashesInfo> {
}

View File

@ -0,0 +1,88 @@
package cd.casic.ci.process.process.dataObject.localAflCrashes;
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author HopeLi
* @version v1.0
* @ClassName LocalAflCrashesInfo
* @Date: 2025/7/30 11:22
* @Description:
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName(value = "local_afl_crashes_info")
public class LocalAflCrashesInfo extends PipBaseElement {
/**
* 流水线id
*/
private String pipelineId;
/**
* 流水线历史记录id
*/
private String pipelineHistoryId;
/**
* 任务节点id
*/
private String taskId;
/**
* 流水线节点类型
*/
private String taskType;
/**
* 分组标识
*/
private String groupIdentifier;
/**
* 文件信息
*/
private byte[] fileContent;
/**
* 流水线执行记录id
*/
private String instanceId;
/**
* 发现的目标漏洞类型源码二进制固件
*/
private String targetType;
/**
* 目标名称
*/
private String targetName;
/**
* 城市
*/
private String city;
/**
* 崩溃文件名称
*/
private String crashesName;
/**
* 崩溃文件生成时间
*/
private String crashesCreateTime;
/**
* 崩溃文件大小
*/
private Integer crashesLength;
/**
* 崩溃文件描述
*/
private String crashesRemark;
}

View File

@ -1,6 +1,5 @@
package cd.casic.ci.process.process.dataObject.volumnInfo; package cd.casic.ci.process.process.dataObject.volumnInfo;
import cd.casic.framework.mybatis.core.type.EncryptTypeHandler;
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableId;
@ -8,7 +7,6 @@ import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.ibatis.type.JdbcType; import org.apache.ibatis.type.JdbcType;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -131,4 +129,6 @@ public class VulInfo {
* 节点类型 * 节点类型
* */ * */
private String taskType; private String taskType;
private String localAflCrashesId;
} }

View File

@ -0,0 +1,23 @@
package cd.casic.ci.process.process.service.localAflCrashes;
import cd.casic.ci.process.dto.req.localAflCrashes.LocalAflCrashesQueryReq;
import cd.casic.ci.process.dto.req.localAflCrashes.LocalAflCrashesSaveReq;
import cd.casic.ci.process.dto.resp.localAflCrashes.LocalAflCrashesResp;
import cd.casic.ci.process.process.dataObject.localAflCrashes.LocalAflCrashesInfo;
import cd.casic.framework.commons.pojo.PageResult;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.Valid;
/**
* @author HopeLi
* @version v1.0
* @ClassName LocalAflCrashesService
* @Date: 2025/5/17 10:20
* @Description:
*/
public interface LocalAflCrashesService extends IService<LocalAflCrashesInfo> {
void saveLocalAflCrashesInfo(@Valid LocalAflCrashesSaveReq req);
PageResult<LocalAflCrashesResp> getLocalAflCrashesSearch(LocalAflCrashesQueryReq req);
}

View File

@ -0,0 +1,218 @@
package cd.casic.ci.process.process.service.localAflCrashes.impl;
import cd.casic.ci.process.dto.req.localAflCrashes.LocalAflCrashesQueryReq;
import cd.casic.ci.process.dto.req.localAflCrashes.LocalAflCrashesSaveReq;
import cd.casic.ci.process.dto.resp.localAflCrashes.LocalAflCrashesResp;
import cd.casic.ci.process.dto.resp.resource.ResourceDetailResp;
import cd.casic.ci.process.engine.constant.PipelineVariableConstant;
import cd.casic.ci.process.engine.manager.RunContextManager;
import cd.casic.ci.process.engine.runContext.BaseRunContext;
import cd.casic.ci.process.process.converter.LocalAflCrashesConverter;
import cd.casic.ci.process.process.dao.localAflCrashes.LocalAflCrashesDao;
import cd.casic.ci.process.process.dataObject.localAflCrashes.LocalAflCrashesInfo;
import cd.casic.ci.process.process.service.localAflCrashes.LocalAflCrashesService;
import cd.casic.ci.process.process.service.resource.ResourceManagerService;
import cd.casic.ci.process.util.SftpUploadUtil;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
import cd.casic.framework.commons.pojo.PageResult;
import cd.casic.module.machine.dal.dataobject.MachineInfoDO;
import cd.casic.module.machine.utils.CryptogramUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @author HopeLi
* @version v1.0
* @ClassName TargetVersionServiceImpl
* @Date: 2025/5/17 15:20
* @Description:
*/
@Service
@Slf4j
public class LocalAflCrashesServiceImpl extends ServiceImpl<LocalAflCrashesDao, LocalAflCrashesInfo> implements LocalAflCrashesService {
private static final String remoteFilePath = "/home/casic/706/yunqi/";
private static final String crashesFilePath = "/ai_afl/default/crashes/";
@Resource
private RunContextManager runContextManager;
@Resource
private ResourceManagerService resourceManagerService;
@Override
public void saveLocalAflCrashesInfo(LocalAflCrashesSaveReq req) {
List<LocalAflCrashesInfo> aflCrashesInfos = new ArrayList<>(0);
if (StringUtils.isEmpty(req.getPipelineId()) || StringUtils.isEmpty(req.getTaskId())){
throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"pipelineId或者taskId不能为空");
}
String uuid = req.getGroupIdentifier();
BaseRunContext runContext = runContextManager.getContext(req.getPipelineId());
if (ObjectUtils.isEmpty(runContext)){
throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到运行上下文");
}
String resourceId = runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY) instanceof String ? ((String) runContext.getGlobalVariables().get(PipelineVariableConstant.AFL_RESOURCE_MANAGER_ID_KEY)) : null;
if (StringUtils.isEmpty(resourceId)){
throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"未找到资源");
}
ResourceDetailResp resourceById = resourceManagerService.findResourceDetailById(resourceId);
if (resourceById == null || resourceById.getMachineInfo() == null || resourceById.getMachineInfo().getAuthenticationType()!=1) {
throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"资源信息错误");
}
MachineInfoDO resourceMachine = resourceById.getMachineInfo();
String password = CryptogramUtil.doDecrypt(resourceMachine.getPassword());
resourceMachine.setPassword(password);
try {
// 步骤1使用自定义sql获取崩溃文件的创建时间
//查询创建时间
List<String> resultList = SftpUploadUtil.findCreateTimeByCommand(
resourceMachine.getHostIp(),
resourceMachine.getSshPort(),
resourceMachine.getUsername(),
resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);
if (!CollectionUtils.isEmpty(resultList)){
//解析文件名和创建时间
for (String o : resultList) {
String[] lineParts = o.split("\\s+创建时间:\\s+");
if (lineParts.length < 2) {
System.err.println("无效格式: " + o);
break;
}
LocalAflCrashesInfo aflCrashesInfo = new LocalAflCrashesInfo();
String fileName = lineParts[0].substring(2).trim(); // 文件信息部分
String createTime = lineParts[1].trim(); // 创建时间
aflCrashesInfo.setCrashesName(fileName);
aflCrashesInfo.setCrashesCreateTime(createTime);
aflCrashesInfos.add(aflCrashesInfo);
}
}
// 步驟2.查询文件大小
List<String> fileResultList = SftpUploadUtil.findFileByteByCommand(
resourceMachine.getHostIp(),
resourceMachine.getSshPort(),
resourceMachine.getUsername(),
resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);
if (!CollectionUtils.isEmpty(fileResultList) && !CollectionUtils.isEmpty(aflCrashesInfos)){
//解析文件名和文件大小
for (String o : fileResultList) {
// 按空白字符分割
String[] parts = o.trim().split("\\s+");
String fileName = parts[0]; // 文件名称
Integer fileLength = Integer.parseInt(parts[1]); // 文件大小
for (LocalAflCrashesInfo aflCrashesInfo : aflCrashesInfos) {
if (aflCrashesInfo.getCrashesName().equals(fileName)) {
aflCrashesInfo.setCrashesLength(fileLength);
}
aflCrashesInfo.setPipelineId(req.getPipelineId());
if (!StringUtils.isEmpty(req.getTaskId())){
aflCrashesInfo.setTaskId(req.getTaskId());
}
if (!StringUtils.isEmpty(req.getPipelineHistoryId())){
aflCrashesInfo.setPipelineHistoryId(req.getPipelineHistoryId());
}
aflCrashesInfo.setGroupIdentifier(uuid);
}
}
}
// 步骤3列出源目录下的所有文件
List<String> files = SftpUploadUtil.listFilesInRemoteDirectory(
resourceMachine.getHostIp(), resourceMachine.getSshPort(), resourceMachine.getUsername(), resourceMachine.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);
if (!CollectionUtils.isEmpty(files)) {
// 步骤4批量下载文件流与文件绑定
Map<String,byte[]> copiedFiles = SftpUploadUtil.copyRemoteFilesToByteArray(
resourceMachine.getHostIp(), resourceMachine.getSshPort(), resourceMachine.getUsername(), resourceMachine.getPassword(), null, files);
System.out.println("共复制 " + copiedFiles.size() + " 个文件");
//绑定对应的文件流
if (!CollectionUtils.isEmpty(copiedFiles)){
for (Map.Entry<String, byte[]> entry : copiedFiles.entrySet()) {
if (!CollectionUtils.isEmpty(aflCrashesInfos)){
for (LocalAflCrashesInfo aflCrashesInfo : aflCrashesInfos) {
if (aflCrashesInfo.getCrashesName().equals(entry.getKey())) {
aflCrashesInfo.setFileContent(entry.getValue());
}
aflCrashesInfo.setPipelineId(req.getPipelineId());
if (!StringUtils.isEmpty(req.getTaskId())){
aflCrashesInfo.setTaskId(req.getTaskId());
}
aflCrashesInfo.setPipelineHistoryId(req.getPipelineHistoryId());
aflCrashesInfo.setTaskType("AFL");
aflCrashesInfo.setInstanceId(req.getInstanceId());
aflCrashesInfo.setTargetType(req.getTargetType());
aflCrashesInfo.setTargetName(req.getTargetName());
aflCrashesInfo.setCity(req.getCity());
}
}
}
//新增数据到crashes表中
baseMapper.insertBatch(aflCrashesInfos);
}
}
} catch (Exception e) {
e.printStackTrace();
throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"crashes入库失败");
}
}
@Override
public PageResult<LocalAflCrashesResp> getLocalAflCrashesSearch(LocalAflCrashesQueryReq req) {
QueryWrapper<LocalAflCrashesInfo> wrapper = new QueryWrapper<>();
if (!ObjectUtils.isEmpty(req.getId())){
wrapper.eq("id",req.getId());
}
if (!ObjectUtils.isEmpty(req.getPipelineId())){
wrapper.eq("pipeline_id",req.getPipelineId());
}
if (!ObjectUtils.isEmpty(req.getPipelineHistoryId())){
wrapper.eq("pipeline_history_id",req.getPipelineHistoryId());
}
if (!ObjectUtils.isEmpty(req.getTaskId())){
wrapper.eq("task_id",req.getTaskId());
}
if (!ObjectUtils.isEmpty(req.getGroupIdentifier())){
wrapper.eq("group_identifier",req.getGroupIdentifier());
}
Page<LocalAflCrashesInfo> localAflCrashesInfoPage = baseMapper.selectPage(new Page<>(req.getPageNo(), req.getPageSize()), wrapper);
if (ObjectUtils.isEmpty(localAflCrashesInfoPage)){
return new PageResult<>();
}
List<LocalAflCrashesResp> respList = LocalAflCrashesConverter.INSTANCE.toRespList(localAflCrashesInfoPage.getRecords());
PageResult<LocalAflCrashesResp> result = new PageResult<>(respList, localAflCrashesInfoPage.getTotal(), localAflCrashesInfoPage.getCurrent(), localAflCrashesInfoPage.getSize());
return result;
}
}

View File

@ -1,11 +1,11 @@
package cd.casic.ci.process.process.service.vulInfo; package cd.casic.ci.process.process.service.vulInfo;
import cd.casic.ci.process.dto.req.vulInfo.SearchDto;
import cd.casic.ci.process.dto.req.vulInfo.VulInfoDto;
import cd.casic.ci.process.dto.resp.ScaVulInfoResp; import cd.casic.ci.process.dto.resp.ScaVulInfoResp;
import cd.casic.ci.process.process.dao.vulInfo.VulInfoDao;
import cd.casic.ci.process.process.dataObject.volumnInfo.VulInfo; import cd.casic.ci.process.process.dataObject.volumnInfo.VulInfo;
import cd.casic.framework.commons.pojo.PageResult;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStoreException; import java.security.KeyStoreException;
@ -16,4 +16,8 @@ public interface VulInfoService extends IService<VulInfo> {
void scaToVulInfoSave(Integer scaTaskId,String targetType,String targetName,String city,String instanceId,String taskId,String taskType); void scaToVulInfoSave(Integer scaTaskId,String targetType,String targetName,String city,String instanceId,String taskId,String taskType);
Integer scaVulCountGet(Integer scaTaskId) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException; Integer scaVulCountGet(Integer scaTaskId) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
List<ScaVulInfoResp> scaVulListGet(Integer scaTaskId) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException; List<ScaVulInfoResp> scaVulListGet(Integer scaTaskId) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException;
void upload(VulInfo vulInfo);
PageResult<VulInfoDto> getSearch(SearchDto searchDto);
} }

View File

@ -1,15 +1,23 @@
package cd.casic.ci.process.process.service.vulInfo.impl; package cd.casic.ci.process.process.service.vulInfo.impl;
import cd.casic.ci.process.dto.req.report.ReportAssetTaskReq; import cd.casic.ci.process.dto.req.report.ReportAssetTaskReq;
import cd.casic.ci.process.dto.req.vulInfo.SearchDto;
import cd.casic.ci.process.dto.req.vulInfo.VulInfoDto;
import cd.casic.ci.process.dto.resp.ScaVulInfoResp; import cd.casic.ci.process.dto.resp.ScaVulInfoResp;
import cd.casic.ci.process.engine.context.ConstantContextHolder; import cd.casic.ci.process.engine.context.ConstantContextHolder;
import cd.casic.ci.process.process.converter.VulInfoConverter; import cd.casic.ci.process.process.converter.VulInfoConverter;
import cd.casic.ci.process.process.dao.vulInfo.VulInfoDao; import cd.casic.ci.process.process.dao.vulInfo.VulInfoDao;
import cd.casic.ci.process.process.dataObject.volumnInfo.VulInfo; import cd.casic.ci.process.process.dataObject.volumnInfo.VulInfo;
import cd.casic.ci.process.process.service.vulInfo.VulInfoService; import cd.casic.ci.process.process.service.vulInfo.VulInfoService;
import cd.casic.ci.process.util.vulInfo.BeanUtil;
import cd.casic.ci.process.util.vulInfo.TimeUtils;
import cd.casic.framework.commons.exception.ServiceException;
import cd.casic.framework.commons.pojo.PageResult;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -21,7 +29,7 @@ import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -114,6 +122,61 @@ public class VulInfoServiceImpl extends ServiceImpl<VulInfoDao,VulInfo> implemen
return List.of(); return List.of();
} }
public void upload(VulInfo vulInfo) {
vulInfo.setVulId(vulInfo.getTargetType() + TimeUtils.ConvertTimeToStringUtils());
try {
save(vulInfo);
} catch (Exception e) {
throw new ServiceException(500, e.getMessage());
}
}
@Override
public PageResult<VulInfoDto> getSearch(SearchDto searchDto) {
Page<VulInfo> page = new Page<>(searchDto.getPageNo(), searchDto.getPageSize());
String cwes = null;
if (searchDto.getCwes() != null && !searchDto.getCwes().isEmpty()) {
cwes = JSONObject.toJSONString(searchDto.getCwes());
}
QueryWrapper<VulInfo> queryWrapper = new QueryWrapper<>();
if (searchDto.getVulId() != null && !searchDto.getVulId().isEmpty()) {
queryWrapper.like("vul_id", searchDto.getVulId());
}
if (searchDto.getVulTitle() != null && !searchDto.getVulTitle().isEmpty()) {
queryWrapper.like("vul_title", searchDto.getVulTitle());
}
if (cwes != null && !cwes.isEmpty()) {
queryWrapper.like("cwes", cwes);
}
if (searchDto.getAffectProjectId() != null && !searchDto.getAffectProjectId().isEmpty()) {
queryWrapper.like("affect_project_id", searchDto.getAffectProjectId());
}
if (searchDto.getVerifyStatus() != null && !searchDto.getVerifyStatus().isEmpty()) {
queryWrapper.like("verify_status", searchDto.getVerifyStatus());
}
if (searchDto.getTargetName() != null && !searchDto.getTargetName().isEmpty()) {
queryWrapper.like("target_name", searchDto.getTargetName());
}
if (searchDto.getUnaffectedVersions() != null && !searchDto.getUnaffectedVersions().isEmpty()) {
queryWrapper.like("unaffected_versions", searchDto.getUnaffectedVersions());
}
if (searchDto.getAffectProductName() != null && !searchDto.getAffectProductName().isEmpty()) {
queryWrapper.like("affect_product_name", searchDto.getAffectProductName());
}
Page<VulInfo> vulInfoPage = baseMapper.selectPage(page, queryWrapper);
List<VulInfo> records = vulInfoPage.getRecords();
List<VulInfoDto> vulInfoDtos = new ArrayList<>();
if (records != null && !records.isEmpty()) {
//VulInfo转VulInfoDto
for (VulInfo vulInfo : records) {
vulInfoDtos.add(BeanUtil.ConvertVulInfoToVulInfoDto(vulInfo));
}
}
return new PageResult<>(vulInfoDtos,vulInfoPage.getTotal(),vulInfoPage.getCurrent(),vulInfoPage.getSize());
}
private HttpHeaders createHeaders() { private HttpHeaders createHeaders() {
HttpHeaders headers = new HttpHeaders(); HttpHeaders headers = new HttpHeaders();
headers.add("OpenApiUserToken", ConstantContextHolder.getScaToken()); headers.add("OpenApiUserToken", ConstantContextHolder.getScaToken());

View File

@ -1524,6 +1524,123 @@ public class SftpUploadUtil {
} }
/**
* 从远程服务器下载文件并直接返回文件的二进制流
*
* @param remoteHost 远程服务器地址
* @param remotePort 远程服务器端口
* @param username 远程服务器用户名
* @param password 远程服务器密码
* @param sshKeyPath SSH密钥路径
* @param remoteFilePaths 远程文件路径列表
* @return Map<String, byte[]> 文件名和对应的二进制流
* @throws SftpUploadException 如果下载文件过程中发生错误
*/
public static Map<String, byte[]> copyRemoteFilesToByteArray(String remoteHost,
Integer remotePort,
String username,
String password,
String sshKeyPath,
List<String> remoteFilePaths) throws SftpUploadException {
Map<String, byte[]> fileByteArrays = new HashMap<>();
Session session = null;
Channel channel = null;
ChannelSftp channelSftp = null;
try {
JSch jsch = new JSch();
// 1. 添加身份认证信息 (密码或密钥)
if (sshKeyPath != null && !sshKeyPath.trim().isEmpty()) {
// 使用 SSH Key 认证
File sshKeyFile = new File(sshKeyPath);
if (!sshKeyFile.exists() || !sshKeyFile.isFile()) {
throw new SftpUploadException("SSH Key 文件不存在或不是一个有效文件: " + sshKeyPath);
}
jsch.addIdentity(sshKeyPath);
System.out.println("使用 SSH Key 认证: " + sshKeyPath);
} else if (password == null || password.trim().isEmpty()) {
// 如果没有提供密码或密钥路径则认证信息不全
throw new SftpUploadException("必须提供密码或 SSH Key 路径进行 SFTP 认证.");
}
// 2. 获取 Session
int port = (remotePort != null && remotePort > 0) ? remotePort : DEFAULT_SFTP_PORT;
session = jsch.getSession(username, remoteHost, port);
System.out.println("尝试连接 SFTP 服务器: " + username + "@" + remoteHost + ":" + port);
// 如果使用密码认证且提供了密码
if (password != null && !password.trim().isEmpty() && (sshKeyPath == null || sshKeyPath.trim().isEmpty())) {
session.setPassword(password);
System.out.println("使用密码认证.");
}
// 设置连接不进行主机密钥检查
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
// 3. 连接 Session
session.connect();
System.out.println("SFTP Session 连接成功.");
// 4. 打开 SFTP Channel
channel = session.openChannel("sftp");
channel.connect();
System.out.println("SFTP Channel 打开成功.");
channelSftp = (ChannelSftp) channel;
// 遍历远程文件路径并下载
for (String remoteFile : remoteFilePaths) {
String chmodDir = remoteFile.substring(0, remoteFile.indexOf("default"));
log.info("分配权限路径,{}", chmodDir);
// 切换目录并列出内容用于调试
sudoChmodORwx(session, chmodDir, password);
String fileName = new File(remoteFile).getName();
try (InputStream in = channelSftp.get(remoteFile);
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
fileByteArrays.put(fileName, out.toByteArray()); // 添加文件名和二进制数据
} catch (Exception e) {
throw new SftpUploadException("文件读取失败: " + remoteFile, e);
}
}
return fileByteArrays;
} catch (JSchException e) {
throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
} catch (SftpUploadException e) {
// 重新抛出自定义异常
throw e;
} catch (Exception e) {
// 捕获其他未知异常
throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
} finally {
// 关闭资源 (确保在任何情况下都关闭)
if (channelSftp != null) {
channelSftp.disconnect();
System.out.println("SFTP Channel 已断开.");
}
if (channel != null) {
channel.disconnect();
System.out.println("SFTP Channel 资源已释放.");
}
if (session != null) {
session.disconnect();
System.out.println("SFTP Session 已断开.");
}
}
}
/** /**
* 从远程服务器下载文件并返回文件输入流并设置AflInfo对象 * 从远程服务器下载文件并返回文件输入流并设置AflInfo对象
* *

View File

@ -0,0 +1,32 @@
package cd.casic.ci.process.util.vulInfo;
import cd.casic.ci.process.dto.req.vulInfo.VulInfoDto;
import cd.casic.ci.process.process.dataObject.volumnInfo.VulInfo;
import org.springframework.beans.BeanUtils;
public class BeanUtil {
public static VulInfoDto ConvertVulInfoToVulInfoDto(VulInfo vulInfo) {
VulInfoDto vulInfoDto = new VulInfoDto();
BeanUtils.copyProperties(vulInfo,vulInfoDto);
if (vulInfo.getIsOpenSource()){
vulInfoDto.setIsOpenSource("开源");
}else {
vulInfoDto.setIsOpenSource("非开源");
}
vulInfoDto.setSeverity(
(vulInfo.getSeverity() == 3)? "" :
((vulInfo.getSeverity() == 2)? "" :
((vulInfo.getSeverity() == 1)? "" : null))
);
if (vulInfo.getLevel()){
vulInfoDto.setIsOpenSource("可利用");
}else {
vulInfoDto.setIsOpenSource("不可利用");
}
vulInfoDto.setTargetType(vulInfo.getTargetType());
return vulInfoDto;
}
}

View File

@ -0,0 +1,12 @@
package cd.casic.ci.process.util.vulInfo;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class TimeUtils {
public static String ConvertTimeToStringUtils() {
return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
}