| <!DOCTYPE html> |
| <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 = []; |
| |
| |
| 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> |
|
|