GitHub Issue Triage: Separating Real Bugs from False Positives
Two issues in the Koin project looked like critical bugs at first glance. On closer inspection, both were false positives — mislabeled by the scanner and misunderstood by me. The lesson: always read the code before assigning severity.
Issue #119: “SQL Injection Risk” → Wildcard Escaping
Initial label: Security: SQL Injection Risk in AI Command Filters (🔴 High)
Actual problem: The code used Drizzle ORM with parameterized queries. There was no SQL injection — the ORM handles escaping automatically. The real issue was wildcard characters (%, _) in user search input being passed literally to ILIKE, causing unexpected match behavior.
// This is SAFE from SQL injection (Drizzle parameterizes)
.where(like(commands.name, `%${search}%`))
// But wildcards in 'search' behave unexpectedly:
// search = "100%" matches "100%", "1000", "10000"...
Correction: Changed title to “Wildcard Characters in AI Search Filters” and reduced severity to 🟡 Low.
Issue #127: “N+1 Query” → In-Memory Filtering
Initial label: Performance: N+1 Rule Evaluation (🔴 High)
Actual problem: The code fetched all rules in one query, then looped through them in JavaScript memory. There were zero additional database queries inside the loop.
// ONE query — gets all rules
const rules = await db.select().from(categoryRules).where(...);
// Pure JS loop — no DB calls
for (const rule of rules) {
evaluateRule(rule, transaction); // CPU only, no await
}
What N+1 actually looks like:
// 1 query for rules, then N queries inside the loop = N+1
for (const rule of rules) {
const category = await db.select().from(categories).where(...); // ❌ Query in loop!
}
Correction: Updated issue to clarify this is about unbounded memory usage (loading unlimited rules), not N+1 queries.
The Triage Checklist
Before labeling something “High Severity”:
- Read the actual code — not just the issue title
- Verify the bug pattern — N+1 requires queries in loops; SQL injection requires string concatenation into queries
- Check the framework — ORMs like Drizzle, Prisma, or SQLx handle parameterization automatically
- Reproduce if possible — can you actually trigger the claimed behavior?
Scanner-generated issues are starting points, not verdicts. The human review step exists for a reason.