Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>SecretsAuditEnv - Debug UI</title> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| body { | |
| font-family: 'Segoe UI', system-ui, monospace; | |
| background: #0d1117; | |
| color: #c9d1d9; | |
| padding: 1.5rem 2rem; | |
| min-height: 100vh; | |
| } | |
| h1 { color: #58a6ff; font-size: 1.5rem; margin-bottom: 0.25rem; } | |
| .subtitle { color: #8b949e; font-size: 0.85rem; margin-bottom: 1.5rem; } | |
| h2 { | |
| color: #f0883e; | |
| font-size: 1rem; | |
| border-bottom: 1px solid #30363d; | |
| padding-bottom: 4px; | |
| margin: 1.25rem 0 0.6rem; | |
| } | |
| #status { | |
| background: #161b22; | |
| border: 1px solid #30363d; | |
| border-left: 3px solid #3fb950; | |
| border-radius: 4px; | |
| padding: 0.5rem 0.75rem; | |
| font-size: 0.85rem; | |
| color: #3fb950; | |
| margin-bottom: 1rem; | |
| } | |
| button { | |
| background: #238636; | |
| color: #fff; | |
| border: none; | |
| padding: 7px 14px; | |
| cursor: pointer; | |
| border-radius: 5px; | |
| margin: 3px; | |
| font-size: 13px; | |
| font-weight: 600; | |
| transition: background 0.15s; | |
| } | |
| button:hover { background: #2ea043; } | |
| button.danger { background: #da3633; } | |
| button.danger:hover { background: #f85149; } | |
| button.secondary { background: #21262d; border: 1px solid #30363d; } | |
| button.secondary:hover { background: #30363d; } | |
| pre { | |
| background: #161b22; | |
| padding: 0.75rem 1rem; | |
| border-radius: 6px; | |
| border: 1px solid #30363d; | |
| white-space: pre-wrap; | |
| word-break: break-all; | |
| font-size: 12px; | |
| max-height: 340px; | |
| overflow-y: auto; | |
| } | |
| .action-row { display: flex; gap: 8px; margin: 6px 0; align-items: center; flex-wrap: wrap; } | |
| select, input[type=text] { | |
| background: #161b22; | |
| border: 1px solid #30363d; | |
| color: #c9d1d9; | |
| padding: 6px 10px; | |
| border-radius: 4px; | |
| font-family: 'Courier New', monospace; | |
| font-size: 13px; | |
| } | |
| select { min-width: 180px; } | |
| input[type=text] { width: 320px; } | |
| .grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; } | |
| .metric-row { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: 0.5rem; } | |
| .metric { | |
| background: #161b22; | |
| border: 1px solid #30363d; | |
| border-radius: 6px; | |
| padding: 0.5rem 0.9rem; | |
| min-width: 120px; | |
| text-align: center; | |
| } | |
| .metric .val { font-size: 1.4rem; font-weight: 700; color: #58a6ff; } | |
| .metric .lbl { font-size: 11px; color: #8b949e; margin-top: 2px; } | |
| .hint { color: #8b949e; font-size: 12px; margin-top: 4px; } | |
| @media (max-width: 760px) { .grid { grid-template-columns: 1fr; } } | |
| .task-grid { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; } | |
| .task-grid button { font-size: 12px; padding: 5px 10px; } | |
| .task-grid button.easy-btn { background: #1f6feb; } | |
| .task-grid button.easy-btn:hover { background: #388bfd; } | |
| .task-grid button.med-btn { background: #9e6a03; } | |
| .task-grid button.med-btn:hover { background: #bb8009; } | |
| .task-grid button.hard-btn { background: #b62324; } | |
| .task-grid button.hard-btn:hover { background: #da3633; } | |
| .category-label { color: #8b949e; font-size: 11px; font-weight: 700; text-transform: uppercase; margin-right: 6px; min-width: 55px; display: inline-block; padding-top: 7px; } | |
| .task-row { display: flex; align-items: flex-start; margin-bottom: 4px; } | |
| .how-it-works { | |
| background: #161b22; | |
| border: 1px solid #30363d; | |
| border-radius: 6px; | |
| padding: 0.75rem 1rem; | |
| font-size: 12px; | |
| color: #8b949e; | |
| margin-bottom: 1rem; | |
| } | |
| .how-it-works b { color: #c9d1d9; } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>SecretsAuditEnv</h1> | |
| <div class="subtitle">Judge Debug UI - interactive environment walkthrough</div> | |
| <div class="how-it-works"> | |
| <b>How this works:</b> You are the agent. Pick a task below to start an episode. | |
| Then use the Manual Action section to send commands (inspect files, fix secrets, run bash). | |
| The environment grades your work after every action and updates the metrics. | |
| Goal: get Reward to 1.0 by finding and fixing all secret leaks. | |
| </div> | |
| <div id="status">Ready. Pick a task below to start.</div> | |
| <h2>Pick a Task (all 13 tasks)</h2> | |
| <div class="task-row"> | |
| <span class="category-label">Easy</span> | |
| <div class="task-grid" id="easyTasks"></div> | |
| </div> | |
| <div class="task-row"> | |
| <span class="category-label">Medium</span> | |
| <div class="task-grid" id="medTasks"></div> | |
| </div> | |
| <div class="task-row"> | |
| <span class="category-label">Hard</span> | |
| <div class="task-grid" id="hardTasks"></div> | |
| </div> | |
| <button class="danger" id="btnReset" style="margin-top:8px">Reset UI</button> | |
| <h2>Metrics</h2> | |
| <div class="metric-row"> | |
| <div class="metric"><div class="val" id="mReward">-</div><div class="lbl">Reward</div></div> | |
| <div class="metric"><div class="val" id="mLeaks">-</div><div class="lbl">Leaks</div></div> | |
| <div class="metric"><div class="val" id="mHidden">-</div><div class="lbl">Hidden</div></div> | |
| <div class="metric"><div class="val" id="mSteps">-</div><div class="lbl">Steps Left</div></div> | |
| <div class="metric"><div class="val" id="mEfficiency">-</div><div class="lbl">Efficiency Bonus</div></div> | |
| <div class="metric"><div class="val" id="mHealth">-</div><div class="lbl">Health</div></div> | |
| </div> | |
| <h2>Manual Action</h2> | |
| <div class="action-row"> | |
| <select id="actionSelect"> | |
| <option value="inspect_file">inspect_file (reveal hidden secrets in a file)</option> | |
| <option value="inspect_git_history">inspect_git_history (scan git commits)</option> | |
| <option value="inspect_encoded">inspect_encoded (decode base64 blobs)</option> | |
| <option value="bash">bash command (sed, cat, grep, etc.)</option> | |
| </select> | |
| <input type="text" id="actionArgs" placeholder="config.py" value="" /> | |
| <button id="btnSend">Send</button> | |
| <button class="secondary" id="btnSuggested">Use top suggestion</button> | |
| </div> | |
| <div class="hint" id="actionHint"> | |
| <b>inspect_file</b>: type a filename like <code>config.py</code> | | |
| <b>bash</b>: type any shell command like <code>cat config.py</code> or <code>sed -i 's/OLD/NEW/' config.py</code> | |
| </div> | |
| <div class="grid"> | |
| <div> | |
| <h2>Top Blocker</h2> | |
| <pre id="blockerBox">-</pre> | |
| <h2>Ranked Actions (heuristic)</h2> | |
| <pre id="rankedBox">-</pre> | |
| </div> | |
| <div> | |
| <h2>Visible Secrets</h2> | |
| <pre id="secretsBox">-</pre> | |
| <h2>Conflict Map</h2> | |
| <pre id="conflictBox">-</pre> | |
| </div> | |
| </div> | |
| <h2>Full Observation</h2> | |
| <pre id="obsBox">-</pre> | |
| <h2>Action Log</h2> | |
| <pre id="logBox">No actions yet.</pre> | |
| <script> | |
| var episodeId = null; | |
| var logs = []; | |
| var topRanked = []; | |
| // --- All 13 tasks --- | |
| var ALL_TASKS = [ | |
| {id: 1, cat: "easy", title: "Cloud Provisioning"}, | |
| {id: 2, cat: "easy", title: "Database Layer"}, | |
| {id: 3, cat: "easy", title: "Frontend Config"}, | |
| {id: 4, cat: "easy", title: "System Logging"}, | |
| {id: 5, cat: "easy", title: "Git Basics"}, | |
| {id: 6, cat: "medium", title: "Utility Module"}, | |
| {id: 7, cat: "medium", title: "CI/CD Pipeline"}, | |
| {id: 8, cat: "medium", title: "Noise Filtering"}, | |
| {id: 9, cat: "medium", title: "DB Migration"}, | |
| {id: 10, cat: "medium", title: "Deployment"}, | |
| {id: 11, cat: "hard", title: "Microservices"}, | |
| {id: 12, cat: "hard", title: "Deep Logic"}, | |
| {id: 13, cat: "hard", title: "Legacy Audit"} | |
| ]; | |
| function buildTaskButtons() { | |
| var containers = {easy: "easyTasks", medium: "medTasks", hard: "hardTasks"}; | |
| var btnClass = {easy: "easy-btn", medium: "med-btn", hard: "hard-btn"}; | |
| for (var i = 0; i < ALL_TASKS.length; i++) { | |
| var t = ALL_TASKS[i]; | |
| var btn = document.createElement("button"); | |
| btn.className = btnClass[t.cat]; | |
| btn.textContent = "#" + t.id + " " + t.title; | |
| btn.setAttribute("data-task-id", t.id); | |
| btn.addEventListener("click", function(e) { | |
| var taskId = parseInt(e.target.getAttribute("data-task-id")); | |
| newEpisode(taskId); | |
| }); | |
| document.getElementById(containers[t.cat]).appendChild(btn); | |
| } | |
| } | |
| buildTaskButtons(); | |
| document.getElementById("btnReset").addEventListener("click", function() { hardReset(); }); | |
| document.getElementById("btnSend").addEventListener("click", function() { takeAction(); }); | |
| document.getElementById("btnSuggested").addEventListener("click", function() { useSuggested(); }); | |
| function newEpisode(taskId) { | |
| document.getElementById("status").textContent = "Starting task " + taskId + "..."; | |
| fetch("/reset", { | |
| method: "POST", | |
| headers: {"Content-Type": "application/json"}, | |
| body: JSON.stringify({"task_id": taskId}) | |
| }) | |
| .then(function(res) { return res.json(); }) | |
| .then(function(data) { | |
| var s = data.session || {}; | |
| episodeId = s.session_id; | |
| logs = []; | |
| document.getElementById("status").textContent = | |
| "Task " + s.task_id + " (" + s.category + ") | " + s.title + " | " + s.description; | |
| renderState(data); | |
| document.getElementById("logBox").textContent = "No actions yet."; | |
| }) | |
| .catch(function(err) { | |
| document.getElementById("status").textContent = "Error: " + err.message; | |
| }); | |
| } | |
| function takeAction() { | |
| if (!episodeId) { | |
| alert("Start a task first! Click one of the task buttons above."); | |
| return; | |
| } | |
| var action = buildAction(); | |
| if (!action) { | |
| alert("Enter a file path or bash command in the text box"); | |
| return; | |
| } | |
| fetch("/step", { | |
| method: "POST", | |
| headers: {"Content-Type": "application/json"}, | |
| body: JSON.stringify({"action": action}) | |
| }) | |
| .then(function(res) { return res.json(); }) | |
| .then(function(data) { | |
| renderState(data); | |
| var s = data.session || {}; | |
| var reward = s.reward || 0; | |
| var done = (reward >= 1.0); | |
| logs.push( | |
| "[Step " + s.step_count + "] " + action + | |
| " | reward=" + reward + " done=" + done + | |
| " leaks=" + s.current_leaks + " hidden=" + s.hidden_count_hint | |
| ); | |
| document.getElementById("logBox").textContent = logs.slice().reverse().join("\n\n"); | |
| if (done) { | |
| document.getElementById("status").textContent = "SOLVED! Reward = 1.0 - All secrets fixed!"; | |
| } | |
| }) | |
| .catch(function(err) { | |
| document.getElementById("status").textContent = "Error: " + err.message; | |
| }); | |
| } | |
| function buildAction() { | |
| var sel = document.getElementById("actionSelect").value; | |
| var args = document.getElementById("actionArgs").value.trim(); | |
| if (sel === "inspect_file") return "inspect_file " + args; | |
| if (sel === "inspect_git_history") return "inspect_git_history " + (args || "."); | |
| if (sel === "inspect_encoded") return "inspect_encoded " + args; | |
| return args; | |
| } | |
| function useSuggested() { | |
| if (!topRanked || topRanked.length === 0) { | |
| alert("No suggestions yet. Start a task first."); | |
| return; | |
| } | |
| var top = topRanked[0]; | |
| var sel = document.getElementById("actionSelect"); | |
| var input = document.getElementById("actionArgs"); | |
| if (top.action === "inspect_file") { | |
| sel.value = "inspect_file"; | |
| input.value = (top.args && top.args.path) || ""; | |
| } else if (top.action === "inspect_git_history") { | |
| sel.value = "inspect_git_history"; | |
| input.value = (top.args && top.args.path) || "."; | |
| } else if (top.action === "inspect_encoded") { | |
| sel.value = "inspect_encoded"; | |
| input.value = ((top.args && top.args.path) || ".") + " " + ((top.args && top.args.line) || "0"); | |
| } else { | |
| sel.value = "bash"; | |
| input.value = top.action + " " + JSON.stringify(top.args || {}); | |
| } | |
| } | |
| function renderState(data) { | |
| var s = data.session || {}; | |
| document.getElementById("mReward").textContent = (s.reward != null) ? s.reward : "-"; | |
| document.getElementById("mLeaks").textContent = (s.current_leaks != null) ? s.current_leaks : "-"; | |
| document.getElementById("mHidden").textContent = (s.hidden_count_hint != null) ? s.hidden_count_hint : "-"; | |
| document.getElementById("mSteps").textContent = (s.steps_remaining != null) ? s.steps_remaining : "-"; | |
| document.getElementById("mEfficiency").textContent = (s.efficiency_bonus != null) ? s.efficiency_bonus : "-"; | |
| document.getElementById("mHealth").textContent = (s.health_score != null) ? s.health_score : "-"; | |
| document.getElementById("blockerBox").textContent = s.top_blocker || "-"; | |
| topRanked = s.ranked_actions || []; | |
| document.getElementById("rankedBox").textContent = JSON.stringify(topRanked, null, 2); | |
| document.getElementById("secretsBox").textContent = JSON.stringify(s.visible_secrets || [], null, 2); | |
| document.getElementById("conflictBox").textContent = JSON.stringify(s.conflict_map || {}, null, 2); | |
| document.getElementById("obsBox").textContent = s.observation || "-"; | |
| } | |
| function hardReset() { | |
| episodeId = null; | |
| logs = []; | |
| topRanked = []; | |
| document.getElementById("status").textContent = "Reset. Pick a task to start."; | |
| var boxes = ["obsBox", "logBox", "blockerBox", "rankedBox", "secretsBox", "conflictBox"]; | |
| for (var i = 0; i < boxes.length; i++) { | |
| document.getElementById(boxes[i]).textContent = "-"; | |
| } | |
| var metrics = ["mReward", "mLeaks", "mHidden", "mSteps", "mEfficiency", "mHealth"]; | |
| for (var j = 0; j < metrics.length; j++) { | |
| document.getElementById(metrics[j]).textContent = "-"; | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> | |