|
|
|
let task_list = []
|
|
|
|
let cur_task = null //当前选择的task--用于修改缓存时使用
|
|
|
|
let cur_task_id = 0 //当前选择的cur_task_id
|
|
|
|
let ws = null
|
|
|
|
|
|
|
|
// 页面卸载时断开连接
|
|
|
|
window.addEventListener("beforeunload", function() {
|
|
|
|
if (ws) {
|
|
|
|
ws.close();
|
|
|
|
ws =null;
|
|
|
|
}
|
|
|
|
task_list = []
|
|
|
|
cur_task = null;
|
|
|
|
cur_task_id = 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
// 页面加载完成后调用接口获取任务列表数据
|
|
|
|
document.addEventListener("DOMContentLoaded", async () => {
|
|
|
|
//建立wbsocket
|
|
|
|
initWebSocket()
|
|
|
|
//获取左侧任务列表
|
|
|
|
getTasklist();
|
|
|
|
//当前选中的数据要清空
|
|
|
|
cur_task = null;
|
|
|
|
cur_task_id = 0;
|
|
|
|
//任务基本信息界面初始化
|
|
|
|
document.getElementById("detailTestTarget").textContent = "-";
|
|
|
|
document.getElementById("detailTestStatus").textContent = "-";
|
|
|
|
document.getElementById("detailSafeStatus").textContent = '-';
|
|
|
|
//单选按钮
|
|
|
|
set_radio_selection('testMode', 'auto');
|
|
|
|
setSetpBtnStatus();
|
|
|
|
//节点树界面初始化
|
|
|
|
update_select_node_data_show("-","-","-","-","-",false)
|
|
|
|
//单选按钮点击事件 ------ 是不是更规范的编程方式,应该把控件事件的添加都放页面加载完成后处理
|
|
|
|
const autoRadio = document.getElementById("autoMode");
|
|
|
|
const manualRadio = document.getElementById("manualMode");
|
|
|
|
autoRadio.addEventListener("click", () => updateTestMode(1));
|
|
|
|
manualRadio.addEventListener("click", () => updateTestMode(0));
|
|
|
|
|
|
|
|
//双击任务列表节点读取数据
|
|
|
|
searchInstructions();
|
|
|
|
searchVulnerabilities();
|
|
|
|
// renderTableRows(document.querySelector("#instrTable tbody"), []);
|
|
|
|
// renderTableRows(document.querySelector("#vulTable tbody"), []);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
//----------------------左侧任务列表-----------------------
|
|
|
|
function getstrsafeR(safeRank){
|
|
|
|
if(safeRank === 0){
|
|
|
|
safeR = "安全";
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
safeR = "存在风险";
|
|
|
|
}
|
|
|
|
return safeR;
|
|
|
|
}
|
|
|
|
function getstrTaskS(taskStatus){
|
|
|
|
if(taskStatus === 0){
|
|
|
|
taskS = "暂停中";
|
|
|
|
}else if(taskStatus === 1){
|
|
|
|
taskS = "执行中";
|
|
|
|
}else {
|
|
|
|
taskS = "已完成";
|
|
|
|
}
|
|
|
|
return taskS;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function getTasklist(){
|
|
|
|
try {
|
|
|
|
const res = await fetch("/api/task/getlist");
|
|
|
|
if (!res.ok) {
|
|
|
|
const errorData = await res.json();
|
|
|
|
throw new Error(errorData.error || `HTTP错误 ${res.status}`);
|
|
|
|
}
|
|
|
|
const data = await res.json();
|
|
|
|
task_list = data.tasks
|
|
|
|
const taskList = document.getElementById("taskList");
|
|
|
|
taskList.innerHTML = ""; // 清空“加载中”提示
|
|
|
|
// 遍历任务数组,生成任务项
|
|
|
|
task_list.forEach((task) => {
|
|
|
|
const taskItem = document.createElement("div");
|
|
|
|
taskItem.dataset.taskID =task.taskID //在taskItem添加数据属性,这是关联数据的第二种方法,selectedEl.dataset.taskId; 感觉比cur_task更好
|
|
|
|
taskItem.className = "task-item";
|
|
|
|
|
|
|
|
// 第一行:测试目标
|
|
|
|
const targetDiv = document.createElement("div");
|
|
|
|
targetDiv.className = "task-target";
|
|
|
|
targetDiv.textContent = task.testTarget;
|
|
|
|
taskItem.appendChild(targetDiv);
|
|
|
|
|
|
|
|
// 第二行:测试状态,带缩进
|
|
|
|
const statusDiv = document.createElement("div");
|
|
|
|
statusDiv.className = "task-status";
|
|
|
|
let safeR = getstrsafeR(task.safeRank);
|
|
|
|
let taskS = getstrTaskS(task.taskStatus);
|
|
|
|
statusDiv.textContent = `${taskS}-${safeR}`;
|
|
|
|
taskItem.appendChild(statusDiv);
|
|
|
|
|
|
|
|
// 可绑定点击事件:点击任务项更新右侧详情
|
|
|
|
taskItem.addEventListener("click", () => {
|
|
|
|
// 取消所有任务项的选中状态
|
|
|
|
document.querySelectorAll(".task-item.selected").forEach(item => {
|
|
|
|
item.classList.remove("selected");
|
|
|
|
});
|
|
|
|
// 给当前点击的任务项添加选中状态
|
|
|
|
taskItem.classList.add("selected");
|
|
|
|
//执行业务代码 --参数当前选中task的数据
|
|
|
|
cur_task = task;
|
|
|
|
selected_task_item()
|
|
|
|
});
|
|
|
|
taskList.appendChild(taskItem);
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
console.error("加载任务列表出错:", error);
|
|
|
|
document.getElementById("taskList").innerHTML = "<p>加载任务列表失败!</p>";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//选中tasklist--更新界面数据
|
|
|
|
function selected_task_item(){
|
|
|
|
if(cur_task_id === cur_task.taskID) return;
|
|
|
|
cur_task_id = cur_task.taskID;
|
|
|
|
//按钮状态更新
|
|
|
|
actionButton = document.getElementById("actionButton");
|
|
|
|
if(cur_task.taskStatus === 0){
|
|
|
|
actionButton.textContent = "继续";
|
|
|
|
}else if(cur_task.taskStatus === 1){
|
|
|
|
actionButton.textContent = "暂停";
|
|
|
|
}else {
|
|
|
|
}
|
|
|
|
//基本信息
|
|
|
|
let safeR = getstrsafeR(cur_task.safeRank);
|
|
|
|
let taskS = getstrTaskS(cur_task.taskStatus);
|
|
|
|
document.getElementById("detailTestTarget").textContent = cur_task.testTarget;
|
|
|
|
document.getElementById("detailTestStatus").textContent = taskS;
|
|
|
|
document.getElementById("detailSafeStatus").textContent = safeR;
|
|
|
|
//单选按钮
|
|
|
|
if(cur_task.workType === 0){ //人工
|
|
|
|
set_radio_selection('testMode', 'manual');
|
|
|
|
}else { //1-自动
|
|
|
|
set_radio_selection('testMode', 'auto');
|
|
|
|
}
|
|
|
|
//更新单步按钮
|
|
|
|
setSetpBtnStatus()
|
|
|
|
//加载任务其他信息--node_tree.js
|
|
|
|
loadNodeTree(cur_task_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------任务基本信息区域--------------------
|
|
|
|
//单选按钮--测试模式修改
|
|
|
|
async function updateTestMode(mode){ //0-人工,1-自动
|
|
|
|
if(cur_task){
|
|
|
|
if(cur_task.workType !== mode){
|
|
|
|
if(cur_task.taskStatus === 1){
|
|
|
|
alert("执行状态,不允许修改测试模式!");
|
|
|
|
if( cur_task.workType === 0){ //人工
|
|
|
|
set_radio_selection('testMode', 'manual');
|
|
|
|
}else { //1-自动
|
|
|
|
set_radio_selection('testMode', 'auto');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {//不是执行状态,可以修改测试模式
|
|
|
|
try {
|
|
|
|
const res = await fetch("/api/task/taskworktype", {
|
|
|
|
method: "POST",
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
body: JSON.stringify({ cur_task_id,mode }), //task_id:task_id
|
|
|
|
});
|
|
|
|
// 新增状态码校验
|
|
|
|
if (!res.ok) {
|
|
|
|
const errorData = await res.json();
|
|
|
|
throw new Error(errorData.error || `HTTP错误 ${res.status}`);
|
|
|
|
}
|
|
|
|
const data = await res.json();
|
|
|
|
bsuccess = data.bsuccess;
|
|
|
|
if(bsuccess){
|
|
|
|
cur_task.workType = mode; //更新前端缓存
|
|
|
|
//更新单步按钮状态
|
|
|
|
setSetpBtnStatus()
|
|
|
|
}else {
|
|
|
|
alert("修改测试模失败!")
|
|
|
|
if( cur_task.workType === 0){ //人工 修改失败还原单选按钮点击状态
|
|
|
|
set_radio_selection('testMode', 'manual');
|
|
|
|
}else { //1-自动
|
|
|
|
set_radio_selection('testMode', 'auto');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
console.error("控制任务状态异常:", error);
|
|
|
|
alert("控制任务状态异常:",error);
|
|
|
|
if( cur_task.workType === 0){ //人工 修改失败还原单选按钮点击状态
|
|
|
|
set_radio_selection('testMode', 'manual');
|
|
|
|
}else { //1-自动
|
|
|
|
set_radio_selection('testMode', 'auto');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//点击暂停/继续按钮
|
|
|
|
document.getElementById("actionButton").addEventListener("click",() => {
|
|
|
|
controlTask();
|
|
|
|
});
|
|
|
|
async function controlTask(){
|
|
|
|
if(cur_task_id === 0){
|
|
|
|
alert("请先选择一个任务!")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
const res = await fetch("/api/task/taskcontrol", {
|
|
|
|
method: "POST",
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
body: JSON.stringify({ cur_task_id }), //task_id:task_id
|
|
|
|
});
|
|
|
|
// 新增状态码校验
|
|
|
|
if (!res.ok) {
|
|
|
|
const errorData = await res.json();
|
|
|
|
throw new Error(errorData.error || `HTTP错误 ${res.status}`);
|
|
|
|
}
|
|
|
|
const data = await res.json();
|
|
|
|
newstatus = data.newstatus;
|
|
|
|
//更新页面
|
|
|
|
if(newstatus === 0){
|
|
|
|
document.getElementById("detailTestStatus").textContent = "暂停中";
|
|
|
|
actionButton.textContent = "继续";
|
|
|
|
}else if(newstatus === 1){
|
|
|
|
document.getElementById("detailTestStatus").textContent = "执行中";
|
|
|
|
actionButton.textContent = "暂停";
|
|
|
|
}else {
|
|
|
|
document.getElementById("detailTestStatus").textContent = "已完成";
|
|
|
|
actionButton.textContent = "已完成";
|
|
|
|
}
|
|
|
|
cur_task.taskStatus = newstatus; //光有个cur_taks也可以
|
|
|
|
setSetpBtnStatus();
|
|
|
|
//更新task_list的显示
|
|
|
|
updateTaskList();
|
|
|
|
} catch (error) {
|
|
|
|
console.error("控制任务状态异常:", error);
|
|
|
|
alert("控制任务状态异常:",error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//结束任务-brun-false
|
|
|
|
document.getElementById("btnTaskOver").addEventListener("click",()=>{
|
|
|
|
overTask();
|
|
|
|
})
|
|
|
|
async function overTask(){
|
|
|
|
if(cur_task_id === 0){
|
|
|
|
alert("请先选择一个任务!")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
if (confirm('确定要结束此任务吗?')){
|
|
|
|
const res = await fetch("/api/task/taskover", {
|
|
|
|
method: "POST",
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
body: JSON.stringify({ cur_task_id }), //task_id:task_id
|
|
|
|
});
|
|
|
|
// 新增状态码校验
|
|
|
|
if (!res.ok) {
|
|
|
|
const errorData = await res.json();
|
|
|
|
throw new Error(errorData.error || `HTTP错误 ${res.status}`);
|
|
|
|
}
|
|
|
|
const data = await res.json();
|
|
|
|
bsuccess = data.bsuccess;
|
|
|
|
error = data.error;
|
|
|
|
if(bsuccess){
|
|
|
|
//更新页面
|
|
|
|
task_list = []
|
|
|
|
cur_task = null //当前选择的task--用于修改缓存时使用
|
|
|
|
cur_task_id = 0 //当前选择的cur_task_id
|
|
|
|
//重新获取任务list
|
|
|
|
getTasklist();
|
|
|
|
}else {
|
|
|
|
alert("结束任务失败:",error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
alert("结束任务失败:",error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//修改了涉及到tasklist的展示内容,修改tasklist显示
|
|
|
|
function updateTaskList(){
|
|
|
|
//更新数据
|
|
|
|
const selectedEl = document.querySelector(".task-item.selected");
|
|
|
|
if (selectedEl) {
|
|
|
|
let safeR = getstrsafeR(cur_task.safeRank);
|
|
|
|
let taskS = getstrTaskS(cur_task.taskStatus);
|
|
|
|
const statusDiv = selectedEl.querySelector(".task-status");
|
|
|
|
statusDiv.textContent = `${taskS}-${safeR}`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//设置单步按钮的可点击状态状态
|
|
|
|
function setSetpBtnStatus(){
|
|
|
|
if(cur_task){
|
|
|
|
task_status = cur_task.taskStatus;
|
|
|
|
work_type = cur_task.workType;
|
|
|
|
}else {
|
|
|
|
task_status = 0;
|
|
|
|
work_type = 0;
|
|
|
|
}
|
|
|
|
btn_TaskStep = document.getElementById("one_step");
|
|
|
|
btn_NodeStep = document.getElementById("btnNodeStep");
|
|
|
|
if(task_status===1 && work_type===0){ //执行中且是人工模式
|
|
|
|
btn_TaskStep.disabled= false;
|
|
|
|
btn_TaskStep.classList.remove("disabled-btn");
|
|
|
|
//node-step
|
|
|
|
if (selectedNodeData) {
|
|
|
|
if(selectedNodeData.node_bwork){ //有选中node,且节点为工作状态
|
|
|
|
btn_NodeStep.disabled= false;
|
|
|
|
btn_NodeStep.classList.remove("disabled-btn");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
btn_NodeStep.disabled = true;
|
|
|
|
btn_NodeStep.classList.add("disabled-btn"); //css会去重
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}else{ //其他情况都是不可用状态
|
|
|
|
btn_TaskStep.disabled = true; // 添加 disabled 属性
|
|
|
|
btn_TaskStep.classList.add("disabled-btn"); // 添加自定义样式
|
|
|
|
if (selectedNodeData) {
|
|
|
|
btn_NodeStep.disabled = true;
|
|
|
|
btn_NodeStep.classList.add("disabled-btn"); //css会去重
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//单步按钮--任务单步
|
|
|
|
document.getElementById("one_step").addEventListener("click",() => {
|
|
|
|
if(cur_task_id===0){
|
|
|
|
return
|
|
|
|
}
|
|
|
|
one_step_task();
|
|
|
|
});
|
|
|
|
async function one_step_task(){
|
|
|
|
try {
|
|
|
|
const res = await fetch("/api/task/taskstep", {
|
|
|
|
method: "POST",
|
|
|
|
headers: { "Content-Type": "application/json" },
|
|
|
|
body: JSON.stringify({ cur_task_id }), //task_id:task_id
|
|
|
|
});
|
|
|
|
if (!res.ok) {
|
|
|
|
const errorData = await res.json();
|
|
|
|
throw new Error(errorData.error || `HTTP错误 ${res.status}`);
|
|
|
|
}
|
|
|
|
//修改成功
|
|
|
|
const data = await res.json();
|
|
|
|
const bsuccess = data.bsuccess;
|
|
|
|
if(bsuccess){
|
|
|
|
alert("该任务已提交单步工作,请稍候查看执行结果!")
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
error = data.erroe;
|
|
|
|
alert("该任务单步失败!",error)
|
|
|
|
}
|
|
|
|
}catch (error) {
|
|
|
|
alert("该节点单步失败,请联系管理员!", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------测试数据和漏洞数据tab-------------------
|
|
|
|
// 复用:根据返回的数据数组渲染表格 tbody,保证固定 10 行
|
|
|
|
function renderTableRows(tbody, rowsData) {
|
|
|
|
tbody.innerHTML = "";
|
|
|
|
// 遍历数据行,生成 <tr>
|
|
|
|
rowsData.forEach((row, index) => {
|
|
|
|
const tr = document.createElement("tr");
|
|
|
|
// 这里假设 row 为对象,包含各个字段;下标从1开始显示序号
|
|
|
|
for (const cellData of Object.values(row)) {
|
|
|
|
const td = document.createElement("td");
|
|
|
|
td.textContent = cellData;
|
|
|
|
tr.appendChild(td);
|
|
|
|
}
|
|
|
|
tbody.appendChild(tr);
|
|
|
|
});
|
|
|
|
// 补足空行
|
|
|
|
const rowCount = rowsData.length;
|
|
|
|
for (let i = rowCount; i < 10; i++) {
|
|
|
|
const tr = document.createElement("tr");
|
|
|
|
for (let j = 0; j < tbody.parentElement.querySelectorAll("th").length; j++) {
|
|
|
|
const td = document.createElement("td");
|
|
|
|
td.innerHTML = " ";
|
|
|
|
tr.appendChild(td);
|
|
|
|
}
|
|
|
|
tbody.appendChild(tr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 查询测试指令
|
|
|
|
async function searchInstructions(page = 1) {
|
|
|
|
if(cur_task_id === 0){
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const nodeName = document.getElementById("instrNodeName").value.trim();
|
|
|
|
try {
|
|
|
|
const res = await fetch("/api/task/getinstr", {
|
|
|
|
method: "POST",
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
cur_task_id,
|
|
|
|
nodeName
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
if (!res.ok) {
|
|
|
|
const errorData = await res.json();
|
|
|
|
throw new Error(errorData.error || `HTTP错误 ${res.status}`);
|
|
|
|
}
|
|
|
|
const data = await res.json();
|
|
|
|
// data.instrs 数组中包含查询结果
|
|
|
|
renderTableRows(document.querySelector("#instrTable tbody"), data.instrs || []);
|
|
|
|
// 此处可更新分页控件(示例只简单绑定上一页下一页)
|
|
|
|
document.getElementById("instrPrev").dataset.page = page > 1 ? page - 1 : 1;
|
|
|
|
document.getElementById("instrNext").dataset.page = page + 1;
|
|
|
|
} catch (error) {
|
|
|
|
console.error("获取测试指令失败:", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 查询漏洞数据
|
|
|
|
async function searchVulnerabilities(page = 1) {
|
|
|
|
if(cur_task_id === 0){return;}
|
|
|
|
const nodeName = document.getElementById("vulNodeName").value.trim();
|
|
|
|
const vulType = document.getElementById("vulType").value.trim();
|
|
|
|
const vulLevel = document.getElementById("vulLevel").value;
|
|
|
|
try {
|
|
|
|
const res = await fetch("/api/task/getvul", {
|
|
|
|
method: "POST",
|
|
|
|
headers: {
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
},
|
|
|
|
body: JSON.stringify({
|
|
|
|
cur_task_id,
|
|
|
|
nodeName,
|
|
|
|
vulType,
|
|
|
|
vulLevel
|
|
|
|
}),
|
|
|
|
});
|
|
|
|
if (!res.ok) {
|
|
|
|
const errorData = await res.json();
|
|
|
|
throw new Error(errorData.error || `HTTP错误 ${res.status}`);
|
|
|
|
}
|
|
|
|
const data = await res.json();
|
|
|
|
renderTableRows(document.querySelector("#vulTable tbody"), data.vuls || []);
|
|
|
|
document.getElementById("vulPrev").dataset.page = page > 1 ? page - 1 : 1;
|
|
|
|
document.getElementById("vulNext").dataset.page = page + 1;
|
|
|
|
} catch (error) {
|
|
|
|
console.error("获取漏洞数据失败:", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 绑定测试指令查询按钮事件
|
|
|
|
document.getElementById("instrSearchBtn").addEventListener("click", () => {
|
|
|
|
searchInstructions();
|
|
|
|
});
|
|
|
|
// 绑定测试指令分页点击事件
|
|
|
|
document.getElementById("instrPrev").addEventListener("click", (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
searchInstructions(parseInt(e.target.dataset.page));
|
|
|
|
});
|
|
|
|
document.getElementById("instrNext").addEventListener("click", (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
searchInstructions(parseInt(e.target.dataset.page));
|
|
|
|
});
|
|
|
|
|
|
|
|
// 绑定漏洞数据查询按钮事件
|
|
|
|
document.getElementById("vulSearchBtn").addEventListener("click", () => {
|
|
|
|
searchVulnerabilities();
|
|
|
|
});
|
|
|
|
// 绑定漏洞数据分页点击事件
|
|
|
|
document.getElementById("vulPrev").addEventListener("click", (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
searchVulnerabilities(parseInt(e.target.dataset.page));
|
|
|
|
});
|
|
|
|
document.getElementById("vulNext").addEventListener("click", (e) => {
|
|
|
|
e.preventDefault();
|
|
|
|
searchVulnerabilities(parseInt(e.target.dataset.page));
|
|
|
|
});
|