Skip to content

Commit dc08c80

Browse files
Maffoochclaude
andcommitted
fix(locations): LocationData.code robust to unhashable context; progpilot scalars
CI (the V3-on rest-framework matrix variant) caught two coupled defects the local V3-off run could not, since the emission only fires under V3: - LocationData.code filtered unset context with `value not in {"", None}` — a SET membership test that hashes `value`, so any unhashable value crashed with "cannot use 'list' as a set element". Switched to tuple membership (`in ("", None)`), which compares by equality and never hashes — robust for every call site, present and future. - progpilot reports taint-source fields as single-element arrays (source_name=["$sql"], source_line=[610]); the sweep passed those lists straight into the context kwargs. Now collapsed to scalars via a _first() helper, with str()/int guards. Audited every other converted parser's context kwargs: all pass .text/int/ string scalars (checkmarx pathnode elements, sarif get_snippet, etc.) — progpilot was the only list-valued source. Verified against the full unittests.tools suite run with V3_FEATURE_LOCATIONS=True. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 58938cd commit dc08c80

2 files changed

Lines changed: 22 additions & 5 deletions

File tree

dojo/tools/locations.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ def code(
6868
"source_file_path": source_file_path,
6969
"source_line": source_line,
7070
}
71-
data.update({key: value for key, value in context.items() if value not in {"", None}})
71+
# Tuple (not set) membership: uses == rather than hashing, so an unset
72+
# check never crashes on an unhashable value a parser might pass.
73+
data.update({key: value for key, value in context.items() if value not in ("", None)})
7274
return cls(type="code", data=data)
7375

7476
@classmethod

dojo/tools/progpilot/parser.py

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,18 @@
66
from dojo.tools.locations import LocationData
77

88

9+
def _first(value):
10+
"""
11+
Collapse progpilot taint-source fields to a scalar.
12+
13+
progpilot reports them as single-element arrays (e.g. source_name=["$sql"],
14+
source_line=[610]); the location context expects the scalar value.
15+
"""
16+
if isinstance(value, list):
17+
return value[0] if value else None
18+
return value
19+
20+
921
class ProgpilotParser:
1022
def get_scan_types(self):
1123
return ["Progpilot Scan"]
@@ -80,14 +92,17 @@ def get_findings(self, filename, test):
8092
if vuln_cwe is not None:
8193
find.cwe = int(vuln_cwe.split("CWE_")[1])
8294
if settings.V3_FEATURE_LOCATIONS and find.file_path:
95+
source_name_scalar = _first(source_name)
96+
source_file_scalar = _first(source_file)
97+
source_line_scalar = _first(source_line)
8398
find.unsaved_locations.append(
8499
LocationData.code(
85100
file_path=find.file_path,
86101
line=find.line,
87-
source_object=source_name or "",
88-
sink_object=sink_name or "",
89-
source_file_path=source_file or "",
90-
source_line=source_line if isinstance(source_line, int) else None,
102+
source_object=str(source_name_scalar) if source_name_scalar else "",
103+
sink_object=str(sink_name) if sink_name else "",
104+
source_file_path=str(source_file_scalar) if source_file_scalar else "",
105+
source_line=source_line_scalar if isinstance(source_line_scalar, int) else None,
91106
),
92107
)
93108
findings.append(find)

0 commit comments

Comments
 (0)