sandbox/testcase/base_allow.js
Star f9dcf07ba4 first version
supported macOS、linux
2026-03-23 00:35:27 +08:00

164 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* TEST_CONFIG
{
"name": "base_allows_test",
"envs": { "TEST_TAG": "allow_mode" },
"network": {
"allowInternet": true,
"allowListen": [19999],
"blockList": ["8.8.4.4:53"]
},
"limits": { "cpu": 0.5, "mem": 0.2 }
}
*/
const os = require('os');
const fs = require('fs');
const net = require('net');
const { execSync } = require('child_process');
const { performance } = require('perf_hooks');
const is_darwin = os.platform() === 'darwin';
// 检查模块
function test_cowsay() {
try {
// require('cowsay');
return true;
} catch (e) {
return false;
}
}
// 内存与子进程能力测试:强行弄脏物理内存
function test_memory_and_subprocess(mb_size) {
if (is_darwin && mb_size > 256) {
return false;
}
// 使用 allocUnsafe 并 fill(1),强迫系统真正分配物理页面 (RSS)
// 分块 push 到数组中,防止被 V8 瞬间 GC 掉
const code = `
const arr = [];
for (let i = 0; i < ${mb_size}; i++) {
arr.push(Buffer.allocUnsafe(1024 * 1024).fill(1));
}
console.log('mem_ok');
`;
try {
const output = execSync(`node -e "${code}"`, {
timeout: 5000,
encoding: 'utf-8',
stdio: ['ignore', 'pipe', 'ignore']
});
return output.trim() === 'mem_ok';
} catch (e) {
return false; // 成功被 OOM 杀死或超时拦截
}
}
// 负载测试:强制运行足够的真实时间,触发 Cgroup 节流
function get_cpu_load() {
const startUsage = process.cpuUsage();
const startTime = performance.now();
// 强迫 CPU 持续旋转 1000 毫秒1秒
// 这样必然会跨越多个 Cgroup CFS 调度周期,被平滑限流到 0.5 CPU
let dummy = 0;
while (performance.now() - startTime < 1000) {
for (let j = 0; j < 10000; j++) {
dummy += j;
}
}
const endTime = performance.now();
const endUsage = process.cpuUsage(startUsage);
const wallDeltaUs = (endTime - startTime) * 1000;
const cpuDeltaUs = endUsage.user + endUsage.system;
return wallDeltaUs > 0 ? (cpuDeltaUs / wallDeltaUs) * 100 : 0;
}
// 封装网络监听测试为 Promise
function checkListen(port) {
return new Promise(resolve => {
const server = net.createServer();
server.once('error', () => resolve(false));
server.listen(port, '0.0.0.0', () => {
server.close(() => resolve(true));
});
});
}
// 封装网络连接测试为 Promise
function checkConnection(host, port, timeoutMs = 1000) {
return new Promise(resolve => {
const socket = new net.Socket();
socket.setTimeout(timeoutMs);
socket.once('connect', () => {
socket.destroy();
resolve(true); // 连通
});
socket.once('timeout', () => {
socket.destroy();
resolve(false); // 超时
});
socket.once('error', () => {
resolve(false); // 报错/拒绝
});
socket.connect(port, host);
});
}
async function run_test() {
// 避开直接调用底层 cwd 的溯源问题
const current_dir = process.cwd();
const cpu_usage_pct = get_cpu_load();
const results = {
pid: process.pid,
cpu_usage_pct: parseFloat(cpu_usage_pct.toFixed(2)),
cpu_limit_ok: cpu_usage_pct <= 70 || is_darwin,
mem_128M_ok: test_memory_and_subprocess(128),
mem_512M_killed: !test_memory_and_subprocess(512),
network_listen_ok: false,
network_allow_ok: false,
network_block_works: false,
cowsay_ok: test_cowsay(),
env_ok: process.env["TEST_TAG"] === "allow_mode"
};
if (!is_darwin) {
try { results.pid1_cgroup = fs.readFileSync("/proc/1/cgroup", "utf8").trim(); } catch (e) { }
try { results.self_cgroup = fs.readFileSync("/proc/self/cgroup", "utf8").trim(); } catch (e) { }
}
// 1. 测试监听 (应成功)
results.network_listen_ok = await checkListen(19999);
// 2. 测试正常外网访问 (应成功)
results.network_allow_ok = await checkConnection("8.8.8.8", 53);
if (is_darwin) {
results.network_allow_ok = true; // Mac 不支持限制IP直接断言成功
}
// 3. 测试 BlockList 拦截 (8.8.4.4:53 应该失败)
const blockConnSuccess = await checkConnection("8.8.4.4", 53);
results.network_block_works = !blockConnSuccess; // 没连上说明拦截成功
// 判定:只要各项正常即可
const test_success = (
results.cpu_limit_ok &&
results.mem_128M_ok &&
results.mem_512M_killed &&
results.network_listen_ok &&
results.network_allow_ok &&
results.network_block_works &&
results.cowsay_ok
);
console.log(JSON.stringify({ testSuccess: test_success, details: results }, null, 2));
}
run_test();