2.3 KiB
pandas — CWE-407 Disclosure Brief
2026-03-27 · Patch available — awaiting upstream merge
Finding
One O(n²) defect in pandas's DataFrame style renderer. style_render.py checks r not in self.hidden_rows — a list membership test — inside an O(R×C) body-cell loop, causing O(R²×C) overhead for styled DataFrame rendering with hidden rows. Patch ready for upstream review.
The Defects
pandas-0001 (PATCHED — HIGH): pandas/io/formats/style_render.py
# Inside body-cell loop — O(R×C) outer iteration:
for r in range(len(data)):
for c in range(len(data.columns)):
if r not in self.hidden_rows: # O(R) list scan per cell
...
# O(R²×C) total
r not in self.hidden_rows performs O(R) list scan for every body cell rendered. For R rows and C columns: O(R² × C) total. Measured ratio: 350×.
Complexity Proof
For R=350 rows, C=10 columns:
- Per render: R×C = 3,500 cell iterations × O(R) scan = 1,225,000 comparisons
- Fixed:
hidden_rows_set: set[int]pre-built → 3,500 set lookups - 350× measured ratio.
Impact
All pandas users using DataFrame.style with hide() for rows — a common pattern in Jupyter notebook reporting, data visualization, and dashboard generation. .style rendering is used extensively in data science workflows to produce HTML table output. DataFrames with many rows and hidden row subsets hit worst case. pandas is the most widely used Python data analysis library.
The Fix
Pre-build a set[int] from hidden_rows before the render loop:
# Before
if r not in self.hidden_rows: # O(R) list scan per cell
# After
# CWE-407 fix: pre-built set for O(1) hidden_rows check instead of O(R) list scan.
hidden_rows_set = set(self.hidden_rows)
if r not in hidden_rows_set: # O(1) set lookup
...
Patch
defects/pandas/patch/pandas-0001-style-render-hidden-rows-set.patch
What We Ask
- Confirm receipt and assign a GitHub Security Advisory or issue reference.
- Validate the patch against your style rendering test suite.
- Assess CVE eligibility — fires on every styled DataFrame render with hidden rows.
- Coordinate a disclosure date — we are targeting 90 days from first contact.
Contact: see cover email. This brief is confidential until coordinated disclosure.