Repository navigation
Expand file tree
/
Copy pathgold.cases.json
More file actions
370 lines (370 loc) · 24.3 KB
/
Copy pathgold.cases.json
File metadata and controls
370 lines (370 loc) · 24.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
{
"_doc": "Real historical bugfix-commit cases for the code-review-skill task. Each case's snippet_text is a VERBATIM excerpt of the PRE-FIX file at the commit's parent SHA (fetched via `gh api repos/<repo>/contents/<path>?ref=<parent_sha>`), from a permissively-licensed (MIT/Apache-2.0) public repo. buggy_line is the 1-indexed absolute line number of the actual bug IN THE ORIGINAL FILE, verified against the fetched content. inputs/ and expected/ are GENERATED from this file by build_cases.py -- edit here, never there.",
"cases": [
{
"id": "case_01",
"bug_category": "off_by_one",
"source_repo": "pallets-eco/croniter",
"source_commit_url": "https://github.com/pallets-eco/croniter/commit/3ddcd1385b53adabcce42bd14c6b8dfb83899fa6",
"license": "MIT",
"file_path": "src/croniter/croniter.py",
"snippet_start_line": 1267,
"snippet_text": " def _get_low_from_current_date_number(cls, field_index, step, from_timestamp):\n dt = datetime.datetime.fromtimestamp(from_timestamp, tz=UTC_DT)\n if field_index == MINUTE_FIELD:\n return dt.minute % step\n if field_index == HOUR_FIELD:\n return dt.hour % step\n if field_index == DAY_FIELD:\n return ((dt.day - 1) % step) + 1\n if field_index == MONTH_FIELD:\n return dt.month % step\n if field_index == DOW_FIELD:\n return (dt.weekday() + 1) % step\n\n raise ValueError(\"Can't get current date number for index larger than 4\")\n",
"buggy_line": 1276,
"line_tolerance": 2,
"keywords": [
"off-by-one",
"off by one",
"1-based",
"1-indexed",
"boundary condition",
"wrong range",
"out of range",
"+1 correction",
"1-12 range",
"valid 1-12",
"falls outside",
"outside the valid range",
"no correction to keep"
],
"bug_description": "The month field is 1-based (1-12), but its low-bound is computed with `dt.month % step` instead of the 1-based formula `((dt.month - 1) % step) + 1` already used for the day field a few lines above. When step evenly divides the month, this yields 0, which is outside the valid 1-12 range."
},
{
"id": "case_02",
"bug_category": "null_deref",
"source_repo": "googlefonts/gftools",
"source_commit_url": "https://github.com/googlefonts/gftools/commit/f28317cab96bda1d8e07f5b044f645e6e4c07cab",
"license": "Apache-2.0",
"file_path": "Lib/gftools/push/trafficjam.py",
"snippet_start_line": 523,
"snippet_text": " for item in board_items:\n if item[\"type\"] != \"PULL_REQUEST\":\n raise ValueError(\n \"Traffic Jam contains issues! All items must be pull requests. \"\n \"Please remove the issues and rerun the tool, \"\n \"https://github.com/orgs/google/projects/74/views/1\"\n )\n # sort items by pr number\n board_items.sort(key=lambda k: k[\"content\"][\"url\"])\n\n # get files for prs which have more than 100 changed files\n for item in board_items:\n changed_files = item[\"content\"][\"files\"][\"totalCount\"]\n if changed_files <= 100:\n continue\n pr_number = item[\"content\"][\"number\"]\n pr_url = item[\"content\"][\"url\"]\n log.warn(\n f\"{pr_url} has {changed_files} changed files. Attempting to fetch them.\"\n )\n files = g.pr_files(pr_number)\n item[\"content\"][\"files\"][\"nodes\"] = [\n {\"path\": f[\"filename\"]} for f in files\n ]",
"buggy_line": 535,
"line_tolerance": 2,
"keywords": [
"missing null check",
"missing none check",
"null check",
"none check",
"not subscriptable"
],
"bug_description": "The loop indexes item[\"content\"][\"files\"][\"totalCount\"] for every board item without checking whether the PR is closed-but-not-merged; for such PRs the API returns files: null, so subscripting it raises \"TypeError: 'NoneType' object is not subscriptable\"."
},
{
"id": "case_03",
"bug_category": "elif_short_circuit_wrong_deny",
"source_repo": "rsinger86/drf-access-policy",
"source_commit_url": "https://github.com/rsinger86/drf-access-policy/commit/fd628ad7cf0c30f039ca33f12881ac00d4c2d99d",
"license": "MIT",
"file_path": "rest_access_policy/access_policy.py",
"snippet_start_line": 88,
"snippet_text": " def _get_statements_matching_principal(\n self, request, statements: List[dict]\n ) -> List[dict]:\n user = request.user\n user_roles = None\n matched = []\n\n for statement in statements:\n principals = statement[\"principal\"]\n found = False\n\n if \"*\" in principals:\n found = True\n elif \"admin\" in principals:\n found = user.is_superuser\n elif \"staff\" in principals:\n found = user.is_staff\n elif \"authenticated\" in principals:\n found = not user.is_anonymous\n elif \"anonymous\" in principals:\n found = user.is_anonymous\n elif self.id_prefix + str(user.pk) in principals:\n found = True\n else:\n if not user_roles:\n user_roles = self.get_user_group_values(user)\n\n for user_role in user_roles:\n if self.group_prefix + user_role in principals:\n found = True\n break\n\n if found:\n matched.append(statement)\n\n return matched",
"buggy_line": 102,
"line_tolerance": 8,
"keywords": [
"elif chain stops",
"stops evaluating",
"doesn't fall through",
"should fall through",
"multiple principal types",
"multiple principals",
"list of principals",
"wrongly denies access",
"wrongly denied",
"incorrectly denying access",
"incorrectly denied",
"terminates the check",
"first matching keyword",
"should continue checking",
"prevents checking the remaining",
"never reached",
"elif short-circuits",
"stops checking",
"mutually exclusive"
],
"keyword_groups": [
[
"elif\\s+chains?",
"elif\\s+branch(es)?",
"if\\s*/\\s*elif"
],
[
"stop\\w*\\s+(evaluat\\w*|check\\w*)",
"mutually\\s+exclusive",
"never\\s+reached",
"only\\s+evaluat\\w*",
"evaluat\\w*\\s+only",
"fail\\w*\\s+to\\s+match",
"wrongly\\s+den\\w*",
"incorrectly\\s+den\\w*",
"short.?circuit\\w*",
"den(y|ies|ied)\\s+access",
"not\\s+(fall|fallen)\\s+through",
"should\\s+fall\\s+through"
]
],
"bug_description": "principals is a list that can declare multiple principal types at once (e.g. ['admin', 'id:5']), but the elif chain only tests which keyword is present, not whether that keyword's own check actually passes. If a user matches the 'admin' keyword but isn't a superuser, found=False and the elif chain stops there -- it never falls through to check the id_prefix (or any other) keyword also present in the same list, wrongly denying access the statement should have granted."
},
{
"id": "case_04",
"bug_category": "stale_closure_state",
"source_repo": "litl/backoff",
"source_commit_url": "https://github.com/litl/backoff/commit/732eaa34f782e31e891bbbd4aa524e42b27f3bc7",
"license": "MIT",
"file_path": "backoff/_sync.py",
"snippet_start_line": 23,
"snippet_text": "def retry_predicate(target, wait_gen, predicate,\n *,\n max_tries, max_time, jitter,\n on_success, on_backoff, on_giveup,\n wait_gen_kwargs):\n\n @functools.wraps(target)\n def retry(*args, **kwargs):\n\n # update variables from outer function args\n nonlocal max_tries, max_time\n max_tries = _maybe_call(max_tries)\n max_time = _maybe_call(max_time)\n\n tries = 0\n start = datetime.datetime.now()\n wait = _init_wait_gen(wait_gen, wait_gen_kwargs)\n while True:\n tries += 1\n elapsed = timedelta.total_seconds(datetime.datetime.now() - start)\n details = {\n \"target\": target,\n \"args\": args,\n \"kwargs\": kwargs,\n \"tries\": tries,\n \"elapsed\": elapsed,\n }\n\n ret = target(*args, **kwargs)\n if predicate(ret):\n max_tries_exceeded = (tries == max_tries)\n max_time_exceeded = (max_time is not None and\n elapsed >= max_time)",
"buggy_line": 34,
"line_tolerance": 2,
"keywords": [
"frozen after the first call",
"resolved only once",
"permanently overwrites",
"reassigning the nonlocal",
"mutates the enclosing scope",
"never re-evaluated",
"silently frozen",
"stale after the first call",
"only resolved on the first",
"overwrites the outer",
"no longer callable",
"overwritten with their called results",
"re-evaluated fresh each time",
"overwrites the closure variables",
"shared nonlocal state",
"receive the already-resolved",
"closure state shared"
],
"keyword_groups": [
[
"\\bnonlocal\\b"
],
[
"overwrit\\w*",
"resolv\\w*\\s+only",
"only\\s+resolv\\w*",
"froz\\w*",
"\\bstale\\b",
"permanent\\w*",
"only\\s+(evaluat\\w*|call\\w*|resolv\\w*)\\s+once",
"once\\s+and\\s+permanent\\w*",
"first\\s+(call|invocation)",
"reassign\\w*"
]
],
"bug_description": "retry() declares max_tries/max_time as nonlocal and reassigns them to the already-resolved value; because of nonlocal this mutates the enclosing retry_predicate scope permanently. max_tries/max_time can be passed as a callable specifically so it's re-evaluated on every call, but after the first invocation the reassignment replaces it with a plain value, so _maybe_call's callable(f) check is never true again -- every subsequent call to the decorated function silently reuses whatever the first call happened to resolve, forever."
},
{
"id": "case_05",
"bug_category": "resource_leak",
"source_repo": "miketheman/pytest-socket",
"source_commit_url": "https://github.com/miketheman/pytest-socket/commit/2aaaee1bc226ddb996dd5498f3f64e9387f91db5",
"license": "MIT",
"file_path": "src/pytest_socket/__init__.py",
"snippet_start_line": 344,
"snippet_text": " def guarded_connect(inst: socket.socket, *args: Any) -> None:\n host = host_from_connect_args(args)\n if host in allowed_ip_hosts_and_hostnames or (\n _is_unix_socket(inst.family) and allow_unix_socket\n ):\n return _true_connect(inst, *args)\n\n if host and networks and is_ipaddress(host):\n ip = ipaddress.ip_address(host)\n if any(ip in net for net in networks):\n return _true_connect(inst, *args)\n\n raise SocketConnectBlockedError(allowed_list, host)\n",
"buggy_line": 356,
"line_tolerance": 2,
"keywords": [
"resource leak",
"file descriptor leak",
"file descriptor leaks",
"socket not closed",
"not closed",
"leaked fd",
"fd leak"
],
"bug_description": "When a connection is disallowed, the function raises SocketConnectBlockedError without first closing the already-created real socket `inst`, leaking its file descriptor."
},
{
"id": "case_06",
"bug_category": "security_missing_auth",
"source_repo": "homeassistant-ai/ha-mcp",
"source_commit_url": "https://github.com/homeassistant-ai/ha-mcp/commit/9f5b085ad4a7b38b067c9da0dc5b45462c4d796e",
"license": "MIT",
"file_path": "src/ha_mcp/settings_ui.py",
"snippet_start_line": 3033,
"snippet_text": " def _mount(prefix: str) -> None:\n for path, methods, handler_key in routes:\n mcp.custom_route(f\"{prefix}{path}\", methods=methods)(handlers[handler_key])\n\n if is_addon:\n # Root mount lets HA ingress proxy localhost:9583/ \u2192 settings UI.\n # Direct port 9583 LAN access also reaches these routes; in this\n # respect they share the existing add-on networking model where\n # port 9583 is exposed via host_network and the secret path is\n # the auth for direct access. Document this in DOCS.md.\n mcp.custom_route(\"/\", methods=[\"GET\"])(handlers[\"root_page\"])\n _mount(\"\")\n\n if secret_prefix:\n # Mount under the MCP secret path so Docker / standalone clients\n # need the same secret to reach the UI as they do for the MCP\n # endpoint.\n _mount(secret_prefix)",
"buggy_line": 3044,
"line_tolerance": 2,
"keywords": [
"missing authentication",
"no authentication",
"without authentication",
"no auth check",
"no auth",
"unauthenticated access",
"authorization bypass",
"publicly reachable",
"without requiring the secret",
"bypassing the secret",
"secret-based auth",
"without the secret path",
"no secret required",
"without the secret"
],
"keyword_groups": [
[
"unauthenticat\\w*",
"without\\s+(any\\s+)?auth\\w*",
"no\\s+auth\\w*",
"missing\\s+auth\\w*",
"auth\\w*\\s+bypass",
"bypass\\w*",
"not\\s+enforc\\w*",
"no\\s+secret",
"without\\s+(the\\s+)?secret"
],
[
"expos\\w*",
"reachable",
"direct\\s+(lan\\s+)?access",
"publicly",
"root\\s+(prefix|path|mount)",
"empty\\s+prefix",
"bare\\s+root"
]
],
"bug_description": "In add-on mode, the entire settings/backup/policy API is mounted at the bare root path via `_mount(\"\")` with zero authentication check, while the secret-path mount is the only place a secret is required; any client reaching the port directly can invoke these state-changing endpoints unauthenticated."
},
{
"id": "case_07",
"bug_category": "sql_injection",
"source_repo": "learningequality/kolibri",
"source_commit_url": "https://github.com/learningequality/kolibri/commit/520b0778568694b5cbb4961eaec5b1979b28dc5c",
"license": "MIT",
"file_path": "kolibri/content/api.py",
"snippet_start_line": 238,
"snippet_text": " def filter_in_lesson(self, queryset, name, value):\n \"\"\"\n Show only content associated with this lesson\n\n :param queryset: all content nodes\n :param value: id of target lesson\n :return: content nodes for this lesson\n \"\"\"\n try:\n resources = Lesson.objects.get(id=value).resources\n contentnode_id_list = [node['contentnode_id'] for node in resources]\n # adapted from https://codybonney.com/creating-a-queryset-from-a-list-while-preserving-order-using-django/\n clauses = ' '.join([\"WHEN {}.id='{}' THEN {}\".format(models.ContentNode._meta.db_table,\n pk, i) for i, pk in enumerate(contentnode_id_list)])\n ordering = 'CASE {} END'.format(clauses)\n return queryset.filter(pk__in=contentnode_id_list) \\\n .extra(select={'ordering': ordering}, order_by=('ordering',))\n except (Lesson.DoesNotExist, ValueError): # also handles invalid uuid\n queryset.none()",
"buggy_line": 250,
"line_tolerance": 2,
"keywords": [
"sql injection",
"not parameterized",
"raw sql",
"unsanitized",
"string formatting into sql",
"break out of the string literal",
"user-controlled value",
"injectable",
"extra() with unsanitized",
"escape the quote",
"inject into the order by"
],
"bug_description": "clauses interpolates pk (a content-node id sourced from a lesson's resources field, editable by any coach/admin via the API) directly into a raw SQL fragment via .format(), which is passed to Django's .extra() -- a documented SQL-injection vector. A malicious id containing a single quote could break out of the string literal and inject arbitrary SQL into the resulting ORDER BY clause."
},
{
"id": "case_08",
"bug_category": "broad_except",
"source_repo": "xarray-contrib/xarray-spatial",
"source_commit_url": "https://github.com/xarray-contrib/xarray-spatial/commit/c6daae54b8776b7c6ac67c77fcb9f9531cb56069",
"license": "MIT",
"file_path": "xrspatial/geotiff/_vrt.py",
"snippet_start_line": 347,
"snippet_text": " src_r0 = sr.y_off + int((clip_r0 - dst_r0) * scale_y)\n src_c0 = sr.x_off + int((clip_c0 - dst_c0) * scale_x)\n src_r1 = sr.y_off + int((clip_r1 - dst_r0) * scale_y)\n src_c1 = sr.x_off + int((clip_c1 - dst_c0) * scale_x)\n\n # Read from source file using windowed read\n try:\n src_arr, _ = read_to_array(\n src.filename,\n window=(src_r0, src_c0, src_r1, src_c1),\n band=src.band - 1, # convert 1-based to 0-based\n )\n except Exception as e:\n # Under XRSPATIAL_GEOTIFF_STRICT=1, surface the read failure\n # so partial mosaics are caught in CI. Default mode warns\n # once per missing source then continues, preserving the\n # historical behaviour. See issue #1662.\n import warnings\n from . import _geotiff_strict_mode, GeoTIFFFallbackWarning\n if _geotiff_strict_mode():\n raise\n warnings.warn(\n f\"VRT source {src.filename!r} could not be read \"\n f\"({type(e).__name__}: {e}); skipping. The output \"\n f\"mosaic will have a hole at this tile.\",\n GeoTIFFFallbackWarning,\n stacklevel=2,\n )\n continue # skip missing/unreadable sources",
"buggy_line": 359,
"line_tolerance": 2,
"keywords": [
"bare except",
"except exception",
"too broad",
"overly broad",
"broadest exception",
"broadest `exception`",
"swallowing exceptions",
"catches too much",
"catches everything",
"should be narrowed",
"narrowed to",
"can mask genuine",
"mask genuine errors",
"masks genuine",
"masking genuine",
"hiding real defects",
"hiding genuine defects",
"hides real defects",
"hides genuine defects"
],
"bug_description": "The windowed read of each VRT source tile is wrapped in `except Exception as e`, far broader than the I/O errors read_to_array actually raises, silently catching genuine unrelated bugs like RuntimeError or MemoryError."
},
{
"id": "case_09",
"bug_category": "cache_invalidate_keyerror",
"source_repo": "Suor/funcy",
"source_commit_url": "https://github.com/Suor/funcy/commit/a96b449ee08d7355805e4c6037e9782e74241ff7",
"license": "BSD-3-Clause",
"file_path": "funcy/calc.py",
"snippet_start_line": 100,
"snippet_text": "def cache(timeout, key_func=None):\n \"\"\"Caches a function results for timeout seconds.\"\"\"\n if isinstance(timeout, int):\n timeout = timedelta(seconds=timeout)\n\n if key_func is None:\n key_func = lambda *a, **kw: a + tuple(sorted(kw.items())) if kw else a\n\n def decorator(func):\n cache = {}\n\n @wraps(func)\n def wrapper(*args, **kwargs):\n key = key_func(*args, **kwargs)\n if key in cache:\n result, timestamp = cache[key]\n if datetime.now() - timestamp < timeout:\n return result\n else:\n del cache[key]\n\n result = func(*args, **kwargs)\n cache[key] = result, datetime.now()\n return result\n\n def invalidate(*args, **kwargs):\n cache.pop(key_func(*args, **kwargs))\n wrapper.invalidate = invalidate\n\n def invalidate_all():\n cache.clear()\n wrapper.invalidate_all = invalidate_all\n",
"buggy_line": 126,
"line_tolerance": 2,
"keywords": [
"dict.pop without default",
"missing default value",
"pop raises keyerror",
"race condition",
"concurrent invalidation",
"cache invalidate crash",
"works in tests fails in production",
"already removed",
"raises a keyerror",
"raises keyerror if",
"key is not present",
"was never cached",
"already expired"
],
"keyword_groups": [
[
"key\\s*error"
],
[
"without\\s+a?\\s*default",
"isn'?t\\s+cached",
"is\\s+not\\s+cached",
"not\\s+present",
"not\\s+in\\s+the\\s+cache",
"no\\s+longer\\s+cached",
"was\\s+never\\s+cached",
"already\\s+(removed|expired|invalidated|evicted)",
"doesn'?t\\s+exist",
"does\\s+not\\s+exist",
"missing\\s+key",
"absent\\s+key",
"key\\s+is\\s*n'?t\\s+(present|found|cached)"
]
],
"bug_description": "The invalidate() closure calls cache.pop(key_func(*args, **kwargs)) without a default value; if the key was already removed -- e.g. it expired via the timeout-based eviction on the next read, or a concurrent thread already invalidated it -- pop() raises KeyError. Because unit tests rarely hit this timing/race window, the code appears correct until it crashes in production."
},
{
"id": "case_10",
"bug_category": "floating_point_truncation",
"source_repo": "spulec/freezegun",
"source_commit_url": "https://github.com/spulec/freezegun/commit/9591645e0fd49b54bab688ac7eeeec14b87a9226",
"license": "Apache-2.0",
"file_path": "freezegun/api.py",
"snippet_start_line": 176,
"snippet_text": "def fake_time():\n if _should_use_real_time():\n return real_time()\n current_time = get_current_time()\n return calendar.timegm(current_time.timetuple()) + current_time.microsecond / 1000000.0\n\nif _TIME_NS_PRESENT:\n def fake_time_ns():\n if _should_use_real_time():\n return real_time_ns()\n return int(int(fake_time()) * 1e9)\n",
"buggy_line": 186,
"line_tolerance": 0,
"keywords": [
"truncation order",
"int() called too early",
"microsecond precision lost",
"precision loss",
"floating point truncation",
"rounds down to the second",
"sub-second precision discarded",
"double truncation",
"multiply before truncating",
"uuid collision",
"loses microseconds",
"discarding all sub-second precision",
"discarding sub-second precision",
"discards sub-second precision",
"rounded down to a whole second",
"before multiplying by 1e9",
"loses the microseconds"
],
"bug_description": "fake_time_ns() computes int(int(fake_time()) * 1e9): the inner int() truncates fake_time()'s fractional (microsecond) part to zero BEFORE multiplying by 1e9, discarding all sub-second precision. It should multiply first and truncate once: int(fake_time() * 1e9). The practical symptom is that uuid1() calls made within the same frozen second collide, since time_ns() loses the microseconds that would otherwise differentiate them."
}
]
}