mirror of
https://github.com/ZoneMinder/zoneminder.git
synced 2026-08-03 08:46:44 -04:00
Zone.Name, Event.Name, Event.Notes and Server.Name are persisted
user-controllable strings that were emitted without escaping, so a user
with edit rights on the object could store markup that runs in the
browser of anyone who later views the page, including an administrator.
Zone and Server save through getFormChanges + raw SQL, so they never
pass the object filter_regexp layer that strips markup elsewhere.
HTML and SVG sinks now use validHtmlStr():
- Zone::svg_polygon() escapes the <title>, covering every caller
(event view, montage and stream).
- views/zone.php and views/plugin.php headings.
- ajax/modals/server.php modal title.
The two inline-JS sinks in views/js/event.js.php are require_once'd
inside a <script nonce> block, so a </script> in Event.Name or Notes
broke out of the element and the nonce did not help. Notes was also
interpolated into a template literal, making backtick and ${} live.
Both now emit json_encode(..., JSON_HEX_TAG|JSON_HEX_APOS|
JSON_HEX_QUOT|JSON_HEX_AMP), which supplies its own double quotes.
Refs GHSA-72c3-g86w-f587.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
86 lines
2.5 KiB
PHP
86 lines
2.5 KiB
PHP
<?php
|
|
namespace ZM;
|
|
require_once('database.php');
|
|
require_once('Object.php');
|
|
require_once('Monitor.php');
|
|
|
|
class Zone extends ZM_Object {
|
|
protected static $table = 'Zones';
|
|
|
|
protected $defaults = array(
|
|
'Id' => null,
|
|
'MonitorId' => null,
|
|
'Name' => '',
|
|
'Type' => 'Active',
|
|
'Units' => 'Percent',
|
|
'NumCoords' => '4',
|
|
'Coords' => '',
|
|
'Area' => '0',
|
|
'AlarmRGB' => 0xff0000,
|
|
'CheckMethod' => 'Blobs',
|
|
'MinPixelThreshold' => 25,
|
|
'MaxPixelThreshold' => null,
|
|
'MinAlarmPixels' => null,
|
|
'MaxAlarmPixels' => null,
|
|
'FilterX' => 3,
|
|
'FilterY' => 3,
|
|
'MinFilterPixels' => null,
|
|
'MaxFilterPixels' => null,
|
|
'MinBlobPixels' => null,
|
|
'MaxBlobPixels' => null,
|
|
'MinBlobs' => 1,
|
|
'MaxBlobs' => null,
|
|
'OverloadFrames' => 0,
|
|
'ExtendAlarmFrames' => 0,
|
|
);
|
|
|
|
public static function find( $parameters = array(), $options = array() ) {
|
|
return ZM_Object::_find(self::class, $parameters, $options);
|
|
}
|
|
|
|
public static function find_one( $parameters = array(), $options = array() ) {
|
|
return ZM_Object::_find_one(self::class, $parameters, $options);
|
|
}
|
|
|
|
public function Monitor() {
|
|
if (isset($this->{'MonitorId'})) {
|
|
$Monitor = Monitor::find_one(array('Id'=>$this->{'MonitorId'}));
|
|
if ( $Monitor )
|
|
return $Monitor;
|
|
}
|
|
return new Monitor();
|
|
}
|
|
|
|
public function Points() {
|
|
return coordsToPoints($this->Coords());
|
|
}
|
|
|
|
public function AreaCoords() {
|
|
return preg_replace('/\s+/', ',', $this->Coords());
|
|
}
|
|
|
|
public function svg_polygon($width=0, $height=0) {
|
|
$areaCoords = $this->AreaCoords();
|
|
if ($width && $height) {
|
|
$points = coordsToPoints($this->Coords());
|
|
$isPixel = false;
|
|
foreach ($points as $point) {
|
|
if ($point['x'] > 100 || $point['y'] > 100) {
|
|
$isPixel = true;
|
|
break;
|
|
}
|
|
}
|
|
if ($isPixel) {
|
|
foreach ($points as &$point) {
|
|
$point['x'] = round($point['x'] / $width * 100, 2);
|
|
$point['y'] = round($point['y'] / $height * 100, 2);
|
|
}
|
|
unset($point);
|
|
$areaCoords = preg_replace('/\s+/', ',', pointsToCoords($points));
|
|
}
|
|
}
|
|
return '<polygon points="'.$areaCoords.'" class="'.$this->Type().'" data-mid="'.$this->MonitorId().'" data-zid="'.$this->Id().'"><title>'.validHtmlStr($this->Name()).'</title></polygon>';
|
|
}
|
|
} # end class Zone
|
|
?>
|