Compare commits
No commits in common. "91c2d0874590f26e3ebeed1115070714e344f15e" and "046c03ec68769a471d9ecbd1bb7e7f39283bc4d4" have entirely different histories.
91c2d08745
...
046c03ec68
@ -1,73 +0,0 @@
|
|||||||
package cd.casic.ci.api;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
|
|
||||||
import cd.casic.ci.process.dto.resp.aflManager.SeedsCountResp;
|
|
||||||
import cd.casic.ci.process.process.service.aflManager.AflCrashesInfoService;
|
|
||||||
import cd.casic.ci.process.process.service.aflManager.AflInfoService;
|
|
||||||
import cd.casic.ci.process.process.service.aflManager.AflSeedInfoService;
|
|
||||||
import cd.casic.ci.process.util.SftpUploadUtil;
|
|
||||||
import cd.casic.framework.commons.pojo.CommonResult;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoController
|
|
||||||
* @Date: 2025/7/1 9:03
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/aflManager")
|
|
||||||
public class AflManagerController {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AflInfoService aflInfoService;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AflCrashesInfoService aflCrashesInfoService;
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private AflSeedInfoService aflSeedInfoService;
|
|
||||||
|
|
||||||
@PostMapping(path="/saveAflInfo")
|
|
||||||
public CommonResult<String> saveAflInfo(@RequestBody @Valid AflManagerReq req) throws SftpUploadUtil.SftpUploadException {
|
|
||||||
|
|
||||||
String aflInfoId = aflInfoService.saveAflInfo(req);
|
|
||||||
|
|
||||||
return CommonResult.success(aflInfoId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping(path="/saveAflCrashesInfo")
|
|
||||||
public CommonResult<Void> saveAflCrashesInfo(@RequestBody @Valid AflManagerReq req) throws SftpUploadUtil.SftpUploadException {
|
|
||||||
|
|
||||||
aflCrashesInfoService.saveAflCrashesInfo(req);
|
|
||||||
|
|
||||||
return CommonResult.success();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping(path="/findSeedsCount")
|
|
||||||
public CommonResult<List<SeedsCountResp>> findSeedsCount(@RequestBody @Valid AflManagerReq req) throws SftpUploadUtil.SftpUploadException {
|
|
||||||
|
|
||||||
List<SeedsCountResp> seedsCount = aflSeedInfoService.findSeedsCount(req);
|
|
||||||
|
|
||||||
return CommonResult.success(seedsCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@PostMapping(path="/saveAflSeedInfo")
|
|
||||||
public CommonResult<Void> saveAflSeedInfo(@RequestBody @Valid AflManagerReq req) throws SftpUploadUtil.SftpUploadException {
|
|
||||||
|
|
||||||
aflSeedInfoService.saveAflSeedInfo(req);
|
|
||||||
|
|
||||||
return CommonResult.success();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package cd.casic.ci.process.dto.req.aflManager;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class AflManagerReq {
|
|
||||||
private String pipelineId;
|
|
||||||
private String pipelineHistoryId;
|
|
||||||
private String taskId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主机地址
|
|
||||||
*/
|
|
||||||
private String machineHost;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 描述
|
|
||||||
*/
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 机器状态
|
|
||||||
*/
|
|
||||||
private String machineStatus;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 登录用户名
|
|
||||||
*/
|
|
||||||
private String username;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SSH 端口
|
|
||||||
*/
|
|
||||||
private String sshPort;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 密码
|
|
||||||
*/
|
|
||||||
private String password;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 秘钥 ID
|
|
||||||
*/
|
|
||||||
private Long secretKeyId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 代理 ID
|
|
||||||
*/
|
|
||||||
private Long proxyId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 认证类型编码
|
|
||||||
*/
|
|
||||||
private Integer authenticationTypeCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 认证类型
|
|
||||||
*/
|
|
||||||
private String authType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 操作系统
|
|
||||||
*/
|
|
||||||
private String osSystem;
|
|
||||||
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package cd.casic.ci.process.dto.resp.aflManager;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @ClassName ReportResp
|
|
||||||
* @Author hopeli
|
|
||||||
* @Date 2025/5/10 10:53
|
|
||||||
* @Version 1.0
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class SeedsCountResp {
|
|
||||||
private Integer repValue;
|
|
||||||
|
|
||||||
private Integer minutesDifference;
|
|
||||||
|
|
||||||
private String seedId;
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dao.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflCrashesInfo;
|
|
||||||
import cd.casic.framework.mybatis.core.mapper.BaseMapperX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoDao
|
|
||||||
* @Date: 2025/5/13 14:39
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflCrashesInfoDao extends BaseMapperX<AflCrashesInfo> {
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dao.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflFileChunk;
|
|
||||||
import cd.casic.framework.mybatis.core.mapper.BaseMapperX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoDao
|
|
||||||
* @Date: 2025/5/13 14:39
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflFileChunkDao extends BaseMapperX<AflFileChunk> {
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dao.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflInfo;
|
|
||||||
import cd.casic.framework.mybatis.core.mapper.BaseMapperX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoDao
|
|
||||||
* @Date: 2025/5/13 14:39
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflInfoDao extends BaseMapperX<AflInfo> {
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dao.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflSeedInfo;
|
|
||||||
import cd.casic.framework.mybatis.core.mapper.BaseMapperX;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoDao
|
|
||||||
* @Date: 2025/5/13 14:39
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflSeedInfoDao extends BaseMapperX<AflSeedInfo> {
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dataObject.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
@TableName("afl_crashes_info")
|
|
||||||
public class AflCrashesInfo extends PipBaseElement {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 流水线id
|
|
||||||
*/
|
|
||||||
private String pipelineId;
|
|
||||||
|
|
||||||
private String pipelineHistoryId;
|
|
||||||
private String taskId;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件地址路径
|
|
||||||
*/
|
|
||||||
private String filePath;
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dataObject.aflManager;
|
|
||||||
|
|
||||||
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 FileChunk
|
|
||||||
* @Date: 2025/7/1 16:03
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("afl_file_chunk")
|
|
||||||
public class AflFileChunk extends PipBaseElement {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件唯一标识 ID
|
|
||||||
*/
|
|
||||||
private String contentId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分片索引,从0开始计数
|
|
||||||
*/
|
|
||||||
private Integer chunkIndex;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 总分片数量
|
|
||||||
*/
|
|
||||||
private Integer chunksSum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分片内容(二进制数据)
|
|
||||||
*/
|
|
||||||
private byte[] chunkContent;
|
|
||||||
}
|
|
@ -1,270 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dataObject.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
@TableName("afl_info")
|
|
||||||
public class AflInfo extends PipBaseElement {
|
|
||||||
private String pipelineId;
|
|
||||||
private String pipelineHistoryId;
|
|
||||||
private String taskId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模糊测试进程的开始时间
|
|
||||||
*/
|
|
||||||
private String startTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最后一次更新时间戳
|
|
||||||
*/
|
|
||||||
private String lastUpdate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 总运行时间
|
|
||||||
*/
|
|
||||||
private String runTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模糊器的进程 ID
|
|
||||||
*/
|
|
||||||
private String fuzzerPid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 已完成的循环次数
|
|
||||||
*/
|
|
||||||
private String cyclesDone;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 未发现新内容的循环次数
|
|
||||||
*/
|
|
||||||
private String cyclesWoFinds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 未发现新内容的时间
|
|
||||||
*/
|
|
||||||
private String timeWoFinds;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模糊测试所用时间
|
|
||||||
*/
|
|
||||||
private String fuzzTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校准时间
|
|
||||||
*/
|
|
||||||
private String calibrationTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 比较日志时间
|
|
||||||
*/
|
|
||||||
private String cmplogTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步时间
|
|
||||||
*/
|
|
||||||
private String syncTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 裁剪时间
|
|
||||||
*/
|
|
||||||
private String trimTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 已执行的次数
|
|
||||||
*/
|
|
||||||
private String execsDone;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 每秒执行次数
|
|
||||||
*/
|
|
||||||
private String execsPerSec;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上一分钟内的每秒执行次数
|
|
||||||
*/
|
|
||||||
private String execsPsLastMin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 语料库中的条目数量
|
|
||||||
*/
|
|
||||||
private String corpusCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 语料库中被优先选择的条目
|
|
||||||
*/
|
|
||||||
private String corpusFavored;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 语料库中发现的条目
|
|
||||||
*/
|
|
||||||
private String corpusFound;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导入到语料库的条目
|
|
||||||
*/
|
|
||||||
private String corpusImported;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 语料库中的可变条目
|
|
||||||
*/
|
|
||||||
private String corpusVariable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 达到的最大深度
|
|
||||||
*/
|
|
||||||
private String maxDepth;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 当前处理的条目索引
|
|
||||||
*/
|
|
||||||
private String curItem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 待处理的优先条目数
|
|
||||||
*/
|
|
||||||
private String pendingFavs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 总待处理条目数
|
|
||||||
*/
|
|
||||||
private String pendingTotal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 稳定性百分比
|
|
||||||
*/
|
|
||||||
private String stability;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 位图覆盖率百分比
|
|
||||||
*/
|
|
||||||
private String bitmapCvg;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存的崩溃次数
|
|
||||||
*/
|
|
||||||
private String savedCrashes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存的挂起次数
|
|
||||||
*/
|
|
||||||
private String savedHangs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 总超时次数
|
|
||||||
*/
|
|
||||||
private String totalTmout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上次发现的时间戳
|
|
||||||
*/
|
|
||||||
private String lastFind;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上次崩溃的时间戳
|
|
||||||
*/
|
|
||||||
private String lastCrash;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上次挂起的时间戳
|
|
||||||
*/
|
|
||||||
private String lastHang;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自上次崩溃以来的执行次数
|
|
||||||
*/
|
|
||||||
private String execsSinceCrash;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行超时值
|
|
||||||
*/
|
|
||||||
private String execTimeout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最慢执行时间(毫秒)
|
|
||||||
*/
|
|
||||||
private String slowestExecMs;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 峰值 RSS 内存使用量(MB)
|
|
||||||
*/
|
|
||||||
private String peakRssMb;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CPU 亲和性设置
|
|
||||||
*/
|
|
||||||
private String cpuAffinity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发现的边数(edges)
|
|
||||||
*/
|
|
||||||
private String edgesFound;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 总可用边数(edges)
|
|
||||||
*/
|
|
||||||
private String totalEdges;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 可变字节计数
|
|
||||||
*/
|
|
||||||
private String varByteCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Havoc 扩展因子
|
|
||||||
*/
|
|
||||||
private String havocExpansion;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自动词典条目数
|
|
||||||
*/
|
|
||||||
private String autoDictEntries;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试缓存大小
|
|
||||||
*/
|
|
||||||
private String testcacheSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试缓存条目数
|
|
||||||
*/
|
|
||||||
private String testcacheCount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试缓存驱逐次数
|
|
||||||
*/
|
|
||||||
private String testcacheEvict;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 目标程序 banner
|
|
||||||
*/
|
|
||||||
private String aflBanner;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 使用的 AFL 版本
|
|
||||||
*/
|
|
||||||
private String aflVersion;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 目标模式配置
|
|
||||||
*/
|
|
||||||
private String targetMode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 执行使用的命令行
|
|
||||||
*/
|
|
||||||
private String commandLine;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件二进制内容
|
|
||||||
*/
|
|
||||||
private String contentId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件地址路径
|
|
||||||
*/
|
|
||||||
private String filePath;
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.dataObject.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.base.PipBaseElement;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@Data
|
|
||||||
@TableName("afl_seed_info")
|
|
||||||
public class AflSeedInfo extends PipBaseElement {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 模糊测试进程的开始时间
|
|
||||||
*/
|
|
||||||
private String seedId;
|
|
||||||
|
|
||||||
private String pipelineId;
|
|
||||||
private String pipelineHistoryId;
|
|
||||||
private String taskId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 最后一次更新时间戳
|
|
||||||
*/
|
|
||||||
private String seedAmount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 文件地址路径
|
|
||||||
*/
|
|
||||||
private String filePath;
|
|
||||||
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflCrashesInfo;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoService
|
|
||||||
* @Date: 2025/5/17 10:20
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflCrashesInfoService extends IService<AflCrashesInfo> {
|
|
||||||
|
|
||||||
void saveAflCrashesInfo(@Valid AflManagerReq req);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflFileChunk;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName FileChunkRepository
|
|
||||||
* @Date: 2025/7/1 16:03
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflFileChunkService extends IService<AflFileChunk> {
|
|
||||||
byte[] getFileById(String contentId) throws IOException;
|
|
||||||
|
|
||||||
String saveFileChunk(InputStream file);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflInfo;
|
|
||||||
import cd.casic.ci.process.util.SftpUploadUtil;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoService
|
|
||||||
* @Date: 2025/5/17 10:20
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflInfoService extends IService<AflInfo> {
|
|
||||||
|
|
||||||
String saveAflInfo(@Valid AflManagerReq req) throws SftpUploadUtil.SftpUploadException;
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
|
|
||||||
import cd.casic.ci.process.dto.resp.aflManager.SeedsCountResp;
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflSeedInfo;
|
|
||||||
import cd.casic.ci.process.util.SftpUploadUtil;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
|
||||||
import jakarta.validation.Valid;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName AflInfoService
|
|
||||||
* @Date: 2025/5/17 10:20
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
public interface AflSeedInfoService extends IService<AflSeedInfo> {
|
|
||||||
|
|
||||||
List<SeedsCountResp> findSeedsCount(@Valid AflManagerReq req) throws SftpUploadUtil.SftpUploadException;
|
|
||||||
|
|
||||||
void saveAflSeedInfo(@Valid AflManagerReq req);
|
|
||||||
}
|
|
@ -1,66 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager.impl;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
|
|
||||||
import cd.casic.ci.process.process.dao.aflManager.AflCrashesInfoDao;
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflCrashesInfo;
|
|
||||||
import cd.casic.ci.process.process.service.aflManager.AflCrashesInfoService;
|
|
||||||
import cd.casic.ci.process.util.SftpUploadUtil;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
import org.springframework.util.CollectionUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName TargetVersionServiceImpl
|
|
||||||
* @Date: 2025/5/17 15:20
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@Slf4j
|
|
||||||
public class AflCrashesInfoServiceImpl extends ServiceImpl<AflCrashesInfoDao, AflCrashesInfo> implements AflCrashesInfoService {
|
|
||||||
|
|
||||||
private static final String remoteFilePath = "/home/casic/706/yunqi/";
|
|
||||||
private static final String crashesFilePath = "/ai_afl/default/crashes/";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveAflCrashesInfo(AflManagerReq req) {
|
|
||||||
try {
|
|
||||||
// 步骤1:列出源目录下的所有文件
|
|
||||||
List<String> files = SftpUploadUtil.listFilesInRemoteDirectory(
|
|
||||||
req.getMachineHost(), Integer.parseInt(req.getSshPort()), req.getUsername(), req.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);
|
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(files)) {
|
|
||||||
// 步骤2:批量复制文件到目标目录
|
|
||||||
List<String> copiedFiles = SftpUploadUtil.copyRemoteFile(
|
|
||||||
req.getMachineHost(), Integer.parseInt(req.getSshPort()), req.getUsername(), req.getPassword(), null, files, remoteFilePath + "PIP_" + req.getPipelineId() + "/crashes_result/");
|
|
||||||
System.out.println("共复制 " + copiedFiles.size() + " 个文件");
|
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(copiedFiles)){
|
|
||||||
List<AflCrashesInfo> aflCrashesInfos = new ArrayList<>(0);
|
|
||||||
//新增数据到crashes表中
|
|
||||||
copiedFiles.forEach(o->{
|
|
||||||
AflCrashesInfo aflCrashesInfo = new AflCrashesInfo();
|
|
||||||
aflCrashesInfo.setFilePath(o);
|
|
||||||
aflCrashesInfo.setPipelineId(req.getPipelineId());
|
|
||||||
aflCrashesInfo.setTaskId(req.getTaskId());
|
|
||||||
aflCrashesInfo.setPipelineHistoryId(req.getPipelineHistoryId());
|
|
||||||
aflCrashesInfos.add(aflCrashesInfo);
|
|
||||||
});
|
|
||||||
baseMapper.insertBatch(aflCrashesInfos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 步骤3:删除旧目录
|
|
||||||
SftpUploadUtil.clearRemoteDirectoryFiles(req.getMachineHost(), Integer.parseInt(req.getSshPort()), req.getUsername(), req.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);
|
|
||||||
System.out.println("crashes目录下文件已删除: " + remoteFilePath + "PIP_" + req.getPipelineId() + crashesFilePath);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager.impl;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dao.aflManager.AflFileChunkDao;
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflFileChunk;
|
|
||||||
import cd.casic.ci.process.process.service.aflManager.AflFileChunkService;
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
||||||
import de.danielbechler.util.Collections;
|
|
||||||
import jakarta.annotation.Resource;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName TargetVersionServiceImpl
|
|
||||||
* @Date: 2025/5/17 15:20
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@Slf4j
|
|
||||||
public class AflFileChunkServiceImpl extends ServiceImpl<AflFileChunkDao, AflFileChunk> implements AflFileChunkService {
|
|
||||||
@Resource
|
|
||||||
private AflFileChunkDao aflFileChunkDao;
|
|
||||||
|
|
||||||
// 每个分片大小为 2GB
|
|
||||||
private static final Long CHUNK_SIZE = 1024 * 1024 * 1024 * 2L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public byte[] getFileById(String contentId) throws IOException {
|
|
||||||
QueryWrapper<AflFileChunk> wrapper = new QueryWrapper<>();
|
|
||||||
wrapper.eq("content_id", contentId);
|
|
||||||
List<AflFileChunk> chunks = aflFileChunkDao.selectList(wrapper);
|
|
||||||
if (chunks == null || chunks.isEmpty()) {
|
|
||||||
return new byte[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
int totalLength = chunks.stream()
|
|
||||||
.mapToInt(chunk -> chunk.getChunkContent() != null ? chunk.getChunkContent().length : 0)
|
|
||||||
.sum();
|
|
||||||
|
|
||||||
byte[] result = new byte[totalLength];
|
|
||||||
int position = 0;
|
|
||||||
|
|
||||||
for (AflFileChunk chunk : chunks) {
|
|
||||||
byte[] data = chunk.getChunkContent();
|
|
||||||
if (data != null) {
|
|
||||||
System.arraycopy(data, 0, result, position, data.length);
|
|
||||||
position += data.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String saveFileChunk(InputStream file) {
|
|
||||||
if (file == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String contentId = UUID.randomUUID().toString();
|
|
||||||
|
|
||||||
try {
|
|
||||||
byte[] buffer = new byte[CHUNK_SIZE.intValue()];
|
|
||||||
int bytesRead;
|
|
||||||
int chunkIndex = 0;
|
|
||||||
|
|
||||||
// 第一次遍历:读取所有分片并保存
|
|
||||||
while ((bytesRead = file.read(buffer)) != -1) {
|
|
||||||
byte[] chunkData = new byte[bytesRead]; // 实际读取的大小
|
|
||||||
System.arraycopy(buffer, 0, chunkData, 0, bytesRead);
|
|
||||||
|
|
||||||
AflFileChunk chunk = new AflFileChunk();
|
|
||||||
chunk.setContentId(contentId);
|
|
||||||
chunk.setChunkIndex(chunkIndex++);
|
|
||||||
chunk.setChunkContent(chunkData);
|
|
||||||
|
|
||||||
this.baseMapper.insert(chunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第二次遍历:更新每个分片的总分片数
|
|
||||||
QueryWrapper<AflFileChunk> wrapper = new QueryWrapper<>();
|
|
||||||
wrapper.eq("content_id", contentId);
|
|
||||||
List<AflFileChunk> aflFileChunks = baseMapper.selectList(wrapper);
|
|
||||||
if (!Collections.isEmpty(aflFileChunks)){
|
|
||||||
aflFileChunks.forEach(o->{
|
|
||||||
o.setChunksSum(aflFileChunks.size());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
baseMapper.updateBatch(aflFileChunks);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("文件分片上传失败", e);
|
|
||||||
throw new RuntimeException("文件上传失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
return contentId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager.impl;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
|
|
||||||
import cd.casic.ci.process.process.dao.aflManager.AflInfoDao;
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflInfo;
|
|
||||||
import cd.casic.ci.process.process.dataObject.resource.PipResourceMachine;
|
|
||||||
import cd.casic.ci.process.process.service.aflManager.AflInfoService;
|
|
||||||
import cd.casic.ci.process.util.SftpUploadUtil;
|
|
||||||
import cd.casic.framework.commons.exception.ServiceException;
|
|
||||||
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName TargetVersionServiceImpl
|
|
||||||
* @Date: 2025/5/17 15:20
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@Slf4j
|
|
||||||
public class AflInfoServiceImpl extends ServiceImpl<AflInfoDao, AflInfo> implements AflInfoService {
|
|
||||||
private static final String remoteFilePath = "/home/casic/706/yunqi/";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String saveAflInfo(AflManagerReq req) throws SftpUploadUtil.SftpUploadException {
|
|
||||||
if (StringUtils.isEmpty(req.getPipelineId())){
|
|
||||||
throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"pipelineId不能为空");
|
|
||||||
}
|
|
||||||
PipResourceMachine pipResourceMachine = new PipResourceMachine();
|
|
||||||
pipResourceMachine.setMachineHost(req.getMachineHost());
|
|
||||||
pipResourceMachine.setSshPort(req.getSshPort());
|
|
||||||
pipResourceMachine.setUsername(req.getUsername());
|
|
||||||
pipResourceMachine.setPassword(req.getPassword());
|
|
||||||
AflInfo aflInfo = SftpUploadUtil.downloadFileSftpForInputStreamAndSetAflInfo(req.getMachineHost(), Integer.parseInt(req.getSshPort()), req.getUsername(), req.getPassword(),null, remoteFilePath + "PIP_" + req.getPipelineId() + "/ai_afl/default/fuzzer_stats");
|
|
||||||
//创建一个新文件夹,将远程文件复制一份过去
|
|
||||||
List<String> sourceFilePaths = new ArrayList<>();
|
|
||||||
sourceFilePaths.add(remoteFilePath + "PIP_" + req.getPipelineId() + "/ai_afl/default/fuzzer_stats");
|
|
||||||
List<String> strings = SftpUploadUtil.copyRemoteFile(pipResourceMachine.getMachineHost(), Integer.parseInt(pipResourceMachine.getSshPort()), pipResourceMachine.getUsername(), pipResourceMachine.getPassword(), null, sourceFilePaths, remoteFilePath + "PIP_" + req.getPipelineId() + "/fuzzer/" + UUID.randomUUID() + "/");
|
|
||||||
|
|
||||||
aflInfo.setFilePath(strings.get(0));
|
|
||||||
aflInfo.setPipelineId(req.getPipelineId());
|
|
||||||
aflInfo.setTaskId(req.getTaskId());
|
|
||||||
aflInfo.setPipelineHistoryId(req.getPipelineHistoryId());
|
|
||||||
baseMapper.insert(aflInfo);
|
|
||||||
|
|
||||||
return aflInfo.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取远程文件的输入流并设值给aflInfo
|
|
||||||
*
|
|
||||||
* @param machineInfo 机器信息
|
|
||||||
* @param remoteFilePath 远程文件路径
|
|
||||||
* @return 文件输入流
|
|
||||||
*/
|
|
||||||
private AflInfo getFileStreamAndSetAflInfo(PipResourceMachine machineInfo, String remoteFilePath) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
return SftpUploadUtil.downloadFileSftpForInputStreamAndSetAflInfo(machineInfo.getMachineHost(), Integer.parseInt(machineInfo.getSshPort()), machineInfo.getUsername(), machineInfo.getPassword(),null, remoteFilePath);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("获取远程文件流失败: {}", e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
package cd.casic.ci.process.process.service.aflManager.impl;
|
|
||||||
|
|
||||||
import cd.casic.ci.process.dto.req.aflManager.AflManagerReq;
|
|
||||||
import cd.casic.ci.process.dto.resp.aflManager.SeedsCountResp;
|
|
||||||
import cd.casic.ci.process.engine.manager.RunContextManager;
|
|
||||||
import cd.casic.ci.process.engine.runContext.BaseRunContext;
|
|
||||||
import cd.casic.ci.process.process.dao.aflManager.AflSeedInfoDao;
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflSeedInfo;
|
|
||||||
import cd.casic.ci.process.process.service.aflManager.AflSeedInfoService;
|
|
||||||
import cd.casic.ci.process.util.SftpUploadUtil;
|
|
||||||
import cd.casic.framework.commons.exception.ServiceException;
|
|
||||||
import cd.casic.framework.commons.exception.enums.GlobalErrorCodeConstants;
|
|
||||||
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 java.time.Duration;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.ZonedDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author HopeLi
|
|
||||||
* @version v1.0
|
|
||||||
* @ClassName TargetVersionServiceImpl
|
|
||||||
* @Date: 2025/5/17 15:20
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
@Slf4j
|
|
||||||
public class AflSeedInfoServiceImpl extends ServiceImpl<AflSeedInfoDao, AflSeedInfo> implements AflSeedInfoService {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private RunContextManager runContextManager;
|
|
||||||
|
|
||||||
private static final String remoteFilePath = "/home/casic/706/yunqi/";
|
|
||||||
|
|
||||||
private static final String seedFilePath = "/ai_afl/default/queue/";
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<SeedsCountResp> findSeedsCount(AflManagerReq req) throws SftpUploadUtil.SftpUploadException {
|
|
||||||
List<SeedsCountResp> seedsCounts = new ArrayList<>(0);
|
|
||||||
|
|
||||||
if (StringUtils.isEmpty(req.getPipelineId())){
|
|
||||||
throw new ServiceException(GlobalErrorCodeConstants.INTERNAL_SERVER_ERROR.getCode(),"pipelineId不能为空");
|
|
||||||
}
|
|
||||||
List<String> resultList = SftpUploadUtil.findSeedCount(req.getMachineHost(), Integer.parseInt(req.getSshPort()), req.getUsername(), req.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + "/ai_afl/default/queue/");
|
|
||||||
|
|
||||||
//解析resultList
|
|
||||||
|
|
||||||
//todo 分区计算交由前端处理了,代码暂且注释掉,若前端无法处理则延续算法后端解决
|
|
||||||
//分区计算
|
|
||||||
//int timeWindow = 0;
|
|
||||||
for (String o : resultList) {
|
|
||||||
SeedsCountResp seedsCount = new SeedsCountResp();
|
|
||||||
|
|
||||||
String[] lineParts = o.split("\\s+创建时间:\\s+");
|
|
||||||
if (lineParts.length < 2) {
|
|
||||||
System.err.println("无效格式: " + o);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
String fileNamePart = lineParts[0]; // 文件信息部分
|
|
||||||
String createTime = lineParts[1]; // 创建时间
|
|
||||||
|
|
||||||
// 提取 rep 值
|
|
||||||
int repValue = 0;
|
|
||||||
String seedId = "";
|
|
||||||
for (String part : fileNamePart.split(",")) {
|
|
||||||
if (part.startsWith("rep:")) {
|
|
||||||
repValue = Integer.parseInt(part.substring(4));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (part.startsWith("id:")) {
|
|
||||||
seedId = part.substring(4);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析创建时间
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss[.SSSSSSSSS] XXX");
|
|
||||||
ZonedDateTime zonedDateTime = ZonedDateTime.parse(createTime, formatter);
|
|
||||||
LocalDateTime createDateTime = zonedDateTime.toLocalDateTime();
|
|
||||||
|
|
||||||
// 获取开始时间
|
|
||||||
BaseRunContext runContext = runContextManager.getContext(req.getTaskId());
|
|
||||||
LocalDateTime startTime = runContext.getStartTime();
|
|
||||||
|
|
||||||
// 计算时间差(分钟)
|
|
||||||
Duration duration = Duration.between(startTime, createDateTime);
|
|
||||||
int minutesDifference = Math.toIntExact(Math.abs(duration.toMinutes()));
|
|
||||||
|
|
||||||
seedsCount.setRepValue(repValue);
|
|
||||||
seedsCount.setMinutesDifference(minutesDifference);
|
|
||||||
seedsCount.setSeedId(seedId);
|
|
||||||
seedsCounts.add(seedsCount);
|
|
||||||
|
|
||||||
// if (minutesDifference < timeWindow){
|
|
||||||
// if (!ObjectUtils.isEmpty(seedsCounts)){
|
|
||||||
// //将数据统计到上一个时间窗口中
|
|
||||||
// SeedsCountResp seedsCountResp = seedsCounts.get(seedsCounts.size() - 1);
|
|
||||||
// Integer value = seedsCountResp.getRepValue();
|
|
||||||
// seedsCountResp.setRepValue(value + repValue);
|
|
||||||
// }
|
|
||||||
// } else if (minutesDifference > timeWindow){
|
|
||||||
// timeWindow = timeWindow + 100;
|
|
||||||
// seedsCount.setTimeWindow(timeWindow);
|
|
||||||
// seedsCount.setRepValue(repValue);
|
|
||||||
// seedsCounts.add(seedsCount);
|
|
||||||
//
|
|
||||||
// }else {
|
|
||||||
// if (!ObjectUtils.isEmpty(seedsCounts)){
|
|
||||||
// //将数据统计到上一个时间窗口中
|
|
||||||
// SeedsCountResp seedsCountResp = seedsCounts.get(seedsCounts.size() - 1);
|
|
||||||
// Integer value = seedsCountResp.getRepValue();
|
|
||||||
// seedsCountResp.setRepValue(value + repValue);
|
|
||||||
//
|
|
||||||
// //同时新增下一个100分钟统计的时间窗口数据
|
|
||||||
// seedsCount.setTimeWindow(timeWindow + 100);
|
|
||||||
// seedsCount.setRepValue(0);
|
|
||||||
// seedsCounts.add(seedsCount);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
return seedsCounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void saveAflSeedInfo(AflManagerReq req) {
|
|
||||||
try {
|
|
||||||
// 步骤1:列出源目录下的所有文件
|
|
||||||
List<String> files = SftpUploadUtil.listFilesInRemoteDirectory(
|
|
||||||
req.getMachineHost(), Integer.parseInt(req.getSshPort()), req.getUsername(), req.getPassword(), null, remoteFilePath + "PIP_" + req.getPipelineId() + seedFilePath);
|
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(files)) {
|
|
||||||
// 步骤2:批量复制文件到目标目录
|
|
||||||
List<String> copiedFiles = SftpUploadUtil.copyRemoteFile(
|
|
||||||
req.getMachineHost(), Integer.parseInt(req.getSshPort()), req.getUsername(), req.getPassword(), null, files, remoteFilePath + "PIP_" + req.getPipelineId() + "/seed_result/");
|
|
||||||
System.out.println("共复制 " + copiedFiles.size() + " 个文件");
|
|
||||||
|
|
||||||
if (!CollectionUtils.isEmpty(copiedFiles)){
|
|
||||||
List<AflSeedInfo> aflSeedInfos = new ArrayList<>(0);
|
|
||||||
//新增数据到crashes表中
|
|
||||||
copiedFiles.forEach(o->{
|
|
||||||
AflSeedInfo aflSeedInfo = new AflSeedInfo();
|
|
||||||
|
|
||||||
// 提取文件名部分(queue/之后的内容)
|
|
||||||
String fileNamePart = o.substring(o.indexOf("seed_result/") + 12);
|
|
||||||
|
|
||||||
// 使用正则匹配 id: 开头的内容
|
|
||||||
java.util.regex.Pattern patternId = java.util.regex.Pattern.compile("id:([^,]+)");
|
|
||||||
java.util.regex.Matcher matcherId = patternId.matcher(fileNamePart);
|
|
||||||
|
|
||||||
// 使用正则匹配 rep: 开头的内容
|
|
||||||
java.util.regex.Pattern patternRep = java.util.regex.Pattern.compile("rep:(\\d+)");
|
|
||||||
java.util.regex.Matcher matcherRep = patternRep.matcher(fileNamePart);
|
|
||||||
|
|
||||||
if (matcherRep.find()) {
|
|
||||||
String repValue = matcherRep.group(1);
|
|
||||||
aflSeedInfo.setSeedAmount(repValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matcherId.find()) {
|
|
||||||
String idValue = matcherId.group(1);
|
|
||||||
aflSeedInfo.setSeedId(idValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
aflSeedInfo.setFilePath(o);
|
|
||||||
aflSeedInfo.setTaskId(req.getTaskId());
|
|
||||||
aflSeedInfo.setPipelineId(req.getPipelineId());
|
|
||||||
aflSeedInfo.setPipelineHistoryId(req.getPipelineHistoryId());
|
|
||||||
|
|
||||||
aflSeedInfos.add(aflSeedInfo);
|
|
||||||
});
|
|
||||||
baseMapper.insertBatch(aflSeedInfos);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,5 @@
|
|||||||
package cd.casic.ci.process.util;
|
package cd.casic.ci.process.util;
|
||||||
|
|
||||||
import cd.casic.ci.process.process.dataObject.aflManager.AflInfo;
|
|
||||||
import cd.casic.framework.commons.exception.ServiceException;
|
import cd.casic.framework.commons.exception.ServiceException;
|
||||||
import com.amazonaws.util.IOUtils;
|
import com.amazonaws.util.IOUtils;
|
||||||
import com.jcraft.jsch.*;
|
import com.jcraft.jsch.*;
|
||||||
@ -9,7 +8,8 @@ import jakarta.servlet.http.HttpServletResponse;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
import java.util.Vector;
|
||||||
|
|
||||||
public class SftpUploadUtil {
|
public class SftpUploadUtil {
|
||||||
|
|
||||||
@ -506,694 +506,6 @@ public class SftpUploadUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 从远程服务器下载文件并返回文件输入流,并设置AflInfo对象
|
|
||||||
*
|
|
||||||
* @param remoteHost 远程服务器地址
|
|
||||||
* @param remotePort 远程服务器端口
|
|
||||||
* @param username 远程服务器用户名
|
|
||||||
* @param password 远程服务器密码
|
|
||||||
* @param sshKeyPath SSH密钥路径
|
|
||||||
* @param remoteFilePath 远程文件路径
|
|
||||||
* @return AflInfo对象,包含文件输入流和文件信息
|
|
||||||
* @throws SftpUploadException 如果下载文件或设置AflInfo对象时发生错误
|
|
||||||
*/
|
|
||||||
public static AflInfo downloadFileSftpForInputStreamAndSetAflInfo(String remoteHost,
|
|
||||||
Integer remotePort,
|
|
||||||
String username,
|
|
||||||
String password,
|
|
||||||
String sshKeyPath,
|
|
||||||
String remoteFilePath) throws SftpUploadException {
|
|
||||||
|
|
||||||
Session session = null;
|
|
||||||
Channel channel = null;
|
|
||||||
ChannelSftp channelSftp = null;
|
|
||||||
InputStream inputStream = 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 认证.");
|
|
||||||
}
|
|
||||||
// 如果提供了密码,将在 getSession 后设置,因为 getSession 需要用户名、主机和端口先建立连接意图
|
|
||||||
|
|
||||||
|
|
||||||
// 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("使用密码认证.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置连接不进行主机密钥检查 (生产环境不推荐,应该配置 known_hosts)
|
|
||||||
// 在实际应用中,应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 获取远程文件名和目录
|
|
||||||
String remoteDir = remoteFilePath.substring(0, remoteFilePath.lastIndexOf('/'));
|
|
||||||
String fileName = remoteFilePath.substring(remoteFilePath.lastIndexOf('/') + 1);
|
|
||||||
|
|
||||||
// 切换目录并列出内容用于调试
|
|
||||||
try {
|
|
||||||
channelSftp.cd(remoteDir);
|
|
||||||
} catch (SftpException e) {
|
|
||||||
throw new SftpUploadException("切换远程目录失败: " + remoteDir, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 列出目录内容用于调试
|
|
||||||
Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(".");
|
|
||||||
List<String> fileNames = entries.stream()
|
|
||||||
.map(entry -> entry.getFilename())
|
|
||||||
.filter(name -> !name.equals(".") && !name.equals(".."))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
System.out.println("远程目录中的文件列表: " + fileNames);
|
|
||||||
|
|
||||||
// 尝试获取文件流
|
|
||||||
try {
|
|
||||||
inputStream = channelSftp.get(fileName);
|
|
||||||
} catch (SftpException e) {
|
|
||||||
throw new SftpUploadException("获取远程文件流失败: " + fileName, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputStream == null) {
|
|
||||||
throw new SftpUploadException("无法获取远程文件输入流,请检查文件是否存在或被其他进程占用: " + fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
//转字符串
|
|
||||||
String fileContent = null;
|
|
||||||
try {
|
|
||||||
fileContent = IOUtils.toString(inputStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//解析 key=value 内容
|
|
||||||
Map<String, String> statsMap = new HashMap<>();
|
|
||||||
for (String line : fileContent.split("\\r?\\n")) {
|
|
||||||
if (line.contains("=")) {
|
|
||||||
String[] parts = line.split("=", 2);
|
|
||||||
statsMap.put(parts[0].trim(), parts[1].trim());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return setAflinfo(statsMap);
|
|
||||||
} catch (JSchException e) {
|
|
||||||
throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
|
|
||||||
} catch (SftpException e) {
|
|
||||||
throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
|
|
||||||
} catch (SftpUploadException e) {
|
|
||||||
// 重新抛出自定义异常
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 捕获其他未知异常
|
|
||||||
throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
|
|
||||||
} finally {
|
|
||||||
// 9. 关闭资源 (确保在任何情况下都关闭)
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("关闭本地文件流失败: " + e.getMessage());
|
|
||||||
e.printStackTrace(); // 打印堆栈以便调试
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 已断开.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static List<String> copyRemoteFile(String remoteHost,
|
|
||||||
Integer remotePort,
|
|
||||||
String username,
|
|
||||||
String password,
|
|
||||||
String sshKeyPath,
|
|
||||||
List<String> sourceFilePaths,
|
|
||||||
String targetDir) throws SftpUploadException {
|
|
||||||
|
|
||||||
Session session = null;
|
|
||||||
Channel channel = null;
|
|
||||||
ChannelSftp channelSftp = null;
|
|
||||||
InputStream inputStream = 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 认证.");
|
|
||||||
}
|
|
||||||
// 如果提供了密码,将在 getSession 后设置,因为 getSession 需要用户名、主机和端口先建立连接意图
|
|
||||||
|
|
||||||
|
|
||||||
// 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("使用密码认证.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置连接不进行主机密钥检查 (生产环境不推荐,应该配置 known_hosts)
|
|
||||||
// 在实际应用中,应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 创建目标目录(递归)
|
|
||||||
createRemoteDirRecursive(channelSftp, targetDir);
|
|
||||||
|
|
||||||
// 切换到目标目录
|
|
||||||
channelSftp.cd(targetDir);
|
|
||||||
|
|
||||||
List<String> copiedFiles = new ArrayList<>();
|
|
||||||
|
|
||||||
for (String sourceFilePath : sourceFilePaths) {
|
|
||||||
// 获取源文件名
|
|
||||||
String sourceFileName = sourceFilePath.substring(sourceFilePath.lastIndexOf('/') + 1);
|
|
||||||
|
|
||||||
// 切换到源目录
|
|
||||||
String sourceDir = sourceFilePath.substring(0, sourceFilePath.lastIndexOf('/'));
|
|
||||||
channelSftp.cd(sourceDir);
|
|
||||||
|
|
||||||
// 获取文件输入流
|
|
||||||
inputStream = channelSftp.get(sourceFileName);
|
|
||||||
|
|
||||||
// 复制文件
|
|
||||||
channelSftp.put(inputStream, sourceFileName);
|
|
||||||
|
|
||||||
System.out.println("文件复制成功: " + sourceFilePath + " -> " + targetDir + "/" + sourceFileName);
|
|
||||||
copiedFiles.add(targetDir + "/" + sourceFileName);
|
|
||||||
|
|
||||||
// 关闭输入流以释放资源
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("关闭本地文件流失败: " + e.getMessage());
|
|
||||||
e.printStackTrace(); // 打印堆栈以便调试
|
|
||||||
}
|
|
||||||
inputStream = null; // 重置 inputStream 以便下一个文件使用
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return copiedFiles;
|
|
||||||
} catch (JSchException e) {
|
|
||||||
throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
|
|
||||||
} catch (SftpException e) {
|
|
||||||
throw new SftpUploadException("SFTP 操作失败 (如切换目录或上传文件): " + e.getMessage(), e);
|
|
||||||
} catch (SftpUploadException e) {
|
|
||||||
// 重新抛出自定义异常
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// 捕获其他未知异常
|
|
||||||
throw new SftpUploadException("SFTP 上传过程中发生未知异常: " + e.getMessage(), e);
|
|
||||||
} finally {
|
|
||||||
// 9. 关闭资源 (确保在任何情况下都关闭)
|
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("关闭本地文件流失败: " + e.getMessage());
|
|
||||||
e.printStackTrace(); // 打印堆栈以便调试
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 已断开.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static List<String> listFilesInRemoteDirectory( String remoteHost,
|
|
||||||
Integer remotePort,
|
|
||||||
String username,
|
|
||||||
String password,
|
|
||||||
String sshKeyPath,
|
|
||||||
String remoteDir) throws SftpUploadException {
|
|
||||||
|
|
||||||
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 认证.");
|
|
||||||
}
|
|
||||||
// 如果提供了密码,将在 getSession 后设置,因为 getSession 需要用户名、主机和端口先建立连接意图
|
|
||||||
|
|
||||||
|
|
||||||
// 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("使用密码认证.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置连接不进行主机密钥检查 (生产环境不推荐,应该配置 known_hosts)
|
|
||||||
// 在实际应用中,应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 切换到目标目录
|
|
||||||
channelSftp.cd(remoteDir);
|
|
||||||
|
|
||||||
// 列出文件
|
|
||||||
Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(".");
|
|
||||||
List<String> fileNames = new ArrayList<>();
|
|
||||||
for (ChannelSftp.LsEntry entry : entries) {
|
|
||||||
String name = entry.getFilename();
|
|
||||||
if (!name.equals(".") && !name.equals("..")) {
|
|
||||||
fileNames.add(remoteDir + name); // 返回完整路径
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileNames;
|
|
||||||
} catch (JSchException e) {
|
|
||||||
throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
|
|
||||||
} catch (SftpException 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 已断开.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void clearRemoteDirectoryFiles( String remoteHost,
|
|
||||||
Integer remotePort,
|
|
||||||
String username,
|
|
||||||
String password,
|
|
||||||
String sshKeyPath,
|
|
||||||
String remoteDir) throws SftpUploadException {
|
|
||||||
|
|
||||||
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 认证.");
|
|
||||||
}
|
|
||||||
// 如果提供了密码,将在 getSession 后设置,因为 getSession 需要用户名、主机和端口先建立连接意图
|
|
||||||
|
|
||||||
|
|
||||||
// 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("使用密码认证.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置连接不进行主机密钥检查 (生产环境不推荐,应该配置 known_hosts)
|
|
||||||
// 在实际应用中,应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
|
|
||||||
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;
|
|
||||||
|
|
||||||
// 切换到目标目录
|
|
||||||
channelSftp.cd(remoteDir);
|
|
||||||
|
|
||||||
// 列出目录下的所有条目
|
|
||||||
Vector<ChannelSftp.LsEntry> lsEntriesVector = channelSftp.ls(".");
|
|
||||||
|
|
||||||
// 转换为 List 并过滤文件(排除 . 和 ..)
|
|
||||||
List<ChannelSftp.LsEntry> entries = new ArrayList<>();
|
|
||||||
for (ChannelSftp.LsEntry entry : lsEntriesVector) {
|
|
||||||
String name = entry.getFilename();
|
|
||||||
if (!name.equals(".") && !name.equals("..")) {
|
|
||||||
entries.add(entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 遍历并删除所有文件(不包括子目录)
|
|
||||||
for (ChannelSftp.LsEntry entry : entries) {
|
|
||||||
String name = entry.getFilename();
|
|
||||||
String fullPath = remoteDir + "/" + name;
|
|
||||||
|
|
||||||
// 检查是否是文件(非目录)
|
|
||||||
if (!thisDirectory(channelSftp, fullPath)) {
|
|
||||||
channelSftp.rm(fullPath); // 删除文件
|
|
||||||
System.out.println("已删除文件: " + fullPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (JSchException e) {
|
|
||||||
throw new SftpUploadException("SFTP 连接或认证失败: " + e.getMessage(), e);
|
|
||||||
} catch (SftpException 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 已断开.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static AflInfo setAflinfo(Map<String, String> statsMap) {
|
|
||||||
//映射到 AflInfo 实体
|
|
||||||
AflInfo aflInfo = new AflInfo();
|
|
||||||
|
|
||||||
aflInfo.setStartTime(statsMap.get("start_time"));
|
|
||||||
aflInfo.setLastUpdate(statsMap.get("last_update"));
|
|
||||||
aflInfo.setRunTime(statsMap.get("run_time"));
|
|
||||||
aflInfo.setFuzzerPid(statsMap.get("fuzzer_pid"));
|
|
||||||
aflInfo.setCyclesDone(statsMap.get("cycles_done"));
|
|
||||||
aflInfo.setCyclesWoFinds(statsMap.get("cycles_wo_finds"));
|
|
||||||
aflInfo.setTimeWoFinds(statsMap.get("time_wo_finds"));
|
|
||||||
aflInfo.setFuzzTime(statsMap.get("fuzz_time"));
|
|
||||||
aflInfo.setCalibrationTime(statsMap.get("calibration_time"));
|
|
||||||
aflInfo.setCmplogTime(statsMap.get("cmplog_time"));
|
|
||||||
aflInfo.setSyncTime(statsMap.get("sync_time"));
|
|
||||||
aflInfo.setTrimTime(statsMap.get("trim_time"));
|
|
||||||
aflInfo.setExecsDone(statsMap.get("execs_done"));
|
|
||||||
aflInfo.setExecsPerSec(statsMap.get("execs_per_sec"));
|
|
||||||
aflInfo.setExecsPsLastMin(statsMap.get("execs_ps_last_min"));
|
|
||||||
aflInfo.setCorpusCount(statsMap.get("corpus_count"));
|
|
||||||
aflInfo.setCorpusFavored(statsMap.get("corpus_favored"));
|
|
||||||
aflInfo.setCorpusFound(statsMap.get("corpus_found"));
|
|
||||||
aflInfo.setCorpusImported(statsMap.get("corpus_imported"));
|
|
||||||
aflInfo.setCorpusVariable(statsMap.get("corpus_variable"));
|
|
||||||
aflInfo.setMaxDepth(statsMap.get("max_depth"));
|
|
||||||
aflInfo.setCurItem(statsMap.get("cur_item"));
|
|
||||||
aflInfo.setPendingFavs(statsMap.get("pending_favs"));
|
|
||||||
aflInfo.setPendingTotal(statsMap.get("pending_total"));
|
|
||||||
aflInfo.setStability(statsMap.get("stability"));
|
|
||||||
aflInfo.setBitmapCvg(statsMap.get("bitmap_cvg"));
|
|
||||||
aflInfo.setSavedCrashes(statsMap.get("saved_crashes"));
|
|
||||||
aflInfo.setSavedHangs(statsMap.get("saved_hangs"));
|
|
||||||
aflInfo.setTotalTmout(statsMap.get("total_tmout"));
|
|
||||||
aflInfo.setLastFind(statsMap.get("last_find"));
|
|
||||||
aflInfo.setLastCrash(statsMap.get("last_crash"));
|
|
||||||
aflInfo.setLastHang(statsMap.get("last_hang"));
|
|
||||||
aflInfo.setExecsSinceCrash(statsMap.get("execs_since_crash"));
|
|
||||||
aflInfo.setExecTimeout(statsMap.get("exec_timeout"));
|
|
||||||
aflInfo.setSlowestExecMs(statsMap.get("slowest_exec_ms"));
|
|
||||||
aflInfo.setPeakRssMb(statsMap.get("peak_rss_mb"));
|
|
||||||
aflInfo.setCpuAffinity(statsMap.get("cpu_affinity"));
|
|
||||||
aflInfo.setEdgesFound(statsMap.get("edges_found"));
|
|
||||||
aflInfo.setTotalEdges(statsMap.get("total_edges"));
|
|
||||||
aflInfo.setVarByteCount(statsMap.get("var_byte_count"));
|
|
||||||
aflInfo.setHavocExpansion(statsMap.get("havoc_expansion"));
|
|
||||||
aflInfo.setAutoDictEntries(statsMap.get("auto_dict_entries"));
|
|
||||||
aflInfo.setTestcacheSize(statsMap.get("testcache_size"));
|
|
||||||
aflInfo.setTestcacheCount(statsMap.get("testcache_count"));
|
|
||||||
aflInfo.setTestcacheEvict(statsMap.get("testcache_evict"));
|
|
||||||
aflInfo.setAflBanner(statsMap.get("afl_banner"));
|
|
||||||
aflInfo.setAflVersion(statsMap.get("afl_version"));
|
|
||||||
aflInfo.setTargetMode(statsMap.get("target_mode"));
|
|
||||||
aflInfo.setCommandLine(statsMap.get("command_line"));
|
|
||||||
|
|
||||||
return aflInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static boolean thisDirectory(ChannelSftp channelSftp, String fullPath) {
|
|
||||||
try {
|
|
||||||
SftpATTRS attrs = channelSftp.lstat(fullPath);
|
|
||||||
return attrs.isDir();
|
|
||||||
} catch (SftpException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<String> findSeedCount(String remoteHost,
|
|
||||||
Integer remotePort,
|
|
||||||
String username,
|
|
||||||
String password,
|
|
||||||
String sshKeyPath,
|
|
||||||
String remoteDir) throws SftpUploadException {
|
|
||||||
|
|
||||||
Session session = 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 认证.");
|
|
||||||
}
|
|
||||||
// 如果提供了密码,将在 getSession 后设置,因为 getSession 需要用户名、主机和端口先建立连接意图
|
|
||||||
|
|
||||||
|
|
||||||
// 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("使用密码认证.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置连接不进行主机密钥检查 (生产环境不推荐,应该配置 known_hosts)
|
|
||||||
// 在实际应用中,应该引导用户信任主机密钥或提前将主机密钥加入 known_hosts
|
|
||||||
java.util.Properties config = new java.util.Properties();
|
|
||||||
config.put("StrictHostKeyChecking", "no"); // !!! 生产环境请谨慎使用或配置正确的主机密钥检查 !!!
|
|
||||||
session.setConfig(config);
|
|
||||||
|
|
||||||
// 3. 连接 Session
|
|
||||||
session.connect();
|
|
||||||
System.out.println("SFTP Session 连接成功.");
|
|
||||||
|
|
||||||
//切换目录
|
|
||||||
ChannelSftp sftpChannel = (ChannelSftp) session.openChannel("sftp");
|
|
||||||
sftpChannel.connect();
|
|
||||||
try {
|
|
||||||
sftpChannel.cd(remoteDir);
|
|
||||||
System.out.println("切换到远程目录: " + remoteDir);
|
|
||||||
} catch (SftpException e) {
|
|
||||||
System.err.println("切换目录失败: " + e.getMessage());
|
|
||||||
}
|
|
||||||
sftpChannel.disconnect();
|
|
||||||
|
|
||||||
// 执行命令打印输出
|
|
||||||
ChannelExec execChannel = (ChannelExec) session.openChannel("exec");
|
|
||||||
execChannel.setCommand("find . -maxdepth 1 -type f -exec stat -c \"%n 创建时间: %w\" {} \\;");
|
|
||||||
InputStream in = execChannel.getInputStream();
|
|
||||||
execChannel.connect();
|
|
||||||
|
|
||||||
byte[] tmp = new byte[1024];
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
while (in.available() > 0) {
|
|
||||||
int i = in.read(tmp, 0, 1024);
|
|
||||||
if (i < 0) break;
|
|
||||||
outputStream.write(tmp, 0, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (execChannel.isClosed()) {
|
|
||||||
if (in.available() > 0) continue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(1000);
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 转成字符串并按行拆分
|
|
||||||
String outputStr = outputStream.toString();
|
|
||||||
String[] lines = outputStr.split("\\r?\\n");
|
|
||||||
|
|
||||||
// 添加进 list
|
|
||||||
List<String> result = new ArrayList<>(Arrays.asList(lines));
|
|
||||||
execChannel.disconnect();
|
|
||||||
|
|
||||||
return result;
|
|
||||||
} 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 (session != null) {
|
|
||||||
session.disconnect();
|
|
||||||
System.out.println("SFTP Session 已断开.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 自定义异常类,用于封装上传过程中的错误
|
// 自定义异常类,用于封装上传过程中的错误
|
||||||
public static class SftpUploadException extends Exception {
|
public static class SftpUploadException extends Exception {
|
||||||
public SftpUploadException(String message) {
|
public SftpUploadException(String message) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user