Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 146 additions & 2 deletions backend/app/services/code_assistant.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,8 +495,8 @@ class BugPattern:
# ── Java ──
BugPattern(
"Null Pointer Risk",
r"\w+\s*\.\s*\w+\s*\(",
"Method called on object that may be null — NullPointerException risk.",
r"=\s*null\b.{0,200}\.(\w+)\s*\(|\breturn\s+null\b",
"Variable assigned null then used, or method returns null directly — NullPointerException risk.",
"Add null check: `if (obj != null) { ... }` or use `Optional<T>`.",
"warning",
["Java"],
Expand Down Expand Up @@ -533,6 +533,78 @@ class BugPattern:
"error",
["Java"],
),
BugPattern(
"Empty Catch Block",
r"catch\s*\([^)]+\)\s*\{\s*\}",
"Empty catch block silently swallows exceptions — bugs become invisible.",
"At minimum log the exception: `e.printStackTrace()` or use a logger.",
"warning",
["Java"],
),
BugPattern(
"printStackTrace Usage",
r"\.printStackTrace\s*\(\s*\)",
"`printStackTrace()` writes to stderr and is not suitable for production logging.",
"Use a proper logger: `logger.error(\"msg\", e)`.",
"warning",
["Java"],
),
BugPattern(
"Mutable Static Field",
r"\bstatic\b(?!.*\bfinal\b).*\b(List|Map|Set|ArrayList|HashMap|HashSet)\b",
"Mutable static field shared across all instances — thread-safety and state leak risk.",
"Make it `static final` and use an immutable collection, or use instance fields.",
"warning",
["Java"],
),
BugPattern(
"String Concatenation in Loop",
r"(for|while)[^{]*\{[^}]*\+=[^}]*\"",
"String concatenation with `+=` inside a loop creates many temporary objects — O(n²).",
"Use `StringBuilder` and call `.append()` inside the loop, then `.toString()` after.",
"warning",
["Java"],
),
BugPattern(
"Catching Throwable",
r"catch\s*\(\s*Throwable\s+\w+\s*\)",
"Catching `Throwable` includes `Error` subclasses like `OutOfMemoryError` — almost never correct.",
"Catch `Exception` or a specific checked exception instead.",
"error",
["Java"],
),
BugPattern(
"Hardcoded IP Address",
r"\"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\"",
"Hardcoded IP address found — breaks portability across environments.",
"Move the IP to a configuration file or environment variable.",
"warning",
["Java"],
),
BugPattern(
"Thread.sleep in Production",
r"Thread\.sleep\s*\(",
"`Thread.sleep()` blocks the thread and is a sign of polling anti-pattern.",
"Use `ScheduledExecutorService`, `CompletableFuture`, or event-driven design instead.",
"warning",
["Java"],
),
BugPattern(
"Ignoring Return Value",
r"^\s*\w+\.\w+\([^)]*\)\s*;\s*$",
"Return value of method call is ignored — may silently discard errors or results.",
"Assign the return value or verify the method has no meaningful return.",
"info",
["Java"],
),
BugPattern(
"Finalize Method",
r"protected\s+void\s+finalize\s*\(",
"`finalize()` is deprecated in Java 9+ and unreliable for resource cleanup.",
"Use `try-with-resources` or implement `AutoCloseable` instead.",
"warning",
["Java"],
),
# ── C++ ──
BugPattern(
"Memory Leak",
Expand Down Expand Up @@ -710,6 +782,78 @@ class BugPattern:
"error",
["C++", "Java", "JavaScript", "TypeScript"],
),
BugPattern(
"strcpy/strcat Usage",
r"\b(strcpy|strcat)\s*\(",
"`strcpy`/`strcat` do not check buffer size — classic buffer overflow vulnerability.",
"Use `strncpy`/`strncat` with explicit size, or prefer `std::string`.",
"error",
["C++"],
),
BugPattern(
"Missing Virtual Destructor",
r"class\s+\w+[^{]*\{[^}]*virtual[^}]*(?<!virtual\s+~\w+)[^}]*\}",
"Class with virtual methods is missing a virtual destructor — derived class resources may leak.",
"Add `virtual ~ClassName() = default;` to the base class.",
"error",
["C++"],
),
BugPattern(
"reinterpret_cast Usage",
r"\breinterpret_cast\s*<",
"`reinterpret_cast` bypasses the type system and is undefined behavior in most cases.",
"Use `static_cast` or redesign to avoid the cast entirely.",
"warning",
["C++"],
),
BugPattern(
"Double Delete Risk",
r"delete\s+\w+;[\s\S]{0,200}delete\s+\w+;",
"Multiple `delete` calls detected — double-free causes undefined behavior and crashes.",
"Set pointer to `nullptr` after delete, or use smart pointers to prevent this entirely.",
"error",
["C++"],
),
BugPattern(
"Null Pointer Dereference",
r"\*\s*nullptr|nullptr\s*->",
"Explicit null pointer dereference detected — immediate crash at runtime.",
"Always check pointer validity before dereferencing.",
"error",
["C++"],
),
BugPattern(
"sprintf Usage",
r"\bsprintf\s*\(",
"`sprintf` does not check buffer bounds — use `snprintf` with explicit size.",
"Replace with `snprintf(buf, sizeof(buf), ...)` to prevent buffer overflow.",
"error",
["C++"],
),
BugPattern(
"Catching All Exceptions",
r"catch\s*\(\.\.\.\)",
"`catch(...)` silently swallows all exceptions including system signals.",
"Catch specific exception types and log or rethrow appropriately.",
"warning",
["C++"],
),
BugPattern(
"Integer Overflow Risk",
r"\b(int|short)\s+\w+\s*\*\s*\w+",
"Multiplication of `int`/`short` values may silently overflow.",
"Cast to `long long` before multiplying: `(long long)a * b`.",
"warning",
["C++"],
),
BugPattern(
"Hardcoded File Path",
r'"(/home/|/tmp/|C:\\\\|/var/)',
"Hardcoded absolute file path — breaks portability across systems.",
"Use relative paths or read the path from a config/environment variable.",
"warning",
["C++", "Java"],
),
# ── PHP ──
BugPattern(
"PHP MySQL Deprecated",
Expand Down
4 changes: 3 additions & 1 deletion backend/tests/test_share.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from __future__ import annotations

from datetime import UTC, datetime, timedelta
from datetime import datetime, timedelta, timezone

UTC = timezone.utc

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
Expand Down
99 changes: 98 additions & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,44 @@
border-right: 1px solid var(--border);
}

