js演练场

This commit is contained in:
q
2025-11-29 03:41:51 +08:00
parent 1dfdff7024
commit e74d5ea97e
25 changed files with 6379 additions and 112 deletions

View File

@@ -7,6 +7,9 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.*;
/**
@@ -279,4 +282,476 @@ public class JsHttpClientTest {
fail("错误响应测试失败: " + e.getMessage());
}
}
// ==================== 新增方法测试 ====================
@Test
public void testPutHeaders() {
System.out.println("\n[测试8] 批量设置请求头 - putHeaders方法");
try {
String url = "https://httpbin.org/headers";
System.out.println("请求URL: " + url);
// 批量设置请求头
Map<String, String> headers = new HashMap<>();
headers.put("X-Test-Header-1", "value1");
headers.put("X-Test-Header-2", "value2");
headers.put("X-Test-Header-3", "value3");
httpClient.putHeaders(headers);
System.out.println("批量设置请求头: " + headers);
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.get(url);
long endTime = System.currentTimeMillis();
System.out.println("请求完成,耗时: " + (endTime - startTime) + "ms");
System.out.println("状态码: " + response.statusCode());
String body = response.body();
// 验证结果
assertNotNull("响应不能为null", response);
assertEquals("状态码应该是200", 200, response.statusCode());
assertNotNull("响应体不能为null", body);
assertTrue("响应体应该包含设置的请求头",
body.contains("X-Test-Header-1") || body.contains("value1"));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("批量设置请求头测试失败: " + e.getMessage());
}
}
@Test
public void testRemoveHeader() {
System.out.println("\n[测试9] 删除请求头 - removeHeader方法");
try {
String url = "https://httpbin.org/headers";
System.out.println("请求URL: " + url);
// 先设置请求头
httpClient.putHeader("X-To-Be-Removed", "test-value");
httpClient.putHeader("X-To-Keep", "keep-value");
// 获取所有请求头
Map<String, String> headersBefore = httpClient.getHeaders();
System.out.println("删除前请求头数量: " + headersBefore.size());
assertTrue("应该包含要删除的请求头", headersBefore.containsKey("X-To-Be-Removed"));
// 删除指定请求头
httpClient.removeHeader("X-To-Be-Removed");
System.out.println("删除请求头: X-To-Be-Removed");
// 获取所有请求头
Map<String, String> headersAfter = httpClient.getHeaders();
System.out.println("删除后请求头数量: " + headersAfter.size());
// 验证结果
assertFalse("不应该包含已删除的请求头", headersAfter.containsKey("X-To-Be-Removed"));
assertTrue("应该保留未删除的请求头", headersAfter.containsKey("X-To-Keep"));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("删除请求头测试失败: " + e.getMessage());
}
}
@Test
public void testClearHeaders() {
System.out.println("\n[测试10] 清空请求头 - clearHeaders方法");
try {
// 先设置一些自定义请求头
httpClient.putHeader("X-Custom-1", "value1");
httpClient.putHeader("X-Custom-2", "value2");
Map<String, String> headersBefore = httpClient.getHeaders();
System.out.println("清空前请求头数量: " + headersBefore.size());
assertTrue("应该包含自定义请求头", headersBefore.size() > 3); // 3个默认头
// 清空请求头
httpClient.clearHeaders();
System.out.println("清空所有请求头(保留默认头)");
Map<String, String> headersAfter = httpClient.getHeaders();
System.out.println("清空后请求头数量: " + headersAfter.size());
System.out.println("保留的默认头: " + headersAfter.keySet());
// 验证结果
assertFalse("不应该包含自定义请求头", headersAfter.containsKey("X-Custom-1"));
assertFalse("不应该包含自定义请求头", headersAfter.containsKey("X-Custom-2"));
// 应该保留默认头
assertTrue("应该保留Accept-Encoding默认头",
headersAfter.containsKey("Accept-Encoding"));
assertTrue("应该保留User-Agent默认头",
headersAfter.containsKey("User-Agent"));
assertTrue("应该保留Accept-Language默认头",
headersAfter.containsKey("Accept-Language"));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("清空请求头测试失败: " + e.getMessage());
}
}
@Test
public void testGetHeaders() {
System.out.println("\n[测试11] 获取所有请求头 - getHeaders方法");
try {
// 设置一些请求头
httpClient.putHeader("X-Test-1", "value1");
httpClient.putHeader("X-Test-2", "value2");
Map<String, String> headers = httpClient.getHeaders();
System.out.println("获取到的请求头数量: " + headers.size());
System.out.println("请求头列表: " + headers);
// 验证结果
assertNotNull("请求头Map不能为null", headers);
assertTrue("应该包含设置的请求头", headers.containsKey("X-Test-1"));
assertTrue("应该包含设置的请求头", headers.containsKey("X-Test-2"));
assertEquals("X-Test-1的值应该是value1", "value1", headers.get("X-Test-1"));
assertEquals("X-Test-2的值应该是value2", "value2", headers.get("X-Test-2"));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("获取请求头测试失败: " + e.getMessage());
}
}
@Test
public void testPutRequest() {
System.out.println("\n[测试12] PUT请求 - put方法");
try {
String url = "https://httpbin.org/put";
System.out.println("请求URL: " + url);
Map<String, String> data = new HashMap<>();
data.put("key1", "value1");
data.put("key2", "value2");
System.out.println("PUT数据: " + data);
System.out.println("开始请求...");
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.put(url, data);
long endTime = System.currentTimeMillis();
System.out.println("请求完成,耗时: " + (endTime - startTime) + "ms");
System.out.println("状态码: " + response.statusCode());
String body = response.body();
// 验证结果
assertNotNull("响应不能为null", response);
assertEquals("状态码应该是200", 200, response.statusCode());
assertNotNull("响应体不能为null", body);
assertTrue("响应体应该包含PUT的数据",
body.contains("key1") || body.contains("value1"));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("PUT请求测试失败: " + e.getMessage());
}
}
@Test
public void testDeleteRequest() {
System.out.println("\n[测试13] DELETE请求 - delete方法");
try {
String url = "https://httpbin.org/delete";
System.out.println("请求URL: " + url);
System.out.println("开始请求...");
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.delete(url);
long endTime = System.currentTimeMillis();
System.out.println("请求完成,耗时: " + (endTime - startTime) + "ms");
System.out.println("状态码: " + response.statusCode());
String body = response.body();
// 验证结果
assertNotNull("响应不能为null", response);
assertEquals("状态码应该是200", 200, response.statusCode());
assertNotNull("响应体不能为null", body);
assertTrue("响应体应该包含DELETE相关信息",
body.contains("\"url\"") || body.contains("delete"));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("DELETE请求测试失败: " + e.getMessage());
}
}
@Test
public void testPatchRequest() {
System.out.println("\n[测试14] PATCH请求 - patch方法");
try {
String url = "https://httpbin.org/patch";
System.out.println("请求URL: " + url);
Map<String, String> data = new HashMap<>();
data.put("field1", "newValue1");
data.put("field2", "newValue2");
System.out.println("PATCH数据: " + data);
System.out.println("开始请求...");
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.patch(url, data);
long endTime = System.currentTimeMillis();
System.out.println("请求完成,耗时: " + (endTime - startTime) + "ms");
System.out.println("状态码: " + response.statusCode());
String body = response.body();
// 验证结果
assertNotNull("响应不能为null", response);
assertEquals("状态码应该是200", 200, response.statusCode());
assertNotNull("响应体不能为null", body);
assertTrue("响应体应该包含PATCH的数据",
body.contains("field1") || body.contains("newValue1"));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("PATCH请求测试失败: " + e.getMessage());
}
}
@Test
public void testSetTimeout() {
System.out.println("\n[测试15] 设置超时时间 - setTimeout方法");
try {
String url = "https://httpbin.org/delay/2";
System.out.println("请求URL: " + url);
// 设置超时时间为10秒
httpClient.setTimeout(10);
System.out.println("设置超时时间: 10秒");
long startTime = System.currentTimeMillis();
JsHttpClient.JsHttpResponse response = httpClient.get(url);
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
System.out.println("请求完成,耗时: " + duration + "ms");
System.out.println("状态码: " + response.statusCode());
// 验证结果
assertNotNull("响应不能为null", response);
assertEquals("状态码应该是200", 200, response.statusCode());
assertTrue("应该在合理时间内完成2-5秒", duration >= 2000 && duration < 5000);
// 测试更短的超时时间(应该失败)
httpClient.setTimeout(1);
System.out.println("设置超时时间为1秒请求延迟2秒的URL应该超时");
try {
httpClient.get("https://httpbin.org/delay/2");
fail("应该抛出超时异常");
} catch (Exception e) {
System.out.println("✓ 正确抛出超时异常: " + e.getMessage());
assertTrue("异常应该包含超时相关信息",
e.getMessage().contains("超时") ||
e.getMessage().contains("timeout") ||
e.getMessage().contains("Timeout"));
}
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("设置超时时间测试失败: " + e.getMessage());
}
}
@Test
public void testUrlEncode() {
System.out.println("\n[测试16] URL编码 - urlEncode静态方法");
try {
// 测试各种字符串
String[] testStrings = {
"hello world",
"测试中文",
"a+b=c&d=e",
"特殊字符!@#$%^&*()",
"123456"
};
for (String original : testStrings) {
String encoded = JsHttpClient.urlEncode(original);
System.out.println("原文: " + original);
System.out.println("编码: " + encoded);
// 验证结果
assertNotNull("编码结果不能为null", encoded);
assertNotEquals("编码后应该与原文不同(如果包含特殊字符)", original, encoded);
// 验证编码后的字符串不包含空格(空格应该被编码为%20
if (original.contains(" ")) {
assertFalse("编码后的字符串不应该包含空格", encoded.contains(" "));
}
}
// 测试null
String nullEncoded = JsHttpClient.urlEncode(null);
assertNull("null应该返回null", nullEncoded);
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("URL编码测试失败: " + e.getMessage());
}
}
@Test
public void testUrlDecode() {
System.out.println("\n[测试17] URL解码 - urlDecode静态方法");
try {
// 测试编码和解码的往返
String[] testStrings = {
"hello world",
"测试中文",
"a+b=c&d=e",
"123456"
};
for (String original : testStrings) {
String encoded = JsHttpClient.urlEncode(original);
String decoded = JsHttpClient.urlDecode(encoded);
System.out.println("原文: " + original);
System.out.println("编码: " + encoded);
System.out.println("解码: " + decoded);
// 验证结果
assertEquals("解码后应该与原文相同", original, decoded);
}
// 测试null
String nullDecoded = JsHttpClient.urlDecode(null);
assertNull("null应该返回null", nullDecoded);
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("URL解码测试失败: " + e.getMessage());
}
}
@Test
public void testBodyBytes() {
System.out.println("\n[测试18] 获取响应体字节数组 - bodyBytes方法");
try {
String url = "https://httpbin.org/get";
System.out.println("请求URL: " + url);
System.out.println("开始请求...");
JsHttpClient.JsHttpResponse response = httpClient.get(url);
System.out.println("状态码: " + response.statusCode());
// 获取响应体字符串和字节数组
String bodyString = response.body();
byte[] bodyBytes = response.bodyBytes();
System.out.println("响应体字符串长度: " + (bodyString != null ? bodyString.length() : 0));
System.out.println("响应体字节数组长度: " + (bodyBytes != null ? bodyBytes.length : 0));
// 验证结果
assertNotNull("响应体字节数组不能为null", bodyBytes);
assertTrue("字节数组长度应该大于0", bodyBytes.length > 0);
assertTrue("字节数组长度应该与字符串长度相关",
bodyBytes.length >= bodyString.length());
// 验证字节数组可以转换为字符串
String bytesAsString = new String(bodyBytes);
assertTrue("字节数组转换的字符串应该包含关键内容",
bytesAsString.contains("\"url\""));
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("获取响应体字节数组测试失败: " + e.getMessage());
}
}
@Test
public void testBodySize() {
System.out.println("\n[测试19] 获取响应体大小 - bodySize方法");
try {
String url = "https://httpbin.org/get";
System.out.println("请求URL: " + url);
System.out.println("开始请求...");
JsHttpClient.JsHttpResponse response = httpClient.get(url);
System.out.println("状态码: " + response.statusCode());
// 获取响应体大小和字符串
long bodySize = response.bodySize();
String bodyString = response.body();
System.out.println("响应体大小: " + bodySize + " 字节");
System.out.println("响应体字符串长度: " + (bodyString != null ? bodyString.length() : 0));
// 验证结果
assertTrue("响应体大小应该大于0", bodySize > 0);
assertTrue("响应体大小应该与字符串长度相关",
bodySize >= bodyString.length());
// 验证bodySize与bodyBytes长度一致
byte[] bodyBytes = response.bodyBytes();
assertEquals("bodySize应该等于bodyBytes的长度",
bodyBytes.length, bodySize);
System.out.println("✓ 测试通过");
} catch (Exception e) {
System.err.println("✗ 测试失败: " + e.getMessage());
e.printStackTrace();
fail("获取响应体大小测试失败: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,393 @@
package cn.qaiu.parser;
import cn.qaiu.entity.ShareLinkInfo;
import cn.qaiu.parser.customjs.JsPlaygroundExecutor;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
/**
* JavaScript执行器安全测试
* 用于验证JavaScript代码执行环境的安全性
*
* @author <a href="https://qaiu.top">QAIU</a>
*/
public class SecurityTest {
private static final Logger log = LoggerFactory.getLogger(SecurityTest.class);
/**
* 测试1: 尝试通过Java类执行系统命令
*/
@Test
public void testSystemCommandExecution() {
String dangerousJs = """
// ==UserScript==
// @name 危险测试-系统命令执行
// @type security_test
// @match https://test.com/*
// ==/UserScript==
function parse(shareLinkInfo, http, logger) {
logger.info("尝试执行系统命令...");
try {
// 尝试1: 直接访问Runtime类执行命令
var Runtime = Java.type('java.lang.Runtime');
var runtime = Runtime.getRuntime();
var process = runtime.exec("whoami");
var reader = new java.io.BufferedReader(new java.io.InputStreamReader(process.getInputStream()));
var output = reader.readLine();
logger.error("【安全漏洞】成功执行系统命令: " + output);
return "危险: 系统命令执行成功 - " + output;
} catch (e) {
logger.info("方法1失败: " + e.message);
}
try {
// 尝试2: 使用ProcessBuilder
var ProcessBuilder = Java.type('java.lang.ProcessBuilder');
var pb = new ProcessBuilder(["ls", "-la"]);
var process = pb.start();
logger.error("【安全漏洞】ProcessBuilder执行成功");
return "危险: ProcessBuilder执行成功";
} catch (e) {
logger.info("方法2失败: " + e.message);
}
return "安全: 无法执行系统命令";
}
""";
testJavaScriptSecurity(dangerousJs, "系统命令执行测试");
}
/**
* 测试2: 尝试文件系统访问
*/
@Test
public void testFileSystemAccess() {
String dangerousJs = """
// ==UserScript==
// @name 危险测试-文件系统访问
// @type security_test
// @match https://test.com/*
// ==/UserScript==
function parse(shareLinkInfo, http, logger) {
logger.info("尝试访问文件系统...");
try {
// 尝试读取敏感文件
var Files = Java.type('java.nio.file.Files');
var Paths = Java.type('java.nio.file.Paths');
var path = Paths.get("/etc/passwd");
var content = Files.readAllLines(path);
logger.error("【安全漏洞】成功读取文件: " + content.get(0));
return "危险: 文件读取成功";
} catch (e) {
logger.info("方法1失败: " + e.message);
}
try {
// 尝试写入文件
var FileWriter = Java.type('java.io.FileWriter');
var writer = new FileWriter("/tmp/test.txt");
writer.write("test");
writer.close();
logger.error("【安全漏洞】成功写入文件");
return "危险: 文件写入成功";
} catch (e) {
logger.info("方法2失败: " + e.message);
}
return "安全: 无法访问文件系统";
}
""";
testJavaScriptSecurity(dangerousJs, "文件系统访问测试");
}
/**
* 测试3: 尝试访问系统属性和环境变量
*/
@Test
public void testSystemPropertiesAccess() {
String dangerousJs = """
// ==UserScript==
// @name 危险测试-系统属性访问
// @type security_test
// @match https://test.com/*
// ==/UserScript==
function parse(shareLinkInfo, http, logger) {
logger.info("尝试访问系统属性...");
try {
// 尝试读取系统属性
var System = Java.type('java.lang.System');
var userHome = System.getProperty("user.home");
var userName = System.getProperty("user.name");
logger.error("【安全漏洞】获取到系统属性 - HOME: " + userHome + ", USER: " + userName);
return "危险: 系统属性访问成功 - " + userName;
} catch (e) {
logger.info("方法1失败: " + e.message);
}
try {
// 尝试读取环境变量
var System = Java.type('java.lang.System');
var env = System.getenv();
var path = env.get("PATH");
logger.error("【安全漏洞】获取到环境变量 PATH: " + path);
return "危险: 环境变量访问成功";
} catch (e) {
logger.info("方法2失败: " + e.message);
}
return "安全: 无法访问系统属性";
}
""";
testJavaScriptSecurity(dangerousJs, "系统属性访问测试");
}
/**
* 测试4: 尝试反射攻击
*/
@Test
public void testReflectionAttack() {
String dangerousJs = """
// ==UserScript==
// @name 危险测试-反射攻击
// @type security_test
// @match https://test.com/*
// ==/UserScript==
function parse(shareLinkInfo, http, logger) {
logger.info("尝试使用反射...");
try {
// 尝试通过反射访问私有字段
var Class = Java.type('java.lang.Class');
var Field = Java.type('java.lang.reflect.Field');
var systemClass = Class.forName("java.lang.System");
var methods = systemClass.getDeclaredMethods();
logger.error("【安全漏洞】反射访问成功,获取到 " + methods.length + " 个方法");
return "危险: 反射访问成功";
} catch (e) {
logger.info("方法1失败: " + e.message);
}
try {
// 尝试获取ClassLoader
var Thread = Java.type('java.lang.Thread');
var classLoader = Thread.currentThread().getContextClassLoader();
logger.error("【安全漏洞】获取到ClassLoader: " + classLoader);
return "危险: ClassLoader访问成功";
} catch (e) {
logger.info("方法2失败: " + e.message);
}
return "安全: 无法使用反射";
}
""";
testJavaScriptSecurity(dangerousJs, "反射攻击测试");
}
/**
* 测试5: 尝试网络攻击
*/
@Test
public void testNetworkAttack() {
String dangerousJs = """
// ==UserScript==
// @name 危险测试-网络攻击
// @type security_test
// @match https://test.com/*
// ==/UserScript==
function parse(shareLinkInfo, http, logger) {
logger.info("尝试发起网络连接...");
try {
// 尝试创建Socket连接
var Socket = Java.type('java.net.Socket');
var socket = new Socket("127.0.0.1", 22);
logger.error("【安全漏洞】Socket连接成功");
socket.close();
return "危险: Socket连接成功";
} catch (e) {
logger.info("方法1失败: " + e.message);
}
try {
// 尝试使用URL访问
var URL = Java.type('java.net.URL');
var url = new URL("http://localhost:8080");
var conn = url.openConnection();
logger.error("【安全漏洞】URL连接成功");
return "危险: URL连接成功";
} catch (e) {
logger.info("方法2失败: " + e.message);
}
return "安全: 无法创建网络连接";
}
""";
testJavaScriptSecurity(dangerousJs, "网络攻击测试");
}
/**
* 测试6: 尝试退出JVM
*/
@Test
public void testJvmExit() {
String dangerousJs = """
// ==UserScript==
// @name 危险测试-JVM退出
// @type security_test
// @match https://test.com/*
// ==/UserScript==
function parse(shareLinkInfo, http, logger) {
logger.info("尝试退出JVM...");
try {
// 尝试退出JVM
var System = Java.type('java.lang.System');
logger.warn("准备执行 System.exit(1)...");
System.exit(1);
return "危险: JVM退出成功";
} catch (e) {
logger.info("退出失败: " + e.message);
}
try {
// 尝试终止运行时
var Runtime = Java.type('java.lang.Runtime');
Runtime.getRuntime().halt(1);
return "危险: Runtime.halt成功";
} catch (e) {
logger.info("halt失败: " + e.message);
}
return "安全: 无法退出JVM";
}
""";
testJavaScriptSecurity(dangerousJs, "JVM退出测试");
}
/**
* 测试7: 尝试访问注入的httpClient执行任意HTTP请求
*/
@Test
public void testHttpClientAbuse() {
String dangerousJs = """
// ==UserScript==
// @name 危险测试-HTTP客户端滥用
// @type security_test
// @match https://test.com/*
// ==/UserScript==
function parse(shareLinkInfo, http, logger) {
logger.info("测试HTTP客户端访问控制...");
try {
// 尝试访问内网地址
logger.info("尝试访问内网地址...");
var response = http.get("http://127.0.0.1:8080/admin");
logger.warn("【潜在风险】可以访问内网地址: " + response.substring(0, 50));
return "警告: 可以通过HTTP访问内网";
} catch (e) {
logger.info("内网访问失败: " + e.message);
}
try {
// 尝试访问敏感API
logger.info("尝试访问云服务元数据API...");
var response = http.get("http://169.254.169.254/latest/meta-data/");
logger.error("【严重漏洞】可以访问云服务元数据: " + response);
return "危险: 可以访问云服务元数据";
} catch (e) {
logger.info("元数据访问失败: " + e.message);
}
return "提示: HTTP客户端访问受限";
}
""";
testJavaScriptSecurity(dangerousJs, "HTTP客户端滥用测试");
}
/**
* 执行JavaScript安全测试的辅助方法
*/
private void testJavaScriptSecurity(String jsCode, String testName) {
log.info("\n" + "=".repeat(80));
log.info("开始执行安全测试: {}", testName);
log.info("=".repeat(80));
try {
// 创建测试用的ShareLinkInfo
ShareLinkInfo shareLinkInfo = ShareLinkInfo.newBuilder()
.shareKey("test_key")
.sharePassword("test_pwd")
.type("security_test")
.shareUrl("https://test.com/share/test")
.standardUrl("https://test.com/share/test")
.otherParam(new HashMap<>())
.build();
// 创建执行器并执行
JsPlaygroundExecutor executor = new JsPlaygroundExecutor(shareLinkInfo, jsCode);
executor.executeParseAsync()
.onSuccess(result -> {
log.info("测试结果: {}", result);
// 打印所有日志
log.info("\n执行日志:");
executor.getLogs().forEach(logEntry -> {
String logLevel = logEntry.getLevel();
String message = logEntry.getMessage();
log.info("[{}] [{}] {}", logLevel, logEntry.getSource(), message);
// 检查是否有安全漏洞警告
if (message.contains("【安全漏洞】") || message.contains("【严重漏洞】")) {
log.error("!!! 发现安全漏洞 !!!");
}
});
})
.onFailure(e -> {
log.info("执行失败: {}", e.getMessage());
// 打印所有日志
log.info("\n执行日志:");
executor.getLogs().forEach(logEntry -> {
log.info("[{}] [{}] {}",
logEntry.getLevel(),
logEntry.getSource(),
logEntry.getMessage());
});
})
.toCompletionStage()
.toCompletableFuture()
.join(); // 等待异步执行完成
} catch (Exception e) {
log.error("测试执行异常", e);
}
log.info("=".repeat(80));
log.info("测试完成: {}\n", testName);
}
}