Skip to content

Commit 3762438

Browse files
committed
fix: prevent ID collisions and namespace global variable
- fix(html): add unique suffix to canvas element IDs - fix(js): namespace global chart instances as bbsnlyChartJSInstances - fix(security): prevent potential ID conflicts in loops - test: update tests to match new ID and namespace patterns
1 parent f7efd25 commit 3762438

File tree

2 files changed

+26
-11
lines changed

2 files changed

+26
-11
lines changed

src/Chart.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,10 @@ public function toHtml(string $element, Chart $chart = null): string
116116
$chart = $this;
117117
}
118118

119-
$elementId = htmlspecialchars($element, ENT_QUOTES, 'UTF-8');
120-
$chartId = 'chart_' . uniqid();
119+
$uniqueSuffix = '_' . uniqid();
120+
$elementId = htmlspecialchars($element . $uniqueSuffix, ENT_QUOTES, 'UTF-8');
121+
$chartId = 'chart' . $uniqueSuffix;
122+
$instancesVar = 'bbsnlyChartJSInstances'; // Namespace the global variable
121123

122124
return '<canvas id="' . $elementId . '"></canvas>
123125
<script>
@@ -131,16 +133,16 @@ public function toHtml(string $element, Chart $chart = null): string
131133
const ' . $chartId . ' = new Chart(ctx, ' . $chart->toJson() . ');
132134
133135
// Store chart instance for potential external access
134-
if (typeof window.chartInstances === "undefined") {
135-
window.chartInstances = {};
136+
if (typeof window.' . $instancesVar . ' === "undefined") {
137+
window.' . $instancesVar . ' = {};
136138
}
137-
window.chartInstances["' . $elementId . '"] = ' . $chartId . ';
139+
window.' . $instancesVar . '["' . $elementId . '"] = ' . $chartId . ';
138140
139141
// Cleanup on page unload to prevent memory leaks
140142
window.addEventListener("unload", function() {
141-
if (window.chartInstances["' . $elementId . '"]) {
143+
if (window.' . $instancesVar . '["' . $elementId . '"]) {
142144
' . $chartId . '.destroy();
143-
delete window.chartInstances["' . $elementId . '"];
145+
delete window.' . $instancesVar . '["' . $elementId . '"];
144146
}
145147
});
146148
})();

tests/ChartTest.php

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,8 @@ public function test_html_special_chars_are_escaped()
355355
$maliciousId = '"><script>alert("xss")</script>';
356356
$html = $this->chart->toHtml($maliciousId);
357357

358-
$this->assertStringContains('id="&quot;&gt;&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;"', $html);
358+
// Update assertion to include unique suffix
359+
$this->assertStringContains('id="&quot;&gt;&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;_', $html);
359360
$this->assertStringNotContains($maliciousId, $html);
360361
}
361362

@@ -396,9 +397,21 @@ public function test_chart_instances_are_tracked()
396397
{
397398
$html = $this->chart->toHtml('test-chart');
398399

399-
$this->assertStringContains('window.chartInstances', $html);
400-
$this->assertStringContains('window.chartInstances["test-chart"]', $html);
401-
$this->assertStringContains('delete window.chartInstances', $html);
400+
// Update assertions to use correct global variable name
401+
$this->assertStringContains('window.bbsnlyChartJSInstances', $html);
402+
$this->assertStringContains('bbsnlyChartJSInstances["test-chart_', $html);
403+
$this->assertStringContains('delete window.bbsnlyChartJSInstances', $html);
404+
}
405+
406+
public function test_element_ids_are_unique()
407+
{
408+
$html1 = $this->chart->toHtml('my-chart');
409+
$html2 = $this->chart->toHtml('my-chart');
410+
411+
preg_match('/id="(my-chart_[^"]+)"/', $html1, $matches1);
412+
preg_match('/id="(my-chart_[^"]+)"/', $html2, $matches2);
413+
414+
$this->assertNotEquals($matches1[1], $matches2[1]);
402415
}
403416

404417
/**

0 commit comments

Comments
 (0)