Spaces:
Sleeping
Sleeping
Lucifer-cyber007 Claude commited on
Commit ·
2514bac
1
Parent(s): d4a0163
feat: add free code review endpoint and associated features
Browse files- Add new /review/free endpoint for arbitrary code review
- Implement free_review.py module with review_free_code function
- Update app.py with FreeReviewRequest and FreeReviewResponse models
- Update dashboard.html with free review functionality
- Update tasks.py with enhancements
This allows users to review any code without being confined to grading,
perfect for ad-hoc reviews and demonstrations.
Co-Authored-By: Claude <noreply@anthropic.com>
- app.py +62 -0
- dashboard.html +473 -0
- free_review.py +91 -0
- tasks.py +500 -168
- tmpclaude-46e1-cwd +1 -0
- tmpclaude-5602-cwd +1 -0
- tmpclaude-ee5f-cwd +1 -0
app.py
CHANGED
|
@@ -28,6 +28,7 @@ from models import (
|
|
| 28 |
from environment import CodeReviewEnv
|
| 29 |
from graders import grade_episode
|
| 30 |
from tasks import get_all_tasks
|
|
|
|
| 31 |
|
| 32 |
# ── App setup ─────────────────────────────────────────────────────────────
|
| 33 |
app = FastAPI(
|
|
@@ -251,6 +252,67 @@ def baseline(request: Optional[BaselineRequest] = None):
|
|
| 251 |
inference.parse_llm_response = original_parse
|
| 252 |
|
| 253 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 254 |
# ── Debug Route ─────────────────────────────────────────────────────────
|
| 255 |
|
| 256 |
@app.post("/debug-baseline", tags=["Debug"])
|
|
|
|
| 28 |
from environment import CodeReviewEnv
|
| 29 |
from graders import grade_episode
|
| 30 |
from tasks import get_all_tasks
|
| 31 |
+
from free_review import review_free_code
|
| 32 |
|
| 33 |
# ── App setup ─────────────────────────────────────────────────────────────
|
| 34 |
app = FastAPI(
|
|
|
|
| 252 |
inference.parse_llm_response = original_parse
|
| 253 |
|
| 254 |
|
| 255 |
+
# ── Free Review Route ───────────────────────────────────────────────────
|
| 256 |
+
|
| 257 |
+
class FreeReviewRequest(BaseModel):
|
| 258 |
+
code: str
|
| 259 |
+
language: Optional[str] = "python"
|
| 260 |
+
context: Optional[str] = ""
|
| 261 |
+
|
| 262 |
+
class FreeReviewResponse(BaseModel):
|
| 263 |
+
issues: list
|
| 264 |
+
overall_verdict: str
|
| 265 |
+
summary: str
|
| 266 |
+
positive_aspects: list
|
| 267 |
+
total_issues: int
|
| 268 |
+
critical_count: int
|
| 269 |
+
major_count: int
|
| 270 |
+
minor_count: int
|
| 271 |
+
error: Optional[str] = None
|
| 272 |
+
|
| 273 |
+
@app.post("/review/free", tags=["Free Review"])
|
| 274 |
+
def free_review(request: FreeReviewRequest):
|
| 275 |
+
"""
|
| 276 |
+
Review any arbitrary code using AI.
|
| 277 |
+
No grading — works on any code, any language.
|
| 278 |
+
Perfect for ad-hoc reviews and demos.
|
| 279 |
+
"""
|
| 280 |
+
result = review_free_code(
|
| 281 |
+
code=request.code,
|
| 282 |
+
language=request.language,
|
| 283 |
+
context=request.context
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
if "error" in result:
|
| 287 |
+
return FreeReviewResponse(
|
| 288 |
+
issues=[],
|
| 289 |
+
overall_verdict="error",
|
| 290 |
+
summary=result["error"],
|
| 291 |
+
positive_aspects=[],
|
| 292 |
+
total_issues=0,
|
| 293 |
+
critical_count=0,
|
| 294 |
+
major_count=0,
|
| 295 |
+
minor_count=0,
|
| 296 |
+
error=result["error"]
|
| 297 |
+
)
|
| 298 |
+
|
| 299 |
+
issues = result.get("issues", [])
|
| 300 |
+
return FreeReviewResponse(
|
| 301 |
+
issues=issues,
|
| 302 |
+
overall_verdict=result.get("overall_verdict", "comment"),
|
| 303 |
+
summary=result.get("summary", ""),
|
| 304 |
+
positive_aspects=result.get("positive_aspects", []),
|
| 305 |
+
total_issues=len(issues),
|
| 306 |
+
critical_count=sum(1 for i in issues
|
| 307 |
+
if i.get("severity") == "critical"),
|
| 308 |
+
major_count=sum(1 for i in issues
|
| 309 |
+
if i.get("severity") == "major"),
|
| 310 |
+
minor_count=sum(1 for i in issues
|
| 311 |
+
if i.get("severity") == "minor"),
|
| 312 |
+
error=None
|
| 313 |
+
)
|
| 314 |
+
|
| 315 |
+
|
| 316 |
# ── Debug Route ─────────────────────────────────────────────────────────
|
| 317 |
|
| 318 |
@app.post("/debug-baseline", tags=["Debug"])
|
dashboard.html
CHANGED
|
@@ -1609,6 +1609,248 @@
|
|
| 1609 |
stroke-width: 2;
|
| 1610 |
}
|
| 1611 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1612 |
/* ── TOAST ──────────────────────────────────────────────────────────── */
|
| 1613 |
#toast-container {
|
| 1614 |
position: fixed;
|
|
@@ -1927,8 +2169,13 @@
|
|
| 1927 |
<label class="field-label" for="task-select">Task Difficulty</label>
|
| 1928 |
<select id="task-select" aria-label="Select task difficulty">
|
| 1929 |
<option value="easy">Easy — Basic Bug Detection</option>
|
|
|
|
| 1930 |
<option value="medium">Medium — Security Review</option>
|
|
|
|
|
|
|
| 1931 |
<option value="hard">Hard — Concurrency Hunt</option>
|
|
|
|
|
|
|
| 1932 |
</select>
|
| 1933 |
|
| 1934 |
<button class="btn btn-purple btn-full" id="start-btn" style="margin-top:16px;" onclick="startReview()">
|
|
@@ -2074,6 +2321,112 @@
|
|
| 2074 |
</div>
|
| 2075 |
</section>
|
| 2076 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2077 |
<hr class="divider" />
|
| 2078 |
|
| 2079 |
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
|
@@ -2167,6 +2520,126 @@
|
|
| 2167 |
<button class="task-try-btn" id="try-hard-btn" onclick="jumpToDemo('hard')" aria-label="Try hard task">Try
|
| 2168 |
this task →</button>
|
| 2169 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2170 |
</div>
|
| 2171 |
</div>
|
| 2172 |
</section>
|
|
|
|
| 1609 |
stroke-width: 2;
|
| 1610 |
}
|
| 1611 |
|
| 1612 |
+
/* ── FREE REVIEW ────────────────────────────────────────────────────── */
|
| 1613 |
+
.free-review-container {
|
| 1614 |
+
display: grid;
|
| 1615 |
+
grid-template-columns: 45% 1fr;
|
| 1616 |
+
gap: 32px;
|
| 1617 |
+
margin-top: 32px;
|
| 1618 |
+
}
|
| 1619 |
+
|
| 1620 |
+
.free-input-panel {
|
| 1621 |
+
display: flex;
|
| 1622 |
+
flex-direction: column;
|
| 1623 |
+
gap: 16px;
|
| 1624 |
+
}
|
| 1625 |
+
|
| 1626 |
+
.free-editor-wrapper {
|
| 1627 |
+
position: relative;
|
| 1628 |
+
background: #0d1117;
|
| 1629 |
+
border: 1px solid var(--border);
|
| 1630 |
+
border-radius: var(--radius-md);
|
| 1631 |
+
overflow: hidden;
|
| 1632 |
+
flex: 1;
|
| 1633 |
+
display: flex;
|
| 1634 |
+
flex-direction: column;
|
| 1635 |
+
min-height: 300px;
|
| 1636 |
+
}
|
| 1637 |
+
|
| 1638 |
+
.free-editor-wrapper:focus-within {
|
| 1639 |
+
border-color: var(--purple);
|
| 1640 |
+
box-shadow: 0 0 0 1px var(--purple);
|
| 1641 |
+
}
|
| 1642 |
+
|
| 1643 |
+
#free-code-input {
|
| 1644 |
+
background: transparent;
|
| 1645 |
+
border: none;
|
| 1646 |
+
color: #e6edf3;
|
| 1647 |
+
font-family: 'JetBrains Mono', monospace;
|
| 1648 |
+
font-size: 13px;
|
| 1649 |
+
padding: 16px;
|
| 1650 |
+
width: 100%;
|
| 1651 |
+
height: 100%;
|
| 1652 |
+
resize: vertical;
|
| 1653 |
+
min-height: 300px;
|
| 1654 |
+
outline: none;
|
| 1655 |
+
}
|
| 1656 |
+
|
| 1657 |
+
#free-code-input::placeholder {
|
| 1658 |
+
color: var(--muted);
|
| 1659 |
+
}
|
| 1660 |
+
|
| 1661 |
+
.free-output-panel {
|
| 1662 |
+
background: var(--surface);
|
| 1663 |
+
border: 1px solid var(--border);
|
| 1664 |
+
border-radius: var(--radius-md);
|
| 1665 |
+
overflow: hidden;
|
| 1666 |
+
display: flex;
|
| 1667 |
+
flex-direction: column;
|
| 1668 |
+
}
|
| 1669 |
+
|
| 1670 |
+
.empty-state {
|
| 1671 |
+
display: flex;
|
| 1672 |
+
flex-direction: column;
|
| 1673 |
+
align-items: center;
|
| 1674 |
+
justify-content: center;
|
| 1675 |
+
padding: 64px 32px;
|
| 1676 |
+
color: var(--muted);
|
| 1677 |
+
text-align: center;
|
| 1678 |
+
height: 100%;
|
| 1679 |
+
}
|
| 1680 |
+
|
| 1681 |
+
.empty-state svg {
|
| 1682 |
+
width: 48px;
|
| 1683 |
+
height: 48px;
|
| 1684 |
+
margin-bottom: 16px;
|
| 1685 |
+
stroke: var(--border2);
|
| 1686 |
+
fill: none;
|
| 1687 |
+
stroke-width: 1.5;
|
| 1688 |
+
}
|
| 1689 |
+
|
| 1690 |
+
.loading-state {
|
| 1691 |
+
display: none;
|
| 1692 |
+
flex-direction: column;
|
| 1693 |
+
align-items: center;
|
| 1694 |
+
justify-content: center;
|
| 1695 |
+
padding: 64px 32px;
|
| 1696 |
+
height: 100%;
|
| 1697 |
+
}
|
| 1698 |
+
|
| 1699 |
+
.results-state {
|
| 1700 |
+
display: none;
|
| 1701 |
+
flex-direction: column;
|
| 1702 |
+
height: 100%;
|
| 1703 |
+
}
|
| 1704 |
+
|
| 1705 |
+
.results-summary-bar {
|
| 1706 |
+
padding: 16px 24px;
|
| 1707 |
+
border-bottom: 1px solid var(--border);
|
| 1708 |
+
display: flex;
|
| 1709 |
+
align-items: center;
|
| 1710 |
+
justify-content: space-between;
|
| 1711 |
+
flex-wrap: wrap;
|
| 1712 |
+
gap: 16px;
|
| 1713 |
+
}
|
| 1714 |
+
|
| 1715 |
+
.verdict-badge {
|
| 1716 |
+
padding: 4px 10px;
|
| 1717 |
+
border-radius: 4px;
|
| 1718 |
+
font-size: 12px;
|
| 1719 |
+
font-weight: 700;
|
| 1720 |
+
text-transform: uppercase;
|
| 1721 |
+
letter-spacing: 0.5px;
|
| 1722 |
+
}
|
| 1723 |
+
|
| 1724 |
+
.verdict-approve { background: rgba(35, 134, 54, 0.15); color: #3fb950; border: 1px solid rgba(63, 185, 80, 0.3); }
|
| 1725 |
+
.verdict-request_changes { background: rgba(218, 54, 51, 0.15); color: #ff7b72; border: 1px solid rgba(255, 123, 114, 0.3); }
|
| 1726 |
+
.verdict-comment { background: rgba(210, 153, 34, 0.15); color: #e3b341; border: 1px solid rgba(227, 179, 65, 0.3); }
|
| 1727 |
+
.verdict-error { background: rgba(218, 54, 51, 0.15); color: #ff7b72; border: 1px solid rgba(255, 123, 114, 0.3); }
|
| 1728 |
+
|
| 1729 |
+
.severity-counts {
|
| 1730 |
+
display: flex;
|
| 1731 |
+
gap: 12px;
|
| 1732 |
+
font-size: 13px;
|
| 1733 |
+
}
|
| 1734 |
+
|
| 1735 |
+
.scount { display: flex; align-items: center; gap: 4px; }
|
| 1736 |
+
.sc-crit { color: #ff7b72; }
|
| 1737 |
+
.sc-maj { color: #e3b341; }
|
| 1738 |
+
.sc-min { color: var(--muted); }
|
| 1739 |
+
|
| 1740 |
+
.results-body {
|
| 1741 |
+
padding: 24px;
|
| 1742 |
+
overflow-y: auto;
|
| 1743 |
+
flex: 1;
|
| 1744 |
+
}
|
| 1745 |
+
|
| 1746 |
+
.ai-summary {
|
| 1747 |
+
font-size: 14px;
|
| 1748 |
+
line-height: 1.6;
|
| 1749 |
+
margin-bottom: 24px;
|
| 1750 |
+
padding: 16px;
|
| 1751 |
+
background: rgba(142, 68, 173, 0.05);
|
| 1752 |
+
border-left: 3px solid var(--purple);
|
| 1753 |
+
border-radius: 0 4px 4px 0;
|
| 1754 |
+
}
|
| 1755 |
+
|
| 1756 |
+
.positives-list {
|
| 1757 |
+
margin-bottom: 32px;
|
| 1758 |
+
}
|
| 1759 |
+
|
| 1760 |
+
.positives-list h4 {
|
| 1761 |
+
font-size: 13px;
|
| 1762 |
+
color: var(--muted);
|
| 1763 |
+
text-transform: uppercase;
|
| 1764 |
+
letter-spacing: 0.5px;
|
| 1765 |
+
margin-bottom: 12px;
|
| 1766 |
+
display: flex;
|
| 1767 |
+
align-items: center;
|
| 1768 |
+
gap: 8px;
|
| 1769 |
+
}
|
| 1770 |
+
|
| 1771 |
+
.positives-list ul {
|
| 1772 |
+
list-style: none;
|
| 1773 |
+
padding: 0;
|
| 1774 |
+
margin: 0;
|
| 1775 |
+
display: flex;
|
| 1776 |
+
flex-direction: column;
|
| 1777 |
+
gap: 8px;
|
| 1778 |
+
}
|
| 1779 |
+
|
| 1780 |
+
.positives-list li {
|
| 1781 |
+
display: flex;
|
| 1782 |
+
align-items: flex-start;
|
| 1783 |
+
gap: 8px;
|
| 1784 |
+
font-size: 13px;
|
| 1785 |
+
}
|
| 1786 |
+
|
| 1787 |
+
.positives-list li::before {
|
| 1788 |
+
content: '✓';
|
| 1789 |
+
color: #3fb950;
|
| 1790 |
+
font-weight: bold;
|
| 1791 |
+
}
|
| 1792 |
+
|
| 1793 |
+
.issues-list {
|
| 1794 |
+
display: flex;
|
| 1795 |
+
flex-direction: column;
|
| 1796 |
+
gap: 16px;
|
| 1797 |
+
}
|
| 1798 |
+
|
| 1799 |
+
.issue-card {
|
| 1800 |
+
padding: 16px;
|
| 1801 |
+
border: 1px solid var(--border);
|
| 1802 |
+
border-radius: var(--radius-sm);
|
| 1803 |
+
background: var(--bg);
|
| 1804 |
+
}
|
| 1805 |
+
|
| 1806 |
+
.ic-critical { border-left: 3px solid #ff7b72; }
|
| 1807 |
+
.ic-major { border-left: 3px solid #e3b341; }
|
| 1808 |
+
.ic-minor { border-left: 3px solid var(--border2); }
|
| 1809 |
+
|
| 1810 |
+
.issue-header {
|
| 1811 |
+
display: flex;
|
| 1812 |
+
align-items: center;
|
| 1813 |
+
gap: 12px;
|
| 1814 |
+
margin-bottom: 8px;
|
| 1815 |
+
}
|
| 1816 |
+
|
| 1817 |
+
.issue-line {
|
| 1818 |
+
font-family: 'JetBrains Mono', monospace;
|
| 1819 |
+
font-size: 12px;
|
| 1820 |
+
color: var(--muted);
|
| 1821 |
+
background: var(--surface);
|
| 1822 |
+
padding: 2px 6px;
|
| 1823 |
+
border-radius: 4px;
|
| 1824 |
+
}
|
| 1825 |
+
|
| 1826 |
+
.issue-badge {
|
| 1827 |
+
font-size: 11px;
|
| 1828 |
+
padding: 2px 6px;
|
| 1829 |
+
border-radius: 4px;
|
| 1830 |
+
font-weight: 600;
|
| 1831 |
+
text-transform: uppercase;
|
| 1832 |
+
}
|
| 1833 |
+
|
| 1834 |
+
.ib-type { background: var(--surface); border: 1px solid var(--border2); }
|
| 1835 |
+
.ib-crit { background: rgba(218, 54, 51, 0.1); color: #ff7b72; }
|
| 1836 |
+
.ib-maj { background: rgba(210, 153, 34, 0.1); color: #e3b341; }
|
| 1837 |
+
.ib-min { background: var(--surface); color: var(--muted); }
|
| 1838 |
+
|
| 1839 |
+
.issue-desc {
|
| 1840 |
+
font-size: 14px;
|
| 1841 |
+
margin-bottom: 8px;
|
| 1842 |
+
}
|
| 1843 |
+
|
| 1844 |
+
.issue-fix {
|
| 1845 |
+
font-size: 13px;
|
| 1846 |
+
color: #3fb950;
|
| 1847 |
+
background: rgba(63, 185, 80, 0.05);
|
| 1848 |
+
padding: 8px 12px;
|
| 1849 |
+
border-radius: 4px;
|
| 1850 |
+
margin-top: 8px;
|
| 1851 |
+
font-family: 'JetBrains Mono', monospace;
|
| 1852 |
+
}
|
| 1853 |
+
|
| 1854 |
/* ── TOAST ──────────────────────────────────────────────────────────── */
|
| 1855 |
#toast-container {
|
| 1856 |
position: fixed;
|
|
|
|
| 2169 |
<label class="field-label" for="task-select">Task Difficulty</label>
|
| 2170 |
<select id="task-select" aria-label="Select task difficulty">
|
| 2171 |
<option value="easy">Easy — Basic Bug Detection</option>
|
| 2172 |
+
<option value="js-async">Easy-Medium — JS Async Flow</option>
|
| 2173 |
<option value="medium">Medium — Security Review</option>
|
| 2174 |
+
<option value="sql-injection">Medium — SQL Injection Hunt</option>
|
| 2175 |
+
<option value="react-security">Medium — React Security</option>
|
| 2176 |
<option value="hard">Hard — Concurrency Hunt</option>
|
| 2177 |
+
<option value="django-auth">Hard — Django Auth Logic</option>
|
| 2178 |
+
<option value="node-race">Hard — Node.js Race Condition</option>
|
| 2179 |
</select>
|
| 2180 |
|
| 2181 |
<button class="btn btn-purple btn-full" id="start-btn" style="margin-top:16px;" onclick="startReview()">
|
|
|
|
| 2321 |
</div>
|
| 2322 |
</section>
|
| 2323 |
|
| 2324 |
+
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
| 2325 |
+
<!-- FREE CODE REVIEW -->
|
| 2326 |
+
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
| 2327 |
+
<section class="section" id="free-review">
|
| 2328 |
+
<div class="container">
|
| 2329 |
+
<div class="section-header fade-up">
|
| 2330 |
+
<div style="display:flex;align-items:center;gap:12px;">
|
| 2331 |
+
<div class="section-label">Beta Feature</div>
|
| 2332 |
+
<span style="background:rgba(210,153,34,0.15);color:#e3b341;border:1px solid rgba(227,179,65,0.3);padding:2px 8px;border-radius:12px;font-size:11px;font-weight:bold;text-transform:uppercase;">BETA</span>
|
| 2333 |
+
</div>
|
| 2334 |
+
<h2 class="section-title">Free Code Review</h2>
|
| 2335 |
+
<p class="section-sub">Paste any code — get instant AI feedback</p>
|
| 2336 |
+
</div>
|
| 2337 |
+
|
| 2338 |
+
<div class="free-review-container fade-up delay-1">
|
| 2339 |
+
<!-- LEFT: Input -->
|
| 2340 |
+
<div class="free-input-panel">
|
| 2341 |
+
<div style="display:flex;gap:12px;">
|
| 2342 |
+
<div style="flex:1;">
|
| 2343 |
+
<select id="free-language" aria-label="Language selector" style="width:100%;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);padding:10px 14px;outline:none;">
|
| 2344 |
+
<option value="python">Python</option>
|
| 2345 |
+
<option value="javascript">JavaScript</option>
|
| 2346 |
+
<option value="typescript">TypeScript</option>
|
| 2347 |
+
<option value="sql">SQL</option>
|
| 2348 |
+
<option value="go">Go</option>
|
| 2349 |
+
<option value="java">Java</option>
|
| 2350 |
+
<option value="cpp">C++</option>
|
| 2351 |
+
</select>
|
| 2352 |
+
</div>
|
| 2353 |
+
<div style="flex:2;">
|
| 2354 |
+
<input type="text" id="free-context" placeholder="Context: What does this code do? (optional)"
|
| 2355 |
+
style="width:100%;background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);padding:10px 14px;outline:none;">
|
| 2356 |
+
</div>
|
| 2357 |
+
</div>
|
| 2358 |
+
|
| 2359 |
+
<div class="free-editor-wrapper">
|
| 2360 |
+
<textarea id="free-code-input" placeholder="Paste your code here... Supports Python, JS, SQL, Go, Java, C++ and more"></textarea>
|
| 2361 |
+
</div>
|
| 2362 |
+
|
| 2363 |
+
<button class="btn btn-purple btn-full" id="run-free-btn" onclick="runFreeReview()" style="height:48px;font-size:15px;font-weight:600;box-shadow:0 0 16px rgba(142,68,173,0.3);">
|
| 2364 |
+
<svg viewBox="0 0 24 24" style="width:18px;height:18px;margin-right:8px;stroke:currentColor;fill:none;stroke-width:2;vertical-align:middle;margin-top:-2px;">
|
| 2365 |
+
<circle cx="11" cy="11" r="8"></circle>
|
| 2366 |
+
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
| 2367 |
+
</svg>
|
| 2368 |
+
Review My Code
|
| 2369 |
+
</button>
|
| 2370 |
+
|
| 2371 |
+
<div style="font-size:11px;color:var(--muted);text-align:center;">
|
| 2372 |
+
AI-powered review. No ground truth scoring. For exploration and demos only.
|
| 2373 |
+
</div>
|
| 2374 |
+
</div>
|
| 2375 |
+
|
| 2376 |
+
<!-- RIGHT: Output -->
|
| 2377 |
+
<div class="free-output-panel">
|
| 2378 |
+
|
| 2379 |
+
<!-- Empty State -->
|
| 2380 |
+
<div class="empty-state" id="free-empty">
|
| 2381 |
+
<svg viewBox="0 0 24 24">
|
| 2382 |
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
| 2383 |
+
<polyline points="14 2 14 8 20 8"></polyline>
|
| 2384 |
+
<line x1="16" y1="13" x2="8" y2="13"></line>
|
| 2385 |
+
<line x1="16" y1="17" x2="8" y2="17"></line>
|
| 2386 |
+
<polyline points="10 9 9 9 8 9"></polyline>
|
| 2387 |
+
</svg>
|
| 2388 |
+
<div>Paste code and click <strong>Review</strong> to get AI feedback</div>
|
| 2389 |
+
</div>
|
| 2390 |
+
|
| 2391 |
+
<!-- Loading State -->
|
| 2392 |
+
<div class="loading-state" id="free-loading">
|
| 2393 |
+
<div class="spinner" style="width:32px;height:32px;border-width:3px;margin-bottom:16px;"></div>
|
| 2394 |
+
<div style="color:var(--text);font-weight:600;">AI is reviewing your code...</div>
|
| 2395 |
+
<div style="color:var(--muted);font-size:13px;margin-top:8px;">This usually takes 5-10 seconds depending on code length.</div>
|
| 2396 |
+
</div>
|
| 2397 |
+
|
| 2398 |
+
<!-- Results State -->
|
| 2399 |
+
<div class="results-state" id="free-results">
|
| 2400 |
+
<div class="results-summary-bar">
|
| 2401 |
+
<div style="display:flex;align-items:center;gap:12px;">
|
| 2402 |
+
<span id="free-verdict" class="verdict-badge verdict-comment">COMMENT</span>
|
| 2403 |
+
<span style="font-weight:600;font-size:15px;" id="free-issue-count">0 issues found</span>
|
| 2404 |
+
</div>
|
| 2405 |
+
<div class="severity-counts">
|
| 2406 |
+
<div class="scount"><span class="sc-crit">🔴</span> <span id="sc-c">0</span> critical</div>
|
| 2407 |
+
<div class="scount"><span class="sc-maj">🟡</span> <span id="sc-m">0</span> major</div>
|
| 2408 |
+
<div class="scount"><span class="sc-min">⚪</span> <span id="sc-mi">0</span> minor</div>
|
| 2409 |
+
</div>
|
| 2410 |
+
</div>
|
| 2411 |
+
|
| 2412 |
+
<div class="results-body">
|
| 2413 |
+
<div class="ai-summary" id="free-summary"></div>
|
| 2414 |
+
|
| 2415 |
+
<div class="positives-list" id="free-positives">
|
| 2416 |
+
<h4>What looks good</h4>
|
| 2417 |
+
<ul id="free-pos-ul"></ul>
|
| 2418 |
+
</div>
|
| 2419 |
+
|
| 2420 |
+
<div class="issues-list" id="free-issues">
|
| 2421 |
+
<!-- Issues populated by JS -->
|
| 2422 |
+
</div>
|
| 2423 |
+
</div>
|
| 2424 |
+
</div>
|
| 2425 |
+
</div>
|
| 2426 |
+
</div>
|
| 2427 |
+
</div>
|
| 2428 |
+
</section>
|
| 2429 |
+
|
| 2430 |
<hr class="divider" />
|
| 2431 |
|
| 2432 |
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
|
|
|
| 2520 |
<button class="task-try-btn" id="try-hard-btn" onclick="jumpToDemo('hard')" aria-label="Try hard task">Try
|
| 2521 |
this task →</button>
|
| 2522 |
</div>
|
| 2523 |
+
|
| 2524 |
+
<!-- JS Async -->
|
| 2525 |
+
<div class="task-card easy fade-up delay-1" id="task-card-js-async">
|
| 2526 |
+
<div class="task-card-top">
|
| 2527 |
+
<span class="diff-badge">EASY-MEDIUM</span>
|
| 2528 |
+
</div>
|
| 2529 |
+
<h3 class="task-name">JavaScript Async Flow Review</h3>
|
| 2530 |
+
<p class="task-file">api/fetcher.js</p>
|
| 2531 |
+
<p class="task-desc">Review a JavaScript module handling data fetching and state updates. Find async/await pitfalls and race conditions.</p>
|
| 2532 |
+
<div class="task-tags">
|
| 2533 |
+
<span class="tag">Missing Await</span>
|
| 2534 |
+
<span class="tag">Promise Errors</span>
|
| 2535 |
+
<span class="tag">Race Condition</span>
|
| 2536 |
+
<span class="tag">Caching Logic</span>
|
| 2537 |
+
</div>
|
| 2538 |
+
<div class="task-meta">
|
| 2539 |
+
<span>Max 4 steps</span>
|
| 2540 |
+
<span class="sep">•</span>
|
| 2541 |
+
<span>3 issues hidden</span>
|
| 2542 |
+
<span class="sep">•</span>
|
| 2543 |
+
<span>Score: 0.92</span>
|
| 2544 |
+
</div>
|
| 2545 |
+
<button class="task-try-btn" id="try-js-async-btn" onclick="jumpToDemo('js-async')" aria-label="Try JS async task">Try this task →</button>
|
| 2546 |
+
</div>
|
| 2547 |
+
|
| 2548 |
+
<!-- SQL Injection -->
|
| 2549 |
+
<div class="task-card medium fade-up delay-2" id="task-card-sql-injection">
|
| 2550 |
+
<div class="task-card-top">
|
| 2551 |
+
<span class="diff-badge">MEDIUM</span>
|
| 2552 |
+
</div>
|
| 2553 |
+
<h3 class="task-name">Advanced SQL Injection Hunt</h3>
|
| 2554 |
+
<p class="task-file">db/reports.js</p>
|
| 2555 |
+
<p class="task-desc">Review a Node.js database service. Identify multiple sophisticated SQL injection patterns in dynamic queries.</p>
|
| 2556 |
+
<div class="task-tags">
|
| 2557 |
+
<span class="tag">Order By Injection</span>
|
| 2558 |
+
<span class="tag">Limit Injection</span>
|
| 2559 |
+
<span class="tag">Template Literals</span>
|
| 2560 |
+
<span class="tag">Parameterization</span>
|
| 2561 |
+
</div>
|
| 2562 |
+
<div class="task-meta">
|
| 2563 |
+
<span>Max 5 steps</span>
|
| 2564 |
+
<span class="sep">•</span>
|
| 2565 |
+
<span>4 vulnerabilities</span>
|
| 2566 |
+
<span class="sep">•</span>
|
| 2567 |
+
<span>Score: 0.88</span>
|
| 2568 |
+
</div>
|
| 2569 |
+
<button class="task-try-btn" id="try-sql-injection-btn" onclick="jumpToDemo('sql-injection')" aria-label="Try SQL injection task">Try this task →</button>
|
| 2570 |
+
</div>
|
| 2571 |
+
|
| 2572 |
+
<!-- React Security -->
|
| 2573 |
+
<div class="task-card medium fade-up delay-3" id="task-card-react-security">
|
| 2574 |
+
<div class="task-card-top">
|
| 2575 |
+
<span class="diff-badge">MEDIUM</span>
|
| 2576 |
+
</div>
|
| 2577 |
+
<h3 class="task-name">React Component Security</h3>
|
| 2578 |
+
<p class="task-file">components/UserProfile.jsx</p>
|
| 2579 |
+
<p class="task-desc">Review a React component for XSS risks (dangerouslySetInnerHTML) and sensitive data leaks in console/URLs.</p>
|
| 2580 |
+
<div class="task-tags">
|
| 2581 |
+
<span class="tag">XSS</span>
|
| 2582 |
+
<span class="tag">Data Leaks</span>
|
| 2583 |
+
<span class="tag">Sanitization</span>
|
| 2584 |
+
<span class="tag">React Hooks</span>
|
| 2585 |
+
</div>
|
| 2586 |
+
<div class="task-meta">
|
| 2587 |
+
<span>Max 4 steps</span>
|
| 2588 |
+
<span class="sep">•</span>
|
| 2589 |
+
<span>3 issues hidden</span>
|
| 2590 |
+
<span class="sep">•</span>
|
| 2591 |
+
<span>Score: 0.90</span>
|
| 2592 |
+
</div>
|
| 2593 |
+
<button class="task-try-btn" id="try-react-security-btn" onclick="jumpToDemo('react-security')" aria-label="Try React task">Try this task →</button>
|
| 2594 |
+
</div>
|
| 2595 |
+
|
| 2596 |
+
<!-- Django Auth -->
|
| 2597 |
+
<div class="task-card hard fade-up delay-1" id="task-card-django-auth">
|
| 2598 |
+
<div class="task-card-top">
|
| 2599 |
+
<span class="diff-badge">HARD</span>
|
| 2600 |
+
</div>
|
| 2601 |
+
<h3 class="task-name">Django Auth Logic Review</h3>
|
| 2602 |
+
<p class="task-file">auth/middleware.py</p>
|
| 2603 |
+
<p class="task-desc">Review Django middleware and auth backends for bypasses, timing attacks, and improper exception handling.</p>
|
| 2604 |
+
<div class="task-tags">
|
| 2605 |
+
<span class="tag">Auth Bypass</span>
|
| 2606 |
+
<span class="tag">Timing Attack</span>
|
| 2607 |
+
<span class="tag">Middleware Logic</span>
|
| 2608 |
+
<span class="tag">Security Logic</span>
|
| 2609 |
+
</div>
|
| 2610 |
+
<div class="task-meta">
|
| 2611 |
+
<span>Max 7 steps</span>
|
| 2612 |
+
<span class="sep">•</span>
|
| 2613 |
+
<span>3 critical issues</span>
|
| 2614 |
+
<span class="sep">•</span>
|
| 2615 |
+
<span>Score: 0.85</span>
|
| 2616 |
+
</div>
|
| 2617 |
+
<button class="task-try-btn" id="try-django-auth-btn" onclick="jumpToDemo('django-auth')" aria-label="Try Django task">Try this task →</button>
|
| 2618 |
+
</div>
|
| 2619 |
+
|
| 2620 |
+
<!-- Node Race -->
|
| 2621 |
+
<div class="task-card hard fade-up delay-2" id="task-card-node-race">
|
| 2622 |
+
<div class="task-card-top">
|
| 2623 |
+
<span class="diff-badge">HARD</span>
|
| 2624 |
+
</div>
|
| 2625 |
+
<h3 class="task-name">Node.js Concurrency Issues</h3>
|
| 2626 |
+
<p class="task-file">services/inventory.js</p>
|
| 2627 |
+
<p class="task-desc">Identify race conditions and stale state updates in a Node.js singleton service responsible for inventory tracking.</p>
|
| 2628 |
+
<div class="task-tags">
|
| 2629 |
+
<span class="tag">Race Condition</span>
|
| 2630 |
+
<span class="tag">Stale State</span>
|
| 2631 |
+
<span class="tag">Atomicity</span>
|
| 2632 |
+
<span class="tag">Singleton Pattern</span>
|
| 2633 |
+
</div>
|
| 2634 |
+
<div class="task-meta">
|
| 2635 |
+
<span>Max 6 steps</span>
|
| 2636 |
+
<span class="sep">•</span>
|
| 2637 |
+
<span>2 critical loops</span>
|
| 2638 |
+
<span class="sep">•</span>
|
| 2639 |
+
<span>Score: 0.82</span>
|
| 2640 |
+
</div>
|
| 2641 |
+
<button class="task-try-btn" id="try-node-race-btn" onclick="jumpToDemo('node-race')" aria-label="Try Node race task">Try this task →</button>
|
| 2642 |
+
</div>
|
| 2643 |
</div>
|
| 2644 |
</div>
|
| 2645 |
</section>
|
free_review.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from openai import OpenAI
|
| 2 |
+
import os
|
| 3 |
+
import json
|
| 4 |
+
|
| 5 |
+
import inference
|
| 6 |
+
|
| 7 |
+
GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY", "")
|
| 8 |
+
GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
|
| 9 |
+
DEFAULT_MODEL = "gemini-2.0-flash"
|
| 10 |
+
# use whatever model name is working in inference.py currently
|
| 11 |
+
|
| 12 |
+
SYSTEM_PROMPT = """You are an expert code reviewer with 15+ years
|
| 13 |
+
of experience. Review the provided code and identify ALL issues.
|
| 14 |
+
|
| 15 |
+
For each issue found, provide:
|
| 16 |
+
- line_number: exact line where issue occurs (integer)
|
| 17 |
+
- issue_type: one of bug/security/performance/style/logic
|
| 18 |
+
- severity: one of critical/major/minor
|
| 19 |
+
- description: clear explanation of the problem
|
| 20 |
+
- suggested_fix: how to fix it
|
| 21 |
+
|
| 22 |
+
Also provide:
|
| 23 |
+
- overall_verdict: approve/request_changes/comment
|
| 24 |
+
- summary: 2-3 sentence overall assessment
|
| 25 |
+
- positive_aspects: list of 2-3 things done well
|
| 26 |
+
|
| 27 |
+
Respond ONLY with valid JSON, no markdown:
|
| 28 |
+
{
|
| 29 |
+
"issues": [
|
| 30 |
+
{
|
| 31 |
+
"line_number": <int>,
|
| 32 |
+
"issue_type": "<type>",
|
| 33 |
+
"severity": "<severity>",
|
| 34 |
+
"description": "<description>",
|
| 35 |
+
"suggested_fix": "<fix>"
|
| 36 |
+
}
|
| 37 |
+
],
|
| 38 |
+
"overall_verdict": "<verdict>",
|
| 39 |
+
"summary": "<summary>",
|
| 40 |
+
"positive_aspects": ["<aspect1>", "<aspect2>"]
|
| 41 |
+
}"""
|
| 42 |
+
|
| 43 |
+
def review_free_code(code: str, language: str = "python",
|
| 44 |
+
context: str = "") -> dict:
|
| 45 |
+
"""
|
| 46 |
+
Review any arbitrary code using Gemini.
|
| 47 |
+
Returns structured findings without a grader score.
|
| 48 |
+
"""
|
| 49 |
+
api_key_to_use = GEMINI_API_KEY if GEMINI_API_KEY else inference._api_key
|
| 50 |
+
base_url_to_use = GEMINI_BASE_URL if GEMINI_API_KEY else inference.API_BASE_URL
|
| 51 |
+
|
| 52 |
+
if not api_key_to_use:
|
| 53 |
+
return {"error": "GEMINI_API_KEY not set and inference proxy key unavailable"}
|
| 54 |
+
|
| 55 |
+
client = OpenAI(
|
| 56 |
+
api_key=api_key_to_use,
|
| 57 |
+
base_url=base_url_to_use
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
user_prompt = f"""Language: {language}
|
| 61 |
+
Context: {context if context else "General code review"}
|
| 62 |
+
|
| 63 |
+
Code to review:
|
| 64 |
+
```{language}
|
| 65 |
+
{code}
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
Review this code thoroughly and return JSON only."""
|
| 69 |
+
|
| 70 |
+
try:
|
| 71 |
+
response = client.chat.completions.create(
|
| 72 |
+
model=DEFAULT_MODEL,
|
| 73 |
+
messages=[
|
| 74 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 75 |
+
{"role": "user", "content": user_prompt}
|
| 76 |
+
],
|
| 77 |
+
temperature=0.1,
|
| 78 |
+
max_tokens=3000,
|
| 79 |
+
)
|
| 80 |
+
content = response.choices[0].message.content.strip()
|
| 81 |
+
if content.startswith("```"):
|
| 82 |
+
lines = content.split("\n")
|
| 83 |
+
content = "\n".join(lines[1:])
|
| 84 |
+
if content.strip().endswith("```"):
|
| 85 |
+
content = content.strip()[:-3].strip()
|
| 86 |
+
return json.loads(content)
|
| 87 |
+
except json.JSONDecodeError:
|
| 88 |
+
return {"error": "Failed to parse AI response",
|
| 89 |
+
"raw": content[:500]}
|
| 90 |
+
except Exception as e:
|
| 91 |
+
return {"error": str(e)}
|
tasks.py
CHANGED
|
@@ -19,38 +19,38 @@ TASKS: Dict[str, Dict[str, Any]] = {
|
|
| 19 |
"file_name": "utils/statistics.py",
|
| 20 |
"diff": """\
|
| 21 |
--- a/utils/statistics.py
|
| 22 |
-
++
|
| 23 |
@@ -0,0 +1,30 @@
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
""",
|
| 55 |
"known_issues": [
|
| 56 |
{
|
|
@@ -104,51 +104,51 @@ TASKS: Dict[str, Dict[str, Any]] = {
|
|
| 104 |
"file_name": "auth/user_manager.py",
|
| 105 |
"diff": """\
|
| 106 |
--- a/auth/user_manager.py
|
| 107 |
-
++
|
| 108 |
@@ -0,0 +1,52 @@
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
""",
|
| 153 |
"known_issues": [
|
| 154 |
{
|
|
@@ -230,98 +230,98 @@ TASKS: Dict[str, Dict[str, Any]] = {
|
|
| 230 |
"file_name": "core/rate_limiter.py",
|
| 231 |
"diff": """\
|
| 232 |
--- a/core/rate_limiter.py
|
| 233 |
-
++
|
| 234 |
@@ -0,0 +1,82 @@
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
""",
|
| 326 |
"known_issues": [
|
| 327 |
{
|
|
@@ -384,12 +384,344 @@ TASKS: Dict[str, Dict[str, Any]] = {
|
|
| 384 |
"required_verdict": "request_changes",
|
| 385 |
"success_threshold": 0.35,
|
| 386 |
},
|
| 387 |
-
}
|
| 388 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
|
| 390 |
def get_task(task_id: str) -> Dict[str, Any]:
|
| 391 |
return TASKS.get(task_id, {})
|
| 392 |
|
| 393 |
-
|
| 394 |
def get_all_tasks() -> List[Dict[str, Any]]:
|
| 395 |
return list(TASKS.values())
|
|
|
|
| 19 |
"file_name": "utils/statistics.py",
|
| 20 |
"diff": """\
|
| 21 |
--- a/utils/statistics.py
|
| 22 |
+
++ b/utils/statistics.py
|
| 23 |
@@ -0,0 +1,30 @@
|
| 24 |
+
def calculate_average(numbers):
|
| 25 |
+
total = 0
|
| 26 |
+
for num in numbers:
|
| 27 |
+
total += num
|
| 28 |
+
return total / len(numbers)
|
| 29 |
+
|
| 30 |
+
def calculate_max(numbers):
|
| 31 |
+
max_val = numbers[0]
|
| 32 |
+
for num in numbers:
|
| 33 |
+
if num > max_val:
|
| 34 |
+
max_val = num
|
| 35 |
+
return max_val
|
| 36 |
+
|
| 37 |
+
def get_percentage(value, total):
|
| 38 |
+
return (value / total) * 100
|
| 39 |
+
|
| 40 |
+
def find_duplicates(items):
|
| 41 |
+
seen = []
|
| 42 |
+
duplicates = []
|
| 43 |
+
for item in items:
|
| 44 |
+
if item in seen:
|
| 45 |
+
duplicates.append(item)
|
| 46 |
+
seen.append(item)
|
| 47 |
+
return duplicates
|
| 48 |
+
|
| 49 |
+
def safe_divide(a, b):
|
| 50 |
+
if b != 0:
|
| 51 |
+
return a / b
|
| 52 |
+
else:
|
| 53 |
+
return 0
|
| 54 |
""",
|
| 55 |
"known_issues": [
|
| 56 |
{
|
|
|
|
| 104 |
"file_name": "auth/user_manager.py",
|
| 105 |
"diff": """\
|
| 106 |
--- a/auth/user_manager.py
|
| 107 |
+
++ b/auth/user_manager.py
|
| 108 |
@@ -0,0 +1,52 @@
|
| 109 |
+
import hashlib
|
| 110 |
+
|
| 111 |
+
DB_PASSWORD = "admin123"
|
| 112 |
+
API_SECRET = "supersecret_key_do_not_share"
|
| 113 |
+
|
| 114 |
+
def get_user(user_id):
|
| 115 |
+
query = f"SELECT * FROM users WHERE id = {user_id}"
|
| 116 |
+
return execute_query(query)
|
| 117 |
+
|
| 118 |
+
def login(username, password):
|
| 119 |
+
hashed = hashlib.md5(password.encode()).hexdigest()
|
| 120 |
+
query = f"SELECT * FROM users WHERE username = '{username}' AND password = '{hashed}'"
|
| 121 |
+
user = execute_query(query)
|
| 122 |
+
if user:
|
| 123 |
+
return {"status": "success", "user": user}
|
| 124 |
+
return {"status": "failed"}
|
| 125 |
+
|
| 126 |
+
def reset_password(email, new_password):
|
| 127 |
+
if len(new_password) > 6:
|
| 128 |
+
query = f"UPDATE users SET password = '{new_password}' WHERE email = '{email}'"
|
| 129 |
+
execute_query(query)
|
| 130 |
+
return True
|
| 131 |
+
return False
|
| 132 |
+
|
| 133 |
+
def delete_user(user_id):
|
| 134 |
+
execute_query(f"DELETE FROM users WHERE id = {user_id}")
|
| 135 |
+
return True
|
| 136 |
+
|
| 137 |
+
def serialize_user(user_data):
|
| 138 |
+
import pickle
|
| 139 |
+
return pickle.dumps(user_data)
|
| 140 |
+
|
| 141 |
+
def deserialize_user(data):
|
| 142 |
+
import pickle
|
| 143 |
+
return pickle.loads(data)
|
| 144 |
+
|
| 145 |
+
def log_action(user, action):
|
| 146 |
+
log_entry = f"[{action}] User: {user}"
|
| 147 |
+
print(log_entry)
|
| 148 |
+
|
| 149 |
+
def check_permission(user_role, required_role):
|
| 150 |
+
roles = ["user", "moderator", "admin"]
|
| 151 |
+
return roles.index(user_role) == roles.index(required_role)
|
| 152 |
""",
|
| 153 |
"known_issues": [
|
| 154 |
{
|
|
|
|
| 230 |
"file_name": "core/rate_limiter.py",
|
| 231 |
"diff": """\
|
| 232 |
--- a/core/rate_limiter.py
|
| 233 |
+
++ b/core/rate_limiter.py
|
| 234 |
@@ -0,0 +1,82 @@
|
| 235 |
+
import time
|
| 236 |
+
import threading
|
| 237 |
+
from collections import defaultdict
|
| 238 |
+
|
| 239 |
+
class RateLimiter:
|
| 240 |
+
def __init__(self, max_requests, window_seconds):
|
| 241 |
+
self.max_requests = max_requests
|
| 242 |
+
self.window_seconds = window_seconds
|
| 243 |
+
self.requests = defaultdict(list)
|
| 244 |
+
self.lock = threading.Lock()
|
| 245 |
+
|
| 246 |
+
def is_allowed(self, user_id):
|
| 247 |
+
now = time.time()
|
| 248 |
+
window_start = now - self.window_seconds
|
| 249 |
+
with self.lock:
|
| 250 |
+
self.requests[user_id] = [
|
| 251 |
+
t for t in self.requests[user_id] if t > window_start
|
| 252 |
+
]
|
| 253 |
+
if len(self.requests[user_id]) < self.max_requests:
|
| 254 |
+
self.requests[user_id].append(now)
|
| 255 |
+
return True
|
| 256 |
+
return False
|
| 257 |
+
|
| 258 |
+
class TaskQueue:
|
| 259 |
+
def __init__(self, max_workers=4):
|
| 260 |
+
self.queue = []
|
| 261 |
+
self.max_workers = max_workers
|
| 262 |
+
self.workers = []
|
| 263 |
+
self.running = False
|
| 264 |
+
|
| 265 |
+
def add_task(self, task_fn, *args):
|
| 266 |
+
self.queue.append((task_fn, args))
|
| 267 |
+
|
| 268 |
+
def _worker(self):
|
| 269 |
+
while self.running:
|
| 270 |
+
if self.queue:
|
| 271 |
+
task_fn, args = self.queue.pop(0)
|
| 272 |
+
try:
|
| 273 |
+
task_fn(*args)
|
| 274 |
+
except Exception:
|
| 275 |
+
pass
|
| 276 |
+
time.sleep(0.01)
|
| 277 |
+
|
| 278 |
+
def start(self):
|
| 279 |
+
self.running = True
|
| 280 |
+
for _ in range(self.max_workers):
|
| 281 |
+
t = threading.Thread(target=self._worker)
|
| 282 |
+
t.start()
|
| 283 |
+
self.workers.append(t)
|
| 284 |
+
|
| 285 |
+
def stop(self):
|
| 286 |
+
self.running = False
|
| 287 |
+
|
| 288 |
+
class RetryQueue:
|
| 289 |
+
def __init__(self, max_retries=3):
|
| 290 |
+
self.max_retries = max_retries
|
| 291 |
+
self.failed_tasks = {}
|
| 292 |
+
self.retry_counts = defaultdict(int)
|
| 293 |
+
|
| 294 |
+
def add_failed(self, task_id, task_fn):
|
| 295 |
+
self.failed_tasks[task_id] = task_fn
|
| 296 |
+
|
| 297 |
+
def retry_all(self):
|
| 298 |
+
for task_id, task_fn in self.failed_tasks.items():
|
| 299 |
+
if self.retry_counts[task_id] < self.max_retries:
|
| 300 |
+
try:
|
| 301 |
+
task_fn()
|
| 302 |
+
del self.failed_tasks[task_id]
|
| 303 |
+
except Exception:
|
| 304 |
+
self.retry_counts[task_id] += 1
|
| 305 |
+
|
| 306 |
+
def get_stats(self):
|
| 307 |
+
return {
|
| 308 |
+
"pending": len(self.failed_tasks),
|
| 309 |
+
"total_retries": sum(self.retry_counts.values())
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
def process_batch(items, batch_size=100):
|
| 313 |
+
results = []
|
| 314 |
+
for i in range(0, len(items), batch_size):
|
| 315 |
+
batch = items[i:i+batch_size]
|
| 316 |
+
result = process_items(batch)
|
| 317 |
+
results.append(result)
|
| 318 |
+
return results
|
| 319 |
+
|
| 320 |
+
def merge_configs(base_config, override_config):
|
| 321 |
+
merged = base_config
|
| 322 |
+
for key, value in override_config.items():
|
| 323 |
+
merged[key] = value
|
| 324 |
+
return merged
|
| 325 |
""",
|
| 326 |
"known_issues": [
|
| 327 |
{
|
|
|
|
| 384 |
"required_verdict": "request_changes",
|
| 385 |
"success_threshold": 0.35,
|
| 386 |
},
|
|
|
|
| 387 |
|
| 388 |
+
"js-async": {
|
| 389 |
+
"id": "js-async",
|
| 390 |
+
"name": "JavaScript Async Flow Review",
|
| 391 |
+
"description": "Review a JavaScript module handling data fetching and state updates. Find async/await pitfalls and race conditions.",
|
| 392 |
+
"difficulty": "easy-medium",
|
| 393 |
+
"max_steps": 4,
|
| 394 |
+
"pr_title": "Fix data loading and caching logic",
|
| 395 |
+
"pr_description": "Implements an async data Fetcher with internal cache.",
|
| 396 |
+
"file_name": "api/fetcher.js",
|
| 397 |
+
"diff": """\
|
| 398 |
+
--- a/api/fetcher.js
|
| 399 |
+
+++ b/api/fetcher.js
|
| 400 |
+
@@ -0,0 +1,35 @@
|
| 401 |
+
+class DataFetcher {
|
| 402 |
+
+ constructor() {
|
| 403 |
+
+ this.cache = {};
|
| 404 |
+
+ this.loading = false;
|
| 405 |
+
+ }
|
| 406 |
+
+
|
| 407 |
+
+ async fetchData(url) {
|
| 408 |
+
+ if (this.cache[url]) return this.cache[url];
|
| 409 |
+
+
|
| 410 |
+
+ this.loading = true;
|
| 411 |
+
+ try {
|
| 412 |
+
+ const response = fetch(url);
|
| 413 |
+
+ const data = await response.json();
|
| 414 |
+
+ this.cache[url] = data;
|
| 415 |
+
+ return data;
|
| 416 |
+
+ } catch (err) {
|
| 417 |
+
+ console.error("Fetch failed");
|
| 418 |
+
+ } finally {
|
| 419 |
+
+ this.loading = false;
|
| 420 |
+
+ }
|
| 421 |
+
+ }
|
| 422 |
+
+
|
| 423 |
+
+ updateUI(data) {
|
| 424 |
+
+ this.fetchData('/api/user').then(user => {
|
| 425 |
+
+ document.getElementById('user').innerText = user.name;
|
| 426 |
+
+ });
|
| 427 |
+
+ }
|
| 428 |
+
+
|
| 429 |
+
+ async loadItems(urls) {
|
| 430 |
+
+ urls.forEach(async (url) => {
|
| 431 |
+
+ await this.fetchData(url);
|
| 432 |
+
+ });
|
| 433 |
+
+ }
|
| 434 |
+
+}
|
| 435 |
+
""",
|
| 436 |
+
"known_issues": [
|
| 437 |
+
{
|
| 438 |
+
"line_number": 11,
|
| 439 |
+
"issue_type": "bug",
|
| 440 |
+
"severity": "critical",
|
| 441 |
+
"description": "Missing await on fetch(url) — response will be a Promise, not the result",
|
| 442 |
+
"keywords": ["await", "fetch", "promise"],
|
| 443 |
+
},
|
| 444 |
+
{
|
| 445 |
+
"line_number": 16,
|
| 446 |
+
"issue_type": "bug",
|
| 447 |
+
"severity": "major",
|
| 448 |
+
"description": "Silent error swallowing in catch block without rethrowing or user notification",
|
| 449 |
+
"keywords": ["catch", "swallow", "error", "rethrow"],
|
| 450 |
+
},
|
| 451 |
+
{
|
| 452 |
+
"line_number": 30,
|
| 453 |
+
"issue_type": "performance",
|
| 454 |
+
"severity": "major",
|
| 455 |
+
"description": "forEach with async callback doesn't await the loop — use Promise.all or for...of",
|
| 456 |
+
"keywords": ["forEach", "async", "Promise.all", "loop"],
|
| 457 |
+
},
|
| 458 |
+
],
|
| 459 |
+
"required_verdict": "request_changes",
|
| 460 |
+
"success_threshold": 0.5,
|
| 461 |
+
},
|
| 462 |
+
|
| 463 |
+
"sql-injection": {
|
| 464 |
+
"id": "sql-injection",
|
| 465 |
+
"name": "Advanced SQL Injection Hunt",
|
| 466 |
+
"description": "Review a Node.js database service. Identify multiple sophisticated SQL injection patterns.",
|
| 467 |
+
"difficulty": "medium",
|
| 468 |
+
"max_steps": 5,
|
| 469 |
+
"pr_title": "Enhance reporting queries with dynamic sorting",
|
| 470 |
+
"pr_description": "Adds support for custom sort orders and limits in reports.",
|
| 471 |
+
"file_name": "db/reports.js",
|
| 472 |
+
"diff": """\
|
| 473 |
+
--- a/db/reports.js
|
| 474 |
+
+++ b/db/reports.js
|
| 475 |
+
@@ -0,0 +1,28 @@
|
| 476 |
+
+const db = require('./connection');
|
| 477 |
+
+
|
| 478 |
+
+async function getReport(type, sortBy = 'created_at', limit = 10) {
|
| 479 |
+
+ const query = `
|
| 480 |
+
+ SELECT * FROM reports
|
| 481 |
+
+ WHERE type = '${type}'
|
| 482 |
+
+ ORDER BY ${sortBy}
|
| 483 |
+
+ LIMIT ${limit}
|
| 484 |
+
+ `;
|
| 485 |
+
+ return db.query(query);
|
| 486 |
+
+}
|
| 487 |
+
+
|
| 488 |
+
+async function getUserSummary(userId) {
|
| 489 |
+
+ const sql = "SELECT * FROM summaries WHERE user_id = " + userId;
|
| 490 |
+
+ return db.query(sql);
|
| 491 |
+
+}
|
| 492 |
+
+
|
| 493 |
+
+async function searchLogs(term) {
|
| 494 |
+
+ const sql = `SELECT * FROM logs WHERE message LIKE '%${term}%'`;
|
| 495 |
+
+ return db.query(sql);
|
| 496 |
+
+}
|
| 497 |
+
""",
|
| 498 |
+
"known_issues": [
|
| 499 |
+
{
|
| 500 |
+
"line_number": 6,
|
| 501 |
+
"issue_type": "security",
|
| 502 |
+
"severity": "critical",
|
| 503 |
+
"description": "Classic SQL Injection in WHERE clause via template literal",
|
| 504 |
+
"keywords": ["sql injection", "injection", "template literal"],
|
| 505 |
+
},
|
| 506 |
+
{
|
| 507 |
+
"line_number": 7,
|
| 508 |
+
"issue_type": "security",
|
| 509 |
+
"severity": "critical",
|
| 510 |
+
"description": "SQL Injection in ORDER BY clause — cannot be parameterized, must use whitelist",
|
| 511 |
+
"keywords": ["order by", "injection", "whitelist"],
|
| 512 |
+
},
|
| 513 |
+
{
|
| 514 |
+
"line_number": 8,
|
| 515 |
+
"issue_type": "security",
|
| 516 |
+
"severity": "critical",
|
| 517 |
+
"description": "SQL Injection in LIMIT clause — ensure limit is a number",
|
| 518 |
+
"keywords": ["limit", "injection", "number"],
|
| 519 |
+
},
|
| 520 |
+
{
|
| 521 |
+
"line_number": 14,
|
| 522 |
+
"issue_type": "security",
|
| 523 |
+
"severity": "critical",
|
| 524 |
+
"description": "Simple string concatenation SQL Injection",
|
| 525 |
+
"keywords": ["concatenation", "injection", "sql"],
|
| 526 |
+
},
|
| 527 |
+
],
|
| 528 |
+
"required_verdict": "request_changes",
|
| 529 |
+
"success_threshold": 0.6,
|
| 530 |
+
},
|
| 531 |
+
|
| 532 |
+
"react-security": {
|
| 533 |
+
"id": "react-security",
|
| 534 |
+
"name": "React Component Security",
|
| 535 |
+
"description": "Review a React component for XSS risks and sensitive data leaks.",
|
| 536 |
+
"difficulty": "medium",
|
| 537 |
+
"max_steps": 4,
|
| 538 |
+
"pr_title": "Implement UserProfile component with markdown support",
|
| 539 |
+
"pr_description": "Adds a profile page that renders user-provided bio content.",
|
| 540 |
+
"file_name": "components/UserProfile.jsx",
|
| 541 |
+
"diff": """\
|
| 542 |
+
--- a/components/UserProfile.jsx
|
| 543 |
+
+++ b/components/UserProfile.jsx
|
| 544 |
+
@@ -0,0 +1,25 @@
|
| 545 |
+
+import React from 'react';
|
| 546 |
+
+
|
| 547 |
+
+export const UserProfile = ({ user, authToken }) => {
|
| 548 |
+
+ console.log("Loading profile for user:", user.id, "Token:", authToken);
|
| 549 |
+
+
|
| 550 |
+
+ const renderBio = (bio) => {
|
| 551 |
+
+ return <div dangerouslySetInnerHTML={{ __html: bio }} />;
|
| 552 |
+
+ };
|
| 553 |
+
+
|
| 554 |
+
+ return (
|
| 555 |
+
+ <div className="profile-card">
|
| 556 |
+
+ <h1>{user.name}</h1>
|
| 557 |
+
+ <div className="bio">
|
| 558 |
+
+ {renderBio(user.bio)}
|
| 559 |
+
+ </div>
|
| 560 |
+
+ <button onClick={() => window.location.href = `/edit?token=${authToken}`}>
|
| 561 |
+
+ Edit Profile
|
| 562 |
+
+ </button>
|
| 563 |
+
+ </div>
|
| 564 |
+
+ );
|
| 565 |
+
+};
|
| 566 |
+
""",
|
| 567 |
+
"known_issues": [
|
| 568 |
+
{
|
| 569 |
+
"line_number": 4,
|
| 570 |
+
"issue_type": "security",
|
| 571 |
+
"severity": "major",
|
| 572 |
+
"description": "Sensitive data (authToken) leaked to browser console",
|
| 573 |
+
"keywords": ["console", "leak", "token", "sensitive"],
|
| 574 |
+
},
|
| 575 |
+
{
|
| 576 |
+
"line_number": 7,
|
| 577 |
+
"issue_type": "security",
|
| 578 |
+
"severity": "critical",
|
| 579 |
+
"description": "XSS vulnerability via dangerouslySetInnerHTML without sanitization",
|
| 580 |
+
"keywords": ["dangerouslySetInnerHTML", "xss", "sanitize", "cross-site scripting"],
|
| 581 |
+
},
|
| 582 |
+
{
|
| 583 |
+
"line_number": 16,
|
| 584 |
+
"issue_type": "security",
|
| 585 |
+
"severity": "major",
|
| 586 |
+
"description": "Sensitive token leaked in URL query parameters",
|
| 587 |
+
"keywords": ["url", "query parameter", "token", "leak"],
|
| 588 |
+
},
|
| 589 |
+
],
|
| 590 |
+
"required_verdict": "request_changes",
|
| 591 |
+
"success_threshold": 0.5,
|
| 592 |
+
},
|
| 593 |
+
|
| 594 |
+
"django-auth": {
|
| 595 |
+
"id": "django-auth",
|
| 596 |
+
"name": "Django Auth Logic Review",
|
| 597 |
+
"description": "Review Django middleware and auth backends for bypasses and timing attacks.",
|
| 598 |
+
"difficulty": "hard",
|
| 599 |
+
"max_steps": 7,
|
| 600 |
+
"pr_title": "Custom Authentication and Security Middleware",
|
| 601 |
+
"pr_description": "Customizes Django auth to support legacy hash formats and IP-based restrictions.",
|
| 602 |
+
"file_name": "auth/middleware.py",
|
| 603 |
+
"diff": """\
|
| 604 |
+
--- a/auth/middleware.py
|
| 605 |
+
+++ b/auth/middleware.py
|
| 606 |
+
@@ -0,0 +1,40 @@
|
| 607 |
+
+from django.shortcuts import redirect
|
| 608 |
+
+from django.conf import settings
|
| 609 |
+
+
|
| 610 |
+
+class IPBlockMiddleware:
|
| 611 |
+
+ def __init__(self, get_response):
|
| 612 |
+
+ self.get_response = get_response
|
| 613 |
+
+
|
| 614 |
+
+ def __call__(self, request):
|
| 615 |
+
+ ip = request.META.get('REMOTE_ADDR')
|
| 616 |
+
+ if ip in settings.BLOCKED_IPS:
|
| 617 |
+
+ return redirect('/blocked/')
|
| 618 |
+
+ return self.get_response(request)
|
| 619 |
+
+
|
| 620 |
+
+class LegacyBackend:
|
| 621 |
+
+ def authenticate(self, request, username=None, password=None):
|
| 622 |
+
+ user = User.objects.get(username=username)
|
| 623 |
+
+ if user.legacy_password == password:
|
| 624 |
+
+ return user
|
| 625 |
+
+ return None
|
| 626 |
+
+
|
| 627 |
+
+def validate_token(token):
|
| 628 |
+
+ if token == settings.SUPER_SECRET_TOKEN:
|
| 629 |
+
+ return True
|
| 630 |
+
+ return False
|
| 631 |
+
+
|
| 632 |
+
+def secure_view(request):
|
| 633 |
+
+ if not request.user.is_authenticated:
|
| 634 |
+
+ return redirect('/login/')
|
| 635 |
+
+ # Sensitive data processing here
|
| 636 |
+
+ pass
|
| 637 |
+
""",
|
| 638 |
+
"known_issues": [
|
| 639 |
+
{
|
| 640 |
+
"line_number": 17,
|
| 641 |
+
"issue_type": "security",
|
| 642 |
+
"severity": "critical",
|
| 643 |
+
"description": "Plaintext password comparison for legacy accounts",
|
| 644 |
+
"keywords": ["plaintext", "password", "comparison"],
|
| 645 |
+
},
|
| 646 |
+
{
|
| 647 |
+
"line_number": 21,
|
| 648 |
+
"issue_type": "security",
|
| 649 |
+
"severity": "critical",
|
| 650 |
+
"description": "Timing attack vulnerability in string comparison for secret token",
|
| 651 |
+
"keywords": ["timing attack", "comparison", "constant time"],
|
| 652 |
+
},
|
| 653 |
+
{
|
| 654 |
+
"line_number": 16,
|
| 655 |
+
"issue_type": "bug",
|
| 656 |
+
"severity": "major",
|
| 657 |
+
"description": "User.objects.get() raises DoesNotExist if user not found; should use filter().first() or try/except",
|
| 658 |
+
"keywords": ["DoesNotExist", "exception", "crash"],
|
| 659 |
+
},
|
| 660 |
+
],
|
| 661 |
+
"required_verdict": "request_changes",
|
| 662 |
+
"success_threshold": 0.4,
|
| 663 |
+
},
|
| 664 |
+
|
| 665 |
+
"node-race": {
|
| 666 |
+
"id": "node-race",
|
| 667 |
+
"name": "Node.js Concurrency Issues",
|
| 668 |
+
"description": "Identify race conditions in a Node.js singleton service responsible for inventory tracking.",
|
| 669 |
+
"difficulty": "hard",
|
| 670 |
+
"max_steps": 6,
|
| 671 |
+
"pr_title": "Inventory Management Singleton",
|
| 672 |
+
"pr_description": "Initial implementation of in-memory inventory tracking for fast access.",
|
| 673 |
+
"file_name": "services/inventory.js",
|
| 674 |
+
"diff": """\
|
| 675 |
+
--- a/services/inventory.js
|
| 676 |
+
+++ b/services/inventory.js
|
| 677 |
+
@@ -0,0 +1,30 @@
|
| 678 |
+
+let inventory = {
|
| 679 |
+
+ 'item_1': 100,
|
| 680 |
+
+ 'item_2': 50
|
| 681 |
+
+};
|
| 682 |
+
+
|
| 683 |
+
+async function purchaseItem(itemId, quantity) {
|
| 684 |
+
+ const currentStock = inventory[itemId];
|
| 685 |
+
+
|
| 686 |
+
+ if (currentStock >= quantity) {
|
| 687 |
+
+ // Simulate DB delay
|
| 688 |
+
+ await new Promise(resolve => setTimeout(resolve, 100));
|
| 689 |
+
+
|
| 690 |
+
+ inventory[itemId] = currentStock - quantity;
|
| 691 |
+
+ return true;
|
| 692 |
+
+ }
|
| 693 |
+
+ return false;
|
| 694 |
+
+}
|
| 695 |
+
+
|
| 696 |
+
+async function restockItem(itemId, amount) {
|
| 697 |
+
+ inventory[itemId] += amount;
|
| 698 |
+
+}
|
| 699 |
+
+
|
| 700 |
+
+module.exports = { purchaseItem, restockItem };
|
| 701 |
+
""",
|
| 702 |
+
"known_issues": [
|
| 703 |
+
{
|
| 704 |
+
"line_number": 7,
|
| 705 |
+
"issue_type": "bug",
|
| 706 |
+
"severity": "critical",
|
| 707 |
+
"description": "Race condition: stock checked at line 7 but updated at line 12 after await. Parallel calls can lead to over-selling.",
|
| 708 |
+
"keywords": ["race condition", "atomic", "oversell"],
|
| 709 |
+
},
|
| 710 |
+
{
|
| 711 |
+
"line_number": 12,
|
| 712 |
+
"issue_type": "bug",
|
| 713 |
+
"severity": "major",
|
| 714 |
+
"description": "Inventory update uses stale 'currentStock' variable instead of incrementing current value.",
|
| 715 |
+
"keywords": ["stale", "atomic", "increment"],
|
| 716 |
+
},
|
| 717 |
+
],
|
| 718 |
+
"required_verdict": "request_changes",
|
| 719 |
+
"success_threshold": 0.3,
|
| 720 |
+
},
|
| 721 |
+
}
|
| 722 |
|
| 723 |
def get_task(task_id: str) -> Dict[str, Any]:
|
| 724 |
return TASKS.get(task_id, {})
|
| 725 |
|
|
|
|
| 726 |
def get_all_tasks() -> List[Dict[str, Any]]:
|
| 727 |
return list(TASKS.values())
|
tmpclaude-46e1-cwd
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
/c/Users/psaru/OneDrive/Desktop/code-review-env
|
tmpclaude-5602-cwd
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
/c/Users/psaru/OneDrive/Desktop/code-review-env
|
tmpclaude-ee5f-cwd
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
/c/Users/psaru/OneDrive/Desktop/code-review-env
|