t1_off_by_one_slice_BEGIN
Issue (line 2): `items[len(items) - n - 1:]` returns the last `n + 1` items, not `n` (off-by-one).
Fix: `return items[-n:] if n > 0 else []` — the `n > 0` guard is required because `items[-0:]` is the full list, not empty.
t1_off_by_one_slice_END
t2_mutable_default_dict_BEGIN
Issue (line 1): Mutable default argument — `history={}` is created once at function-definition time and shared across all calls that omit `history`.
Fix: `def greet(name, history=None):` with `if history is None: history = {}` at the top of the body.
t2_mutable_default_dict_END
t3_command_injection_BEGIN
Issue (line 4): Shell command injection — `directory` is attacker-controlled and interpolated into a `shell=True` string, allowing arbitrary command execution (e.g., `; rm -rf ~`).
Fix: Drop `shell=True` and pass argv as a list: `subprocess.run(["ls", directory], capture_output=True, text=True)`. Also validate `directory` against an allowlist or constrain to a known root to prevent path traversal.
t3_command_injection_END
t4_forgotten_await_BEGIN
Issue (line 3): `httpx.AsyncClient().get(...)` returns a coroutine that is never awaited, so `response` is a coroutine object and `.json()` raises `AttributeError`. The `AsyncClient` is also never closed (resource leak).
Fix:
```
async with httpx.AsyncClient() as client:
    response = await client.get(f"/users/{user_id}")
    return response.json()
```
t4_forgotten_await_END
t5_dict_iter_mutation_BEGIN
Issue (line 2): Mutating `d` (`del d[key]`) while iterating over it raises `RuntimeError: dictionary changed size during iteration`.
Fix: Iterate a snapshot of the keys: `for key in list(d): ...`, or rebuild: `return {k: v for k, v in d.items() if v >= 0}`.
t5_dict_iter_mutation_END
t6_clean_code_BEGIN
Issue (line N/A): No correctness defects. Bounds are validated and `max(lo, min(value, hi))` correctly clamps. NaN propagation through `min`/`max` is implementation-defined (uncertain whether that is desired behavior — worth documenting if NaN inputs are possible).
Fix: None required.
t6_clean_code_END
c1_factorial_BEGIN
Issue (line 3): `range(n)` starts at 0, so `result *= 0` on the first iteration makes `factorial(n)` always return 0 for `n >= 1`. Negative `n` silently returns 1 (uncertain whether that is intended).
Fix: `for i in range(1, n + 1): result *= i`. Optionally raise `ValueError` on negative `n`.
c1_factorial_END
c2_counter_race_BEGIN
Issue (line 6): `self.count += 1` is not atomic — it is a read-modify-write that can interleave under threading, losing increments. The GIL serializes individual bytecodes but not the compound `+=`.
Fix: Guard with a lock:
```
def __init__(self):
    self.count = 0
    self._lock = threading.Lock()

def increment(self):
    with self._lock:
        self.count += 1
```
c2_counter_race_END
c3_sql_injection_BEGIN
Issue (line 1): SQL injection — `user_input` is concatenated directly into the query, allowing inputs like `' OR '1'='1` or `'; DROP TABLE users; --` to alter the statement.
Fix: Parameterize: `cursor.execute("SELECT * FROM users WHERE name = ?", (user_input,))` (placeholder syntax depends on the DB driver — `?` for sqlite3, `%s` for psycopg2/MySQLdb).
c3_sql_injection_END
c4_file_leak_BEGIN
Issue (line 2): File handle is never closed — leaks the descriptor, with closure timing depending on GC (not guaranteed prompt on PyPy/other implementations). Implicit text mode + platform-default encoding (uncertain on non-Linux).
Fix: `with open(path, encoding="utf-8") as f: return f.read()`.
c4_file_leak_END
c5_n_plus_1_BEGIN
Issue (line 3): N+1 query — one SELECT per user. Also SQL injection via f-string interpolation of `u.id` (lower severity if `id` is integer-typed, still bad practice).
Fix: Single parameterized `IN` query plus in-memory grouping:
```
ids = [u.id for u in users]
rows = db.query("SELECT * FROM posts WHERE user_id IN %s", (tuple(ids),))
by_user = {}
for r in rows:
    by_user.setdefault(r.user_id, []).append(r)
return [{"user": u, "posts": by_user.get(u.id, [])} for u in users]
```
c5_n_plus_1_END
c6_ordered_check_BEGIN
Issue (line 2): Two problems — (1) `assert` is stripped under `python -O`, so validation silently disappears in production; (2) `order['total']` is read before `'customer_id' in order` is checked, raising `KeyError` instead of a validation error when keys are missing.
Fix: Replace `assert` with explicit `raise ValueError(...)` and check key presence before subscripting.
c6_ordered_check_END
c7_concat_loop_BEGIN
Issue (line 3): Repeated `+=` on a string is O(n²) in the general case. CPython has an in-place optimization for refcount-1 strings (uncertain on PyPy and other implementations).
Fix: `return '\n'.join(lines) + '\n'` (preserves the trailing newline; drop the `+ '\n'` if not intended).
c7_concat_loop_END
c8_mutable_default_BEGIN
Issue (line 1): Mutable default argument `log: list = []` is shared across all calls that omit `log`, accumulating events across unrelated callers.
Fix: `def add_event(event: str, log: list | None = None) -> list:` with `if log is None: log = []` at the top.
c8_mutable_default_END
c9_late_binding_BEGIN
Issue (line 4): Late-binding closure — every lambda captures the same variable `v`, which holds the loop's final value when the callbacks run. All three callbacks print `3`.
Fix: `callbacks.append(lambda v=v: print(v))` — default arguments are evaluated at lambda-definition time.
c9_late_binding_END
c10_clean_code_BEGIN
Issue (line N/A): No correctness defects. `sum(data) % 256` is an equivalent and faster simplification (uncertain on micro-benchmarks but avoids the Python-level loop).
Fix: Optional: `return sum(data) % 256`.
c10_clean_code_END
