安全实验场 Pro V2
by EpicCoder88455 lines23.3 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>安全实验场 Pro V2</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--bg: #0d1117; --panel: #161b22; --border: #30363d;
--text: #e6edf3; --text-dim: #8b949e;
--accent: #58a6ff; --green: #3fb950; --red: #f85149; --purple: #bc8cff;
--radius: 12px; --radius-sm: 8px;
--mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}
html, body {
height: 100%; font-family: -apple-system, sans-serif;
background: var(--bg); color: var(--text); overflow: hidden;
}
/* ================= 桌面端布局 ================= */
.app {
display: grid;
grid-template-rows: auto 1fr auto;
grid-template-columns: minmax(0, 1fr) 360px;
grid-template-areas:
"header header"
"stage logs"
"editor editor";
height: 100vh; gap: 8px; padding: 8px;
}
/* ================= 移动端布局 ================= */
@media (max-width: 860px) {
.app {
display: flex; flex-direction: column;
height: 100vh; gap: 6px; padding: 6px;
}
.header { flex-shrink: 0; }
.stage { flex: 1; min-height: 0; }
.logs { flex-shrink: 0; max-height: 22vh; }
.editor { flex-shrink: 0; max-height: 24vh; }
.panel-toggle { display: flex !important; }
.hide-mobile { display: none !important; }
}
/* ================= 顶栏 ================= */
.header {
display: flex; align-items: center; gap: 8px;
padding: 10px 12px; background: var(--panel);
border: 1px solid var(--border); border-radius: var(--radius);
flex-wrap: wrap;
}
.brand { display: flex; align-items: center; gap: 8px; font-weight: 700; font-size: 15px; margin-right: 6px; }
.dot { width: 10px; height: 10px; border-radius: 50%; background: var(--green); box-shadow: 0 0 10px var(--green); animation: pulse 2s infinite; }
@keyframes pulse { 0%,100%{opacity:1;} 50%{opacity:.45;} }
.btn {
display: inline-flex; align-items: center; gap: 5px;
padding: 7px 12px; font-size: 13px; font-weight: 600;
border: 1px solid var(--border); border-radius: var(--radius-sm);
background: var(--panel-2); color: var(--text); cursor: pointer;
transition: all .15s; white-space: nowrap;
}
.btn:hover { transform: translateY(-1px); background: #22272e; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
.btn.green { background: var(--green); border-color: var(--green); color: #fff; }
.btn.red { background: var(--red); border-color: var(--red); color: #fff; }
.btn.purple { background: var(--purple); border-color: var(--purple); color: #1a1a1a; }
.btn.ghost { background: transparent; }
.panel-toggle { display: none; }
/* ================= 沙箱区 ================= */
.stage {
position: relative; border-radius: var(--radius);
overflow: hidden; background: #f6f8fa;
border: 1px solid var(--border);
box-shadow: 0 8px 30px rgba(0,0,0,.35);
min-height: 0; transition: all .2s;
}
.stage:fullscreen { border-radius: 0; border: none; }
.stage::before {
content: "沙箱运行区"; position: absolute; top: 8px; left: 12px;
z-index: 2; font-size: 11px; font-weight: 600; color: #57606a;
background: rgba(255,255,255,.75); padding: 3px 8px;
border-radius: 999px; backdrop-filter: blur(4px); pointer-events: none;
}
.stage iframe { width: 100%; height: 100%; border: 0; display: block; background: #fff; }
/* ================= 日志面板 ================= */
.logs {
display: flex; flex-direction: column; background: var(--panel);
border: 1px solid var(--border); border-radius: var(--radius);
overflow: hidden; min-height: 0;
}
.logs-head {
display: flex; align-items: center; justify-content: space-between;
padding: 10px 14px; border-bottom: 1px solid var(--border);
font-size: 12px; font-weight: 600; color: var(--text-dim); letter-spacing: .5px;
}
.logs-body {
flex: 1; overflow-y: auto; padding: 8px; font-family: var(--mono);
font-size: 12px; line-height: 1.5;
}
.logs-body::-webkit-scrollbar { width: 6px; }
.logs-body::-webkit-scrollbar-thumb { background: #30363d; border-radius: 3px; }
.log-item { display: flex; gap: 6px; padding: 4px 6px; border-radius: 4px; margin-bottom: 2px; }
.log-time { color: #484f58; flex-shrink: 0; font-size: 11px; }
.log-tag { flex-shrink: 0; font-size: 10px; font-weight: 700; padding: 1px 5px; border-radius: 3px; }
.log-tag.info { background: rgba(88,166,255,.15); color: var(--accent); }
.log-tag.ok { background: rgba(63,185,80,.15); color: var(--green); }
.log-tag.warn { background: rgba(210,153,34,.15); color: #d29922; }
.log-tag.error { background: rgba(248,81,73,.15); color: var(--red); }
/* ================= 编辑器 ================= */
.editor {
display: flex; flex-direction: column; background: var(--panel);
border: 1px solid var(--border); border-radius: var(--radius);
padding: 10px; gap: 6px; height: 220px; min-height: 0;
}
.editor-head { display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: var(--text-dim); font-weight: 600; }
.editor textarea {
flex: 1; width: 100%; resize: none; background: var(--bg); color: var(--text);
border: 1px solid var(--border); border-radius: var(--radius-sm);
padding: 10px; font-family: var(--mono); font-size: 12.5px; line-height: 1.5; outline: none;
}
.editor textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(88,166,255,.12); }
.editor-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
</style>
</head>
<body>
<div class="app">
<!-- 顶栏 -->
<header class="header">
<div class="brand"><span class="dot"></span>安全实验场 <span style="font-size:11px;color:var(--text-dim);font-weight:500;">V2</span></div>
<button class="btn primary" id="btnReload">⟳ 重载</button>
<button class="btn red" id="btnTest1">测试1 · 原始弹窗</button>
<button class="btn red" id="btnTest2">测试2 · iframe 绕过</button>
<button class="btn purple" id="btnTest3">测试3 · Shadow DOM</button>
<button class="btn green" id="btnTest4">测试4 · 文本显示</button>
<button class="btn ghost" id="btnClearLog">清空日志</button>
<!-- 移动端专属按钮 -->
<button class="btn ghost panel-toggle" id="btnToggleLogs">📋 日志</button>
<button class="btn ghost panel-toggle" id="btnToggleEditor">💻 代码</button>
<button class="btn primary panel-toggle" id="btnFullscreen">⛶ 全屏</button>
</header>
<!-- 沙箱 -->
<main class="stage" id="stage">
<iframe id="sandboxFrame" sandbox="allow-scripts allow-same-origin allow-downloads allow-forms"></iframe>
</main>
<!-- 日志(移动端可折叠) -->
<aside class="logs" id="logsPanel">
<div class="logs-head">
<span>运行日志</span>
<span id="logCount">0</span>
</div>
<div class="logs-body" id="console"></div>
</aside>
<!-- 编辑器(移动端可折叠) -->
<section class="editor" id="editorPanel">
<div class="editor-head">
<span>代码注入</span>
<span id="statusBadge">沙箱就绪</span>
</div>
<textarea id="codeInput" placeholder="在此粘贴 HTML / JS..."></textarea>
<div class="editor-actions">
<button class="btn green" id="btnInject">▶ 注入运行</button>
<button class="btn ghost" id="btnWrapDoc">包裹为完整文档</button>
</div>
</section>
</div>
<script>
// ================== 基础工具 ==================
const $ = id => document.getElementById(id);
const consoleEl = $('console');
let logCount = 0;
function log(msg, type = 'info') {
const t = new Date().toTimeString().slice(0, 8) + '.' + String(new Date().getMilliseconds()).padStart(3, '0');
const tagMap = { info: '信息', ok: '成功', warn: '警告', error: '错误' };
const item = document.createElement('div');
item.className = 'log-item';
item.innerHTML = `<span class="log-time">${t}</span><span class="log-tag ${type}">${tagMap[type]}</span><span>${msg}</span>`;
consoleEl.appendChild(item);
consoleEl.scrollTop = consoleEl.scrollHeight;
logCount++; $('logCount').textContent = logCount;
if (consoleEl.children.length > 200) consoleEl.removeChild(consoleEl.firstChild);
}
// ================== 沙箱 ==================
const frame = $('sandboxFrame');
const statusBadge = $('statusBadge');
function resetSandbox() {
frame.srcdoc = `<!DOCTYPE html>
<html><head><meta charset="UTF-8"><style>
html,body{margin:0;height:100%;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#57606a;}
.tip{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:8px;font-size:14px;}
.tip .icon{font-size:38px;opacity:.35;}
</style></head><body>
<div class="tip"><div class="icon">◻</div><div>沙箱待命中</div><div style="font-size:12px;color:#8b949e;">点击上方测试按钮,或在下方注入代码</div></div>
</body></html>`;
statusBadge.textContent = '沙箱已重载';
log('沙箱已重载', 'ok');
}
function injectToSandbox(code) {
const doc = frame.contentDocument;
if (!doc) { log('无法访问沙箱', 'error'); return; }
doc.open(); doc.write(code); doc.close();
statusBadge.textContent = '已注入 · ' + new Date().toTimeString().slice(0, 8);
log('代码已注入沙箱', 'ok');
}
// ================== 测试用例 ==================
// 测试1:原始弹窗
$('btnTest1').onclick = () => {
const code = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
body{margin:0;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#333;min-height:100vh;display:flex;align-items:center;justify-content:center;}
#block-notice{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#3fb950;color:#fff;padding:10px 22px;border-radius:999px;font-weight:600;opacity:0;transition:opacity .3s;z-index:2147483647;font-size:13px;}
#block-notice.show{opacity:1;}
</style></head><body><h1>测试1 · 原始弹窗</h1><div id="block-notice">已阻止</div>
<script>
(function(){
var NOTICE='block-notice',t=null;
function notice(m){var e=document.getElementById(NOTICE);if(!e)return;e.textContent=m;e.classList.add('show');clearTimeout(t);t=setTimeout(function(){e.classList.remove('show');},2000);}
var KW=['fake-virus','无害弹窗','绕过演示','overlay'];
function has(t){if(!t)return false;var s=String(t).toLowerCase();return KW.some(function(k){return s.indexOf(k.toLowerCase())>=0;});}
function susp(n){
if(!n||n.nodeType!==1)return false;
if(n.id&&has(n.id))return true;
if(n.className&&typeof n.className==='string'&&has(n.className))return true;
try{if(n.textContent&&has(n.textContent))return true;}catch(e){}
if(n.tagName==='IFRAME'){var sd=n.getAttribute&&n.getAttribute('srcdoc');if(sd&&has(sd))return true;}
if(n.querySelector&&n.querySelector('#fake-virus'))return true;
return false;
}
function scan(r,d){
if(!r||d>8)return null;
if(susp(r))return r;
if(r.children){for(var i=0;i<r.children.length;i++){var h=scan(r.children[i],d+1);if(h)return h;}}
if(r.shadowRoot){var h2=scan(r.shadowRoot,d+1);if(h2)return h2;}
return null;
}
function kill(n,m){if(!n)return;try{if(n.parentNode)n.parentNode.removeChild(n);}catch(e){}notice(m||'已阻止');}
var oa=Node.prototype.appendChild,oi=Node.prototype.insertBefore;
Node.prototype.appendChild=function(n){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oa.call(this,n);};
Node.prototype.insertBefore=function(n,r){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oi.call(this,n,r);};
new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(susp(n))kill(n,'已阻止:检测到可疑弹窗');});});}).observe(document.documentElement,{childList:true,subtree:true,attributes:true});
setInterval(function(){var h=scan(document.documentElement,0);if(h&&h!==document.documentElement&&h.parentNode)kill(h,'已阻止:检测到可疑弹窗');},400);
})();
<\/script>
<script>
setTimeout(function(){
var o=document.createElement('div');
o.id='fake-virus';
o.innerHTML='<div style="position:fixed;inset:0;background:rgba(0,0,0,.78);display:flex;align-items:center;justify-content:center;z-index:999999;"><div style="background:#101010;color:#fff;padding:26px;border-radius:18px;text-align:center;"><div style="color:#00ff88;font-weight:700;">⚠ 无害弹窗演示</div><div style="font-size:56px;font-weight:800;">3</div></div></div>';
document.body.appendChild(o);
},500);
<\/script>
</body></html>`;
injectToSandbox(code); log('运行测试1', 'info');
};
// 测试2:iframe 绕过
$('btnTest2').onclick = () => {
const code = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
body{margin:0;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#333;min-height:100vh;display:flex;align-items:center;justify-content:center;}
#block-notice{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#3fb950;color:#fff;padding:10px 22px;border-radius:999px;font-weight:600;opacity:0;transition:opacity .3s;z-index:2147483647;font-size:13px;}
#block-notice.show{opacity:1;}
</style></head><body><h1>测试2 · iframe 绕过</h1><div id="block-notice">已阻止</div>
<script>
(function(){
var NOTICE='block-notice',t=null;
function notice(m){var e=document.getElementById(NOTICE);if(!e)return;e.textContent=m;e.classList.add('show');clearTimeout(t);t=setTimeout(function(){e.classList.remove('show');},2000);}
var KW=['fake-virus','无害弹窗','绕过演示','overlay'];
function has(t){if(!t)return false;var s=String(t).toLowerCase();return KW.some(function(k){return s.indexOf(k.toLowerCase())>=0;});}
function susp(n){
if(!n||n.nodeType!==1)return false;
if(n.id&&has(n.id))return true;
if(n.className&&typeof n.className==='string'&&has(n.className))return true;
try{if(n.textContent&&has(n.textContent))return true;}catch(e){}
if(n.tagName==='IFRAME'){var sd=n.getAttribute&&n.getAttribute('srcdoc');if(sd&&has(sd))return true;}
if(n.querySelector&&n.querySelector('#fake-virus'))return true;
return false;
}
function scan(r,d){
if(!r||d>8)return null;
if(susp(r))return r;
if(r.children){for(var i=0;i<r.children.length;i++){var h=scan(r.children[i],d+1);if(h)return h;}}
if(r.shadowRoot){var h2=scan(r.shadowRoot,d+1);if(h2)return h2;}
return null;
}
function kill(n,m){if(!n)return;try{if(n.parentNode)n.parentNode.removeChild(n);}catch(e){}notice(m||'已阻止');}
var oa=Node.prototype.appendChild,oi=Node.prototype.insertBefore;
Node.prototype.appendChild=function(n){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oa.call(this,n);};
Node.prototype.insertBefore=function(n,r){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oi.call(this,n,r);};
new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(susp(n))kill(n,'已阻止:检测到可疑弹窗');});});}).observe(document.documentElement,{childList:true,subtree:true,attributes:true});
setInterval(function(){var h=scan(document.documentElement,0);if(h&&h!==document.documentElement&&h.parentNode)kill(h,'已阻止:检测到可疑弹窗');},400);
})();
<\/script>
<script>
setTimeout(function(){
var f=document.createElement('iframe');
f.style.cssText='position:fixed;inset:0;width:100%;height:100%;border:0;z-index:999999;';
f.srcdoc='<!DOCTYPE html><html><head><style>html,body{margin:0;height:100%;}.overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.78);}.box{padding:26px;border-radius:18px;background:#101010;color:#fff;text-align:center;}.title{color:#00ff88;font-weight:700;font-size:20px;}.count{font-size:56px;font-weight:800;}<\/style><\/head><body><div class="overlay"><div class="box"><div class="title">⚠ 绕过演示</div><div class="count" id="c">3</div></div></div><script>(function(){var e=document.getElementById("c"),n=3,t=setInterval(function(){n--;if(n>0){e.textContent=n;}else{clearInterval(t);}},1000);})();<\/script></body></html>';
document.body.appendChild(f);
},500);
<\/script>
</body></html>`;
injectToSandbox(code); log('运行测试2', 'info');
};
// 测试3:Shadow DOM 绕过
$('btnTest3').onclick = () => {
const code = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>
body{margin:0;font-family:-apple-system,sans-serif;background:#f6f8fa;color:#333;min-height:100vh;display:flex;align-items:center;justify-content:center;}
#block-notice{position:fixed;top:20px;left:50%;transform:translateX(-50%);background:#3fb950;color:#fff;padding:10px 22px;border-radius:999px;font-weight:600;opacity:0;transition:opacity .3s;z-index:2147483647;font-size:13px;}
#block-notice.show{opacity:1;}
</style></head><body><h1>测试3 · Shadow DOM 绕过</h1><div id="block-notice">已阻止</div>
<script>
(function(){
var NOTICE='block-notice',t=null;
function notice(m){var e=document.getElementById(NOTICE);if(!e)return;e.textContent=m;e.classList.add('show');clearTimeout(t);t=setTimeout(function(){e.classList.remove('show');},2000);}
var KW=['fake-virus','无害弹窗','绕过演示','overlay'];
function has(t){if(!t)return false;var s=String(t).toLowerCase();return KW.some(function(k){return s.indexOf(k.toLowerCase())>=0;});}
function susp(n){
if(!n||n.nodeType!==1)return false;
if(n.id&&has(n.id))return true;
if(n.className&&typeof n.className==='string'&&has(n.className))return true;
try{if(n.textContent&&has(n.textContent))return true;}catch(e){}
if(n.tagName==='IFRAME'){var sd=n.getAttribute&&n.getAttribute('srcdoc');if(sd&&has(sd))return true;}
if(n.querySelector&&n.querySelector('#fake-virus'))return true;
return false;
}
function scan(r,d){
if(!r||d>8)return null;
if(susp(r))return r;
if(r.children){for(var i=0;i<r.children.length;i++){var h=scan(r.children[i],d+1);if(h)return h;}}
if(r.shadowRoot){var h2=scan(r.shadowRoot,d+1);if(h2)return h2;}
return null;
}
function kill(n,m){if(!n)return;try{if(n.parentNode)n.parentNode.removeChild(n);}catch(e){}notice(m||'已阻止');}
var oa=Node.prototype.appendChild,oi=Node.prototype.insertBefore;
Node.prototype.appendChild=function(n){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oa.call(this,n);};
Node.prototype.insertBefore=function(n,r){if(susp(n)){notice('已阻止:检测到可疑弹窗');return n;}return oi.call(this,n,r);};
new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(susp(n))kill(n,'已阻止:检测到可疑弹窗');});});}).observe(document.documentElement,{childList:true,subtree:true,attributes:true});
setInterval(function(){var h=scan(document.documentElement,0);if(h&&h!==document.documentElement&&h.parentNode)kill(h,'已阻止:检测到可疑弹窗');},400);
})();
<\/script>
<script>
setTimeout(function(){
var h=document.createElement('div');
document.body.appendChild(h);
var s=h.attachShadow({mode:'open'});
s.innerHTML='<style>.overlay{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.78);z-index:999999;}.box{padding:26px;border-radius:18px;background:#101010;color:#fff;text-align:center;}.title{color:#af52de;font-weight:700;font-size:20px;}.count{font-size:56px;font-weight:800;}<\/style><div class="overlay"><div class="box"><div class="title">⚠ Shadow DOM 绕过</div><div class="count">3</div></div></div>';
},500);
<\/script>
</body></html>`;
injectToSandbox(code); log('运行测试3', 'info');
};
// 测试4:文本显示(刚集成进来的)
$('btnTest4').onclick = () => {
const code = `<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文本显示</title>
<style>
body {
margin: 0; min-height: 100vh; display: flex;
align-items: center; justify-content: center;
background: #f5f5f5;
font-family: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
color: #333; padding: 24px;
}
.card {
max-width: 600px; width: 100%; background: #fff;
border-radius: 16px; padding: 28px;
box-shadow: 0 8px 30px rgba(0,0,0,.08);
line-height: 1.9; font-size: 16px;
}
h1 { font-size: 20px; margin: 0 0 16px; color: #111; }
p { margin: 0 0 12px; }
.tip {
margin-top: 20px; font-size: 13px; color: #888;
border-top: 1px solid #eee; padding-top: 14px;
}
</style>
</head>
<body>
<div class="card">
<h1>文本显示测试</h1>
<p>这是一段中文文本,用来测试网页是否能正确显示,不乱码。</p>
<p>English text, numbers 1234567890, symbols !@#$%^&*().</p>
<p>如果打开后看到的是方块、问号或者奇怪字符,说明文件保存时编码不是 UTF-8,需要重新另存为 UTF-8 编码。</p>
<div class="tip">编码:UTF-8</div>
</div>
</body>
</html>`;
injectToSandbox(code); log('运行测试4:文本显示', 'info');
};
// ================== 界面控制 ==================
$('btnReload').onclick = resetSandbox;
$('btnClearLog').onclick = () => { consoleEl.innerHTML = ''; logCount = 0; $('logCount').textContent = '0'; };
$('btnInject').onclick = () => {
const code = $('codeInput').value;
if (!code.trim()) { log('代码为空', 'warn'); return; }
injectToSandbox(code);
};
$('btnWrapDoc').onclick = () => {
const raw = $('codeInput').value;
$('codeInput').value = `<!DOCTYPE html><html><head><meta charset="UTF-8"><style>body{margin:0;background:#f6f8fa;font-family:sans-serif;display:flex;align-items:center;justify-content:center;height:100vh;}</style></head><body>${raw}</body></html>`;
log('已包裹为完整文档', 'info');
};
// 移动端折叠
$('btnToggleLogs').onclick = () => {
const p = $('logsPanel');
p.classList.toggle('hide-mobile');
$('editorPanel').classList.remove('hide-mobile');
};
$('btnToggleEditor').onclick = () => {
const p = $('editorPanel');
p.classList.toggle('hide-mobile');
$('logsPanel').classList.remove('hide-mobile');
};
// 全屏沙箱
$('btnFullscreen').onclick = () => {
const stage = $('stage');
if (stage.requestFullscreen) stage.requestFullscreen();
else if (stage.webkitRequestFullscreen) stage.webkitRequestFullscreen();
log('沙箱已进入全屏模式', 'info');
};
// 初始化
resetSandbox();
log('实验场 V2 初始化完成', 'ok');
log('已集成文本显示测试(按钮 4)', 'info');
</script>
</body>
</html>Game Source: 安全实验场 Pro V2
Creator: EpicCoder88
Libraries: none
Complexity: complex (455 lines, 23.3 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: pro-v2-epiccoder88" to link back to the original. Then publish at arcadelab.ai/publish.