.editor-inner {
position: relative;
flex: 1;
overflow: hidden;
}

#lintOverlay {
position: absolute;
top: 0; left: 0;
width: 100%;
pointer-events: none;
padding: 16px;
font-family: var(--font-mono);
font-size: 13px;
line-height: 1.7;
white-space: pre-wrap;
word-break: break-all;
overflow: hidden;
box-sizing: border-box;
color: transparent;
z-index: 2;
}

.lint-error-line {
display: block;
border-bottom: 2px solid var(--red);
background: rgba(242, 87, 87, 0.08);
}

.lint-warning-line {
display: block;
border-bottom: 2px solid var(--yellow);
background: rgba(245, 200, 66, 0.07);
}

#codeEditor {
position: relative;
z-index: 3;
width: 100%;
min-height: 300px;
max-height: 500px;
Expand Down Expand Up @@ -2188,7 +2225,10 @@ <h4 data-i18n="pill_upload_title">File Upload</h4>
<!-- Editor area (line numbers + code editor) -->
<div class="editor-wrap">
<div class="line-numbers" id="lineNumbers">1</div>
<textarea id="codeEditor" placeholder="# Paste your code here or click 'Try Sample Code' above…" aria-label="Code editor" style="width:100%"></textarea>
<div class="editor-inner">
<div id="lintOverlay" aria-hidden="true"></div>
<textarea id="codeEditor" placeholder="# Paste your code here or click 'Try Sample Code' above…" aria-label="Code editor" style="width:100%"></textarea>
</div>
</div>

<div class="editor-footer">
Expand Down Expand Up @@ -2946,9 +2986,11 @@ <h3 data-i18n="empty_suggest_title">No suggestions yet</h3>
editor.addEventListener('input', () => {
selectedZipFile = null;
updateEditor();
scheduleLint();
});
editor.addEventListener('scroll', () => {
lineNumbers.scrollTop = editor.scrollTop;
document.getElementById('lintOverlay').scrollTop = editor.scrollTop;
});

// Tab key support
Expand Down Expand Up @@ -4203,6 +4245,61 @@ <h3>${getTranslation('suggest_quality_score')}</h3>

window.loadSample = loadSample;

/* ═══════════════════════════════════════════════════════════════
REAL-TIME LINT
═══════════════════════════════════════════════════════════════ */
let _lintTimer = null;
let _lintAbort = null;

function scheduleLint() {
clearTimeout(_lintTimer);
_lintTimer = setTimeout(runLint, 500);
}

async function runLint() {
const code = editor.value;
if (!code.trim()) { clearLintOverlay(); return; }

if (_lintAbort) _lintAbort.abort();
_lintAbort = new AbortController();

try {
const base = getApiBase();
const res = await fetch(`${base}/debugging/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code, language: selectedLang }),
signal: _lintAbort.signal,
});
if (!res.ok) return;
const data = await res.json();
applyLintOverlay(code, data.issues || []);
} catch (e) {
if (e.name !== 'AbortError') clearLintOverlay();
}
}

function applyLintOverlay(code, issues) {
const overlay = document.getElementById('lintOverlay');
const errorLines = new Set(issues.filter(i => i.severity === 'error').map(i => i.line));
const warnLines = new Set(issues.filter(i => i.severity === 'warning').map(i => i.line));

const lines = code.split('\n');
overlay.innerHTML = lines.map((line, idx) => {
const lineNum = idx + 1;
const safe = escHtml(line) || ' ';
if (errorLines.has(lineNum)) return `<span class="lint-error-line">${safe}</span>`;
if (warnLines.has(lineNum)) return `<span class="lint-warning-line">${safe}</span>`;
return `<span>${safe}</span>`;
}).join('\n');

overlay.scrollTop = editor.scrollTop;
}

function clearLintOverlay() {
document.getElementById('lintOverlay').innerHTML = '';
}

/* ═══════════════════════════════════════════════════════════════
INIT
═══════════════════════════════════════════════════════════════ */
Expand Down