Skip to content

Commit f24b3a1

Browse files
committed
fix(entrypoints): bound dependency array scan to avoid false positives on unterminated arrays (#27)
An unclosed dependencies = [ in a truncated/corrupt pyproject.toml previously scanned to end of file and harvested quoted strings from later TOML tables as fake dependencies. Bound the scan to the next table header and return None when the array never closes, matching pre-fix behaviour of detecting nothing from a malformed file.
1 parent 84a69fd commit f24b3a1

2 files changed

Lines changed: 31 additions & 2 deletions

File tree

codeanalyzer/entrypoints/detect.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
_REQ = re.compile(r"^\s*['\"]?([A-Za-z0-9_.\-]+)")
1818
_DEPS_START = re.compile(r"dependencies\s*=\s*\[")
19+
_TABLE_HEADER = re.compile(r"(?m)^[ \t]*\[")
1920
_PKG = re.compile(r"['\"]([A-Za-z0-9][A-Za-z0-9_.\-]*)")
2021

2122

@@ -87,14 +88,22 @@ def _strip_comments(text: str) -> str:
8788
def _deps_array_span(text: str) -> Optional[str]:
8889
"""Return the contents between the `dependencies = [` and its matching
8990
`]`, counting bracket depth so a nested `[...]` (extras, e.g.
90-
`celery[redis]`) doesn't close the span early."""
91+
`celery[redis]`) doesn't close the span early.
92+
93+
Bounded by the next TOML table header (a `[` starting a line): if the
94+
array never closes before then, it's unterminated (truncated/corrupt
95+
file) and this returns None rather than harvesting quoted strings out
96+
of whatever table follows.
97+
"""
9198
m = _DEPS_START.search(text)
9299
if not m:
93100
return None
101+
boundary = _TABLE_HEADER.search(text, m.end())
102+
limit = boundary.start() if boundary else len(text)
94103
depth = 1
95104
in_str = None
96105
i = m.end()
97-
while i < len(text) and depth > 0:
106+
while i < limit and depth > 0:
98107
ch = text[i]
99108
if in_str:
100109
if ch == in_str:
@@ -106,4 +115,6 @@ def _deps_array_span(text: str) -> Optional[str]:
106115
elif ch == "]":
107116
depth -= 1
108117
i += 1
118+
if depth != 0:
119+
return None
109120
return text[m.end() : i - 1]

test/test_entrypoint_detect.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,21 @@ def test_commented_out_dependency_is_not_detected(tmp_path: Path):
5757
got = detected_frameworks(_app("os"), tmp_path, load_rules())
5858
assert "celery" not in got
5959
assert "flask" in got
60+
61+
62+
def test_unterminated_dependencies_array_detects_nothing(tmp_path: Path):
63+
"""A truncated/corrupt pyproject.toml must not leak quoted strings from
64+
a later table (e.g. an author email or homepage URL) into the detected
65+
package set -- matching pre-fix behaviour of "malformed file, nothing
66+
detected"."""
67+
(tmp_path / "pyproject.toml").write_text(
68+
"[project]\n"
69+
'name = "x"\n'
70+
"dependencies = [\n"
71+
' "celery>=5"\n'
72+
"\n"
73+
"[project.urls]\n"
74+
'Homepage = "https://flask.example.com"\n'
75+
)
76+
got = detected_frameworks(_app("os"), tmp_path, load_rules())
77+
assert got == set()

0 commit comments

Comments
 (0)