Add diff viewer line marking and submitting fixes by line
This commit is contained in:
parent
b4b7d989cb
commit
6596dc6d9b
15 changed files with 1610 additions and 82 deletions
211
dist/challenges/4/index.html
vendored
Normal file
211
dist/challenges/4/index.html
vendored
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
<!DOCTYPE html><html lang="en" class="dark" data-astro-cid-sckkx6r4> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width"><link rel="icon" type="image/svg+xml" href="/favicon.svg"><meta name="generator" content="Astro v6.1.7"><title>PR Dojo - Code Review Practice</title><link rel="stylesheet" href="/_astro/Layout.D3ovbguN.css">
|
||||
<style>tr[data-astro-cid-2ex44dsa]{line-height:1.6}tr[data-astro-cid-2ex44dsa]:hover{background-color:#161b22}td[data-astro-cid-2ex44dsa]:first-child{border-right:1px solid #30363d}tr[data-astro-cid-2ex44dsa].bugged td[data-astro-cid-2ex44dsa]:first-child{border-left:3px solid #f85149}tr[data-astro-cid-2ex44dsa].bugged .line-number[data-astro-cid-2ex44dsa]{color:#f85149}tr[data-astro-cid-2ex44dsa].bugged{background-color:#f8514918}#fix-editor-container[data-astro-cid-7fgtuneg] textarea[data-astro-cid-7fgtuneg]{resize:vertical;min-height:48px}
|
||||
</style></head> <body data-astro-cid-sckkx6r4> <div class="min-h-screen" data-astro-cid-7fgtuneg> <!-- Header --> <header class="bg-[#161b22] border-b border-[#30363d]" data-astro-cid-7fgtuneg> <div class="max-w-6xl mx-auto px-4 py-3 flex items-center gap-4" data-astro-cid-7fgtuneg> <a href="/" class="text-[#58a6ff] text-sm font-medium no-underline focus:outline-none focus:ring-0" data-astro-cid-7fgtuneg>← Back to Challenges</a> <span class="text-[#8b949e] text-sm" data-astro-cid-7fgtuneg>/ Challenge #4</span> </div> </header> <main class="max-w-6xl mx-auto px-4 py-6" data-astro-cid-7fgtuneg> <!-- Challenge Header --> <div class="bg-[#161b22] border border-[#30363d] rounded-md p-6 mb-6" data-astro-cid-7fgtuneg> <div class="flex justify-between items-start mb-4" data-astro-cid-7fgtuneg> <div data-astro-cid-7fgtuneg> <h1 class="text-2xl font-semibold text-[#c9d1d9] mb-2 no-underline" data-astro-cid-7fgtuneg>SQL Injection Vulnerability</h1> <div class="mb-4" data-astro-cid-7fgtuneg> <span class="text-[#79c0ff] text-xl no-underline" data-astro-cid-7fgtuneg>★★★★★</span> </div> <div class="flex flex-wrap items-center gap-4 text-sm" data-astro-cid-7fgtuneg> <span class="text-[#8b949e]" data-astro-cid-7fgtuneg>📁 <span class="text-[#a5d6ff]" data-astro-cid-7fgtuneg>django/django</span></span> <span class="text-[#8b949e]" data-astro-cid-7fgtuneg>🔢 <code class="bg-[#21262d] px-2 py-0.5 rounded text-xs" data-astro-cid-7fgtuneg>jkl3456</code></span> <span class="px-2 py-0.5 bg-[#0a3064] rounded text-xs text-[#79c0ff]" data-astro-cid-7fgtuneg>Security</span> </div> </div> <div class="text-right" data-astro-cid-7fgtuneg> <div class="text-[#79c0ff] text-lg mb-1 no-underline" data-astro-cid-7fgtuneg>★★★★★</div> <div class="text-xs text-[#8b949e]" data-astro-cid-7fgtuneg>Difficulty</div> </div> </div> <div class="bg-[#0a3064] rounded p-3 flex items-center gap-2" data-astro-cid-7fgtuneg> <svg class="w-5 h-5 text-[#58a6ff]" fill="currentColor" viewBox="0 0 20 20" data-astro-cid-7fgtuneg> <path d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-11a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V7z" data-astro-cid-7fgtuneg></path> </svg> <span class="text-[#79c0ff] font-semibold" data-astro-cid-7fgtuneg>+200 XP reward</span> </div> </div> <!-- Code Viewer --> <div id="dv-ikbmd7r" class="diff-viewer-container" data-dv-id="dv-ikbmd7r" data-astro-cid-2ex44dsa> <div class="bg-[#0d1117] border border-[#30363d] rounded-md overflow-hidden" data-astro-cid-2ex44dsa> <div class="bg-[#161b22] px-4 py-3 border-b border-[#30363d]" data-astro-cid-2ex44dsa> <span class="text-sm text-[#8b949e]" data-astro-cid-2ex44dsa>file.js</span> </div> <div class="overflow-x-auto" data-astro-cid-2ex44dsa> <table class="w-full font-mono text-sm" data-astro-cid-2ex44dsa> <tbody data-astro-cid-2ex44dsa> <tr data-line="1" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 1 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number " data-astro-cid-2ex44dsa> 1 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] " data-astro-cid-2ex44dsa> function findUserByUsername(username) { </span> </td> </tr><tr data-line="2" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 2 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number text-[#ff7b72] font-semibold" data-astro-cid-2ex44dsa> 2 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] bg-[#ff7b7233] px-2 -mx-2" data-astro-cid-2ex44dsa> const query = `SELECT * FROM users WHERE username = '${username}'`; </span> <span class="ml-2 hint text-[#79c0ff] text-xs opacity-0 group-hover:opacity-100 transition-opacity" data-astro-cid-2ex44dsa>
|
||||
• Direct string interpolation allows SQL injection </span> </td> </tr><tr data-line="3" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 3 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number " data-astro-cid-2ex44dsa> 3 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] " data-astro-cid-2ex44dsa> return database.query(query); </span> </td> </tr><tr data-line="4" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 4 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number " data-astro-cid-2ex44dsa> 4 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] " data-astro-cid-2ex44dsa> } </span> </td> </tr><tr data-line="5" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 5 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number " data-astro-cid-2ex44dsa> 5 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] " data-astro-cid-2ex44dsa> </span> </td> </tr><tr data-line="6" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 6 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number " data-astro-cid-2ex44dsa> 6 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] " data-astro-cid-2ex44dsa> function deleteUser(userId) { </span> </td> </tr><tr data-line="7" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 7 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number text-[#ff7b72] font-semibold" data-astro-cid-2ex44dsa> 7 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] bg-[#ff7b7233] px-2 -mx-2" data-astro-cid-2ex44dsa> const query = `DELETE FROM users WHERE id = ${userId}`; </span> <span class="ml-2 hint text-[#79c0ff] text-xs opacity-0 group-hover:opacity-100 transition-opacity" data-astro-cid-2ex44dsa>
|
||||
• Second SQL injection vulnerability in delete operation </span> </td> </tr><tr data-line="8" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 8 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number " data-astro-cid-2ex44dsa> 8 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] " data-astro-cid-2ex44dsa> return database.query(query); </span> </td> </tr><tr data-line="9" class="group hover:bg-[#161b22] cursor-pointer transition-colors" title="Mark line 9 as buggy" data-astro-cid-2ex44dsa> <td class="w-16 text-right pr-4 select-none" data-astro-cid-2ex44dsa> <span class="text-[#8b949e] line-number " data-astro-cid-2ex44dsa> 9 </span> </td> <td class="px-4 py-0.5 whitespace-pre" data-astro-cid-2ex44dsa> <span class="text-[#e6edf3] " data-astro-cid-2ex44dsa> } </span> </td> </tr> </tbody> </table> </div> <div class="bg-[#161b22] px-4 py-3 border-t border-[#30363d]" data-astro-cid-2ex44dsa> <h3 class="text-sm font-semibold text-[#c9d1d9] mb-2" data-astro-cid-2ex44dsa>Hints:</h3> <ul class="space-y-1" data-astro-cid-2ex44dsa> <li class="text-sm text-[#8b949e] flex items-start gap-2" data-astro-cid-2ex44dsa> <span class="text-[#79c0ff] mt-0.5" data-astro-cid-2ex44dsa>•</span> <span data-astro-cid-2ex44dsa>Line 2: Direct string interpolation allows SQL injection</span> </li><li class="text-sm text-[#8b949e] flex items-start gap-2" data-astro-cid-2ex44dsa> <span class="text-[#79c0ff] mt-0.5" data-astro-cid-2ex44dsa>•</span> <span data-astro-cid-2ex44dsa>Line 7: Second SQL injection vulnerability in delete operation</span> </li><li class="text-sm text-[#8b949e] flex items-start gap-2" data-astro-cid-2ex44dsa> <span class="text-[#79c0ff] mt-0.5" data-astro-cid-2ex44dsa>•</span> <span data-astro-cid-2ex44dsa>Should use parameterized queries instead</span> </li> </ul> </div> </div> </div> <script client:load>
|
||||
(() => {
|
||||
const el = document.querySelector('[data-dv-id]');
|
||||
if (!el) return;
|
||||
|
||||
const buggedLines = new Set();
|
||||
|
||||
function updateBuggedState() {
|
||||
const rows = el.querySelectorAll('tr[data-line]');
|
||||
rows.forEach(row => {
|
||||
const lineNum = parseInt(row.getAttribute('data-line') || '0', 10);
|
||||
row.classList.toggle('bugged', buggedLines.has(lineNum));
|
||||
});
|
||||
}
|
||||
|
||||
el.addEventListener('click', function(e) {
|
||||
const target = e.target;
|
||||
if (!(target instanceof HTMLElement)) return;
|
||||
const row = target.closest('tr[data-line]');
|
||||
if (!row) return;
|
||||
|
||||
const lineNum = parseInt(row.getAttribute('data-line') || '0', 10);
|
||||
if (buggedLines.has(lineNum)) {
|
||||
buggedLines.delete(lineNum);
|
||||
} else {
|
||||
buggedLines.add(lineNum);
|
||||
}
|
||||
|
||||
updateBuggedState();
|
||||
|
||||
el.dispatchEvent(new CustomEvent('bugged-line-toggle', {
|
||||
bubbles: true,
|
||||
detail: { line: lineNum, bugged: buggedLines.has(lineNum), allBugged: [...buggedLines] }
|
||||
}));
|
||||
});
|
||||
})();
|
||||
</script> <!-- Fix Submission --> <div id="fix-panel" class="mt-6 bg-[#161b22] border border-[#30363d] rounded-md overflow-hidden" data-astro-cid-7fgtuneg> <div class="bg-[#161b22] px-4 py-3 border-b border-[#30363d] flex items-center justify-between" data-astro-cid-7fgtuneg> <h2 class="text-lg font-semibold text-[#c9d1d9]" data-astro-cid-7fgtuneg>Submit Your Fix</h2> <div id="bugged-count" class="text-sm text-[#8b949e]" data-astro-cid-7fgtuneg>
|
||||
Lines marked: <span id="bugged-count-num" class="text-[#f85149] font-semibold" data-astro-cid-7fgtuneg>0</span> </div> </div> <!-- Marked lines summary --> <div id="marked-lines-summary" class="px-4 py-3 border-b border-[#30363d] hidden" data-astro-cid-7fgtuneg> <p class="text-sm text-[#8b949e] mb-2" data-astro-cid-7fgtuneg>Marked lines:</p> <div id="marked-lines-tags" class="flex flex-wrap gap-2" data-astro-cid-7fgtuneg></div> </div> <!-- Inline fix editor --> <div class="p-4" data-astro-cid-7fgtuneg> <div class="mb-4" data-astro-cid-7fgtuneg> <label class="block text-sm font-medium text-[#c9d1d9] mb-2" data-astro-cid-7fgtuneg>
|
||||
Line-by-line fix:
|
||||
</label> <p class="text-xs text-[#8b949e] mb-3" data-astro-cid-7fgtuneg>Click a marked line number below to edit its fix.</p> <div id="fix-editor-container" class="space-y-2" data-astro-cid-7fgtuneg> <p id="fix-editor-placeholder" class="text-sm text-[#484f58] italic" data-astro-cid-7fgtuneg>Click on lines in the diff above to mark them, then add your fixes here.</p> </div> </div> <!-- Unified diff textarea --> <div class="mb-4" data-astro-cid-7fgtuneg> <label class="block text-sm font-medium text-[#c9d1d9] mb-2" data-astro-cid-7fgtuneg>
|
||||
Or provide as unified diff:
|
||||
</label> <textarea id="diff-textarea" placeholder="- old line
|
||||
+ new line" rows="6" class="w-full bg-[#0d1117] border border-[#30363d] text-[#c9d1d9] rounded-md px-3 py-2 focus:outline-none focus:ring-2 focus:ring-[#58a6ff] focus:border-transparent text-sm font-mono" data-astro-cid-7fgtuneg></textarea> </div> <div class="flex gap-3" data-astro-cid-7fgtuneg> <button id="submit-fix-btn" class="flex-1 bg-[#1f6feb] hover:bg-[#388bfd] disabled:bg-[#21262d] disabled:text-[#484f58] disabled:cursor-not-allowed text-white font-semibold py-2.5 px-4 rounded-md transition-colors focus:outline-none focus:ring-0" data-astro-cid-7fgtuneg>
|
||||
Submit Fix
|
||||
</button> <button id="clear-all-btn" class="bg-[#21262d] hover:bg-[#30363d] text-[#8b949e] font-semibold py-2.5 px-4 rounded-md transition-colors focus:outline-none focus:ring-0" data-astro-cid-7fgtuneg>
|
||||
Clear All
|
||||
</button> </div> <div id="submission-message" class="mt-3 text-sm hidden" data-astro-cid-7fgtuneg></div> </div> </div> </main> </div> </body></html> <script client:load>
|
||||
(() => {
|
||||
const buggedLines = new Set();
|
||||
const fixEditorContainer = document.getElementById('fix-editor-container');
|
||||
const markedLinesTags = document.getElementById('marked-lines-tags');
|
||||
const markedLinesSummary = document.getElementById('marked-lines-summary');
|
||||
const buggedCountNum = document.getElementById('bugged-count-num');
|
||||
const submitBtn = document.getElementById('submit-fix-btn');
|
||||
const clearBtn = document.getElementById('clear-all-btn');
|
||||
const diffTextarea = document.getElementById('diff-textarea');
|
||||
const submissionMessage = document.getElementById('submission-message');
|
||||
const fixEditorPlaceholder = document.getElementById('fix-editor-placeholder');
|
||||
|
||||
function updateUI() {
|
||||
const arr = [...buggedLines].sort((a, b) => a - b);
|
||||
buggedCountNum.textContent = arr.length.toString();
|
||||
|
||||
if (markedLinesTags) {
|
||||
markedLinesTags.innerHTML = '';
|
||||
arr.forEach(lineNum => {
|
||||
const tag = document.createElement('span');
|
||||
tag.className = 'inline-flex items-center gap-1 bg-[#f8514920] text-[#f85149] text-xs px-2 py-0.5 rounded cursor-pointer hover:bg-[#f8514930] transition-colors';
|
||||
tag.textContent = `Line ${lineNum}`;
|
||||
tag.addEventListener('click', () => {
|
||||
showLineFixEditor(lineNum);
|
||||
});
|
||||
markedLinesTags.appendChild(tag);
|
||||
});
|
||||
}
|
||||
|
||||
if (markedLinesSummary) {
|
||||
markedLinesSummary.classList.toggle('hidden', arr.length === 0);
|
||||
}
|
||||
|
||||
if (fixEditorPlaceholder) {
|
||||
fixEditorPlaceholder.classList.toggle('hidden', arr.length > 0);
|
||||
}
|
||||
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = arr.length === 0 && !diffTextarea?.value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
function showLineFixEditor(lineNum) {
|
||||
if (!fixEditorContainer) return;
|
||||
|
||||
const existing = document.getElementById(`fix-editor-${lineNum}`);
|
||||
if (existing) {
|
||||
existing.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
||||
existing.querySelector('textarea')?.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
const editorDiv = document.createElement('div');
|
||||
editorDiv.id = `fix-editor-${lineNum}`;
|
||||
editorDiv.className = 'bg-[#0d1117] border border-[#30363d] rounded-md p-3';
|
||||
|
||||
const header = document.createElement('div');
|
||||
header.className = 'flex items-center justify-between mb-2';
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.className = 'text-sm font-medium text-[#f85149]';
|
||||
label.textContent = `Line ${lineNum} — Your fix:`;
|
||||
|
||||
const removeBtn = document.createElement('button');
|
||||
removeBtn.className = 'text-xs text-[#484f58] hover:text-[#f85149] transition-colors';
|
||||
removeBtn.textContent = '✕';
|
||||
removeBtn.addEventListener('click', () => {
|
||||
buggedLines.delete(lineNum);
|
||||
editorDiv.remove();
|
||||
updateUI();
|
||||
});
|
||||
|
||||
header.appendChild(label);
|
||||
header.appendChild(removeBtn);
|
||||
|
||||
const textarea = document.createElement('textarea');
|
||||
textarea.rows = 3;
|
||||
textarea.className = 'w-full bg-[#0d1117] border border-[#30363d] text-[#c9d1d9] rounded px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-[#58a6ff] focus:border-transparent text-sm font-mono';
|
||||
textarea.placeholder = 'Enter the corrected line...';
|
||||
textarea.addEventListener('input', updateUI);
|
||||
|
||||
editorDiv.appendChild(header);
|
||||
editorDiv.appendChild(textarea);
|
||||
|
||||
fixEditorContainer.appendChild(editorDiv);
|
||||
}
|
||||
|
||||
// Listen for bugged-line-toggle events from DiffViewer
|
||||
document.addEventListener('bugged-line-toggle', (e) => {
|
||||
const detail = e.detail;
|
||||
if (detail.bugged) {
|
||||
buggedLines.add(detail.line);
|
||||
showLineFixEditor(detail.line);
|
||||
} else {
|
||||
buggedLines.delete(detail.line);
|
||||
const editor = document.getElementById(`fix-editor-${detail.line}`);
|
||||
if (editor) editor.remove();
|
||||
}
|
||||
updateUI();
|
||||
});
|
||||
|
||||
// Diff textarea input
|
||||
if (diffTextarea) {
|
||||
diffTextarea.addEventListener('input', updateUI);
|
||||
}
|
||||
|
||||
// Submit
|
||||
if (submitBtn) {
|
||||
submitBtn.addEventListener('click', () => {
|
||||
const lineFixes = {};
|
||||
buggedLines.forEach(lineNum => {
|
||||
const editor = document.getElementById(`fix-editor-${lineNum}`);
|
||||
if (editor) {
|
||||
const ta = editor.querySelector('textarea');
|
||||
if (ta?.value.trim()) {
|
||||
lineFixes[lineNum] = ta.value.trim();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const unifiedDiff = diffTextarea?.value.trim() || '';
|
||||
|
||||
if (Object.keys(lineFixes).length === 0 && !unifiedDiff) {
|
||||
if (submissionMessage) {
|
||||
submissionMessage.textContent = 'Please mark some lines or provide a diff.';
|
||||
submissionMessage.className = 'mt-3 text-sm text-[#f85149]';
|
||||
submissionMessage.classList.remove('hidden');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Show success message
|
||||
if (submissionMessage) {
|
||||
submissionMessage.textContent = 'Fix submitted! (Backend integration coming soon)';
|
||||
submissionMessage.className = 'mt-3 text-sm text-[3ac840]';
|
||||
submissionMessage.classList.remove('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Clear all
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener('click', () => {
|
||||
buggedLines.clear();
|
||||
const editors = fixEditorContainer?.querySelectorAll('[id^="fix-editor-"]');
|
||||
editors?.forEach(e => e.remove());
|
||||
if (diffTextarea) diffTextarea.value = '';
|
||||
if (submissionMessage) submissionMessage.classList.add('hidden');
|
||||
updateUI();
|
||||
|
||||
// Reset bugged state in DiffViewer
|
||||
document.querySelectorAll('tr.bugged').forEach(row => {
|
||||
row.classList.remove('bugged');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateUI();
|
||||
})();
|
||||
</script>
|
||||
Loading…
Add table
Add a link
Reference in a new issue