First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,5 @@
# in case PHPIDS is placed in the web-root
deny from all
# silence is golden
php_flag display_errors off

View file

@ -0,0 +1,151 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Caching/Interface.php';
/**
* APC caching wrapper
*
* This class inhabits functionality to get and set cache via memcached.
*
* @category Security
* @package PHPIDS
* @author Yves Berkholz <godzilla80@gmx.net>
* @copyright 2007-2009 The PHPIDS Groupoup
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id$
* @link http://php-ids.org/
* @since Version 0.6.5
*/
class IDS_Caching_Apc implements IDS_Caching_Interface
{
/**
* Caching type
*
* @var string
*/
private $type = null;
/**
* Cache configuration
*
* @var array
*/
private $config = null;
/**
* Flag if the filter storage has been found in memcached
*
* @var boolean
*/
private $isCached = false;
/**
* Holds an instance of this class
*
* @var object
*/
private static $cachingInstance = null;
/**
* Constructor
*
* @param string $type caching type
* @param array $init the IDS_Init object
*
* @return void
*/
public function __construct($type, $init)
{
$this->type = $type;
$this->config = $init->config['Caching'];
}
/**
* Returns an instance of this class
*
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return object $this
*/
public static function getInstance($type, $init)
{
if (!self::$cachingInstance) {
self::$cachingInstance = new IDS_Caching_Apc($type, $init);
}
return self::$cachingInstance;
}
/**
* Writes cache data
*
* @param array $data the caching data
*
* @return object $this
*/
public function setCache(array $data)
{
if(!$this->isCached)
apc_store($this->config['key_prefix'] . '.storage',
$data, $this->config['expiration_time']);
return $this;
}
/**
* Returns the cached data
*
* Note that this method returns false if either type or file cache is
* not set
*
* @return mixed cache data or false
*/
public function getCache()
{
$data = apc_fetch($this->config['key_prefix'] . '.storage');
$this->isCached = !empty($data);
return $data;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,291 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Caching/Interface.php';
/**
* Needed SQL:
*
#create the database
CREATE DATABASE IF NOT EXISTS `phpids` DEFAULT CHARACTER
SET utf8 COLLATE utf8_general_ci;
DROP TABLE IF EXISTS `cache`;
#now select the created datbase and create the table
CREATE TABLE `cache` (
`type` VARCHAR( 32 ) NOT null ,
`data` TEXT NOT null ,
`created` DATETIME NOT null ,
`modified` DATETIME NOT null
) ENGINE = MYISAM ;
*/
/**
* Database caching wrapper
*
* This class inhabits functionality to get and set cache via a database.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Groupup
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Database.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
* @since Version 0.4
*/
class IDS_Caching_Database implements IDS_Caching_Interface
{
/**
* Caching type
*
* @var string
*/
private $type = null;
/**
* Cache configuration
*
* @var array
*/
private $config = null;
/**
* DBH
*
* @var object
*/
private $handle = null;
/**
* Holds an instance of this class
*
* @var object
*/
private static $cachingInstance = null;
/**
* Constructor
*
* Connects to database.
*
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return void
*/
public function __construct($type, $init)
{
$this->type = $type;
$this->config = $init->config['Caching'];
$this->handle = $this->_connect();
}
/**
* Returns an instance of this class
*
* @static
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return object $this
*/
public static function getInstance($type, $init)
{
if (!self::$cachingInstance) {
self::$cachingInstance = new IDS_Caching_Database($type, $init);
}
return self::$cachingInstance;
}
/**
* Writes cache data into the database
*
* @param array $data the caching data
*
* @throws PDOException if a db error occurred
* @return object $this
*/
public function setCache(array $data)
{
$handle = $this->handle;
$rows = $handle->query('SELECT created FROM `' .
$this->config['table'].'`');
if (!$rows || $rows->rowCount() === 0) {
$this->_write($handle, $data);
} else {
foreach ($rows as $row) {
if ((time()-strtotime($row['created'])) >
$this->config['expiration_time']) {
$this->_write($handle, $data);
}
}
}
return $this;
}
/**
* Returns the cached data
*
* Note that this method returns false if either type or file cache is
* not set
*
* @throws PDOException if a db error occurred
* @return mixed cache data or false
*/
public function getCache()
{
try{
$handle = $this->handle;
$result = $handle->prepare('SELECT * FROM `' .
$this->config['table'] .
'` where type=?');
$result->execute(array($this->type));
foreach ($result as $row) {
return unserialize($row['data']);
}
} catch (PDOException $e) {
throw new PDOException('PDOException: ' . $e->getMessage());
}
return false;
}
/**
* Connect to database and return a handle
*
* @return object PDO
* @throws Exception if connection parameters are faulty
* @throws PDOException if a db error occurred
*/
private function _connect()
{
// validate connection parameters
if (!$this->config['wrapper']
|| !$this->config['user']
|| !$this->config['password']
|| !$this->config['table']) {
throw new Exception('
Insufficient connection parameters'
);
}
// try to connect
try {
$handle = new PDO(
$this->config['wrapper'],
$this->config['user'],
$this->config['password']
);
$handle->setAttribute(
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, true
);
} catch (PDOException $e) {
throw new PDOException('PDOException: ' . $e->getMessage());
}
return $handle;
}
/**
* Write the cache data to the table
*
* @param object $handle the database handle
* @param array $data the caching data
*
* @return object PDO
* @throws PDOException if a db error occurred
*/
private function _write($handle, $data)
{
try {
$handle->query('TRUNCATE ' .
$this->config['table'].'');
$statement = $handle->prepare('
INSERT INTO `' .
$this->config['table'].'` (
type,
data,
created,
modified
)
VALUES (
:type,
:data,
now(),
now()
)
');
$statement->bindParam('type',
$handle->quote($this->type));
$statement->bindParam('data', serialize($data));
if (!$statement->execute()) {
throw new PDOException($statement->errorCode());
}
} catch (PDOException $e) {
throw new PDOException('PDOException: ' . $e->getMessage());
}
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,94 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* Caching factory
*
* This class is used as a factory to load the correct concrete caching
* implementation.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Factory.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
* @since Version 0.4
*/
class IDS_Caching
{
/**
* Factory method
*
* @param object $init the IDS_Init object
* @param string $type the caching type
*
* @return object the caching facility
*/
public static function factory($init, $type)
{
$object = false;
$wrapper = preg_replace(
'/\W+/m',
null,
ucfirst($init->config['Caching']['caching'])
);
$class = 'IDS_Caching_' . $wrapper;
$path = dirname(__FILE__) . DIRECTORY_SEPARATOR .
$wrapper . '.php';
if (file_exists($path)) {
include_once $path;
if (class_exists($class)) {
$object = call_user_func(array($class, 'getInstance'),
$type, $init);
}
}
return $object;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,187 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Caching/Interface.php';
/**
* File caching wrapper
*
* This class inhabits functionality to get and set cache via a static flatfile.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:File.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
* @since Version 0.4
*/
class IDS_Caching_File implements IDS_Caching_Interface
{
/**
* Caching type
*
* @var string
*/
private $type = null;
/**
* Cache configuration
*
* @var array
*/
private $config = null;
/**
* Path to cache file
*
* @var string
*/
private $path = null;
/**
* Holds an instance of this class
*
* @var object
*/
private static $cachingInstance = null;
/**
* Constructor
*
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return void
*/
public function __construct($type, $init)
{
$this->type = $type;
$this->config = $init->config['Caching'];
$this->path = $init->getBasePath() . $this->config['path'];
if (file_exists($this->path) && !is_writable($this->path)) {
throw new Exception('Make sure all files in ' .
htmlspecialchars($this->path, ENT_QUOTES, 'UTF-8') .
'are writeable!');
}
}
/**
* Returns an instance of this class
*
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return object $this
*/
public static function getInstance($type, $init)
{
if (!self::$cachingInstance) {
self::$cachingInstance = new IDS_Caching_File($type, $init);
}
return self::$cachingInstance;
}
/**
* Writes cache data into the file
*
* @param array $data the cache data
*
* @throws Exception if cache file couldn't be created
* @return object $this
*/
public function setCache(array $data)
{
if (!is_writable(preg_replace('/[\/][^\/]+\.[^\/]++$/', null,
$this->path))) {
throw new Exception('Temp directory ' .
htmlspecialchars($this->path, ENT_QUOTES, 'UTF-8') .
' seems not writable');
}
if ((!file_exists($this->path) || (time()-filectime($this->path)) >
$this->config['expiration_time'])) {
$handle = @fopen($this->path, 'w+');
$serialized = @serialize($data);
if (!$handle) {
throw new Exception("Cache file couldn't be created");
}
if (!$serialized) {
throw new Exception("Cache data couldn't be serialized");
}
fwrite($handle, $serialized);
fclose($handle);
}
return $this;
}
/**
* Returns the cached data
*
* Note that this method returns false if either type or file cache is
* not set
*
* @return mixed cache data or false
*/
public function getCache()
{
// make sure filters are parsed again if cache expired
if (file_exists($this->path) && (time()-filectime($this->path)) <
$this->config['expiration_time']) {
$data = unserialize(file_get_contents($this->path));
return $data;
}
return false;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,73 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* Caching wrapper interface
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @version SVN: $Id:Interface.php 517 2007-09-15 15:04:13Z mario $
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @since Version 0.4
* @link http://php-ids.org/
*/
interface IDS_Caching_Interface
{
/**
* Interface method
*
* @param array $data the cache data
*
* @return void
*/
public function setCache(array $data);
/**
* Interface method
*
* @return void
*/
public function getCache();
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,195 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Caching/Interface.php';
/**
* File caching wrapper
*
* This class inhabits functionality to get and set cache via memcached.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Groupoup
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Memcached.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
* @since Version 0.4
*/
class IDS_Caching_Memcached implements IDS_Caching_Interface
{
/**
* Caching type
*
* @var string
*/
private $type = null;
/**
* Cache configuration
*
* @var array
*/
private $config = null;
/**
* Flag if the filter storage has been found in memcached
*
* @var boolean
*/
private $isCached = false;
/**
* Memcache object
*
* @var object
*/
private $memcache = null;
/**
* Holds an instance of this class
*
* @var object
*/
private static $cachingInstance = null;
/**
* Constructor
*
* @param string $type caching type
* @param array $init the IDS_Init object
*
* @return void
*/
public function __construct($type, $init)
{
$this->type = $type;
$this->config = $init->config['Caching'];
$this->_connect();
}
/**
* Returns an instance of this class
*
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return object $this
*/
public static function getInstance($type, $init)
{
if (!self::$cachingInstance) {
self::$cachingInstance = new IDS_Caching_Memcached($type, $init);
}
return self::$cachingInstance;
}
/**
* Writes cache data
*
* @param array $data the caching data
*
* @return object $this
*/
public function setCache(array $data)
{
if(!$this->isCached) {
$this->memcache->set(
$this->config['key_prefix'] . '.storage',
$data, false, $this->config['expiration_time']
);
}
return $this;
}
/**
* Returns the cached data
*
* Note that this method returns false if either type or file cache is
* not set
*
* @return mixed cache data or false
*/
public function getCache()
{
$data = $this->memcache->get(
$this->config['key_prefix'] .
'.storage'
);
$this->isCached = !empty($data);
return $data;
}
/**
* Connect to the memcached server
*
* @throws Exception if connection parameters are insufficient
* @return void
*/
private function _connect()
{
if ($this->config['host'] && $this->config['port']) {
// establish the memcache connection
$this->memcache = new Memcache;
$this->memcache->pconnect(
$this->config['host'],
$this->config['port']
);
} else {
throw new Exception('Insufficient connection parameters');
}
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,146 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Caching/Interface.php';
/**
* File caching wrapper
*
* This class inhabits functionality to get and set cache via session.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Session.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
* @since Version 0.4
*/
class IDS_Caching_Session implements IDS_Caching_Interface
{
/**
* Caching type
*
* @var string
*/
private $type = null;
/**
* Cache configuration
*
* @var array
*/
private $config = null;
/**
* Holds an instance of this class
*
* @var object
*/
private static $cachingInstance = null;
/**
* Constructor
*
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return void
*/
public function __construct($type, $init)
{
$this->type = $type;
$this->config = $init->config['Caching'];
}
/**
* Returns an instance of this class
*
* @param string $type caching type
* @param object $init the IDS_Init object
*
* @return object $this
*/
public static function getInstance($type, $init)
{
if (!self::$cachingInstance) {
self::$cachingInstance = new IDS_Caching_Session($type, $init);
}
return self::$cachingInstance;
}
/**
* Writes cache data into the session
*
* @param array $data the caching data
*
* @return object $this
*/
public function setCache(array $data)
{
$_SESSION['PHPIDS'][$this->type] = $data;
return $this;
}
/**
* Returns the cached data
*
* Note that this method returns false if either type or file cache is not set
*
* @return mixed cache data or false
*/
public function getCache()
{
if ($this->type && $_SESSION['PHPIDS'][$this->type]) {
return $_SESSION['PHPIDS'][$this->type];
}
return false;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,89 @@
; <?php die(); ?>
; PHPIDS Config.ini
; General configuration settings
[General]
; basic settings - customize to make the PHPIDS work at all
filter_type = xml
base_path = /full/path/to/IDS/
use_base_path = false
filter_path = default_filter.xml
tmp_path = tmp
scan_keys = false
; in case you want to use a different HTMLPurifier source, specify it here
; By default, those files are used that are being shipped with PHPIDS
HTML_Purifier_Path = vendors/htmlpurifier/HTMLPurifier.auto.php
HTML_Purifier_Cache = vendors/htmlpurifier/HTMLPurifier/DefinitionCache/Serializer
; define which fields contain html and need preparation before
; hitting the PHPIDS rules (new in PHPIDS 0.5)
;html[] = POST.__wysiwyg
; define which fields contain JSON data and should be treated as such
; for fewer false positives (new in PHPIDS 0.5.3)
;json[] = POST.__jsondata
; define which fields shouldn't be monitored (a[b]=c should be referenced via a.b)
exceptions[] = GET.__utmz
exceptions[] = GET.__utmc
; you can use regular expressions for wildcard exceptions - example: /.*foo/i
; PHPIDS should run with PHP 5.1.2 but this is untested - set
; this value to force compatibilty with minor versions
min_php_version = 5.1.6
; If you use the PHPIDS logger you can define specific configuration here
[Logging]
; file logging
path = tmp/phpids_log.txt
; email logging
; note that enabling safemode you can prevent spam attempts,
; see documentation
recipients[] = test@test.com.invalid
subject = "PHPIDS detected an intrusion attempt!"
header = "From: <PHPIDS> info@phpids.org"
envelope = ""
safemode = true
urlencode = true
allowed_rate = 15
; database logging
wrapper = "mysql:host=localhost;port=3306;dbname=phpids"
user = phpids_user
password = 123456
table = intrusions
; If you would like to use other methods than file caching you can configure them here
[Caching]
; caching: session|file|database|memcached|none
caching = file
expiration_time = 600
; file cache
path = tmp/default_filter.cache
; database cache
wrapper = "mysql:host=localhost;port=3306;dbname=phpids"
user = phpids_user
password = 123456
table = cache
; memcached
;host = localhost
;port = 11211
;key_prefix = PHPIDS

View file

@ -0,0 +1,750 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* PHPIDS specific utility class to convert charsets manually
*
* Note that if you make use of IDS_Converter::runAll(), existing class
* methods will be executed in the same order as they are implemented in the
* class tree!
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Converter.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Converter
{
/**
* Runs all converter functions
*
* Note that if you make use of IDS_Converter::runAll(), existing class
* methods will be executed in the same order as they are implemented in the
* class tree!
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function runAll($value)
{
foreach (get_class_methods(__CLASS__) as $method) {
if (strpos($method, 'run') === 0) {
continue;
}
$value = self::$method($value);
}
return $value;
}
/**
* Check for comments and erases them if available
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromCommented($value)
{
// check for existing comments
if (preg_match('/(?:\<!-|-->|\/\*|\*\/|\/\/\W*\w+\s*$)|' .
'(?:--[^-]*-)/ms', $value)) {
$pattern = array(
'/(?:(?:<!)(?:(?:--(?:[^-]*(?:-[^-]+)*)--\s*)*)(?:>))/ms',
'/(?:(?:\/\*\/*[^\/\*]*)+\*\/)/ms',
'/(?:--[^-]*-)/ms'
);
$converted = preg_replace($pattern, ';', $value);
$value .= "\n" . $converted;
}
//make sure inline comments are detected and converted correctly
$value = preg_replace('/(<\w+)\/+(\w+=?)/m', '$1/$2', $value);
$value = preg_replace('/[^\\\:]\/\/(.*)$/m', '/**/$1', $value);
$value = preg_replace('/([^\-&])#.*[\r\n\v\f]/m', '$1', $value);
$value = preg_replace('/([^&\-])#.*\n/m', '$1 ', $value);
$value = preg_replace('/^#.*\n/m', ' ', $value);
return $value;
}
/**
* Strip newlines
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromWhiteSpace($value)
{
//check for inline linebreaks
$search = array('\r', '\n', '\f', '\t', '\v');
$value = str_replace($search, ';', $value);
// replace replacement characters regular spaces
$value = str_replace('<27>', ' ', $value);
//convert real linebreaks
return preg_replace('/(?:\n|\r|\v)/m', ' ', $value);
}
/**
* Checks for common charcode pattern and decodes them
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromJSCharcode($value)
{
$matches = array();
// check if value matches typical charCode pattern
if (preg_match_all('/(?:[\d+-=\/\* ]+(?:\s?,\s?[\d+-=\/\* ]+)){4,}/ms',
$value, $matches)) {
$converted = '';
$string = implode(',', $matches[0]);
$string = preg_replace('/\s/', '', $string);
$string = preg_replace('/\w+=/', '', $string);
$charcode = explode(',', $string);
foreach ($charcode as $char) {
$char = preg_replace('/\W0/s', '', $char);
if (preg_match_all('/\d*[+-\/\* ]\d+/', $char, $matches)) {
$match = preg_split('/(\W?\d+)/',
(implode('', $matches[0])),
null,
PREG_SPLIT_DELIM_CAPTURE);
if (array_sum($match) >= 20 && array_sum($match) <= 127) {
$converted .= chr(array_sum($match));
}
} elseif (!empty($char) && $char >= 20 && $char <= 127) {
$converted .= chr($char);
}
}
$value .= "\n" . $converted;
}
// check for octal charcode pattern
if (preg_match_all('/(?:(?:[\\\]+\d+[ \t]*){8,})/ims', $value, $matches)) {
$converted = '';
$charcode = explode('\\', preg_replace('/\s/', '', implode(',',
$matches[0])));
foreach ($charcode as $char) {
if (!empty($char)) {
if (octdec($char) >= 20 && octdec($char) <= 127) {
$converted .= chr(octdec($char));
}
}
}
$value .= "\n" . $converted;
}
// check for hexadecimal charcode pattern
if (preg_match_all('/(?:(?:[\\\]+\w+\s*){8,})/ims', $value, $matches)) {
$converted = '';
$charcode = explode('\\', preg_replace('/[ux]/', '', implode(',',
$matches[0])));
foreach ($charcode as $char) {
if (!empty($char)) {
if (hexdec($char) >= 20 && hexdec($char) <= 127) {
$converted .= chr(hexdec($char));
}
}
}
$value .= "\n" . $converted;
}
return $value;
}
/**
* Eliminate JS regex modifiers
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertJSRegexModifiers($value)
{
$value = preg_replace('/\/[gim]+/', '/', $value);
return $value;
}
/**
* Converts from hex/dec entities
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertEntities($value)
{
$converted = null;
//deal with double encoded payload
$value = preg_replace('/&amp;/', '&', $value);
if (preg_match('/&#x?[\w]+/ms', $value)) {
$converted = preg_replace('/(&#x?[\w]{2}\d?);?/ms', '$1;', $value);
$converted = html_entity_decode($converted, ENT_QUOTES, 'UTF-8');
$value .= "\n" . str_replace(';;', ';', $converted);
}
// normalize obfuscated protocol handlers
$value = preg_replace(
'/(?:j\s*a\s*v\s*a\s*s\s*c\s*r\s*i\s*p\s*t\s*:)|(d\s*a\s*t\s*a\s*:)/ms',
'javascript:', $value
);
return $value;
}
/**
* Normalize quotes
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertQuotes($value)
{
// normalize different quotes to "
$pattern = array('\'', '`', '´', '', '');
$value = str_replace($pattern, '"', $value);
//make sure harmless quoted strings don't generate false alerts
$value = preg_replace('/^"([^"=\\!><~]+)"$/', '$1', $value);
return $value;
}
/**
* Converts SQLHEX to plain text
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromSQLHex($value)
{
$matches = array();
if(preg_match_all('/(?:(?:\A|[^\d])0x[a-f\d]{3,}[a-f\d]*)+/im', $value, $matches)) {
foreach($matches[0] as $match) {
$converted = '';
foreach(str_split($match, 2) as $hex_index) {
if(preg_match('/[a-f\d]{2,3}/i', $hex_index)) {
$converted .= chr(hexdec($hex_index));
}
}
$value = str_replace($match, $converted, $value);
}
}
// take care of hex encoded ctrl chars
$value = preg_replace('/0x\d+/m', ' 1 ', $value);
return $value;
}
/**
* Converts basic SQL keywords and obfuscations
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromSQLKeywords($value)
{
$pattern = array('/(?:is\s+null)|(like\s+null)|' .
'(?:(?:^|\W)in[+\s]*\([\s\d"]+[^()]*\))/ims');
$value = preg_replace($pattern, '"=0', $value);
$value = preg_replace('/[^\w\)]+\s*like\s*[^\w\s]+/ims', '1" OR "1"', $value);
$value = preg_replace('/null([,"\s])/ims', '0$1', $value);
$value = preg_replace('/\d+\./ims', ' 1', $value);
$value = preg_replace('/,null/ims', ',0', $value);
$value = preg_replace('/(?:between)/ims', 'or', $value);
$value = preg_replace('/(?:and\s+\d+\.?\d*)/ims', '', $value);
$value = preg_replace('/(?:\s+and\s+)/ims', ' or ', $value);
$pattern = array('/(?:not\s+between)|(?:is\s+not)|(?:not\s+in)|' .
'(?:xor|<>|rlike(?:\s+binary)?)|' .
'(?:regexp\s+binary)|' .
'(?:sounds\s+like)/ims');
$value = preg_replace($pattern, '!', $value);
$value = preg_replace('/"\s+\d/', '"', $value);
$value = preg_replace('/(\W)div(\W)/ims', '$1 OR $2', $value);
$value = preg_replace('/\/(?:\d+|null)/', null, $value);
return $value;
}
/**
* Detects nullbytes and controls chars via ord()
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromControlChars($value)
{
// critical ctrl values
$search = array(
chr(0), chr(1), chr(2), chr(3), chr(4), chr(5),
chr(6), chr(7), chr(8), chr(11), chr(12), chr(14),
chr(15), chr(16), chr(17), chr(18), chr(19), chr(24),
chr(25), chr(192), chr(193), chr(238), chr(255), '\\0'
);
$value = str_replace($search, '%00', $value);
//take care for malicious unicode characters
$value = urldecode(preg_replace('/(?:%E(?:2|3)%8(?:0|1)%(?:A|8|9)' .
'\w|%EF%BB%BF|%EF%BF%BD)|(?:&#(?:65|8)\d{3};?)/i', null,
urlencode($value)));
$value = urldecode(
preg_replace('/(?:%F0%80%BE)/i', '>', urlencode($value)));
$value = urldecode(
preg_replace('/(?:%F0%80%BC)/i', '<', urlencode($value)));
$value = urldecode(
preg_replace('/(?:%F0%80%A2)/i', '"', urlencode($value)));
$value = urldecode(
preg_replace('/(?:%F0%80%A7)/i', '\'', urlencode($value)));
$value = preg_replace('/(?:%ff1c)/', '<', $value);
$value = preg_replace(
'/(?:&[#x]*(200|820|200|820|zwn?j|lrm|rlm)\w?;?)/i', null,$value
);
$value = preg_replace('/(?:&#(?:65|8)\d{3};?)|' .
'(?:&#(?:56|7)3\d{2};?)|' .
'(?:&#x(?:fe|20)\w{2};?)|' .
'(?:&#x(?:d[c-f])\w{2};?)/i', null,
$value);
$value = str_replace(
array('«', '〈', '', '', '〈', '⟨'), '<', $value
);
$value = str_replace(
array('»', '〉', '', '', '〉', '⟩'), '>', $value
);
return $value;
}
/**
* This method matches and translates base64 strings and fragments
* used in data URIs
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromNestedBase64($value)
{
$matches = array();
preg_match_all('/(?:^|[,&?])\s*([a-z0-9]{30,}=*)(?:\W|$)/im',
$value,
$matches);
foreach ($matches[1] as $item) {
if (isset($item) && !preg_match('/[a-f0-9]{32}/i', $item)) {
$base64_item = base64_decode($item);
$value = str_replace($item, $base64_item, $value);
}
}
return $value;
}
/**
* Detects nullbytes and controls chars via ord()
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromOutOfRangeChars($value)
{
$values = str_split($value);
foreach ($values as $item) {
if (ord($item) >= 127) {
$value = str_replace($item, ' ', $value);
}
}
return $value;
}
/**
* Strip XML patterns
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromXML($value)
{
$converted = strip_tags($value);
if ($converted && ($converted != $value)) {
return $value . "\n" . $converted;
}
return $value;
}
/**
* This method converts JS unicode code points to
* regular characters
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromJSUnicode($value)
{
$matches = array();
preg_match_all('/\\\u[0-9a-f]{4}/ims', $value, $matches);
if (!empty($matches[0])) {
foreach ($matches[0] as $match) {
$chr = chr(hexdec(substr($match, 2, 4)));
$value = str_replace($match, $chr, $value);
}
$value .= "\n\u0001";
}
return $value;
}
/**
* Converts relevant UTF-7 tags to UTF-8
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromUTF7($value)
{
if(preg_match('/\+A\w+-?/m', $value)) {
if (function_exists('mb_convert_encoding')) {
if(version_compare(PHP_VERSION, '5.2.8', '<')) {
$tmp_chars = str_split($value);
$value = '';
foreach($tmp_chars as $char) {
if(ord($char) <= 127) {
$value .= $char;
}
}
}
$value .= "\n" . mb_convert_encoding($value, 'UTF-8', 'UTF-7');
} else {
//list of all critical UTF7 codepoints
$schemes = array(
'+ACI-' => '"',
'+ADw-' => '<',
'+AD4-' => '>',
'+AFs-' => '[',
'+AF0-' => ']',
'+AHs-' => '{',
'+AH0-' => '}',
'+AFw-' => '\\',
'+ADs-' => ';',
'+ACM-' => '#',
'+ACY-' => '&',
'+ACU-' => '%',
'+ACQ-' => '$',
'+AD0-' => '=',
'+AGA-' => '`',
'+ALQ-' => '"',
'+IBg-' => '"',
'+IBk-' => '"',
'+AHw-' => '|',
'+ACo-' => '*',
'+AF4-' => '^',
'+ACIAPg-' => '">',
'+ACIAPgA8-' => '">'
);
$value = str_ireplace(array_keys($schemes),
array_values($schemes), $value);
}
}
return $value;
}
/**
* Converts basic concatenations
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromConcatenated($value)
{
//normalize remaining backslashes
if ($value != preg_replace('/(\w)\\\/', "$1", $value)) {
$value .= preg_replace('/(\w)\\\/', "$1", $value);
}
$compare = stripslashes($value);
$pattern = array('/(?:<\/\w+>\+<\w+>)/s',
'/(?:":\d+[^"[]+")/s',
'/(?:"?"\+\w+\+")/s',
'/(?:"\s*;[^"]+")|(?:";[^"]+:\s*")/s',
'/(?:"\s*(?:;|\+).{8,18}:\s*")/s',
'/(?:";\w+=)|(?:!""&&")|(?:~)/s',
'/(?:"?"\+""?\+?"?)|(?:;\w+=")|(?:"[|&]{2,})/s',
'/(?:"\s*\W+")/s',
'/(?:";\w\s*\+=\s*\w?\s*")/s',
'/(?:"[|&;]+\s*[^|&\n]*[|&]+\s*"?)/s',
'/(?:";\s*\w+\W+\w*\s*[|&]*")/s',
'/(?:"\s*"\s*\.)/s',
'/(?:\s*new\s+\w+\s*[+",])/',
'/(?:(?:^|\s+)(?:do|else)\s+)/',
'/(?:[{(]\s*new\s+\w+\s*[)}])/',
'/(?:(this|self)\.)/',
'/(?:undefined)/',
'/(?:in\s+)/');
// strip out concatenations
$converted = preg_replace($pattern, null, $compare);
//strip object traversal
$converted = preg_replace('/\w(\.\w\()/', "$1", $converted);
// normalize obfuscated method calls
$converted = preg_replace('/\)\s*\+/', ")", $converted);
//convert JS special numbers
$converted = preg_replace('/(?:\(*[.\d]e[+-]*[^a-z\W]+\)*)' .
'|(?:NaN|Infinity)\W/ims', 1, $converted);
if ($converted && ($compare != $converted)) {
$value .= "\n" . $converted;
}
return $value;
}
/**
* This method collects and decodes proprietary encoding types
*
* @param string $value the value to convert
*
* @static
* @return string
*/
public static function convertFromProprietaryEncodings($value) {
//Xajax error reportings
$value = preg_replace('/<!\[CDATA\[(\W+)\]\]>/im', '$1', $value);
//strip false alert triggering apostrophes
$value = preg_replace('/(\w)\"(s)/m', '$1$2', $value);
//strip quotes within typical search patterns
$value = preg_replace('/^"([^"=\\!><~]+)"$/', '$1', $value);
//OpenID login tokens
$value = preg_replace('/{[\w-]{8,9}\}(?:\{[\w=]{8}\}){2}/', null, $value);
//convert Content and \sdo\s to null
$value = preg_replace('/Content|\Wdo\s/', null, $value);
//strip emoticons
$value = preg_replace(
'/(?:\s[:;]-[)\/PD]+)|(?:\s;[)PD]+)|(?:\s:[)PD]+)|-\.-|\^\^/m',
null,
$value
);
//normalize separation char repetion
$value = preg_replace('/([.+~=*_\-;])\1{2,}/m', '$1', $value);
//normalize multiple single quotes
$value = preg_replace('/"{2,}/m', '"', $value);
//normalize quoted numerical values and asterisks
$value = preg_replace('/"(\d+)"/m', '$1', $value);
//normalize pipe separated request parameters
$value = preg_replace('/\|(\w+=\w+)/m', '&$1', $value);
//normalize ampersand listings
$value = preg_replace('/(\w\s)&\s(\w)/', '$1$2', $value);
//normalize escaped RegExp modifiers
$value = preg_replace('/\/\\\(\w)/', '/$1', $value);
return $value;
}
/**
* This method is the centrifuge prototype
*
* @param string $value the value to convert
* @param IDS_Monitor $monitor the monitor object
*
* @static
* @return string
*/
public static function runCentrifuge($value, IDS_Monitor $monitor = null)
{
$threshold = 3.49;
if (strlen($value) > 25) {
//strip padding
$tmp_value = preg_replace('/\s{4}|==$/m', null, $value);
$tmp_value = preg_replace(
'/\s{4}|[\p{L}\d\+\-=,.%()]{8,}/m',
'aaa',
$tmp_value
);
// Check for the attack char ratio
$tmp_value = preg_replace('/([*.!?+-])\1{1,}/m', '$1', $tmp_value);
$tmp_value = preg_replace('/"[\p{L}\d\s]+"/m', null, $tmp_value);
$stripped_length = strlen(preg_replace('/[\d\s\p{L}\.:,%&\/><\-)!|]+/m',
null, $tmp_value));
$overall_length = strlen(
preg_replace('/([\d\s\p{L}:,\.]{3,})+/m', 'aaa',
preg_replace('/\s{2,}/m', null, $tmp_value))
);
if ($stripped_length != 0
&& $overall_length/$stripped_length <= $threshold) {
$monitor->centrifuge['ratio'] =
$overall_length/$stripped_length;
$monitor->centrifuge['threshold'] =
$threshold;
$value .= "\n$[!!!]";
}
}
if (strlen($value) > 40) {
// Replace all non-special chars
$converted = preg_replace('/[\w\s\p{L},.:!]/', null, $value);
// Split string into an array, unify and sort
$array = str_split($converted);
$array = array_unique($array);
asort($array);
// Normalize certain tokens
$schemes = array(
'~' => '+',
'^' => '+',
'|' => '+',
'*' => '+',
'%' => '+',
'&' => '+',
'/' => '+'
);
$converted = implode($array);
$_keys = array_keys($schemes);
$_values = array_values($schemes);
$converted = str_replace($_keys, $_values, $converted);
$converted = preg_replace('/[+-]\s*\d+/', '+', $converted);
$converted = preg_replace('/[()[\]{}]/', '(', $converted);
$converted = preg_replace('/[!?:=]/', ':', $converted);
$converted = preg_replace('/[^:(+]/', null, stripslashes($converted));
// Sort again and implode
$array = str_split($converted);
asort($array);
$converted = implode($array);
if (preg_match('/(?:\({2,}\+{2,}:{2,})|(?:\({2,}\+{2,}:+)|' .
'(?:\({3,}\++:{2,})/', $converted)) {
$monitor->centrifuge['converted'] = $converted;
return $value . "\n" . $converted;
}
}
return $value;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,235 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* PHPIDS event object
*
* This class represents a certain event that occured while applying the filters
* to the supplied data. It aggregates a bunch of IDS_Filter implementations and
* is a assembled in IDS_Report.
*
* Note that this class implements both Countable and IteratorAggregate
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Event.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Event implements Countable, IteratorAggregate
{
/**
* Event name
*
* @var scalar
*/
protected $name = null;
/**
* Value of the event
*
* @var scalar
*/
protected $value = null;
/**
* List of filter objects
*
* Filter objects in this array are those that matched the events value
*
* @var array
*/
protected $filters = array();
/**
* Calculated impact
*
* Total impact of the event
*
* @var integer
*/
protected $impact = 0;
/**
* Affecte tags
*
* @var array
*/
protected $tags = array();
/**
* Constructor
*
* Fills event properties
*
* @param scalar $name the event name
* @param scalar $value the event value
* @param array $filters the corresponding filters
*
* @return void
*/
public function __construct($name, $value, Array $filters)
{
if (!is_scalar($name)) {
throw new InvalidArgumentException(
'Expected $name to be a scalar,' . gettype($name) . ' given'
);
}
if (!is_scalar($value)) {
throw new InvalidArgumentException('
Expected $value to be a scalar,' . gettype($value) . ' given'
);
}
$this->name = $name;
$this->value = $value;
foreach ($filters as $filter) {
if (!$filter instanceof IDS_Filter) {
throw new InvalidArgumentException(
'Filter must be derived from IDS_Filter'
);
}
$this->filters[] = $filter;
}
}
/**
* Returns event name
*
* The name of the event usually is the key of the variable that was
* considered to be malicious
*
* @return scalar
*/
public function getName()
{
return $this->name;
}
/**
* Returns event value
*
* @return scalar
*/
public function getValue()
{
return $this->value;
}
/**
* Returns calculated impact
*
* @return integer
*/
public function getImpact()
{
if (!$this->impact) {
$this->impact = 0;
foreach ($this->filters as $filter) {
$this->impact += $filter->getImpact();
}
}
return $this->impact;
}
/**
* Returns affected tags
*
* @return array
*/
public function getTags()
{
$filters = $this->getFilters();
foreach ($filters as $filter) {
$this->tags = array_merge($this->tags,
$filter->getTags());
}
$this->tags = array_values(array_unique($this->tags));
return $this->tags;
}
/**
* Returns list of filter objects
*
* @return array
*/
public function getFilters()
{
return $this->filters;
}
/**
* Returns number of filters
*
* To implement interface Countable this returns the number of filters
* appended.
*
* @return integer
*/
public function count()
{
return count($this->getFilters());
}
/**
* IteratorAggregate iterator getter
*
* Returns an iterator to iterate over the appended filters.
*
* @return ArrayObject the filter collection
*/
public function getIterator()
{
return new ArrayObject($this->getFilters());
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,187 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* PHPIDS Filter object
*
* Each object of this class serves as a container for a specific filter. The
* object provides methods to get information about this particular filter and
* also to match an arbitrary string against it.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Filter.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
* @since Version 0.4
*/
class IDS_Filter
{
/**
* Filter rule
*
* @var string
*/
protected $rule;
/**
* List of tags of the filter
*
* @var array
*/
protected $tags = array();
/**
* Filter impact level
*
* @var integer
*/
protected $impact = 0;
/**
* Filter description
*
* @var string
*/
protected $description = null;
/**
* Constructor
*
* @param integer $id filter id
* @param mixed $rule filter rule
* @param string $description filter description
* @param array $tags list of tags
* @param integer $impact filter impact level
*
* @return void
*/
public function __construct($id, $rule, $description, array $tags, $impact)
{
$this->id = $id;
$this->rule = $rule;
$this->tags = $tags;
$this->impact = $impact;
$this->description = $description;
}
/**
* Matches a string against current filter
*
* Matches given string against the filter rule the specific object of this
* class represents
*
* @param string $string the string to match
*
* @throws InvalidArgumentException if argument is no string
* @return boolean
*/
public function match($string)
{
if (!is_string($string)) {
throw new InvalidArgumentException('
Invalid argument. Expected a string, received ' . gettype($string)
);
}
return (bool) preg_match(
'/' . $this->getRule() . '/ms', strtolower($string)
);
}
/**
* Returns filter description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Return list of affected tags
*
* Each filter rule is concerned with a certain kind of attack vectors.
* This method returns those affected kinds.
*
* @return array
*/
public function getTags()
{
return $this->tags;
}
/**
* Returns filter rule
*
* @return string
*/
public function getRule()
{
return $this->rule;
}
/**
* Get filter impact level
*
* @return integer
*/
public function getImpact()
{
return $this->impact;
}
/**
* Get filter ID
*
* @return integer
*/
public function getId()
{
return $this->id;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,381 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* Filter Storage
*
* This class provides various default functions for gathering filter patterns
* to be used later on by the detection mechanism. You might extend this class
* to your requirements.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Storage.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Filter_Storage
{
/**
* Filter source file
*
* @var string
*/
protected $source = null;
/**
* Holds caching settings
*
* @var array
*/
protected $cacheSettings = null;
/**
* Cache container
*
* @var object IDS_Caching wrapper
*/
protected $cache = null;
/**
* Filter container
*
* @var array
*/
protected $filterSet = array();
/**
* Constructor
*
* Loads filters based on provided IDS_Init settings.
*
* @param object $init IDS_Init instance
*
* @throws Exception if unsupported filter type is given
* @return void
*/
public final function __construct(IDS_Init $init)
{
if ($init->config) {
$caching = isset($init->config['Caching']['caching']) ?
$init->config['Caching']['caching'] : 'none';
$type = $init->config['General']['filter_type'];
$this->source = $init->getBasePath()
. $init->config['General']['filter_path'];
if ($caching && $caching != 'none') {
$this->cacheSettings = $init->config['Caching'];
include_once 'IDS/Caching/Factory.php';
$this->cache = IDS_Caching::factory($init, 'storage');
}
switch ($type) {
case 'xml' :
$this->getFilterFromXML();
break;
case 'json' :
$this->getFilterFromJson();
break;
default :
throw new Exception('Unsupported filter type.');
}
}
}
/**
* Sets the filter array
*
* @param array $filterSet array containing multiple IDS_Filter instances
*
* @return object $this
*/
public final function setFilterSet($filterSet)
{
foreach ($filterSet as $filter) {
$this->addFilter($filter);
}
return $this;
}
/**
* Returns registered filters
*
* @return array
*/
public final function getFilterSet()
{
return $this->filterSet;
}
/**
* Adds a filter
*
* @param object $filter IDS_Filter instance
*
* @return object $this
*/
public final function addFilter(IDS_Filter $filter)
{
$this->filterSet[] = $filter;
return $this;
}
/**
* Checks if any filters are cached
*
* @return mixed $filters cached filters or false
*/
private function _isCached()
{
$filters = false;
if ($this->cacheSettings) {
if ($this->cache) {
$filters = $this->cache->getCache();
}
}
return $filters;
}
/**
* Loads filters from XML using SimpleXML
*
* This function parses the provided source file and stores the result.
* If caching mode is enabled the result will be cached to increase
* the performance.
*
* @throws Exception if problems with fetching the XML data occur
* @return object $this
*/
public function getFilterFromXML()
{
if (extension_loaded('SimpleXML')) {
/*
* See if filters are already available in the cache
*/
$filters = $this->_isCached();
/*
* If they aren't, parse the source file
*/
if (!$filters) {
if (file_exists($this->source)) {
if (LIBXML_VERSION >= 20621) {
$filters = simplexml_load_file($this->source,
null,
LIBXML_COMPACT);
} else {
$filters = simplexml_load_file($this->source);
}
}
}
/*
* In case we still don't have any filters loaded and exception
* will be thrown
*/
if (empty($filters)) {
throw new Exception(
'XML data could not be loaded.' .
' Make sure you specified the correct path.'
);
}
/*
* Now the storage will be filled with IDS_Filter objects
*/
$data = array();
$nocache = $filters instanceof SimpleXMLElement;
$filters = $nocache ? $filters->filter : $filters;
include_once 'IDS/Filter.php';
foreach ($filters as $filter) {
$id = $nocache ? (string) $filter->id :
$filter['id'];
$rule = $nocache ? (string) $filter->rule :
$filter['rule'];
$impact = $nocache ? (string) $filter->impact :
$filter['impact'];
$tags = $nocache ? array_values((array) $filter->tags) :
$filter['tags'];
$description = $nocache ? (string) $filter->description :
$filter['description'];
$this->addFilter(new IDS_Filter($id,
$rule,
$description,
(array) $tags[0],
(int) $impact));
$data[] = array(
'id' => $id,
'rule' => $rule,
'impact' => $impact,
'tags' => $tags,
'description' => $description
);
}
/*
* If caching is enabled, the fetched data will be cached
*/
if ($this->cacheSettings) {
$this->cache->setCache($data);
}
} else {
throw new Exception(
'SimpleXML not loaded.'
);
}
return $this;
}
/**
* Loads filters from Json file using ext/Json
*
* This function parses the provided source file and stores the result.
* If caching mode is enabled the result will be cached to increase
* the performance.
*
* @throws Exception if problems with fetching the JSON data occur
* @return object $this
*/
public function getFilterFromJson()
{
if (extension_loaded('Json')) {
/*
* See if filters are already available in the cache
*/
$filters = $this->_isCached();
/*
* If they aren't, parse the source file
*/
if (!$filters) {
if (file_exists($this->source)) {
$filters = json_decode(file_get_contents($this->source));
} else {
throw new Exception(
'JSON data could not be loaded.' .
' Make sure you specified the correct path.'
);
}
}
if (!$filters) {
throw new Exception(
'JSON data could not be loaded.' .
' Make sure you specified the correct path.'
);
}
/*
* Now the storage will be filled with IDS_Filter objects
*/
$data = array();
$nocache = !is_array($filters);
$filters = $nocache ? $filters->filters->filter : $filters;
include_once 'IDS/Filter.php';
foreach ($filters as $filter) {
$id = $nocache ? (string) $filter->id :
$filter['id'];
$rule = $nocache ? (string) $filter->rule :
$filter['rule'];
$impact = $nocache ? (string) $filter->impact :
$filter['impact'];
$tags = $nocache ? array_values((array) $filter->tags) :
$filter['tags'];
$description = $nocache ? (string) $filter->description :
$filter['description'];
$this->addFilter(new IDS_Filter($id,
$rule,
$description,
(array) $tags[0],
(int) $impact));
$data[] = array(
'id' => $id,
'rule' => $rule,
'impact' => $impact,
'tags' => $tags,
'description' => $description
);
}
/*
* If caching is enabled, the fetched data will be cached
*/
if ($this->cacheSettings) {
$this->cache->setCache($data);
}
} else {
throw new Exception(
'ext/json not loaded.'
);
}
return $this;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,232 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* Framework initiation
*
* This class is used for the purpose to initiate the framework and inhabits
* functionality to parse the needed configuration file.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Groupup
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Init.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
* @since Version 0.4
*/
class IDS_Init
{
/**
* Holds config settings
*
* @var array
*/
public $config = array();
/**
* Instance of this class depending on the supplied config file
*
* @var array
* @static
*/
private static $instances = array();
/**
* Path to the config file
*
* @var string
*/
private $configPath = null;
/**
* Constructor
*
* Includes needed classes and parses the configuration file
*
* @param string $configPath the path to the config file
*
* @return object $this
*/
private function __construct($configPath = null)
{
include_once 'IDS/Monitor.php';
include_once 'IDS/Filter/Storage.php';
if ($configPath) {
$this->setConfigPath($configPath);
$this->config = parse_ini_file($this->configPath, true);
}
}
/**
* Permitting to clone this object
*
* For the sake of correctness of a singleton pattern, this is necessary
*
* @return void
*/
public final function __clone()
{
}
/**
* Returns an instance of this class. Also a PHP version check
* is being performed to avoid compatibility problems with PHP < 5.1.6
*
* @param string $configPath the path to the config file
*
* @return object
*/
public static function init($configPath = null)
{
if (!isset(self::$instances[$configPath])) {
self::$instances[$configPath] = new IDS_Init($configPath);
}
return self::$instances[$configPath];
}
/**
* Sets the path to the configuration file
*
* @param string $path the path to the config
*
* @throws Exception if file not found
* @return void
*/
public function setConfigPath($path)
{
if (file_exists($path)) {
$this->configPath = $path;
} else {
throw new Exception(
'Configuration file could not be found at ' .
htmlspecialchars($path, ENT_QUOTES, 'UTF-8')
);
}
}
/**
* Returns path to configuration file
*
* @return string the config path
*/
public function getConfigPath()
{
return $this->configPath;
}
/**
* This method checks if a base path is given and usage is set to true.
* If all that tests succeed the base path will be returned as a string -
* else null will be returned.
*
* @return string the base path or null
*/
public function getBasePath() {
return ((isset($this->config['General']['base_path'])
&& $this->config['General']['base_path']
&& isset($this->config['General']['use_base_path'])
&& $this->config['General']['use_base_path'])
? $this->config['General']['base_path'] : null);
}
/**
* Merges new settings into the exsiting ones or overwrites them
*
* @param array $config the config array
* @param boolean $overwrite config overwrite flag
*
* @return void
*/
public function setConfig(array $config, $overwrite = false)
{
if ($overwrite) {
$this->config = $this->_mergeConfig($this->config, $config);
} else {
$this->config = $this->_mergeConfig($config, $this->config);
}
}
/**
* Merge config hashes recursivly
*
* The algorithm merges configuration arrays recursively. If an element is
* an array in both, the values will be appended. If it is a scalar in both,
* the value will be replaced.
*
* @param array $current The legacy hash
* @param array $successor The hash which values count more when in doubt
* @return array Merged hash
*/
protected function _mergeConfig($current, $successor)
{
if (is_array($current) and is_array($successor)) {
foreach ($successor as $key => $value) {
if (isset($current[$key])
and is_array($value)
and is_array($current[$key])) {
$current[$key] = $this->_mergeConfig($current[$key], $value);
} else {
$current[$key] = $successor[$key];
}
}
}
return $current;
}
/**
* Returns the config array
*
* @return array the config array
*/
public function getConfig()
{
return $this->config;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,136 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Log/Interface.php';
/**
* Log Composite
*
* This class implements the composite pattern to allow to work with multiple
* logging wrappers at once.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Composite.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Log_Composite
{
/**
* Holds registered logging wrapper
*
* @var array
*/
public $loggers = array();
/**
* Iterates through registered loggers and executes them
*
* @param object $data IDS_Report object
*
* @return void
*/
public function execute(IDS_Report $data)
{
// make sure request uri is set right on IIS
if (!isset($_SERVER['REQUEST_URI'])) {
$_SERVER['REQUEST_URI'] = substr($_SERVER['PHP_SELF'], 1);
if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING']) {
$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
}
}
// make sure server address is set right on IIS
if (isset($_SERVER['LOCAL_ADDR'])) {
$_SERVER['SERVER_ADDR'] = $_SERVER['LOCAL_ADDR'];
}
foreach ($this->loggers as $logger) {
$logger->execute($data);
}
}
/**
* Registers a new logging wrapper
*
* Only valid IDS_Log_Interface instances passed to this function will be
* registered
*
* @return void
*/
public function addLogger()
{
$args = func_get_args();
foreach ($args as $class) {
if (!in_array($class, $this->loggers) &&
($class instanceof IDS_Log_Interface)) {
$this->loggers[] = $class;
}
}
}
/**
* Removes a logger
*
* @param object $logger IDS_Log_Interface object
*
* @return boolean
*/
public function removeLogger(IDS_Log_Interface $logger)
{
$key = array_search($logger, $this->loggers);
if (isset($this->loggers[$key])) {
unset($this->loggers[$key]);
return true;
}
return false;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,300 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Log/Interface.php';
/*
* Needed SQL:
*
CREATE DATABASE IF NOT EXISTS `phpids` DEFAULT CHARACTER
SET utf8 COLLATE utf8_general_ci;
DROP TABLE IF EXISTS `intrusions`;
CREATE TABLE IF NOT EXISTS `intrusions` (
`id` int(11) unsigned NOT null auto_increment,
`name` varchar(128) NOT null,
`value` text NOT null,
`page` varchar(255) NOT null,
`tags` varchar(128) NOT null,
`ip` varchar(15) NOT null,
`ip2` varchar(15) NOT null,
`impact` int(11) unsigned NOT null,
`origin` varchar(15) NOT null,
`created` datetime NOT null,
PRIMARY KEY (`id`)
) ENGINE=MyISAM ;
*
*
*
*/
/**
* Database logging wrapper
*
* The database wrapper is designed to store reports into an sql database. It
* implements the singleton pattern and is based in PDO, supporting
* different database types.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Database.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Log_Database implements IDS_Log_Interface
{
/**
* Database wrapper
*
* @var string
*/
private $wrapper = null;
/**
* Database user
*
* @var string
*/
private $user = null;
/**
* Database password
*
* @var string
*/
private $password = null;
/**
* Database table
*
* @var string
*/
private $table = null;
/**
* Database handle
*
* @var object PDO instance
*/
private $handle = null;
/**
* Prepared SQL statement
*
* @var string
*/
private $statement = null;
/**
* Holds current remote address
*
* @var string
*/
private $ip = 'local/unknown';
/**
* Instance container
*
* Due to the singleton pattern this class allows to initiate only one instance
* for each database wrapper.
*
* @var array
*/
private static $instances = array();
/**
* Constructor
*
* Prepares the SQL statement
*
* @param mixed $config IDS_Init instance | array
*
* @return void
* @throws PDOException if a db error occurred
*/
protected function __construct($config)
{
if ($config instanceof IDS_Init) {
$this->wrapper = $config->config['Logging']['wrapper'];
$this->user = $config->config['Logging']['user'];
$this->password = $config->config['Logging']['password'];
$this->table = $config->config['Logging']['table'];
} elseif (is_array($config)) {
$this->wrapper = $config['wrapper'];
$this->user = $config['user'];
$this->password = $config['password'];
$this->table = $config['table'];
}
// determine correct IP address and concat them if necessary
$this->ip = $_SERVER['REMOTE_ADDR'];
$this->ip2 = isset($_SERVER['HTTP_X_FORWARDED_FOR'])
? $_SERVER['HTTP_X_FORWARDED_FOR']
: '';
try {
$this->handle = new PDO(
$this->wrapper,
$this->user,
$this->password
);
$this->statement = $this->handle->prepare('
INSERT INTO ' . $this->table . ' (
name,
value,
page,
tags,
ip,
ip2,
impact,
origin,
created
)
VALUES (
:name,
:value,
:page,
:tags,
:ip,
:ip2,
:impact,
:origin,
now()
)
');
} catch (PDOException $e) {
throw new PDOException('PDOException: ' . $e->getMessage());
}
}
/**
* Returns an instance of this class
*
* This method allows the passed argument to be either an instance of IDS_Init or
* an array.
*
* @param mixed $config IDS_Init | array
* @param string $classname the class name to use
*
* @return object $this
*/
public static function getInstance($config, $classname = 'IDS_Log_Database')
{
if ($config instanceof IDS_Init) {
$wrapper = $config->config['Logging']['wrapper'];
} elseif (is_array($config)) {
$wrapper = $config['wrapper'];
}
if (!isset(self::$instances[$wrapper])) {
self::$instances[$wrapper] = new $classname($config);
}
return self::$instances[$wrapper];
}
/**
* Permitting to clone this object
*
* For the sake of correctness of a singleton pattern, this is necessary
*
* @return void
*/
private function __clone()
{
}
/**
* Stores given data into the database
*
* @param object $data IDS_Report instance
*
* @throws Exception if db error occurred
* @return boolean
*/
public function execute(IDS_Report $data)
{
if (!isset($_SERVER['REQUEST_URI'])) {
$_SERVER['REQUEST_URI'] = substr($_SERVER['PHP_SELF'], 1);
if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING']) {
$_SERVER['REQUEST_URI'] .= '?' . $_SERVER['QUERY_STRING'];
}
}
foreach ($data as $event) {
$page = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
$ip = $this->ip;
$ip2 = $this->ip2;
$name = $event->getName();
$value = $event->getValue();
$impact = $event->getImpact();
$tags = implode(', ', $event->getTags());
$this->statement->bindParam('name', $name);
$this->statement->bindParam('value', $value);
$this->statement->bindParam('page', $page);
$this->statement->bindParam('tags', $tags);
$this->statement->bindParam('ip', $ip);
$this->statement->bindParam('ip2', $ip2);
$this->statement->bindParam('impact', $impact);
$this->statement->bindParam('origin', $_SERVER['SERVER_ADDR']);
if (!$this->statement->execute()) {
$info = $this->statement->errorInfo();
throw new Exception(
$this->statement->errorCode() . ', ' . $info[1] . ', ' . $info[2]
);
}
}
return true;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,400 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Log/Interface.php';
/**
* Email logging wrapper
*
* The Email wrapper is designed to send reports via email. It implements the
* singleton pattern.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Email.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Log_Email implements IDS_Log_Interface
{
/**
* Recipient container
*
* @var array
*/
protected $recipients = array();
/**
* Mail subject
*
* @var string
*/
protected $subject = null;
/**
* Additional mail headers
*
* @var string
*/
protected $headers = null;
/**
* Safemode switch
*
* Using this switch it is possible to enable safemode, which is a spam
* protection based on the alert frequency.
*
* @var boolean
*/
protected $safemode = true;
/**
* Urlencode for result strings
*
* This switch is true by default. Setting it to false removes
* the 'better safe than sorry' urlencoding for the result string in
* the report mails. Enhances readability but maybe XSSes email clients.
*
* @var boolean
*/
protected $urlencode = true;
/**
* Send rate
*
* If safemode is enabled, this property defines how often reports will be
* sent out. Default value is 15, which means that a mail will be sent on
* condition that the last email has not been sent earlier than 15 seconds ago.
*
* @var integer
*/
protected $allowed_rate = 15;
/**
* PHPIDS temp directory
*
* When safemod is enabled, a path to a temp directory is needed to
* store some information. Default is IDS/tmp/
*
* @var string
*/
protected $tmp_path = 'IDS/tmp/';
/**
* File prefix for tmp files
*
* @var string
*/
protected $file_prefix = 'PHPIDS_Log_Email_';
/**
* Holds current remote address
*
* @var string
*/
protected $ip = 'local/unknown';
/**
* Instance container
*
* @var array
*/
protected static $instance = array();
/**
* Constructor
*
* @param mixed $config IDS_Init instance | array
*
* @return void
*/
protected function __construct($config)
{
if ($config instanceof IDS_Init) {
$this->recipients = $config->config['Logging']['recipients'];
$this->subject = $config->config['Logging']['subject'];
$this->headers = $config->config['Logging']['header'];
$this->envelope = $config->config['Logging']['envelope'];
$this->safemode = $config->config['Logging']['safemode'];
$this->urlencode = $config->config['Logging']['urlencode'];
$this->allowed_rate = $config->config['Logging']['allowed_rate'];
$this->tmp_path = $config->getBasePath()
. $config->config['General']['tmp_path'];
} elseif (is_array($config)) {
$this->recipients[] = $config['recipients'];
$this->subject = $config['subject'];
$this->additionalHeaders = $config['header'];
}
// determine correct IP address and concat them if necessary
$this->ip = $_SERVER['REMOTE_ADDR'] .
(isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
' (' . $_SERVER['HTTP_X_FORWARDED_FOR'] . ')' : '');
}
/**
* Returns an instance of this class
*
* This method allows the passed argument to be either an instance of
* IDS_Init or an array.
*
* @param mixed $config IDS_Init | array
* @param string $classname the class name to use
*
* @return object $this
*/
public static function getInstance($config, $classname = 'IDS_Log_Email')
{
if (!self::$instance) {
self::$instance = new $classname($config);
}
return self::$instance;
}
/**
* Permitting to clone this object
*
* For the sake of correctness of a singleton pattern, this is necessary
*
* @return void
*/
private function __clone()
{
}
/**
* Detects spam attempts
*
* To avoid mail spam through this logging class this function is used
* to detect such attempts based on the alert frequency.
*
* @return boolean
*/
protected function isSpamAttempt()
{
/*
* loop through all files in the tmp directory and
* delete garbage files
*/
$dir = $this->tmp_path;
$numPrefixChars = strlen($this->file_prefix);
$files = scandir($dir);
foreach ($files as $file) {
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
if (substr($file, 0, $numPrefixChars) == $this->file_prefix) {
$lastModified = filemtime($dir . DIRECTORY_SEPARATOR . $file);
if ((time() - $lastModified) > 3600) {
unlink($dir . DIRECTORY_SEPARATOR . $file);
}
}
}
}
/*
* end deleting garbage files
*/
$remoteAddr = $this->ip;
$userAgent = $_SERVER['HTTP_USER_AGENT'];
$filename = $this->file_prefix . md5($remoteAddr.$userAgent) . '.tmp';
$file = $dir . DIRECTORY_SEPARATOR . $filename;
if (!file_exists($file)) {
$handle = fopen($file, 'w');
fwrite($handle, time());
fclose($handle);
return false;
}
$lastAttack = file_get_contents($file);
$difference = time() - $lastAttack;
if ($difference > $this->allowed_rate) {
unlink($file);
} else {
return true;
}
return false;
}
/**
* Prepares data
*
* Converts given data into a format that can be read in an email.
* You might edit this method to your requirements.
*
* @param mixed $data the report data
*
* @return string
*/
protected function prepareData($data)
{
$format = "The following attack has been detected by PHPIDS\n\n";
$format .= "IP: %s \n";
$format .= "Date: %s \n";
$format .= "Impact: %d \n";
$format .= "Affected tags: %s \n";
$attackedParameters = '';
foreach ($data as $event) {
$attackedParameters .= $event->getName() . '=' .
((!isset($this->urlencode) ||$this->urlencode)
? urlencode($event->getValue())
: $event->getValue()) . ", ";
}
$format .= "Affected parameters: %s \n";
$format .= "Request URI: %s \n";
$format .= "Origin: %s \n";
return sprintf($format,
$this->ip,
date('c'),
$data->getImpact(),
join(' ', $data->getTags()),
trim($attackedParameters),
htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES, 'UTF-8'),
$_SERVER['SERVER_ADDR']);
}
/**
* Sends the report to registered recipients
*
* @param object $data IDS_Report instance
*
* @throws Exception if data is no string
* @return boolean
*/
public function execute(IDS_Report $data)
{
if ($this->safemode) {
if ($this->isSpamAttempt()) {
return false;
}
}
/*
* In case the data has been modified before it might
* be necessary to convert it to string since it's pretty
* senseless to send array or object via e-mail
*/
$data = $this->prepareData($data);
if (is_string($data)) {
$data = trim($data);
// if headers are passed as array, we need to make a string of it
if (is_array($this->headers)) {
$headers = "";
foreach ($this->headers as $header) {
$headers .= $header . "\r\n";
}
} else {
$headers = $this->headers;
}
if (!empty($this->recipients)) {
if (is_array($this->recipients)) {
foreach ($this->recipients as $address) {
$this->send(
$address,
$data,
$headers,
$this->envelope
);
}
} else {
$this->send(
$this->recipients,
$data,
$headers,
$this->envelope
);
}
}
} else {
throw new Exception(
'Please make sure that data returned by
IDS_Log_Email::prepareData() is a string.'
);
}
return true;
}
/**
* Sends an email
*
* @param string $address email address
* @param string $data the report data
* @param string $headers the mail headers
* @param string $envelope the optional envelope string
*
* @return boolean
*/
protected function send($address, $data, $headers, $envelope = null)
{
if (!$envelope || strpos(ini_get('sendmail_path'),' -f') !== false) {
return mail($address,
$this->subject,
$data,
$headers);
} else {
return mail($address,
$this->subject,
$data,
$headers,
'-f' . $envelope);
}
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,229 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
require_once 'IDS/Log/Interface.php';
/**
* File logging wrapper
*
* The file wrapper is designed to store data into a flatfile. It implements the
* singleton pattern.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:File.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Log_File implements IDS_Log_Interface
{
/**
* Path to the log file
*
* @var string
*/
private $logfile = null;
/**
* Instance container
*
* Due to the singleton pattern this class allows to initiate only one
* instance for each file.
*
* @var array
*/
private static $instances = array();
/**
* Holds current remote address
*
* @var string
*/
private $ip = 'local/unknown';
/**
* Constructor
*
* @param string $logfile path to the log file
*
* @return void
*/
protected function __construct($logfile)
{
// determine correct IP address and concat them if necessary
$this->ip = $_SERVER['REMOTE_ADDR'] .
(isset($_SERVER['HTTP_X_FORWARDED_FOR']) ?
' (' . $_SERVER['HTTP_X_FORWARDED_FOR'] . ')' : '');
$this->logfile = $logfile;
}
/**
* Returns an instance of this class
*
* This method allows the passed argument to be either an instance of
* IDS_Init or a path to a log file. Due to the singleton pattern only one
* instance for each file can be initiated.
*
* @param mixed $config IDS_Init or path to a file
* @param string $classname the class name to use
*
* @return object $this
*/
public static function getInstance($config, $classname = 'IDS_Log_File')
{
if ($config instanceof IDS_Init) {
$logfile = $config->getBasePath() . $config->config['Logging']['path'];
} elseif (is_string($config)) {
$logfile = $config;
}
if (!isset(self::$instances[$logfile])) {
self::$instances[$logfile] = new $classname($logfile);
}
return self::$instances[$logfile];
}
/**
* Permitting to clone this object
*
* For the sake of correctness of a singleton pattern, this is necessary
*
* @return void
*/
private function __clone()
{
}
/**
* Prepares data
*
* Converts given data into a format that can be stored into a file.
* You might edit this method to your requirements.
*
* @param mixed $data incoming report data
*
* @return string
*/
protected function prepareData($data)
{
$format = '"%s",%s,%d,"%s","%s","%s","%s"';
$attackedParameters = '';
foreach ($data as $event) {
$attackedParameters .= $event->getName() . '=' .
rawurlencode($event->getValue()) . ' ';
}
$dataString = sprintf($format,
urlencode($this->ip),
date('c'),
$data->getImpact(),
join(' ', $data->getTags()),
urlencode(trim($attackedParameters)),
urlencode($_SERVER['REQUEST_URI']),
$_SERVER['SERVER_ADDR']
);
return $dataString;
}
/**
* Stores given data into a file
*
* @param object $data IDS_Report
*
* @throws Exception if the logfile isn't writeable
* @return boolean
*/
public function execute(IDS_Report $data)
{
/*
* In case the data has been modified before it might be necessary
* to convert it to string since we can't store array or object
* into a file
*/
$data = $this->prepareData($data);
if (is_string($data)) {
if (file_exists($this->logfile)) {
$data = trim($data);
if (!empty($data)) {
if (is_writable($this->logfile)) {
$handle = fopen($this->logfile, 'a');
fwrite($handle, trim($data) . "\n");
fclose($handle);
} else {
throw new Exception(
'Please make sure that ' . $this->logfile .
' is writeable.'
);
}
}
} else {
throw new Exception(
'Given file does not exist. Please make sure the
logfile is present in the given directory.'
);
}
} else {
throw new Exception(
'Please make sure that data returned by
IDS_Log_File::prepareData() is a string.'
);
}
return true;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,65 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* Interface for logging wrappers
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @version Release: $Id:Interface.php 517 2007-09-15 15:04:13Z mario $
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
interface IDS_Log_Interface
{
/**
* Interface method
*
* @param IDS_Report $data the report data
*
* @return void
*/
public function execute(IDS_Report $data);
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,778 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* Monitoring engine
*
* This class represents the core of the frameworks attack detection mechanism
* and provides functions to scan incoming data for malicious appearing script
* fragments.
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Monitor.php 949 2008-06-28 01:26:03Z christ1an $
* @link http://php-ids.org/
*/
class IDS_Monitor
{
/**
* Tags to define what to search for
*
* Accepted values are xss, csrf, sqli, dt, id, lfi, rfe, spam, dos
*
* @var array
*/
private $tags = null;
/**
* Request array
*
* Array containing raw data to search in
*
* @var array
*/
private $request = null;
/**
* Container for filter rules
*
* Holds an instance of IDS_Filter_Storage
*
* @var object
*/
private $storage = null;
/**
* Results
*
* Holds an instance of IDS_Report which itself provides an API to
* access the detected results
*
* @var object
*/
private $report = null;
/**
* Scan keys switch
*
* Enabling this property will cause the monitor to scan both the key and
* the value of variables
*
* @var boolean
*/
public $scanKeys = false;
/**
* Exception container
*
* Using this array it is possible to define variables that must not be
* scanned. Per default, utmz google analytics parameters are permitted.
*
* @var array
*/
private $exceptions = array();
/**
* Html container
*
* Using this array it is possible to define variables that legally
* contain html and have to be prepared before hitting the rules to
* avoid too many false alerts
*
* @var array
*/
private $html = array();
/**
* JSON container
*
* Using this array it is possible to define variables that contain
* JSON data - and should be treated as such
*
* @var array
*/
private $json = array();
/**
* Holds HTMLPurifier object
*
* @var object
*/
private $htmlpurifier = NULL;
/**
* Path to HTMLPurifier source
*
* This path might be changed in case one wishes to make use of a
* different HTMLPurifier source file e.g. if already used in the
* application PHPIDS is protecting
*
* @var string
*/
private $pathToHTMLPurifier = '';
/**
* HTMLPurifier cache directory
*
* @var string
*/
private $HTMLPurifierCache = '';
/**
* This property holds the tmp JSON string from the
* _jsonDecodeValues() callback
*
* @var string
*/
private $tmpJsonString = '';
/**
* Constructor
*
* @param array $request array to scan
* @param object $init instance of IDS_Init
* @param array $tags list of tags to which filters should be applied
*
* @return void
*/
public function __construct(array $request, IDS_Init $init, array $tags = null)
{
$version = isset($init->config['General']['min_php_version'])
? $init->config['General']['min_php_version'] : '5.1.6';
if (version_compare(PHP_VERSION, $version, '<')) {
throw new Exception(
'PHP version has to be equal or higher than ' . $version . ' or
PHP version couldn\'t be determined'
);
}
if (!empty($request)) {
$this->storage = new IDS_Filter_Storage($init);
$this->request = $request;
$this->tags = $tags;
$this->scanKeys = $init->config['General']['scan_keys'];
$this->exceptions = isset($init->config['General']['exceptions'])
? $init->config['General']['exceptions'] : false;
$this->html = isset($init->config['General']['html'])
? $init->config['General']['html'] : false;
$this->json = isset($init->config['General']['json'])
? $init->config['General']['json'] : false;
if(isset($init->config['General']['HTML_Purifier_Path'])
&& isset($init->config['General']['HTML_Purifier_Cache'])) {
$this->pathToHTMLPurifier =
$init->config['General']['HTML_Purifier_Path'];
$this->HTMLPurifierCache = $init->getBasePath()
. $init->config['General']['HTML_Purifier_Cache'];
}
}
if (!is_writeable($init->getBasePath()
. $init->config['General']['tmp_path'])) {
throw new Exception(
'Please make sure the ' .
htmlspecialchars($init->getBasePath() .
$init->config['General']['tmp_path'], ENT_QUOTES, 'UTF-8') .
' folder is writable'
);
}
include_once 'IDS/Report.php';
$this->report = new IDS_Report;
}
/**
* Starts the scan mechanism
*
* @return object IDS_Report
*/
public function run()
{
if (!empty($this->request)) {
foreach ($this->request as $key => $value) {
$this->_iterate($key, $value);
}
}
return $this->getReport();
}
/**
* Iterates through given data and delegates it to IDS_Monitor::_detect() in
* order to check for malicious appearing fragments
*
* @param mixed $key the former array key
* @param mixed $value the former array value
*
* @return void
*/
private function _iterate($key, $value)
{
if (!is_array($value)) {
if (is_string($value)) {
if ($filter = $this->_detect($key, $value)) {
include_once 'IDS/Event.php';
$this->report->addEvent(
new IDS_Event(
$key,
$value,
$filter
)
);
}
}
} else {
foreach ($value as $subKey => $subValue) {
$this->_iterate($key . '.' . $subKey, $subValue);
}
}
}
/**
* Checks whether given value matches any of the supplied filter patterns
*
* @param mixed $key the key of the value to scan
* @param mixed $value the value to scan
*
* @return bool|array false or array of filter(s) that matched the value
*/
private function _detect($key, $value)
{
// define the pre-filter
$prefilter = '/[^\w\s\/@!?\.]+|(?:\.\/)|(?:@@\w+)'
. '|(?:\+ADw)|(?:union\s+select)/i';
// to increase performance, only start detection if value
// isn't alphanumeric
if (!$this->scanKeys
&& (!$value || !preg_match($prefilter, $value))) {
return false;
} elseif($this->scanKeys) {
if((!$key || !preg_match($prefilter, $key))
&& (!$value || !preg_match($prefilter, $value))) {
return false;
}
}
// check if this field is part of the exceptions
if (is_array($this->exceptions)) {
foreach($this->exceptions as $exception) {
$matches = array();
if(preg_match('/(\/.*\/[^eE]*)$/', $exception, $matches)) {
if(isset($matches[1]) && preg_match($matches[1], $key)) {
return false;
}
} else {
if($exception === $key) {
return false;
}
}
}
}
// check for magic quotes and remove them if necessary
if (function_exists('get_magic_quotes_gpc')
&& get_magic_quotes_gpc()) {
$value = stripslashes($value);
}
if(function_exists('get_magic_quotes_gpc')
&& !get_magic_quotes_gpc()
&& version_compare(PHP_VERSION, '5.3.0', '>=')) {
$value = preg_replace('/\\\(["\'\/])/im', '$1', $value);
}
// if html monitoring is enabled for this field - then do it!
if (is_array($this->html) && in_array($key, $this->html, true)) {
list($key, $value) = $this->_purifyValues($key, $value);
}
// check if json monitoring is enabled for this field
if (is_array($this->json) && in_array($key, $this->json, true)) {
list($key, $value) = $this->_jsonDecodeValues($key, $value);
}
// use the converter
include_once 'IDS/Converter.php';
$value = IDS_Converter::runAll($value);
$value = IDS_Converter::runCentrifuge($value, $this);
// scan keys if activated via config
$key = $this->scanKeys ? IDS_Converter::runAll($key)
: $key;
$key = $this->scanKeys ? IDS_Converter::runCentrifuge($key, $this)
: $key;
$filters = array();
$filterSet = $this->storage->getFilterSet();
foreach ($filterSet as $filter) {
/*
* in case we have a tag array specified the IDS will only
* use those filters that are meant to detect any of the
* defined tags
*/
if (is_array($this->tags)) {
if (array_intersect($this->tags, $filter->getTags())) {
if ($this->_match($key, $value, $filter)) {
$filters[] = $filter;
}
}
} else {
if ($this->_match($key, $value, $filter)) {
$filters[] = $filter;
}
}
}
return empty($filters) ? false : $filters;
}
/**
* Purifies given key and value variables using HTMLPurifier
*
* This function is needed whenever there is variables for which HTML
* might be allowed like e.g. WYSIWYG post bodies. It will dectect malicious
* code fragments and leaves harmless parts untouched.
*
* @param mixed $key
* @param mixed $value
* @since 0.5
* @throws Exception
*
* @return array
*/
private function _purifyValues($key, $value)
{
/*
* Perform a pre-check if string is valid for purification
*/
if(!$this->_purifierPreCheck($key, $value)) {
return array($key, $value);
}
include_once $this->pathToHTMLPurifier;
if (!is_writeable($this->HTMLPurifierCache)) {
throw new Exception(
$this->HTMLPurifierCache . ' must be writeable');
}
if (class_exists('HTMLPurifier')) {
$config = HTMLPurifier_Config::createDefault();
$config->set('Attr.EnableID', true);
$config->set('Cache.SerializerPath', $this->HTMLPurifierCache);
$config->set('Output.Newline', "\n");
$this->htmlpurifier = new HTMLPurifier($config);
} else {
throw new Exception(
'HTMLPurifier class could not be found - ' .
'make sure the purifier files are valid and' .
' the path is correct'
);
}
$value = preg_replace('/[\x0b-\x0c]/', ' ', $value);
$key = preg_replace('/[\x0b-\x0c]/', ' ', $key);
$purified_value = $this->htmlpurifier->purify($value);
$purified_key = $this->htmlpurifier->purify($key);
$redux_value = strip_tags($value);
$redux_key = strip_tags($key);
if ($value != $purified_value || $redux_value) {
$value = $this->_diff($value, $purified_value, $redux_value);
} else {
$value = NULL;
}
if ($key != $purified_key) {
$key = $this->_diff($key, $purified_key, $redux_key);
} else {
$key = NULL;
}
return array($key, $value);
}
/**
* This method makes sure no dangerous markup can be smuggled in
* attributes when HTML mode is switched on.
*
* If the precheck considers the string too dangerous for
* purification false is being returned.
*
* @param mixed $key
* @param mixed $value
* @since 0.6
*
* @return boolean
*/
private function _purifierPreCheck($key = '', $value = '')
{
/*
* Remove control chars before pre-check
*/
$tmp_value = preg_replace('/\p{C}/', null, $value);
$tmp_key = preg_replace('/\p{C}/', null, $key);
$precheck = '/<(script|iframe|applet|object)\W/i';
if(preg_match($precheck, $tmp_key)
|| preg_match($precheck, $tmp_value)) {
return false;
}
return true;
}
/**
* This method calculates the difference between the original
* and the purified markup strings.
*
* @param string $original the original markup
* @param string $purified the purified markup
* @param string $redux the string without html
* @since 0.5
*
* @return string the difference between the strings
*/
private function _diff($original, $purified, $redux)
{
/*
* deal with over-sensitive alt-attribute addition of the purifier
* and other common html formatting problems
*/
$purified = preg_replace('/\s+alt="[^"]*"/m', null, $purified);
$purified = preg_replace('/=?\s*"\s*"/m', null, $purified);
$original = preg_replace('/\s+alt="[^"]*"/m', null, $original);
$original = preg_replace('/=?\s*"\s*"/m', null, $original);
$original = preg_replace('/style\s*=\s*([^"])/m', 'style = "$1', $original);
# deal with oversensitive CSS normalization
$original = preg_replace('/(?:([\w\-]+:)+\s*([^;]+;\s*))/m', '$1$2', $original);
# strip whitespace between tags
$original = trim(preg_replace('/>\s*</m', '><', $original));
$purified = trim(preg_replace('/>\s*</m', '><', $purified));
$original = preg_replace(
'/(=\s*(["\'`])[^>"\'`]*>[^>"\'`]*["\'`])/m', 'alt$1', $original
);
// no purified html is left
if (!$purified) {
return $original;
}
// calculate the diff length
$length = mb_strlen($original) - mb_strlen($purified);
/*
* Calculate the difference between the original html input
* and the purified string.
*/
$array_1 = preg_split('/(?<!^)(?!$)/u', html_entity_decode(urldecode($original)));
$array_2 = preg_split('/(?<!^)(?!$)/u', $purified);
// create an array containing the single character differences
$differences = array();
foreach ($array_1 as $key => $value) {
if (!isset($array_2[$key]) || $value !== $array_2[$key]) {
$differences[] = $value;
}
}
// return the diff - ready to hit the converter and the rules
if(intval($length) <= 10) {
$diff = trim(join('', $differences));
} else {
$diff = mb_substr(trim(join('', $differences)), 0, strlen($original));
}
// clean up spaces between tag delimiters
$diff = preg_replace('/>\s*</m', '><', $diff);
// correct over-sensitively stripped bad html elements
$diff = preg_replace('/[^<](iframe|script|embed|object' .
'|applet|base|img|style)/m', '<$1', $diff);
if (mb_strlen($diff) < 4) {
return null;
}
return $diff . $redux;
}
/**
* This method prepares incoming JSON data for the PHPIDS detection
* process. It utilizes _jsonConcatContents() as callback and returns a
* string version of the JSON data structures.
*
* @param mixed $key
* @param mixed $value
* @since 0.5.3
*
* @return array
*/
private function _jsonDecodeValues($key, $value) {
$tmp_key = json_decode($key);
$tmp_value = json_decode($value);
if($tmp_value && is_array($tmp_value) || is_object($tmp_value)) {
array_walk_recursive($tmp_value, array($this, '_jsonConcatContents'));
$value = $this->tmpJsonString;
} else {
$this->tmpJsonString .= " " . $tmp_value . "\n";
}
if($tmp_key && is_array($tmp_key) || is_object($tmp_key)) {
array_walk_recursive($tmp_key, array($this, '_jsonConcatContents'));
$key = $this->tmpJsonString;
} else {
$this->tmpJsonString .= " " . $tmp_key . "\n";
}
return array($key, $value);
}
/**
* This is the callback used in _jsonDecodeValues(). The method
* concatenates key and value and stores them in $this->tmpJsonString.
*
* @param mixed $key
* @param mixed $value
* @since 0.5.3
*
* @return void
*/
private function _jsonConcatContents($key, $value) {
if(is_string($key) && is_string($value)) {
$this->tmpJsonString .= $key . " " . $value . "\n";
} else {
$this->_jsonDecodeValues(
json_encode($key), json_encode($value)
);
}
}
/**
* Matches given value and/or key against given filter
*
* @param mixed $key the key to optionally scan
* @param mixed $value the value to scan
* @param object $filter the filter object
*
* @return boolean
*/
private function _match($key, $value, $filter)
{
if ($this->scanKeys) {
if ($filter->match($key)) {
return true;
}
}
if ($filter->match($value)) {
return true;
}
return false;
}
/**
* Sets exception array
*
* @param mixed $exceptions the thrown exceptions
*
* @return void
*/
public function setExceptions($exceptions)
{
if (!is_array($exceptions)) {
$exceptions = array($exceptions);
}
$this->exceptions = $exceptions;
}
/**
* Returns exception array
*
* @return array
*/
public function getExceptions()
{
return $this->exceptions;
}
/**
* Sets html array
*
* @param mixed $html the fields containing html
* @since 0.5
*
* @return void
*/
public function setHtml($html)
{
if (!is_array($html)) {
$html = array($html);
}
$this->html = $html;
}
/**
* Adds a value to the html array
*
* @since 0.5
*
* @return void
*/
public function addHtml($value)
{
$this->html[] = $value;
}
/**
* Returns html array
*
* @since 0.5
*
* @return array the fields that contain allowed html
*/
public function getHtml()
{
return $this->html;
}
/**
* Sets json array
*
* @param mixed $json the fields containing json
* @since 0.5.3
*
* @return void
*/
public function setJson($json)
{
if (!is_array($json)) {
$json = array($json);
}
$this->json = $json;
}
/**
* Adds a value to the json array
*
* @param string the value containing JSON data
* @since 0.5.3
*
* @return void
*/
public function addJson($value)
{
$this->json[] = $value;
}
/**
* Returns json array
*
* @since 0.5.3
*
* @return array the fields that contain json
*/
public function getJson()
{
return $this->json;
}
/**
* Returns storage container
*
* @return array
*/
public function getStorage()
{
return $this->storage;
}
/**
* Returns report object providing various functions to work with
* detected results. Also the centrifuge data is being set as property
* of the report object.
*
* @return object IDS_Report
*/
public function getReport()
{
if (isset($this->centrifuge) && $this->centrifuge) {
$this->report->setCentrifuge($this->centrifuge);
}
return $this->report;
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,341 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* PHPIDS report object
*
* The report objects collects a number of events and thereby presents the
* detected results. It provides a convenient API to work with the results.
*
* Note that this class implements Countable, IteratorAggregate and
* a __toString() method
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Report.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
class IDS_Report implements Countable, IteratorAggregate
{
/**
* Event container
*
* @var array
*/
protected $events = array();
/**
* List of affected tags
*
* This list of tags is collected from the collected event objects on
* demand when IDS_Report->getTags() is called
*
* @var array
*/
protected $tags = array();
/**
* Impact level
*
* The impact level is calculated on demand by adding the results of the
* event objects on IDS_Report->getImpact()
*
* @var integer
*/
protected $impact = 0;
/**
* Centrifuge data
*
* This variable - initiated as an empty array - carries all information
* about the centrifuge data if available
*
* @var array
*/
protected $centrifuge = array();
/**
* Constructor
*
* @param array $events the events the report should include
*
* @return void
*/
public function __construct(array $events = null)
{
if ($events) {
foreach ($events as $event) {
$this->addEvent($event);
}
}
}
/**
* Adds an IDS_Event object to the report
*
* @param object $event IDS_Event
*
* @return object $this
*/
public function addEvent(IDS_Event $event)
{
$this->clear();
$this->events[$event->getName()] = $event;
return $this;
}
/**
* Get event by name
*
* In most cases an event is identified by the key of the variable that
* contained maliciously appearing content
*
* @param scalar $name the event name
*
* @throws InvalidArgumentException if argument is invalid
* @return mixed IDS_Event object or false if the event does not exist
*/
public function getEvent($name)
{
if (!is_scalar($name)) {
throw new InvalidArgumentException(
'Invalid argument type given'
);
}
if ($this->hasEvent($name)) {
return $this->events[$name];
}
return false;
}
/**
* Returns list of affected tags
*
* @return array
*/
public function getTags()
{
if (!$this->tags) {
$this->tags = array();
foreach ($this->events as $event) {
$this->tags = array_merge($this->tags,
$event->getTags());
}
$this->tags = array_values(array_unique($this->tags));
}
return $this->tags;
}
/**
* Returns total impact
*
* Each stored IDS_Event object and its IDS_Filter sub-object are called
* to calculate the overall impact level of this request
*
* @return integer
*/
public function getImpact()
{
if (!$this->impact) {
$this->impact = 0;
foreach ($this->events as $event) {
$this->impact += $event->getImpact();
}
}
return $this->impact;
}
/**
* Checks if a specific event with given name exists
*
* @param scalar $name the event name
*
* @throws InvalidArgumentException if argument is illegal
*
* @return boolean
*/
public function hasEvent($name)
{
if (!is_scalar($name)) {
throw new InvalidArgumentException('Invalid argument given');
}
return isset($this->events[$name]);
}
/**
* Returns total amount of events
*
* @return integer
*/
public function count()
{
return count($this->events);
}
/**
* Return iterator object
*
* In order to provide the possibility to directly iterate over the
* IDS_Event object the IteratorAggregate is implemented. One can easily
* use foreach() to iterate through all stored IDS_Event objects.
*
* @return ArrayObject the event collection
*/
public function getIterator()
{
return new ArrayObject($this->events);
}
/**
* Checks if any events are registered
*
* @return boolean
*/
public function isEmpty()
{
return empty($this->events);
}
/**
* Clears calculated/collected values
*
* @return void
*/
protected function clear()
{
$this->impact = 0;
$this->tags = array();
}
/**
* This method returns the centrifuge property or null if not
* filled with data
*
* @return array/null
*/
public function getCentrifuge()
{
return ($this->centrifuge && count($this->centrifuge) > 0)
? $this->centrifuge : null;
}
/**
* This method sets the centrifuge property
*
* @param array $centrifuge the centrifuge data
*
* @throws InvalidArgumentException if argument is illegal
*
* @return boolean true is arguments were valid
*/
public function setCentrifuge($centrifuge = array())
{
if (is_array($centrifuge) && $centrifuge) {
$this->centrifuge = $centrifuge;
return true;
}
throw new InvalidArgumentException('Invalid argument given');
}
/**
* Directly outputs all available information
*
* @return string
*/
public function __toString()
{
if (!$this->isEmpty()) {
$output = '';
$output .= 'Total impact: ' . $this->getImpact() . "<br/>\n";
$output .= 'Affected tags: ' . join(', ', $this->getTags()) .
"<br/>\n";
foreach ($this->events as $event) {
$output .= "<br/>\nVariable: " .
htmlspecialchars($event->getName()) . ' | Value: ' .
htmlspecialchars($event->getValue()) . "<br/>\n";
$output .= 'Impact: ' . $event->getImpact() . ' | Tags: ' .
join(', ', $event->getTags()) . "<br/>\n";
foreach ($event as $filter) {
$output .= 'Description: ' . $filter->getDescription() .
' | ';
$output .= 'Tags: ' . join(', ', $filter->getTags()) .
' | ';
$output .= 'ID: ' . $filter->getId() .
"<br/>\n";
}
}
$output .= '<br/>';
if ($centrifuge = $this->getCentrifuge()) {
$output .= 'Centrifuge detection data';
$output .= '<br/> Threshold: ' .
((isset($centrifuge['threshold'])&&$centrifuge['threshold']) ?
$centrifuge['threshold'] : '---');
$output .= '<br/> Ratio: ' .
((isset($centrifuge['ratio'])&&$centrifuge['ratio']) ?
$centrifuge['ratio'] : '---');
if(isset($centrifuge['converted'])) {
$output .= '<br/> Converted: ' . $centrifuge['converted'];
}
$output .= "<br/><br/>\n";
}
}
return isset($output) ? $output : '';
}
}
/**
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: sw=4 ts=4 expandtab
*/

View file

@ -0,0 +1,49 @@
<?php
/**
* PHPIDS
*
* Requirements: PHP5, SimpleXML
*
* Copyright (c) 2008 PHPIDS group (https://phpids.org)
*
* PHPIDS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3 of the License, or
* (at your option) any later version.
*
* PHPIDS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with PHPIDS. If not, see <http://www.gnu.org/licenses/>.
*
* PHP version 5.1.6+
*
* @category Security
* @package PHPIDS
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Christian Matthies <ch0012@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @link http://php-ids.org/
*/
/**
* PHPIDS version class
*
* @category Security
* @package PHPIDS
* @author Christian Matthies <ch0012@gmail.com>
* @author Mario Heiderich <mario.heiderich@gmail.com>
* @author Lars Strojny <lars@strojny.net>
* @copyright 2007-2009 The PHPIDS Group
* @license http://www.gnu.org/licenses/lgpl.html LGPL
* @version Release: $Id:Converter.php 517 2007-09-15 15:04:13Z mario $
* @link http://php-ids.org/
*/
abstract class IDS_Version
{
const VERSION = '@@phpIdsVersion@@';
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,799 @@
<filters>
<filter>
<id>1</id>
<rule><![CDATA[(?:"[^"]*[^-]?>)|(?:[^\w\s]\s*\/>)|(?:>")]]></rule>
<description>finds html breaking injections including whitespace attacks</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>2</id>
<rule><![CDATA[(?:"+.*[<=]\s*"[^"]+")|(?:"\s*\w+\s*=)|(?:>\w=\/)|(?:#.+\)["\s]*>)|(?:"\s*(?:src|style|on\w+)\s*=\s*")|(?:[^"]?"[,;\s]+\w*[\[\(])]]></rule>
<description>finds attribute breaking injections including whitespace attacks</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>3</id>
<rule><![CDATA[(?:^>[\w\s]*<\/?\w{2,}>)]]></rule>
<description>finds unquoted attribute breaking injections</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>2</impact>
</filter>
<filter>
<id>4</id>
<rule><![CDATA[(?:[+\/]\s*name[\W\d]*[)+])|(?:;\W*url\s*=)|(?:[^\w\s\/?:>]\s*(?:location|referrer|name)\s*[^\/\w\s-])]]></rule>
<description>Detects url-, name-, JSON, and referrer-contained payload attacks</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>5</id>
<rule><![CDATA[(?:\W\s*hash\s*[^\w\s-])|(?:\w+=\W*[^,]*,[^\s(]\s*\()|(?:\?"[^\s"]":)|(?:(?<!\/)__[a-z]+__)|(?:(?:^|[\s)\]\}])(?:s|g)etter\s*=)]]></rule>
<description>Detects hash-contained xss payload attacks, setter usage and property overloading</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>6</id>
<rule><![CDATA[(?:with\s*\(\s*.+\s*\)\s*\w+\s*\()|(?:(?:do|while|for)\s*\([^)]*\)\s*\{)|(?:\/[\w\s]*\[\W*\w)]]></rule>
<description>Detects self contained xss via with(), common loops and regex to string conversion</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>7</id>
<rule><![CDATA[(?:[=(].+\?.+:)|(?:with\([^)]*\)\))|(?:\.\s*source\W)]]></rule>
<description>Detects JavaScript with(), ternary operators and XML predicate attacks</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>8</id>
<rule><![CDATA[(?:\/\w*\s*\)\s*\()|(?:\([\w\s]+\([\w\s]+\)[\w\s]+\))|(?:(?<!(?:mozilla\/\d\.\d\s))\([^)[]+\[[^\]]+\][^)]*\))|(?:[^\s!][{([][^({[]+[{([][^}\])]+[}\])][\s+",\d]*[}\])])|(?:"\)?\]\W*\[)|(?:=\s*[^\s:;]+\s*[{([][^}\])]+[}\])];)]]></rule>
<description>Detects self-executing JavaScript functions</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>9</id>
<rule><![CDATA[(?:\\u00[a-f0-9]{2})|(?:\\x0*[a-f0-9]{2})|(?:\\\d{2,3})]]></rule>
<description>Detects the IE octal, hex and unicode entities</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>2</impact>
</filter>
<filter>
<id>10</id>
<rule><![CDATA[(?:(?:\/|\\)?\.+(\/|\\)(?:\.+)?)|(?:\w+\.exe\??\s)|(?:;\s*\w+\s*\/[\w*-]+\/)|(?:\d\.\dx\|)|(?:%(?:c0\.|af\.|5c\.))|(?:\/(?:%2e){2})]]></rule>
<description>Detects basic directory traversal</description>
<tags>
<tag>dt</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>11</id>
<rule><![CDATA[(?:%c0%ae\/)|(?:(?:\/|\\)(home|conf|usr|etc|proc|opt|s?bin|local|dev|tmp|kern|[br]oot|sys|system|windows|winnt|program|%[a-z_-]{3,}%)(?:\/|\\))|(?:(?:\/|\\)inetpub|localstart\.asp|boot\.ini)]]></rule>
<description>Detects specific directory and path traversal</description>
<tags>
<tag>dt</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>12</id>
<rule><![CDATA[(?:etc\/\W*passwd)]]></rule>
<description>Detects etc/passwd inclusion attempts</description>
<tags>
<tag>dt</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>13</id>
<rule><![CDATA[(?:%u(?:ff|00|e\d)\w\w)|(?:(?:%(?:e\w|c[^3\W]|))(?:%\w\w)(?:%\w\w)?)]]></rule>
<description>Detects halfwidth/fullwidth encoded unicode HTML breaking attempts</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>3</impact>
</filter>
<filter>
<id>14</id>
<rule><![CDATA[(?:#@~\^\w+)|(?:\w+script:|@import[^\w]|;base64|base64,)|(?:\w+\s*\([\w\s]+,[\w\s]+,[\w\s]+,[\w\s]+,[\w\s]+,[\w\s]+\))]]></rule>
<description>Detects possible includes, VBSCript/JScript encodeed and packed functions</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>15</id>
<rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@\-\|])(\s*return\s*)?(?:create(?:element|attribute|textnode)|[a-z]+events?|setattribute|getelement\w+|appendchild|createrange|createcontextualfragment|removenode|parentnode|decodeuricomponent|\wettimeout|(?:ms)?setimmediate|option|useragent)(?(1)[^\w%"]|(?:\s*[^@\s\w%",.+\-]))]]></rule>
<description>Detects JavaScript DOM/miscellaneous properties and methods</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>16</id>
<rule><![CDATA[([^*\s\w,.\/?+-]\s*)?(?<![a-mo-z]\s)(?<![a-z\/_@])(\s*return\s*)?(?:alert|inputbox|showmod(?:al|eless)dialog|showhelp|infinity|isnan|isnull|iterator|msgbox|executeglobal|expression|prompt|write(?:ln)?|confirm|dialog|urn|(?:un)?eval|exec|execscript|tostring|status|execute|window|unescape|navigate|jquery|getscript|extend|prototype)(?(1)[^\w%"]|(?:\s*[^@\s\w%",.:\/+\-]))]]></rule>
<description>Detects possible includes and typical script methods</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>17</id>
<rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@])(\s*return\s*)?(?:hash|name|href|navigateandfind|source|pathname|close|constructor|port|protocol|assign|replace|back|forward|document|ownerdocument|window|top|this|self|parent|frames|_?content|date|cookie|innerhtml|innertext|csstext+?|outerhtml|print|moveby|resizeto|createstylesheet|stylesheets)(?(1)[^\w%"]|(?:\s*[^@\/\s\w%.+\-]))]]></rule>
<description>Detects JavaScript object properties and methods</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>18</id>
<rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@\-\|])(\s*return\s*)?(?:join|pop|push|reverse|reduce|concat|map|shift|sp?lice|sort|unshift)(?(1)[^\w%"]|(?:\s*[^@\s\w%,.+\-]))]]></rule>
<description>Detects JavaScript array properties and methods</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>19</id>
<rule><![CDATA[([^*:\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z\/_@\-\|])(\s*return\s*)?(?:set|atob|btoa|charat|charcodeat|charset|concat|crypto|frames|fromcharcode|indexof|lastindexof|match|navigator|toolbar|menubar|replace|regexp|slice|split|substr|substring|escape|\w+codeuri\w*)(?(1)[^\w%"]|(?:\s*[^@\s\w%,.+\-]))]]></rule>
<description>Detects JavaScript string properties and methods</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>20</id>
<rule><![CDATA[(?:\)\s*\[)|([^*":\s\w,.\/?+-]\s*)?(?<![a-z]\s)(?<![a-z_@\|])(\s*return\s*)?(?:globalstorage|sessionstorage|postmessage|callee|constructor|content|domain|prototype|try|catch|top|call|apply|url|function|object|array|string|math|if|for\s*(?:each)?|elseif|case|switch|regex|boolean|location|(?:ms)?setimmediate|settimeout|setinterval|void|setexpression|namespace|while)(?(1)[^\w%"]|(?:\s*[^@\s\w%".+\-\/]))]]></rule>
<description>Detects JavaScript language constructs</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>21</id>
<rule><![CDATA[(?:,\s*(?:alert|showmodaldialog|eval)\s*,)|(?::\s*eval\s*[^\s])|([^:\s\w,.\/?+-]\s*)?(?<![a-z\/_@])(\s*return\s*)?(?:(?:document\s*\.)?(?:.+\/)?(?:alert|eval|msgbox|showmod(?:al|eless)dialog|showhelp|prompt|write(?:ln)?|confirm|dialog|open))\s*(?:[^.a-z\s\-]|(?:\s*[^\s\w,.@\/+-]))|(?:java[\s\/]*\.[\s\/]*lang)|(?:\w\s*=\s*new\s+\w+)|(?:&\s*\w+\s*\)[^,])|(?:\+[\W\d]*new\s+\w+[\W\d]*\+)|(?:document\.\w)]]></rule>
<description>Detects very basic XSS probings</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>3</impact>
</filter>
<filter>
<id>22</id>
<rule><![CDATA[(?:=\s*(?:top|this|window|content|self|frames|_content))|(?:\/\s*[gimx]*\s*[)}])|(?:[^\s]\s*=\s*script)|(?:\.\s*constructor)|(?:default\s+xml\s+namespace\s*=)|(?:\/\s*\+[^+]+\s*\+\s*\/)]]></rule>
<description>Detects advanced XSS probings via Script(), RexExp, constructors and XML namespaces</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>23</id>
<rule><![CDATA[(?:\.\s*\w+\W*=)|(?:\W\s*(?:location|document)\s*\W[^({[;]+[({[;])|(?:\(\w+\?[:\w]+\))|(?:\w{2,}\s*=\s*\d+[^&\w]\w+)|(?:\]\s*\(\s*\w+)]]></rule>
<description>Detects JavaScript location/document property access and window access obfuscation</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>24</id>
<rule><![CDATA[(?:[".]script\s*\()|(?:\$\$?\s*\(\s*[\w"])|(?:\/[\w\s]+\/\.)|(?:=\s*\/\w+\/\s*\.)|(?:(?:this|window|top|parent|frames|self|content)\[\s*[(,"]*\s*[\w\$])|(?:,\s*new\s+\w+\s*[,;)])]]></rule>
<description>Detects basic obfuscated JavaScript script injections</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>25</id>
<rule><![CDATA[(?:=\s*[$\w]\s*[\(\[])|(?:\(\s*(?:this|top|window|self|parent|_?content)\s*\))|(?:src\s*=s*(?:\w+:|\/\/))|(?:\w+\[("\w+"|\w+\|\|))|(?:[\d\W]\|\|[\d\W]|\W=\w+,)|(?:\/\s*\+\s*[a-z"])|(?:=\s*\$[^([]*\()|(?:=\s*\(\s*")]]></rule>
<description>Detects obfuscated JavaScript script injections</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>26</id>
<rule><![CDATA[(?:[^:\s\w]+\s*[^\w\/](href|protocol|host|hostname|pathname|hash|port|cookie)[^\w])]]></rule>
<description>Detects JavaScript cookie stealing and redirection attempts</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>27</id>
<rule><![CDATA[(?:(?:vbs|vbscript|data):.*[,+])|(?:\w+\s*=\W*(?!https?)\w+:)|(jar:\w+:)|(=\s*"?\s*vbs(?:ript)?:)|(language\s*=\s?"?\s*vbs(?:ript)?)|on\w+\s*=\*\w+\-"?]]></rule>
<description>Detects data: URL injections, VBS injections and common URI schemes</description>
<tags>
<tag>xss</tag>
<tag>rfe</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>28</id>
<rule><![CDATA[(?:firefoxurl:\w+\|)|(?:(?:file|res|telnet|nntp|news|mailto|chrome)\s*:\s*[%&#xu\/]+)|(wyciwyg|firefoxurl\s*:\s*\/\s*\/)]]></rule>
<description>Detects IE firefoxurl injections, cache poisoning attempts and local file inclusion/execution</description>
<tags>
<tag>xss</tag>
<tag>rfe</tag>
<tag>lfi</tag>
<tag>csrf</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>29</id>
<rule><![CDATA[(?:binding\s?=|moz-binding|behavior\s?=)|(?:[\s\/]style\s*=\s*[-\\])]]></rule>
<description>Detects bindings and behavior injections</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>rfe</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>30</id>
<rule><![CDATA[(?:=\s*\w+\s*\+\s*")|(?:\+=\s*\(\s")|(?:!+\s*[\d.,]+\w?\d*\s*\?)|(?:=\s*\[s*\])|(?:"\s*\+\s*")|(?:[^\s]\[\s*\d+\s*\]\s*[;+])|(?:"\s*[&|]+\s*")|(?:\/\s*\?\s*")|(?:\/\s*\)\s*\[)|(?:\d\?.+:\d)|(?:]\s*\[\W*\w)|(?:[^\s]\s*=\s*\/)]]></rule>
<description>Detects common XSS concatenation patterns 1/2</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>31</id>
<rule><![CDATA[(?:=\s*\d*\.\d*\?\d*\.\d*)|(?:[|&]{2,}\s*")|(?:!\d+\.\d*\?")|(?:\/:[\w.]+,)|(?:=[\d\W\s]*\[[^]]+\])|(?:\?\w+:\w+)]]></rule>
<description>Detects common XSS concatenation patterns 2/2</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>32</id>
<rule><![CDATA[(?:[^\w\s=]on(?!g\&gt;)\w+[^=_+-]*=[^$]+(?:\W|\&gt;)?)]]></rule>
<description>Detects possible event handlers</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>33</id>
<rule><![CDATA[(?:\<\w*:?\s(?:[^\>]*)t(?!rong))|(?:\<scri)|(<\w+:\w+)]]></rule>
<description>Detects obfuscated script tags and XML wrapped HTML</description>
<tags>
<tag>xss</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>34</id>
<rule><![CDATA[(?:\<\/\w+\s\w+)|(?:@(?:cc_on|set)[\s@,"=])]]></rule>
<description>Detects attributes in closing tags and conditional compilation tokens</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>35</id>
<rule><![CDATA[(?:--[^\n]*$)|(?:\<!-|-->)|(?:[^*]\/\*|\*\/[^*])|(?:(?:[\W\d]#|--|{)$)|(?:\/{3,}.*$)|(?:<!\[\W)|(?:\]!>)]]></rule>
<description>Detects common comment types</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
</tags>
<impact>3</impact>
</filter>
<filter>
<id>37</id>
<rule><![CDATA[(?:\<base\s+)|(?:<!(?:element|entity|\[CDATA))]]></rule>
<description>Detects base href injections and XML entity injections</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>38</id>
<rule><![CDATA[(?:\<[\/]?(?:[i]?frame|applet|isindex|marquee|keygen|script|audio|video|input|button|textarea|style|base|body|meta|link|object|embed|param|plaintext|xm\w+|image|im(?:g|port)))]]></rule>
<description>Detects possibly malicious html elements including some attributes</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
<tag>lfi</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>39</id>
<rule><![CDATA[(?:\\x[01fe][\db-ce-f])|(?:%[01fe][\db-ce-f])|(?:&#[01fe][\db-ce-f])|(?:\\[01fe][\db-ce-f])|(?:&#x[01fe][\db-ce-f])]]></rule>
<description>Detects nullbytes and other dangerous characters</description>
<tags>
<tag>id</tag>
<tag>rfe</tag>
<tag>xss</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>40</id>
<rule><![CDATA[(?:\)\s*when\s*\d+\s*then)|(?:"\s*(?:#|--|{))|(?:\/\*!\s?\d+)|(?:ch(?:a)?r\s*\(\s*\d)|(?:(?:(n?and|x?or|not)\s+|\|\||\&\&)\s*\w+\()]]></rule>
<description>Detects MySQL comments, conditions and ch(a)r injections</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>41</id>
<rule><![CDATA[(?:[\s()]case\s*\()|(?:\)\s*like\s*\()|(?:having\s*[^\s]+\s*[^\w\s])|(?:if\s?\([\d\w]\s*[=<>~])]]></rule>
<description>Detects conditional SQL injection attempts</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>42</id>
<rule><![CDATA[(?:"\s*or\s*"?\d)|(?:\\x(?:23|27|3d))|(?:^.?"$)|(?:(?:^["\\]*(?:[\d"]+|[^"]+"))+\s*(?:n?and|x?or|not|\|\||\&\&)\s*[\w"[+&!@(),.-])|(?:[^\w\s]\w+\s*[|-]\s*"\s*\w)|(?:@\w+\s+(and|or)\s*["\d]+)|(?:@[\w-]+\s(and|or)\s*[^\w\s])|(?:[^\w\s:]\s*\d\W+[^\w\s]\s*".)|(?:\Winformation_schema|table_name\W)]]></rule>
<description>Detects classic SQL injection probings 1/2</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>43</id>
<rule><![CDATA[(?:"\s*\*.+(?:or|id)\W*"\d)|(?:\^")|(?:^[\w\s"-]+(?<=and\s)(?<=or\s)(?<=xor\s)(?<=nand\s)(?<=not\s)(?<=\|\|)(?<=\&\&)\w+\()|(?:"[\s\d]*[^\w\s]+\W*\d\W*.*["\d])|(?:"\s*[^\w\s?]+\s*[^\w\s]+\s*")|(?:"\s*[^\w\s]+\s*[\W\d].*(?:#|--))|(?:".*\*\s*\d)|(?:"\s*or\s[^\d]+[\w-]+.*\d)|(?:[()*<>%+-][\w-]+[^\w\s]+"[^,])]]></rule>
<description>Detects classic SQL injection probings 2/2</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>44</id>
<rule><![CDATA[(?:\d"\s+"\s+\d)|(?:^admin\s*"|(\/\*)+"+\s?(?:--|#|\/\*|{)?)|(?:"\s*or[\w\s-]+\s*[+<>=(),-]\s*[\d"])|(?:"\s*[^\w\s]?=\s*")|(?:"\W*[+=]+\W*")|(?:"\s*[!=|][\d\s!=+-]+.*["(].*$)|(?:"\s*[!=|][\d\s!=]+.*\d+$)|(?:"\s*like\W+[\w"(])|(?:\sis\s*0\W)|(?:where\s[\s\w\.,-]+\s=)|(?:"[<>~]+")]]></rule>
<description>Detects basic SQL authentication bypass attempts 1/3</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>45</id>
<rule><![CDATA[(?:union\s*(?:all|distinct|[(!@]*)?\s*[([]*\s*select)|(?:\w+\s+like\s+\")|(?:like\s*"\%)|(?:"\s*like\W*["\d])|(?:"\s*(?:n?and|x?or|not |\|\||\&\&)\s+[\s\w]+=\s*\w+\s*having)|(?:"\s*\*\s*\w+\W+")|(?:"\s*[^?\w\s=.,;)(]+\s*[(@"]*\s*\w+\W+\w)|(?:select\s*[\[\]()\s\w\.,"-]+from)|(?:find_in_set\s*\()]]></rule>
<description>Detects basic SQL authentication bypass attempts 2/3</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>46</id>
<rule><![CDATA[(?:in\s*\(+\s*select)|(?:(?:n?and|x?or|not |\|\||\&\&)\s+[\s\w+]+(?:regexp\s*\(|sounds\s+like\s*"|[=\d]+x))|("\s*\d\s*(?:--|#))|(?:"[%&<>^=]+\d\s*(=|or))|(?:"\W+[\w+-]+\s*=\s*\d\W+")|(?:"\s*is\s*\d.+"?\w)|(?:"\|?[\w-]{3,}[^\w\s.,]+")|(?:"\s*is\s*[\d.]+\s*\W.*")]]></rule>
<description>Detects basic SQL authentication bypass attempts 3/3</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>47</id>
<rule><![CDATA[(?:[\d\W]\s+as\s*["\w]+\s*from)|(?:^[\W\d]+\s*(?:union|select|create|rename|truncate|load|alter|delete|update|insert|desc))|(?:(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s+(?:(?:group_)concat|char|load_file)\s?\(?)|(?:end\s*\);)|("\s+regexp\W)|(?:[\s(]load_file\s*\()]]></rule>
<description>Detects concatenated basic SQL injection and SQLLFI attempts</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
<tag>lfi</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>48</id>
<rule><![CDATA[(?:@.+=\s*\(\s*select)|(?:\d+\s*or\s*\d+\s*[\-+])|(?:\/\w+;?\s+(?:having|and|or|select)\W)|(?:\d\s+group\s+by.+\()|(?:(?:;|#|--)\s*(?:drop|alter))|(?:(?:;|#|--)\s*(?:update|insert)\s*\w{2,})|(?:[^\w]SET\s*@\w+)|(?:(?:n?and|x?or|not |\|\||\&\&)[\s(]+\w+[\s)]*[!=+]+[\s\d]*["=()])]]></rule>
<description>Detects chained SQL injection attempts 1/2</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>49</id>
<rule><![CDATA[(?:"\s+and\s*=\W)|(?:\(\s*select\s*\w+\s*\()|(?:\*\/from)|(?:\+\s*\d+\s*\+\s*@)|(?:\w"\s*(?:[-+=|@]+\s*)+[\d(])|(?:coalesce\s*\(|@@\w+\s*[^\w\s])|(?:\W!+"\w)|(?:";\s*(?:if|while|begin))|(?:"[\s\d]+=\s*\d)|(?:order\s+by\s+if\w*\s*\()|(?:[\s(]+case\d*\W.+[tw]hen[\s(])]]></rule>
<description>Detects chained SQL injection attempts 2/2</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>50</id>
<rule><![CDATA[(?:(select|;)\s+(?:benchmark|if|sleep)\s*?\(\s*\(?\s*\w+)]]></rule>
<description>Detects SQL benchmark and sleep injection attempts including conditional queries</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>51</id>
<rule><![CDATA[(?:create\s+function\s+\w+\s+returns)|(?:;\s*(?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s*[\[(]?\w{2,})]]></rule>
<description>Detects MySQL UDF injection and other data/structure manipulation attempts</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>52</id>
<rule><![CDATA[(?:alter\s*\w+.*character\s+set\s+\w+)|(";\s*waitfor\s+time\s+")|(?:";.*:\s*goto)]]></rule>
<description>Detects MySQL charset switch and MSSQL DoS attempts</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>53</id>
<rule><![CDATA[(?:procedure\s+analyse\s*\()|(?:;\s*(declare|open)\s+[\w-]+)|(?:create\s+(procedure|function)\s*\w+\s*\(\s*\)\s*-)|(?:declare[^\w]+[@#]\s*\w+)|(exec\s*\(\s*@)]]></rule>
<description>Detects MySQL and PostgreSQL stored procedure/function injections</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>54</id>
<rule><![CDATA[(?:select\s*pg_sleep)|(?:waitfor\s*delay\s?"+\s?\d)|(?:;\s*shutdown\s*(?:;|--|#|\/\*|{))]]></rule>
<description>Detects Postgres pg_sleep injection, waitfor delay attacks and database shutdown attempts</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>55</id>
<rule><![CDATA[(?:\sexec\s+xp_cmdshell)|(?:"\s*!\s*["\w])|(?:from\W+information_schema\W)|(?:(?:(?:current_)?user|database|schema|connection_id)\s*\([^\)]*)|(?:";?\s*(?:select|union|having)\s*[^\s])|(?:\wiif\s*\()|(?:exec\s+master\.)|(?:union select @)|(?:union[\w(\s]*select)|(?:select.*\w?user\()|(?:into[\s+]+(?:dump|out)file\s*")]]></rule>
<description>Detects MSSQL code execution and information gathering attempts</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>56</id>
<rule><![CDATA[(?:merge.*using\s*\()|(execute\s*immediate\s*")|(?:\W+\d*\s*having\s*[^\s\-])|(?:match\s*[\w(),+-]+\s*against\s*\()]]></rule>
<description>Detects MATCH AGAINST, MERGE, EXECUTE IMMEDIATE and HAVING injections</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>57</id>
<rule><![CDATA[(?:,.*[)\da-f"]"(?:".*"|\Z|[^"]+))|(?:\Wselect.+\W*from)|((?:select|create|rename|truncate|load|alter|delete|update|insert|desc)\s*\(\s*space\s*\()]]></rule>
<description>Detects MySQL comment-/space-obfuscated injections and backtick termination</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>58</id>
<rule><![CDATA[(?:@[\w-]+\s*\()|(?:]\s*\(\s*["!]\s*\w)|(?:<[?%](?:php)?.*(?:[?%]>)?)|(?:;[\s\w|]*\$\w+\s*=)|(?:\$\w+\s*=(?:(?:\s*\$?\w+\s*[(;])|\s*".*"))|(?:;\s*\{\W*\w+\s*\()]]></rule>
<description>Detects code injection attempts 1/3</description>
<tags>
<tag>id</tag>
<tag>rfe</tag>
<tag>lfi</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>59</id>
<rule><![CDATA[(?:(?:[;]+|(<[?%](?:php)?)).*(?:define|eval|file_get_contents|include|require|require_once|set|shell_exec|phpinfo|system|passthru|preg_\w+|execute)\s*["(@])]]></rule>
<description>Detects code injection attempts 2/3</description>
<tags>
<tag>id</tag>
<tag>rfe</tag>
<tag>lfi</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>60</id>
<rule><![CDATA[(?:(?:[;]+|(<[?%](?:php)?)).*[^\w](?:echo|print|print_r|var_dump|[fp]open))|(?:;\s*rm\s+-\w+\s+)|(?:;.*{.*\$\w+\s*=)|(?:\$\w+\s*\[\]\s*=\s*)]]></rule>
<description>Detects code injection attempts 3/3</description>
<tags>
<tag>id</tag>
<tag>rfe</tag>
<tag>lfi</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>61</id>
<rule><![CDATA[(?:\w+]?(?<!href)(?<!src)(?<!longdesc)(?<!returnurl)=(?:https?|ftp):)|(?:\{\s*\$\s*\{)]]></rule>
<description>Detects url injections and RFE attempts</description>
<tags>
<tag>id</tag>
<tag>rfe</tag>
<tag>lfi</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>62</id>
<rule><![CDATA[(?:function[^(]*\([^)]*\))|(?:(?:delete|void|throw|instanceof|new|typeof)[^\w.]+\w+\s*[([])|([)\]]\s*\.\s*\w+\s*=)|(?:\(\s*new\s+\w+\s*\)\.)]]></rule>
<description>Detects common function declarations and special JS operators</description>
<tags>
<tag>id</tag>
<tag>rfe</tag>
<tag>lfi</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>63</id>
<rule><![CDATA[(?:[\w.-]+@[\w.-]+%(?:[01][\db-ce-f])+\w+:)]]></rule>
<description>Detects common mail header injections</description>
<tags>
<tag>id</tag>
<tag>spam</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>64</id>
<rule><![CDATA[(?:\.pl\?\w+=\w?\|\w+;)|(?:\|\(\w+=\*)|(?:\*\s*\)+\s*;)]]></rule>
<description>Detects perl echo shellcode injection and LDAP vectors</description>
<tags>
<tag>lfi</tag>
<tag>rfe</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>65</id>
<rule><![CDATA[(?:(^|\W)const\s+[\w\-]+\s*=)|(?:(?:do|for|while)\s*\([^;]+;+\))|(?:(?:^|\W)on\w+\s*=[\w\W]*(?:on\w+|alert|eval|print|confirm|prompt))|(?:groups=\d+\(\w+\))|(?:(.)\1{128,})]]></rule>
<description>Detects basic XSS DoS attempts</description>
<tags>
<tag>rfe</tag>
<tag>dos</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>67</id>
<rule><![CDATA[(?:\({2,}\+{2,}:{2,})|(?:\({2,}\+{2,}:+)|(?:\({3,}\++:{2,})|(?:\$\[!!!\])]]></rule>
<description>Detects unknown attack vectors based on PHPIDS Centrifuge detection</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
<tag>id</tag>
<tag>rfe</tag>
<tag>lfi</tag>
</tags>
<impact>7</impact>
</filter>
<filter>
<id>68</id>
<rule><![CDATA[(?:[\s\/"]+[-\w\/\\\*]+\s*=.+(?:\/\s*>))]]></rule>
<description>Finds attribute breaking injections including obfuscated attributes</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>69</id>
<rule><![CDATA[(?:(?:msgbox|eval)\s*\+|(?:language\s*=\*vbscript))]]></rule>
<description>Finds basic VBScript injection attempts</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>70</id>
<rule><![CDATA[(?:\[\$(?:ne|eq|lte?|gte?|n?in|mod|all|size|exists|type|slice|or)\])]]></rule>
<description>Finds basic MongoDB SQL injection attempts</description>
<tags>
<tag>sqli</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>71</id>
<rule><![CDATA[(?:[\s\d\/"]+(?:on\w+|style|poster|background)=[$"\w])|(?:-type\s*:\s*multipart)]]></rule>
<description>finds malicious attribute injection attempts and MHTML attacks</description>
<tags>
<tag>xss</tag>
<tag>csrf</tag>
</tags>
<impact>6</impact>
</filter>
<filter>
<id>72</id>
<rule><![CDATA[(?:(sleep\((\s*)(\d*)(\s*)\)|benchmark\((.*)\,(.*)\)))]]></rule>
<description>Detects blind sqli tests using sleep() or benchmark().</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>73</id>
<rule><![CDATA[(?i:(\%SYSTEMROOT\%))]]></rule>
<description>An attacker is trying to locate a file to read or write.</description>
<tags>
<tag>files</tag>
<tag>id</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>74</id>
<rule><![CDATA[(?i:(ping(.*)[\-(.*)\w|\w(.*)\-]))]]></rule>
<description>Detects remote code exectuion tests. Will match "ping -n 3 localhost" and "ping localhost -n 3" </description>
<tags>
<tag>Command Execution</tag>
<tag>id</tag>
</tags>
<impact>5</impact>
</filter>
<filter>
<id>75</id>
<rule><![CDATA[(?:(((.*)\%[c|d|i|e|f|g|o|s|u|x|p|n]){8}))]]></rule>
<description>Looking for a format string attack</description>
<tags>
<tag>format string</tag>
</tags>
<impact>4</impact>
</filter>
<filter>
<id>76</id>
<rule><![CDATA[(?:(union(.*)select(.*)from))]]></rule>
<description>Looking for basic sql injection. Common attack string for mysql, oracle and others.</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>3</impact>
</filter>
<filter>
<id>77</id>
<rule><![CDATA[(?:^(-0000023456|4294967295|4294967296|2147483648|2147483647|0000012345|-2147483648|-2147483649|0000023456|2.2250738585072007e-308|1e309)$)]]></rule>
<description>Looking for intiger overflow attacks, these are taken from skipfish, except 2.2250738585072007e-308 is the "magic number" crash</description>
<tags>
<tag>sqli</tag>
<tag>id</tag>
</tags>
<impact>3</impact>
</filter>
</filters>

View file

@ -0,0 +1,17 @@
"local/unknown",2008-08-18T18:00:29+02:00,36,"xss csrf id rfe lfi sqli","test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3Ftest%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E"
"local/unknown",2008-08-18T18:00:32+02:00,36,"xss csrf id rfe lfi sqli","test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3Ftest%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E"
"local/unknown",2008-08-18T18:00:46+02:00,36,"xss csrf id rfe lfi sqli","test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3Ftest%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E"
"local/unknown",2008-08-18T18:27:34+02:00,36,"xss csrf id rfe lfi sqli","test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3Ftest%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E"
"local/unknown",2009-06-24T12:58:37+02:00,36,"xss csrf id rfe lfi sqli","REQUEST.test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E GET.test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3Ftest%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E","127.0.0.1"
"local/unknown",2009-06-24T12:59:27+02:00,36,"xss csrf id rfe lfi sqli","REQUEST.test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E GET.test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3Ftest%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E","127.0.0.1"
"local/unknown",2009-06-24T12:59:29+02:00,36,"xss csrf id rfe lfi sqli","REQUEST.test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E GET.test=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3Ftest%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E","127.0.0.1"
"local/unknown",2009-06-24T12:59:42+02:00,36,"xss csrf id rfe lfi sqli","REQUEST.__wysiwyg=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E GET.__wysiwyg=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E","127.0.0.1"
"local/unknown",2009-06-24T13:00:01+02:00,36,"xss csrf id rfe lfi sqli","REQUEST.__wysiwyg=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E GET.__wysiwyg=%5C%22%3E%3Cscript%3Eeval%28window.name%29%3C%2Fscript%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%2522%253E%253Cscript%253Eeval%28window.name%29%253C%2Fscript%253E","127.0.0.1"
"local/unknown",2009-06-24T13:00:21+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E GET.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"
"local/unknown",2009-06-24T13:00:27+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwygx=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E GET.__wysiwygx=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwygx%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"
"local/unknown",2009-06-24T13:00:31+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E GET.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"
"local/unknown",2009-06-24T13:01:48+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E GET.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"
"local/unknown",2009-06-24T13:01:49+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E GET.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"
"local/unknown",2009-06-24T13:01:51+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E GET.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"
"local/unknown",2009-06-24T13:04:46+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"
"local/unknown",2009-06-24T13:05:48+02:00,15,"xss csrf sqli id lfi","REQUEST.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E GET.__wysiwyg=%3Ca%20href%3D%5C%22http%3A%2F%2Fwww.foo.de%2F%3Fbar%5C%22%3Efoobar%3C%2Fa%3E","%2Fphp-ids.org%2Ftrunk%2Fdocs%2Fexamples%2Fexample.php%3F__wysiwyg%3D%253Ca%2520href%3D%2522http%3A%2F%2Fwww.foo.de%2F%3Fbar%2522%253Efoobar%253C%2Fa%253E","127.0.0.1"

View file

@ -0,0 +1,11 @@
<?php
/**
* This is a stub include that automatically configures the include path.
*/
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
require_once 'HTMLPurifier/Bootstrap.php';
require_once 'HTMLPurifier.autoload.php';
// vim: et sw=4 sts=4

View file

@ -0,0 +1,26 @@
<?php
/**
* @file
* Convenience file that registers autoload handler for HTML Purifier.
* It also does some sanity checks.
*/
if (function_exists('spl_autoload_register') && function_exists('spl_autoload_unregister')) {
// We need unregister for our pre-registering functionality
HTMLPurifier_Bootstrap::registerAutoload();
if (function_exists('__autoload')) {
// Be polite and ensure that userland autoload gets retained
spl_autoload_register('__autoload');
}
} elseif (!function_exists('__autoload')) {
function __autoload($class) {
return HTMLPurifier_Bootstrap::autoload($class);
}
}
if (ini_get('zend.ze1_compatibility_mode')) {
trigger_error("HTML Purifier is not compatible with zend.ze1_compatibility_mode; please turn it off", E_USER_ERROR);
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,23 @@
<?php
/**
* @file
* Defines a function wrapper for HTML Purifier for quick use.
* @note ''HTMLPurifier()'' is NOT the same as ''new HTMLPurifier()''
*/
/**
* Purify HTML.
* @param $html String HTML to purify
* @param $config Configuration to use, can be any value accepted by
* HTMLPurifier_Config::create()
*/
function HTMLPurifier($html, $config = null) {
static $purifier = false;
if (!$purifier) {
$purifier = new HTMLPurifier();
}
return $purifier->purify($html, $config);
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,214 @@
<?php
/**
* @file
* This file was auto-generated by generate-includes.php and includes all of
* the core files required by HTML Purifier. Use this if performance is a
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run.
*
* @version 4.3.0
*
* @warning
* You must *not* include any other HTML Purifier files before this file,
* because 'require' not 'require_once' is used.
*
* @warning
* This file requires that the include path contains the HTML Purifier
* library directory; this is not auto-set.
*/
require 'HTMLPurifier.php';
require 'HTMLPurifier/AttrCollections.php';
require 'HTMLPurifier/AttrDef.php';
require 'HTMLPurifier/AttrTransform.php';
require 'HTMLPurifier/AttrTypes.php';
require 'HTMLPurifier/AttrValidator.php';
require 'HTMLPurifier/Bootstrap.php';
require 'HTMLPurifier/Definition.php';
require 'HTMLPurifier/CSSDefinition.php';
require 'HTMLPurifier/ChildDef.php';
require 'HTMLPurifier/Config.php';
require 'HTMLPurifier/ConfigSchema.php';
require 'HTMLPurifier/ContentSets.php';
require 'HTMLPurifier/Context.php';
require 'HTMLPurifier/DefinitionCache.php';
require 'HTMLPurifier/DefinitionCacheFactory.php';
require 'HTMLPurifier/Doctype.php';
require 'HTMLPurifier/DoctypeRegistry.php';
require 'HTMLPurifier/ElementDef.php';
require 'HTMLPurifier/Encoder.php';
require 'HTMLPurifier/EntityLookup.php';
require 'HTMLPurifier/EntityParser.php';
require 'HTMLPurifier/ErrorCollector.php';
require 'HTMLPurifier/ErrorStruct.php';
require 'HTMLPurifier/Exception.php';
require 'HTMLPurifier/Filter.php';
require 'HTMLPurifier/Generator.php';
require 'HTMLPurifier/HTMLDefinition.php';
require 'HTMLPurifier/HTMLModule.php';
require 'HTMLPurifier/HTMLModuleManager.php';
require 'HTMLPurifier/IDAccumulator.php';
require 'HTMLPurifier/Injector.php';
require 'HTMLPurifier/Language.php';
require 'HTMLPurifier/LanguageFactory.php';
require 'HTMLPurifier/Length.php';
require 'HTMLPurifier/Lexer.php';
require 'HTMLPurifier/PercentEncoder.php';
require 'HTMLPurifier/PropertyList.php';
require 'HTMLPurifier/PropertyListIterator.php';
require 'HTMLPurifier/Strategy.php';
require 'HTMLPurifier/StringHash.php';
require 'HTMLPurifier/StringHashParser.php';
require 'HTMLPurifier/TagTransform.php';
require 'HTMLPurifier/Token.php';
require 'HTMLPurifier/TokenFactory.php';
require 'HTMLPurifier/URI.php';
require 'HTMLPurifier/URIDefinition.php';
require 'HTMLPurifier/URIFilter.php';
require 'HTMLPurifier/URIParser.php';
require 'HTMLPurifier/URIScheme.php';
require 'HTMLPurifier/URISchemeRegistry.php';
require 'HTMLPurifier/UnitConverter.php';
require 'HTMLPurifier/VarParser.php';
require 'HTMLPurifier/VarParserException.php';
require 'HTMLPurifier/AttrDef/CSS.php';
require 'HTMLPurifier/AttrDef/Enum.php';
require 'HTMLPurifier/AttrDef/Integer.php';
require 'HTMLPurifier/AttrDef/Lang.php';
require 'HTMLPurifier/AttrDef/Switch.php';
require 'HTMLPurifier/AttrDef/Text.php';
require 'HTMLPurifier/AttrDef/URI.php';
require 'HTMLPurifier/AttrDef/CSS/Number.php';
require 'HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require 'HTMLPurifier/AttrDef/CSS/Background.php';
require 'HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require 'HTMLPurifier/AttrDef/CSS/Border.php';
require 'HTMLPurifier/AttrDef/CSS/Color.php';
require 'HTMLPurifier/AttrDef/CSS/Composite.php';
require 'HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Filter.php';
require 'HTMLPurifier/AttrDef/CSS/Font.php';
require 'HTMLPurifier/AttrDef/CSS/FontFamily.php';
require 'HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
require 'HTMLPurifier/AttrDef/CSS/Length.php';
require 'HTMLPurifier/AttrDef/CSS/ListStyle.php';
require 'HTMLPurifier/AttrDef/CSS/Multiple.php';
require 'HTMLPurifier/AttrDef/CSS/Percentage.php';
require 'HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require 'HTMLPurifier/AttrDef/CSS/URI.php';
require 'HTMLPurifier/AttrDef/HTML/Bool.php';
require 'HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require 'HTMLPurifier/AttrDef/HTML/Class.php';
require 'HTMLPurifier/AttrDef/HTML/Color.php';
require 'HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require 'HTMLPurifier/AttrDef/HTML/ID.php';
require 'HTMLPurifier/AttrDef/HTML/Pixels.php';
require 'HTMLPurifier/AttrDef/HTML/Length.php';
require 'HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require 'HTMLPurifier/AttrDef/HTML/MultiLength.php';
require 'HTMLPurifier/AttrDef/URI/Email.php';
require 'HTMLPurifier/AttrDef/URI/Host.php';
require 'HTMLPurifier/AttrDef/URI/IPv4.php';
require 'HTMLPurifier/AttrDef/URI/IPv6.php';
require 'HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
require 'HTMLPurifier/AttrTransform/Background.php';
require 'HTMLPurifier/AttrTransform/BdoDir.php';
require 'HTMLPurifier/AttrTransform/BgColor.php';
require 'HTMLPurifier/AttrTransform/BoolToCSS.php';
require 'HTMLPurifier/AttrTransform/Border.php';
require 'HTMLPurifier/AttrTransform/EnumToCSS.php';
require 'HTMLPurifier/AttrTransform/ImgRequired.php';
require 'HTMLPurifier/AttrTransform/ImgSpace.php';
require 'HTMLPurifier/AttrTransform/Input.php';
require 'HTMLPurifier/AttrTransform/Lang.php';
require 'HTMLPurifier/AttrTransform/Length.php';
require 'HTMLPurifier/AttrTransform/Name.php';
require 'HTMLPurifier/AttrTransform/NameSync.php';
require 'HTMLPurifier/AttrTransform/Nofollow.php';
require 'HTMLPurifier/AttrTransform/SafeEmbed.php';
require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/AttrTransform/Textarea.php';
require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php';
require 'HTMLPurifier/ChildDef/Empty.php';
require 'HTMLPurifier/ChildDef/Required.php';
require 'HTMLPurifier/ChildDef/Optional.php';
require 'HTMLPurifier/ChildDef/StrictBlockquote.php';
require 'HTMLPurifier/ChildDef/Table.php';
require 'HTMLPurifier/DefinitionCache/Decorator.php';
require 'HTMLPurifier/DefinitionCache/Null.php';
require 'HTMLPurifier/DefinitionCache/Serializer.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
require 'HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require 'HTMLPurifier/HTMLModule/Bdo.php';
require 'HTMLPurifier/HTMLModule/CommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Edit.php';
require 'HTMLPurifier/HTMLModule/Forms.php';
require 'HTMLPurifier/HTMLModule/Hypertext.php';
require 'HTMLPurifier/HTMLModule/Image.php';
require 'HTMLPurifier/HTMLModule/Legacy.php';
require 'HTMLPurifier/HTMLModule/List.php';
require 'HTMLPurifier/HTMLModule/Name.php';
require 'HTMLPurifier/HTMLModule/Nofollow.php';
require 'HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Object.php';
require 'HTMLPurifier/HTMLModule/Presentation.php';
require 'HTMLPurifier/HTMLModule/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Ruby.php';
require 'HTMLPurifier/HTMLModule/SafeEmbed.php';
require 'HTMLPurifier/HTMLModule/SafeObject.php';
require 'HTMLPurifier/HTMLModule/Scripting.php';
require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
require 'HTMLPurifier/HTMLModule/Tables.php';
require 'HTMLPurifier/HTMLModule/Target.php';
require 'HTMLPurifier/HTMLModule/Text.php';
require 'HTMLPurifier/HTMLModule/Tidy.php';
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
require 'HTMLPurifier/HTMLModule/Tidy/Name.php';
require 'HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
require 'HTMLPurifier/HTMLModule/Tidy/Strict.php';
require 'HTMLPurifier/HTMLModule/Tidy/Transitional.php';
require 'HTMLPurifier/HTMLModule/Tidy/XHTML.php';
require 'HTMLPurifier/Injector/AutoParagraph.php';
require 'HTMLPurifier/Injector/DisplayLinkURI.php';
require 'HTMLPurifier/Injector/Linkify.php';
require 'HTMLPurifier/Injector/PurifierLinkify.php';
require 'HTMLPurifier/Injector/RemoveEmpty.php';
require 'HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
require 'HTMLPurifier/Injector/SafeObject.php';
require 'HTMLPurifier/Lexer/DOMLex.php';
require 'HTMLPurifier/Lexer/DirectLex.php';
require 'HTMLPurifier/Strategy/Composite.php';
require 'HTMLPurifier/Strategy/Core.php';
require 'HTMLPurifier/Strategy/FixNesting.php';
require 'HTMLPurifier/Strategy/MakeWellFormed.php';
require 'HTMLPurifier/Strategy/RemoveForeignElements.php';
require 'HTMLPurifier/Strategy/ValidateAttributes.php';
require 'HTMLPurifier/TagTransform/Font.php';
require 'HTMLPurifier/TagTransform/Simple.php';
require 'HTMLPurifier/Token/Comment.php';
require 'HTMLPurifier/Token/Tag.php';
require 'HTMLPurifier/Token/Empty.php';
require 'HTMLPurifier/Token/End.php';
require 'HTMLPurifier/Token/Start.php';
require 'HTMLPurifier/Token/Text.php';
require 'HTMLPurifier/URIFilter/DisableExternal.php';
require 'HTMLPurifier/URIFilter/DisableExternalResources.php';
require 'HTMLPurifier/URIFilter/DisableResources.php';
require 'HTMLPurifier/URIFilter/HostBlacklist.php';
require 'HTMLPurifier/URIFilter/MakeAbsolute.php';
require 'HTMLPurifier/URIFilter/Munge.php';
require 'HTMLPurifier/URIScheme/data.php';
require 'HTMLPurifier/URIScheme/file.php';
require 'HTMLPurifier/URIScheme/ftp.php';
require 'HTMLPurifier/URIScheme/http.php';
require 'HTMLPurifier/URIScheme/https.php';
require 'HTMLPurifier/URIScheme/mailto.php';
require 'HTMLPurifier/URIScheme/news.php';
require 'HTMLPurifier/URIScheme/nntp.php';
require 'HTMLPurifier/VarParser/Flexible.php';
require 'HTMLPurifier/VarParser/Native.php';

View file

@ -0,0 +1,30 @@
<?php
/**
* @file
* Emulation layer for code that used kses(), substituting in HTML Purifier.
*/
require_once dirname(__FILE__) . '/HTMLPurifier.auto.php';
function kses($string, $allowed_html, $allowed_protocols = null) {
$config = HTMLPurifier_Config::createDefault();
$allowed_elements = array();
$allowed_attributes = array();
foreach ($allowed_html as $element => $attributes) {
$allowed_elements[$element] = true;
foreach ($attributes as $attribute => $x) {
$allowed_attributes["$element.$attribute"] = true;
}
}
$config->set('HTML.AllowedElements', $allowed_elements);
$config->set('HTML.AllowedAttributes', $allowed_attributes);
$allowed_schemes = array();
if ($allowed_protocols !== null) {
$config->set('URI.AllowedSchemes', $allowed_protocols);
}
$purifier = new HTMLPurifier($config);
return $purifier->purify($string);
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,11 @@
<?php
/**
* @file
* Convenience stub file that adds HTML Purifier's library file to the path
* without any other side-effects.
*/
set_include_path(dirname(__FILE__) . PATH_SEPARATOR . get_include_path() );
// vim: et sw=4 sts=4

View file

@ -0,0 +1,237 @@
<?php
/*! @mainpage
*
* HTML Purifier is an HTML filter that will take an arbitrary snippet of
* HTML and rigorously test, validate and filter it into a version that
* is safe for output onto webpages. It achieves this by:
*
* -# Lexing (parsing into tokens) the document,
* -# Executing various strategies on the tokens:
* -# Removing all elements not in the whitelist,
* -# Making the tokens well-formed,
* -# Fixing the nesting of the nodes, and
* -# Validating attributes of the nodes; and
* -# Generating HTML from the purified tokens.
*
* However, most users will only need to interface with the HTMLPurifier
* and HTMLPurifier_Config.
*/
/*
HTML Purifier 4.3.0 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/**
* Facade that coordinates HTML Purifier's subsystems in order to purify HTML.
*
* @note There are several points in which configuration can be specified
* for HTML Purifier. The precedence of these (from lowest to
* highest) is as follows:
* -# Instance: new HTMLPurifier($config)
* -# Invocation: purify($html, $config)
* These configurations are entirely independent of each other and
* are *not* merged (this behavior may change in the future).
*
* @todo We need an easier way to inject strategies using the configuration
* object.
*/
class HTMLPurifier
{
/** Version of HTML Purifier */
public $version = '4.3.0';
/** Constant with version of HTML Purifier */
const VERSION = '4.3.0';
/** Global configuration object */
public $config;
/** Array of extra HTMLPurifier_Filter objects to run on HTML, for backwards compatibility */
private $filters = array();
/** Single instance of HTML Purifier */
private static $instance;
protected $strategy, $generator;
/**
* Resultant HTMLPurifier_Context of last run purification. Is an array
* of contexts if the last called method was purifyArray().
*/
public $context;
/**
* Initializes the purifier.
* @param $config Optional HTMLPurifier_Config object for all instances of
* the purifier, if omitted, a default configuration is
* supplied (which can be overridden on a per-use basis).
* The parameter can also be any type that
* HTMLPurifier_Config::create() supports.
*/
public function __construct($config = null) {
$this->config = HTMLPurifier_Config::create($config);
$this->strategy = new HTMLPurifier_Strategy_Core();
}
/**
* Adds a filter to process the output. First come first serve
* @param $filter HTMLPurifier_Filter object
*/
public function addFilter($filter) {
trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING);
$this->filters[] = $filter;
}
/**
* Filters an HTML snippet/document to be XSS-free and standards-compliant.
*
* @param $html String of HTML to purify
* @param $config HTMLPurifier_Config object for this operation, if omitted,
* defaults to the config object specified during this
* object's construction. The parameter can also be any type
* that HTMLPurifier_Config::create() supports.
* @return Purified HTML
*/
public function purify($html, $config = null) {
// :TODO: make the config merge in, instead of replace
$config = $config ? HTMLPurifier_Config::create($config) : $this->config;
// implementation is partially environment dependant, partially
// configuration dependant
$lexer = HTMLPurifier_Lexer::create($config);
$context = new HTMLPurifier_Context();
// setup HTML generator
$this->generator = new HTMLPurifier_Generator($config, $context);
$context->register('Generator', $this->generator);
// set up global context variables
if ($config->get('Core.CollectErrors')) {
// may get moved out if other facilities use it
$language_factory = HTMLPurifier_LanguageFactory::instance();
$language = $language_factory->create($config, $context);
$context->register('Locale', $language);
$error_collector = new HTMLPurifier_ErrorCollector($context);
$context->register('ErrorCollector', $error_collector);
}
// setup id_accumulator context, necessary due to the fact that
// AttrValidator can be called from many places
$id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context);
$context->register('IDAccumulator', $id_accumulator);
$html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context);
// setup filters
$filter_flags = $config->getBatch('Filter');
$custom_filters = $filter_flags['Custom'];
unset($filter_flags['Custom']);
$filters = array();
foreach ($filter_flags as $filter => $flag) {
if (!$flag) continue;
if (strpos($filter, '.') !== false) continue;
$class = "HTMLPurifier_Filter_$filter";
$filters[] = new $class;
}
foreach ($custom_filters as $filter) {
// maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat
$filters[] = $filter;
}
$filters = array_merge($filters, $this->filters);
// maybe prepare(), but later
for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) {
$html = $filters[$i]->preFilter($html, $config, $context);
}
// purified HTML
$html =
$this->generator->generateFromTokens(
// list of tokens
$this->strategy->execute(
// list of un-purified tokens
$lexer->tokenizeHTML(
// un-purified HTML
$html, $config, $context
),
$config, $context
)
);
for ($i = $filter_size - 1; $i >= 0; $i--) {
$html = $filters[$i]->postFilter($html, $config, $context);
}
$html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context);
$this->context =& $context;
return $html;
}
/**
* Filters an array of HTML snippets
* @param $config Optional HTMLPurifier_Config object for this operation.
* See HTMLPurifier::purify() for more details.
* @return Array of purified HTML
*/
public function purifyArray($array_of_html, $config = null) {
$context_array = array();
foreach ($array_of_html as $key => $html) {
$array_of_html[$key] = $this->purify($html, $config);
$context_array[$key] = $this->context;
}
$this->context = $context_array;
return $array_of_html;
}
/**
* Singleton for enforcing just one HTML Purifier in your system
* @param $prototype Optional prototype HTMLPurifier instance to
* overload singleton with, or HTMLPurifier_Config
* instance to configure the generated version with.
*/
public static function instance($prototype = null) {
if (!self::$instance || $prototype) {
if ($prototype instanceof HTMLPurifier) {
self::$instance = $prototype;
} elseif ($prototype) {
self::$instance = new HTMLPurifier($prototype);
} else {
self::$instance = new HTMLPurifier();
}
}
return self::$instance;
}
/**
* @note Backwards compatibility, see instance()
*/
public static function getInstance($prototype = null) {
return HTMLPurifier::instance($prototype);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,208 @@
<?php
/**
* @file
* This file was auto-generated by generate-includes.php and includes all of
* the core files required by HTML Purifier. This is a convenience stub that
* includes all files using dirname(__FILE__) and require_once. PLEASE DO NOT
* EDIT THIS FILE, changes will be overwritten the next time the script is run.
*
* Changes to include_path are not necessary.
*/
$__dir = dirname(__FILE__);
require_once $__dir . '/HTMLPurifier.php';
require_once $__dir . '/HTMLPurifier/AttrCollections.php';
require_once $__dir . '/HTMLPurifier/AttrDef.php';
require_once $__dir . '/HTMLPurifier/AttrTransform.php';
require_once $__dir . '/HTMLPurifier/AttrTypes.php';
require_once $__dir . '/HTMLPurifier/AttrValidator.php';
require_once $__dir . '/HTMLPurifier/Bootstrap.php';
require_once $__dir . '/HTMLPurifier/Definition.php';
require_once $__dir . '/HTMLPurifier/CSSDefinition.php';
require_once $__dir . '/HTMLPurifier/ChildDef.php';
require_once $__dir . '/HTMLPurifier/Config.php';
require_once $__dir . '/HTMLPurifier/ConfigSchema.php';
require_once $__dir . '/HTMLPurifier/ContentSets.php';
require_once $__dir . '/HTMLPurifier/Context.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache.php';
require_once $__dir . '/HTMLPurifier/DefinitionCacheFactory.php';
require_once $__dir . '/HTMLPurifier/Doctype.php';
require_once $__dir . '/HTMLPurifier/DoctypeRegistry.php';
require_once $__dir . '/HTMLPurifier/ElementDef.php';
require_once $__dir . '/HTMLPurifier/Encoder.php';
require_once $__dir . '/HTMLPurifier/EntityLookup.php';
require_once $__dir . '/HTMLPurifier/EntityParser.php';
require_once $__dir . '/HTMLPurifier/ErrorCollector.php';
require_once $__dir . '/HTMLPurifier/ErrorStruct.php';
require_once $__dir . '/HTMLPurifier/Exception.php';
require_once $__dir . '/HTMLPurifier/Filter.php';
require_once $__dir . '/HTMLPurifier/Generator.php';
require_once $__dir . '/HTMLPurifier/HTMLDefinition.php';
require_once $__dir . '/HTMLPurifier/HTMLModule.php';
require_once $__dir . '/HTMLPurifier/HTMLModuleManager.php';
require_once $__dir . '/HTMLPurifier/IDAccumulator.php';
require_once $__dir . '/HTMLPurifier/Injector.php';
require_once $__dir . '/HTMLPurifier/Language.php';
require_once $__dir . '/HTMLPurifier/LanguageFactory.php';
require_once $__dir . '/HTMLPurifier/Length.php';
require_once $__dir . '/HTMLPurifier/Lexer.php';
require_once $__dir . '/HTMLPurifier/PercentEncoder.php';
require_once $__dir . '/HTMLPurifier/PropertyList.php';
require_once $__dir . '/HTMLPurifier/PropertyListIterator.php';
require_once $__dir . '/HTMLPurifier/Strategy.php';
require_once $__dir . '/HTMLPurifier/StringHash.php';
require_once $__dir . '/HTMLPurifier/StringHashParser.php';
require_once $__dir . '/HTMLPurifier/TagTransform.php';
require_once $__dir . '/HTMLPurifier/Token.php';
require_once $__dir . '/HTMLPurifier/TokenFactory.php';
require_once $__dir . '/HTMLPurifier/URI.php';
require_once $__dir . '/HTMLPurifier/URIDefinition.php';
require_once $__dir . '/HTMLPurifier/URIFilter.php';
require_once $__dir . '/HTMLPurifier/URIParser.php';
require_once $__dir . '/HTMLPurifier/URIScheme.php';
require_once $__dir . '/HTMLPurifier/URISchemeRegistry.php';
require_once $__dir . '/HTMLPurifier/UnitConverter.php';
require_once $__dir . '/HTMLPurifier/VarParser.php';
require_once $__dir . '/HTMLPurifier/VarParserException.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Enum.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Integer.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Switch.php';
require_once $__dir . '/HTMLPurifier/AttrDef/Text.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Number.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/AlphaValue.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Background.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Border.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Composite.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Filter.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Font.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/FontFamily.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/ListStyle.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Multiple.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/Percentage.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/TextDecoration.php';
require_once $__dir . '/HTMLPurifier/AttrDef/CSS/URI.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Bool.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Nmtokens.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Class.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Color.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/FrameTarget.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/ID.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Pixels.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/Length.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/LinkTypes.php';
require_once $__dir . '/HTMLPurifier/AttrDef/HTML/MultiLength.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Host.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv4.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/IPv6.php';
require_once $__dir . '/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Background.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BdoDir.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BgColor.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/BoolToCSS.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Border.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/EnumToCSS.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ImgSpace.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Input.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Lang.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Length.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Name.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/NameSync.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Nofollow.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Empty.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Required.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Optional.php';
require_once $__dir . '/HTMLPurifier/ChildDef/StrictBlockquote.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Table.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Null.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Serializer.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php';
require_once $__dir . '/HTMLPurifier/DefinitionCache/Decorator/Memory.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Bdo.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/CommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Edit.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Forms.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Hypertext.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Image.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Legacy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/List.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Name.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Nofollow.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Object.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Presentation.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Proprietary.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Ruby.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeEmbed.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/SafeObject.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Scripting.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Name.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Proprietary.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Strict.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/Transitional.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy/XHTML.php';
require_once $__dir . '/HTMLPurifier/Injector/AutoParagraph.php';
require_once $__dir . '/HTMLPurifier/Injector/DisplayLinkURI.php';
require_once $__dir . '/HTMLPurifier/Injector/Linkify.php';
require_once $__dir . '/HTMLPurifier/Injector/PurifierLinkify.php';
require_once $__dir . '/HTMLPurifier/Injector/RemoveEmpty.php';
require_once $__dir . '/HTMLPurifier/Injector/RemoveSpansWithoutAttributes.php';
require_once $__dir . '/HTMLPurifier/Injector/SafeObject.php';
require_once $__dir . '/HTMLPurifier/Lexer/DOMLex.php';
require_once $__dir . '/HTMLPurifier/Lexer/DirectLex.php';
require_once $__dir . '/HTMLPurifier/Strategy/Composite.php';
require_once $__dir . '/HTMLPurifier/Strategy/Core.php';
require_once $__dir . '/HTMLPurifier/Strategy/FixNesting.php';
require_once $__dir . '/HTMLPurifier/Strategy/MakeWellFormed.php';
require_once $__dir . '/HTMLPurifier/Strategy/RemoveForeignElements.php';
require_once $__dir . '/HTMLPurifier/Strategy/ValidateAttributes.php';
require_once $__dir . '/HTMLPurifier/TagTransform/Font.php';
require_once $__dir . '/HTMLPurifier/TagTransform/Simple.php';
require_once $__dir . '/HTMLPurifier/Token/Comment.php';
require_once $__dir . '/HTMLPurifier/Token/Tag.php';
require_once $__dir . '/HTMLPurifier/Token/Empty.php';
require_once $__dir . '/HTMLPurifier/Token/End.php';
require_once $__dir . '/HTMLPurifier/Token/Start.php';
require_once $__dir . '/HTMLPurifier/Token/Text.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternal.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableExternalResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/DisableResources.php';
require_once $__dir . '/HTMLPurifier/URIFilter/HostBlacklist.php';
require_once $__dir . '/HTMLPurifier/URIFilter/MakeAbsolute.php';
require_once $__dir . '/HTMLPurifier/URIFilter/Munge.php';
require_once $__dir . '/HTMLPurifier/URIScheme/data.php';
require_once $__dir . '/HTMLPurifier/URIScheme/file.php';
require_once $__dir . '/HTMLPurifier/URIScheme/ftp.php';
require_once $__dir . '/HTMLPurifier/URIScheme/http.php';
require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';

View file

@ -0,0 +1,128 @@
<?php
/**
* Defines common attribute collections that modules reference
*/
class HTMLPurifier_AttrCollections
{
/**
* Associative array of attribute collections, indexed by name
*/
public $info = array();
/**
* Performs all expansions on internal data for use by other inclusions
* It also collects all attribute collection extensions from
* modules
* @param $attr_types HTMLPurifier_AttrTypes instance
* @param $modules Hash array of HTMLPurifier_HTMLModule members
*/
public function __construct($attr_types, $modules) {
// load extensions from the modules
foreach ($modules as $module) {
foreach ($module->attr_collections as $coll_i => $coll) {
if (!isset($this->info[$coll_i])) {
$this->info[$coll_i] = array();
}
foreach ($coll as $attr_i => $attr) {
if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) {
// merge in includes
$this->info[$coll_i][$attr_i] = array_merge(
$this->info[$coll_i][$attr_i], $attr);
continue;
}
$this->info[$coll_i][$attr_i] = $attr;
}
}
}
// perform internal expansions and inclusions
foreach ($this->info as $name => $attr) {
// merge attribute collections that include others
$this->performInclusions($this->info[$name]);
// replace string identifiers with actual attribute objects
$this->expandIdentifiers($this->info[$name], $attr_types);
}
}
/**
* Takes a reference to an attribute associative array and performs
* all inclusions specified by the zero index.
* @param &$attr Reference to attribute array
*/
public function performInclusions(&$attr) {
if (!isset($attr[0])) return;
$merge = $attr[0];
$seen = array(); // recursion guard
// loop through all the inclusions
for ($i = 0; isset($merge[$i]); $i++) {
if (isset($seen[$merge[$i]])) continue;
$seen[$merge[$i]] = true;
// foreach attribute of the inclusion, copy it over
if (!isset($this->info[$merge[$i]])) continue;
foreach ($this->info[$merge[$i]] as $key => $value) {
if (isset($attr[$key])) continue; // also catches more inclusions
$attr[$key] = $value;
}
if (isset($this->info[$merge[$i]][0])) {
// recursion
$merge = array_merge($merge, $this->info[$merge[$i]][0]);
}
}
unset($attr[0]);
}
/**
* Expands all string identifiers in an attribute array by replacing
* them with the appropriate values inside HTMLPurifier_AttrTypes
* @param &$attr Reference to attribute array
* @param $attr_types HTMLPurifier_AttrTypes instance
*/
public function expandIdentifiers(&$attr, $attr_types) {
// because foreach will process new elements we add, make sure we
// skip duplicates
$processed = array();
foreach ($attr as $def_i => $def) {
// skip inclusions
if ($def_i === 0) continue;
if (isset($processed[$def_i])) continue;
// determine whether or not attribute is required
if ($required = (strpos($def_i, '*') !== false)) {
// rename the definition
unset($attr[$def_i]);
$def_i = trim($def_i, '*');
$attr[$def_i] = $def;
}
$processed[$def_i] = true;
// if we've already got a literal object, move on
if (is_object($def)) {
// preserve previous required
$attr[$def_i]->required = ($required || $attr[$def_i]->required);
continue;
}
if ($def === false) {
unset($attr[$def_i]);
continue;
}
if ($t = $attr_types->get($def)) {
$attr[$def_i] = $t;
$attr[$def_i]->required = $required;
} else {
unset($attr[$def_i]);
}
}
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,123 @@
<?php
/**
* Base class for all validating attribute definitions.
*
* This family of classes forms the core for not only HTML attribute validation,
* but also any sort of string that needs to be validated or cleaned (which
* means CSS properties and composite definitions are defined here too).
* Besides defining (through code) what precisely makes the string valid,
* subclasses are also responsible for cleaning the code if possible.
*/
abstract class HTMLPurifier_AttrDef
{
/**
* Tells us whether or not an HTML attribute is minimized. Has no
* meaning in other contexts.
*/
public $minimized = false;
/**
* Tells us whether or not an HTML attribute is required. Has no
* meaning in other contexts
*/
public $required = false;
/**
* Validates and cleans passed string according to a definition.
*
* @param $string String to be validated and cleaned.
* @param $config Mandatory HTMLPurifier_Config object.
* @param $context Mandatory HTMLPurifier_AttrContext object.
*/
abstract public function validate($string, $config, $context);
/**
* Convenience method that parses a string as if it were CDATA.
*
* This method process a string in the manner specified at
* <http://www.w3.org/TR/html4/types.html#h-6.2> by removing
* leading and trailing whitespace, ignoring line feeds, and replacing
* carriage returns and tabs with spaces. While most useful for HTML
* attributes specified as CDATA, it can also be applied to most CSS
* values.
*
* @note This method is not entirely standards compliant, as trim() removes
* more types of whitespace than specified in the spec. In practice,
* this is rarely a problem, as those extra characters usually have
* already been removed by HTMLPurifier_Encoder.
*
* @warning This processing is inconsistent with XML's whitespace handling
* as specified by section 3.3.3 and referenced XHTML 1.0 section
* 4.7. However, note that we are NOT necessarily
* parsing XML, thus, this behavior may still be correct. We
* assume that newlines have been normalized.
*/
public function parseCDATA($string) {
$string = trim($string);
$string = str_replace(array("\n", "\t", "\r"), ' ', $string);
return $string;
}
/**
* Factory method for creating this class from a string.
* @param $string String construction info
* @return Created AttrDef object corresponding to $string
*/
public function make($string) {
// default implementation, return a flyweight of this object.
// If $string has an effect on the returned object (i.e. you
// need to overload this method), it is best
// to clone or instantiate new copies. (Instantiation is safer.)
return $this;
}
/**
* Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work
* properly. THIS IS A HACK!
*/
protected function mungeRgb($string) {
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
}
/**
* Parses a possibly escaped CSS string and returns the "pure"
* version of it.
*/
protected function expandCSSEscape($string) {
// flexibly parse it
$ret = '';
for ($i = 0, $c = strlen($string); $i < $c; $i++) {
if ($string[$i] === '\\') {
$i++;
if ($i >= $c) {
$ret .= '\\';
break;
}
if (ctype_xdigit($string[$i])) {
$code = $string[$i];
for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) {
if (!ctype_xdigit($string[$i])) break;
$code .= $string[$i];
}
// We have to be extremely careful when adding
// new characters, to make sure we're not breaking
// the encoding.
$char = HTMLPurifier_Encoder::unichr(hexdec($code));
if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue;
$ret .= $char;
if ($i < $c && trim($string[$i]) !== '') $i--;
continue;
}
if ($string[$i] === "\n") continue;
}
$ret .= $string[$i];
}
return $ret;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,87 @@
<?php
/**
* Validates the HTML attribute style, otherwise known as CSS.
* @note We don't implement the whole CSS specification, so it might be
* difficult to reuse this component in the context of validating
* actual stylesheet declarations.
* @note If we were really serious about validating the CSS, we would
* tokenize the styles and then parse the tokens. Obviously, we
* are not doing that. Doing that could seriously harm performance,
* but would make these components a lot more viable for a CSS
* filtering solution.
*/
class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
{
public function validate($css, $config, $context) {
$css = $this->parseCDATA($css);
$definition = $config->getCSSDefinition();
// we're going to break the spec and explode by semicolons.
// This is because semicolon rarely appears in escaped form
// Doing this is generally flaky but fast
// IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
// for details
$declarations = explode(';', $css);
$propvalues = array();
/**
* Name of the current CSS property being validated.
*/
$property = false;
$context->register('CurrentCSSProperty', $property);
foreach ($declarations as $declaration) {
if (!$declaration) continue;
if (!strpos($declaration, ':')) continue;
list($property, $value) = explode(':', $declaration, 2);
$property = trim($property);
$value = trim($value);
$ok = false;
do {
if (isset($definition->info[$property])) {
$ok = true;
break;
}
if (ctype_lower($property)) break;
$property = strtolower($property);
if (isset($definition->info[$property])) {
$ok = true;
break;
}
} while(0);
if (!$ok) continue;
// inefficient call, since the validator will do this again
if (strtolower(trim($value)) !== 'inherit') {
// inherit works for everything (but only on the base property)
$result = $definition->info[$property]->validate(
$value, $config, $context );
} else {
$result = 'inherit';
}
if ($result === false) continue;
$propvalues[$property] = $result;
}
$context->destroy('CurrentCSSProperty');
// procedure does not write the new CSS simultaneously, so it's
// slightly inefficient, but it's the only way of getting rid of
// duplicates. Perhaps config to optimize it, but not now.
$new_declarations = '';
foreach ($propvalues as $prop => $value) {
$new_declarations .= "$prop:$value;";
}
return $new_declarations ? $new_declarations : false;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,21 @@
<?php
class HTMLPurifier_AttrDef_CSS_AlphaValue extends HTMLPurifier_AttrDef_CSS_Number
{
public function __construct() {
parent::__construct(false); // opacity is non-negative, but we will clamp it
}
public function validate($number, $config, $context) {
$result = parent::validate($number, $config, $context);
if ($result === false) return $result;
$float = (float) $result;
if ($float < 0.0) $result = '0';
if ($float > 1.0) $result = '1';
return $result;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,87 @@
<?php
/**
* Validates shorthand CSS property background.
* @warning Does not support url tokens that have internal spaces.
*/
class HTMLPurifier_AttrDef_CSS_Background extends HTMLPurifier_AttrDef
{
/**
* Local copy of component validators.
* @note See HTMLPurifier_AttrDef_Font::$info for a similar impl.
*/
protected $info;
public function __construct($config) {
$def = $config->getCSSDefinition();
$this->info['background-color'] = $def->info['background-color'];
$this->info['background-image'] = $def->info['background-image'];
$this->info['background-repeat'] = $def->info['background-repeat'];
$this->info['background-attachment'] = $def->info['background-attachment'];
$this->info['background-position'] = $def->info['background-position'];
}
public function validate($string, $config, $context) {
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') return false;
// munge rgb() decl if necessary
$string = $this->mungeRgb($string);
// assumes URI doesn't have spaces in it
$bits = explode(' ', strtolower($string)); // bits to process
$caught = array();
$caught['color'] = false;
$caught['image'] = false;
$caught['repeat'] = false;
$caught['attachment'] = false;
$caught['position'] = false;
$i = 0; // number of catches
$none = false;
foreach ($bits as $bit) {
if ($bit === '') continue;
foreach ($caught as $key => $status) {
if ($key != 'position') {
if ($status !== false) continue;
$r = $this->info['background-' . $key]->validate($bit, $config, $context);
} else {
$r = $bit;
}
if ($r === false) continue;
if ($key == 'position') {
if ($caught[$key] === false) $caught[$key] = '';
$caught[$key] .= $r . ' ';
} else {
$caught[$key] = $r;
}
$i++;
break;
}
}
if (!$i) return false;
if ($caught['position'] !== false) {
$caught['position'] = $this->info['background-position']->
validate($caught['position'], $config, $context);
}
$ret = array();
foreach ($caught as $value) {
if ($value === false) continue;
$ret[] = $value;
}
if (empty($ret)) return false;
return implode(' ', $ret);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,133 @@
<?php
/* W3C says:
[ // adjective and number must be in correct order, even if
// you could switch them without introducing ambiguity.
// some browsers support that syntax
[
<percentage> | <length> | left | center | right
]
[
<percentage> | <length> | top | center | bottom
]?
] |
[ // this signifies that the vertical and horizontal adjectives
// can be arbitrarily ordered, however, there can only be two,
// one of each, or none at all
[
left | center | right
] ||
[
top | center | bottom
]
]
top, left = 0%
center, (none) = 50%
bottom, right = 100%
*/
/* QuirksMode says:
keyword + length/percentage must be ordered correctly, as per W3C
Internet Explorer and Opera, however, support arbitrary ordering. We
should fix it up.
Minor issue though, not strictly necessary.
*/
// control freaks may appreciate the ability to convert these to
// percentages or something, but it's not necessary
/**
* Validates the value of background-position.
*/
class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef
{
protected $length;
protected $percentage;
public function __construct() {
$this->length = new HTMLPurifier_AttrDef_CSS_Length();
$this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage();
}
public function validate($string, $config, $context) {
$string = $this->parseCDATA($string);
$bits = explode(' ', $string);
$keywords = array();
$keywords['h'] = false; // left, right
$keywords['v'] = false; // top, bottom
$keywords['ch'] = false; // center (first word)
$keywords['cv'] = false; // center (second word)
$measures = array();
$i = 0;
$lookup = array(
'top' => 'v',
'bottom' => 'v',
'left' => 'h',
'right' => 'h',
'center' => 'c'
);
foreach ($bits as $bit) {
if ($bit === '') continue;
// test for keyword
$lbit = ctype_lower($bit) ? $bit : strtolower($bit);
if (isset($lookup[$lbit])) {
$status = $lookup[$lbit];
if ($status == 'c') {
if ($i == 0) {
$status = 'ch';
} else {
$status = 'cv';
}
}
$keywords[$status] = $lbit;
$i++;
}
// test for length
$r = $this->length->validate($bit, $config, $context);
if ($r !== false) {
$measures[] = $r;
$i++;
}
// test for percentage
$r = $this->percentage->validate($bit, $config, $context);
if ($r !== false) {
$measures[] = $r;
$i++;
}
}
if (!$i) return false; // no valid values were caught
$ret = array();
// first keyword
if ($keywords['h']) $ret[] = $keywords['h'];
elseif ($keywords['ch']) {
$ret[] = $keywords['ch'];
$keywords['cv'] = false; // prevent re-use: center = center center
}
elseif (count($measures)) $ret[] = array_shift($measures);
if ($keywords['v']) $ret[] = $keywords['v'];
elseif ($keywords['cv']) $ret[] = $keywords['cv'];
elseif (count($measures)) $ret[] = array_shift($measures);
if (empty($ret)) return false;
return implode(' ', $ret);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,43 @@
<?php
/**
* Validates the border property as defined by CSS.
*/
class HTMLPurifier_AttrDef_CSS_Border extends HTMLPurifier_AttrDef
{
/**
* Local copy of properties this property is shorthand for.
*/
protected $info = array();
public function __construct($config) {
$def = $config->getCSSDefinition();
$this->info['border-width'] = $def->info['border-width'];
$this->info['border-style'] = $def->info['border-style'];
$this->info['border-top-color'] = $def->info['border-top-color'];
}
public function validate($string, $config, $context) {
$string = $this->parseCDATA($string);
$string = $this->mungeRgb($string);
$bits = explode(' ', $string);
$done = array(); // segments we've finished
$ret = ''; // return value
foreach ($bits as $bit) {
foreach ($this->info as $propname => $validator) {
if (isset($done[$propname])) continue;
$r = $validator->validate($bit, $config, $context);
if ($r !== false) {
$ret .= $r . ' ';
$done[$propname] = true;
break;
}
}
}
return rtrim($ret);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,78 @@
<?php
/**
* Validates Color as defined by CSS.
*/
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
{
public function validate($color, $config, $context) {
static $colors = null;
if ($colors === null) $colors = $config->get('Core.ColorKeywords');
$color = trim($color);
if ($color === '') return false;
$lower = strtolower($color);
if (isset($colors[$lower])) return $colors[$lower];
if (strpos($color, 'rgb(') !== false) {
// rgb literal handling
$length = strlen($color);
if (strpos($color, ')') !== $length - 1) return false;
$triad = substr($color, 4, $length - 4 - 1);
$parts = explode(',', $triad);
if (count($parts) !== 3) return false;
$type = false; // to ensure that they're all the same type
$new_parts = array();
foreach ($parts as $part) {
$part = trim($part);
if ($part === '') return false;
$length = strlen($part);
if ($part[$length - 1] === '%') {
// handle percents
if (!$type) {
$type = 'percentage';
} elseif ($type !== 'percentage') {
return false;
}
$num = (float) substr($part, 0, $length - 1);
if ($num < 0) $num = 0;
if ($num > 100) $num = 100;
$new_parts[] = "$num%";
} else {
// handle integers
if (!$type) {
$type = 'integer';
} elseif ($type !== 'integer') {
return false;
}
$num = (int) $part;
if ($num < 0) $num = 0;
if ($num > 255) $num = 255;
$new_parts[] = (string) $num;
}
}
$new_triad = implode(',', $new_parts);
$color = "rgb($new_triad)";
} else {
// hexadecimal handling
if ($color[0] === '#') {
$hex = substr($color, 1);
} else {
$hex = $color;
$color = '#' . $color;
}
$length = strlen($hex);
if ($length !== 3 && $length !== 6) return false;
if (!ctype_xdigit($hex)) return false;
}
return $color;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,38 @@
<?php
/**
* Allows multiple validators to attempt to validate attribute.
*
* Composite is just what it sounds like: a composite of many validators.
* This means that multiple HTMLPurifier_AttrDef objects will have a whack
* at the string. If one of them passes, that's what is returned. This is
* especially useful for CSS values, which often are a choice between
* an enumerated set of predefined values or a flexible data type.
*/
class HTMLPurifier_AttrDef_CSS_Composite extends HTMLPurifier_AttrDef
{
/**
* List of HTMLPurifier_AttrDef objects that may process strings
* @todo Make protected
*/
public $defs;
/**
* @param $defs List of HTMLPurifier_AttrDef objects
*/
public function __construct($defs) {
$this->defs = $defs;
}
public function validate($string, $config, $context) {
foreach ($this->defs as $i => $def) {
$result = $this->defs[$i]->validate($string, $config, $context);
if ($result !== false) return $result;
}
return false;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,28 @@
<?php
/**
* Decorator which enables CSS properties to be disabled for specific elements.
*/
class HTMLPurifier_AttrDef_CSS_DenyElementDecorator extends HTMLPurifier_AttrDef
{
public $def, $element;
/**
* @param $def Definition to wrap
* @param $element Element to deny
*/
public function __construct($def, $element) {
$this->def = $def;
$this->element = $element;
}
/**
* Checks if CurrentToken is set and equal to $this->element
*/
public function validate($string, $config, $context) {
$token = $context->get('CurrentToken', true);
if ($token && $token->name == $this->element) return false;
return $this->def->validate($string, $config, $context);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,54 @@
<?php
/**
* Microsoft's proprietary filter: CSS property
* @note Currently supports the alpha filter. In the future, this will
* probably need an extensible framework
*/
class HTMLPurifier_AttrDef_CSS_Filter extends HTMLPurifier_AttrDef
{
protected $intValidator;
public function __construct() {
$this->intValidator = new HTMLPurifier_AttrDef_Integer();
}
public function validate($value, $config, $context) {
$value = $this->parseCDATA($value);
if ($value === 'none') return $value;
// if we looped this we could support multiple filters
$function_length = strcspn($value, '(');
$function = trim(substr($value, 0, $function_length));
if ($function !== 'alpha' &&
$function !== 'Alpha' &&
$function !== 'progid:DXImageTransform.Microsoft.Alpha'
) return false;
$cursor = $function_length + 1;
$parameters_length = strcspn($value, ')', $cursor);
$parameters = substr($value, $cursor, $parameters_length);
$params = explode(',', $parameters);
$ret_params = array();
$lookup = array();
foreach ($params as $param) {
list($key, $value) = explode('=', $param);
$key = trim($key);
$value = trim($value);
if (isset($lookup[$key])) continue;
if ($key !== 'opacity') continue;
$value = $this->intValidator->validate($value, $config, $context);
if ($value === false) continue;
$int = (int) $value;
if ($int > 100) $value = '100';
if ($int < 0) $value = '0';
$ret_params[] = "$key=$value";
$lookup[$key] = true;
}
$ret_parameters = implode(',', $ret_params);
$ret_function = "$function($ret_parameters)";
return $ret_function;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,149 @@
<?php
/**
* Validates shorthand CSS property font.
*/
class HTMLPurifier_AttrDef_CSS_Font extends HTMLPurifier_AttrDef
{
/**
* Local copy of component validators.
*
* @note If we moved specific CSS property definitions to their own
* classes instead of having them be assembled at run time by
* CSSDefinition, this wouldn't be necessary. We'd instantiate
* our own copies.
*/
protected $info = array();
public function __construct($config) {
$def = $config->getCSSDefinition();
$this->info['font-style'] = $def->info['font-style'];
$this->info['font-variant'] = $def->info['font-variant'];
$this->info['font-weight'] = $def->info['font-weight'];
$this->info['font-size'] = $def->info['font-size'];
$this->info['line-height'] = $def->info['line-height'];
$this->info['font-family'] = $def->info['font-family'];
}
public function validate($string, $config, $context) {
static $system_fonts = array(
'caption' => true,
'icon' => true,
'menu' => true,
'message-box' => true,
'small-caption' => true,
'status-bar' => true
);
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') return false;
// check if it's one of the keywords
$lowercase_string = strtolower($string);
if (isset($system_fonts[$lowercase_string])) {
return $lowercase_string;
}
$bits = explode(' ', $string); // bits to process
$stage = 0; // this indicates what we're looking for
$caught = array(); // which stage 0 properties have we caught?
$stage_1 = array('font-style', 'font-variant', 'font-weight');
$final = ''; // output
for ($i = 0, $size = count($bits); $i < $size; $i++) {
if ($bits[$i] === '') continue;
switch ($stage) {
// attempting to catch font-style, font-variant or font-weight
case 0:
foreach ($stage_1 as $validator_name) {
if (isset($caught[$validator_name])) continue;
$r = $this->info[$validator_name]->validate(
$bits[$i], $config, $context);
if ($r !== false) {
$final .= $r . ' ';
$caught[$validator_name] = true;
break;
}
}
// all three caught, continue on
if (count($caught) >= 3) $stage = 1;
if ($r !== false) break;
// attempting to catch font-size and perhaps line-height
case 1:
$found_slash = false;
if (strpos($bits[$i], '/') !== false) {
list($font_size, $line_height) =
explode('/', $bits[$i]);
if ($line_height === '') {
// ooh, there's a space after the slash!
$line_height = false;
$found_slash = true;
}
} else {
$font_size = $bits[$i];
$line_height = false;
}
$r = $this->info['font-size']->validate(
$font_size, $config, $context);
if ($r !== false) {
$final .= $r;
// attempt to catch line-height
if ($line_height === false) {
// we need to scroll forward
for ($j = $i + 1; $j < $size; $j++) {
if ($bits[$j] === '') continue;
if ($bits[$j] === '/') {
if ($found_slash) {
return false;
} else {
$found_slash = true;
continue;
}
}
$line_height = $bits[$j];
break;
}
} else {
// slash already found
$found_slash = true;
$j = $i;
}
if ($found_slash) {
$i = $j;
$r = $this->info['line-height']->validate(
$line_height, $config, $context);
if ($r !== false) {
$final .= '/' . $r;
}
}
$final .= ' ';
$stage = 2;
break;
}
return false;
// attempting to catch font-family
case 2:
$font_family =
implode(' ', array_slice($bits, $i, $size - $i));
$r = $this->info['font-family']->validate(
$font_family, $config, $context);
if ($r !== false) {
$final .= $r . ' ';
// processing completed successfully
return rtrim($final);
}
return false;
}
}
return false;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,197 @@
<?php
/**
* Validates a font family list according to CSS spec
*/
class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
{
protected $mask = null;
public function __construct() {
$this->mask = '- ';
for ($c = 'a'; $c <= 'z'; $c++) $this->mask .= $c;
for ($c = 'A'; $c <= 'Z'; $c++) $this->mask .= $c;
for ($c = '0'; $c <= '9'; $c++) $this->mask .= $c; // cast-y, but should be fine
// special bytes used by UTF-8
for ($i = 0x80; $i <= 0xFF; $i++) {
// We don't bother excluding invalid bytes in this range,
// because the our restriction of well-formed UTF-8 will
// prevent these from ever occurring.
$this->mask .= chr($i);
}
/*
PHP's internal strcspn implementation is
O(length of string * length of mask), making it inefficient
for large masks. However, it's still faster than
preg_match 8)
for (p = s1;;) {
spanp = s2;
do {
if (*spanp == c || p == s1_end) {
return p - s1;
}
} while (spanp++ < (s2_end - 1));
c = *++p;
}
*/
// possible optimization: invert the mask.
}
public function validate($string, $config, $context) {
static $generic_names = array(
'serif' => true,
'sans-serif' => true,
'monospace' => true,
'fantasy' => true,
'cursive' => true
);
$allowed_fonts = $config->get('CSS.AllowedFonts');
// assume that no font names contain commas in them
$fonts = explode(',', $string);
$final = '';
foreach($fonts as $font) {
$font = trim($font);
if ($font === '') continue;
// match a generic name
if (isset($generic_names[$font])) {
if ($allowed_fonts === null || isset($allowed_fonts[$font])) {
$final .= $font . ', ';
}
continue;
}
// match a quoted name
if ($font[0] === '"' || $font[0] === "'") {
$length = strlen($font);
if ($length <= 2) continue;
$quote = $font[0];
if ($font[$length - 1] !== $quote) continue;
$font = substr($font, 1, $length - 2);
}
$font = $this->expandCSSEscape($font);
// $font is a pure representation of the font name
if ($allowed_fonts !== null && !isset($allowed_fonts[$font])) {
continue;
}
if (ctype_alnum($font) && $font !== '') {
// very simple font, allow it in unharmed
$final .= $font . ', ';
continue;
}
// bugger out on whitespace. form feed (0C) really
// shouldn't show up regardless
$font = str_replace(array("\n", "\t", "\r", "\x0C"), ' ', $font);
// Here, there are various classes of characters which need
// to be treated differently:
// - Alphanumeric characters are essentially safe. We
// handled these above.
// - Spaces require quoting, though most parsers will do
// the right thing if there aren't any characters that
// can be misinterpreted
// - Dashes rarely occur, but they fairly unproblematic
// for parsing/rendering purposes.
// The above characters cover the majority of Western font
// names.
// - Arbitrary Unicode characters not in ASCII. Because
// most parsers give little thought to Unicode, treatment
// of these codepoints is basically uniform, even for
// punctuation-like codepoints. These characters can
// show up in non-Western pages and are supported by most
// major browsers, for example: " 明朝" is a
// legitimate font-name
// <http://ja.wikipedia.org/wiki/MS_明朝>. See
// the CSS3 spec for more examples:
// <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
// You can see live samples of these on the Internet:
// <http://www.google.co.jp/search?q=font-family++明朝|ゴシック>
// However, most of these fonts have ASCII equivalents:
// for example, 'MS Mincho', and it's considered
// professional to use ASCII font names instead of
// Unicode font names. Thanks Takeshi Terada for
// providing this information.
// The following characters, to my knowledge, have not been
// used to name font names.
// - Single quote. While theoretically you might find a
// font name that has a single quote in its name (serving
// as an apostrophe, e.g. Dave's Scribble), I haven't
// been able to find any actual examples of this.
// Internet Explorer's cssText translation (which I
// believe is invoked by innerHTML) normalizes any
// quoting to single quotes, and fails to escape single
// quotes. (Note that this is not IE's behavior for all
// CSS properties, just some sort of special casing for
// font-family). So a single quote *cannot* be used
// safely in the font-family context if there will be an
// innerHTML/cssText translation. Note that Firefox 3.x
// does this too.
// - Double quote. In IE, these get normalized to
// single-quotes, no matter what the encoding. (Fun
// fact, in IE8, the 'content' CSS property gained
// support, where they special cased to preserve encoded
// double quotes, but still translate unadorned double
// quotes into single quotes.) So, because their
// fixpoint behavior is identical to single quotes, they
// cannot be allowed either. Firefox 3.x displays
// single-quote style behavior.
// - Backslashes are reduced by one (so \\ -> \) every
// iteration, so they cannot be used safely. This shows
// up in IE7, IE8 and FF3
// - Semicolons, commas and backticks are handled properly.
// - The rest of the ASCII punctuation is handled properly.
// We haven't checked what browsers do to unadorned
// versions, but this is not important as long as the
// browser doesn't /remove/ surrounding quotes (as IE does
// for HTML).
//
// With these results in hand, we conclude that there are
// various levels of safety:
// - Paranoid: alphanumeric, spaces and dashes(?)
// - International: Paranoid + non-ASCII Unicode
// - Edgy: Everything except quotes, backslashes
// - NoJS: Standards compliance, e.g. sod IE. Note that
// with some judicious character escaping (since certain
// types of escaping doesn't work) this is theoretically
// OK as long as innerHTML/cssText is not called.
// We believe that international is a reasonable default
// (that we will implement now), and once we do more
// extensive research, we may feel comfortable with dropping
// it down to edgy.
// Edgy: alphanumeric, spaces, dashes and Unicode. Use of
// str(c)spn assumes that the string was already well formed
// Unicode (which of course it is).
if (strspn($font, $this->mask) !== strlen($font)) {
continue;
}
// Historical:
// In the absence of innerHTML/cssText, these ugly
// transforms don't pose a security risk (as \\ and \"
// might--these escapes are not supported by most browsers).
// We could try to be clever and use single-quote wrapping
// when there is a double quote present, but I have choosen
// not to implement that. (NOTE: you can reduce the amount
// of escapes by one depending on what quoting style you use)
// $font = str_replace('\\', '\\5C ', $font);
// $font = str_replace('"', '\\22 ', $font);
// $font = str_replace("'", '\\27 ', $font);
// font possibly with spaces, requires quoting
$final .= "'$font', ";
}
$final = rtrim($final, ', ');
if ($final === '') return false;
return $final;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,40 @@
<?php
/**
* Decorator which enables !important to be used in CSS values.
*/
class HTMLPurifier_AttrDef_CSS_ImportantDecorator extends HTMLPurifier_AttrDef
{
public $def, $allow;
/**
* @param $def Definition to wrap
* @param $allow Whether or not to allow !important
*/
public function __construct($def, $allow = false) {
$this->def = $def;
$this->allow = $allow;
}
/**
* Intercepts and removes !important if necessary
*/
public function validate($string, $config, $context) {
// test for ! and important tokens
$string = trim($string);
$is_important = false;
// :TODO: optimization: test directly for !important and ! important
if (strlen($string) >= 9 && substr($string, -9) === 'important') {
$temp = rtrim(substr($string, 0, -9));
// use a temp, because we might want to restore important
if (strlen($temp) >= 1 && substr($temp, -1) === '!') {
$string = rtrim(substr($temp, 0, -1));
$is_important = true;
}
}
$string = $this->def->validate($string, $config, $context);
if ($this->allow && $is_important) $string .= ' !important';
return $string;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,47 @@
<?php
/**
* Represents a Length as defined by CSS.
*/
class HTMLPurifier_AttrDef_CSS_Length extends HTMLPurifier_AttrDef
{
protected $min, $max;
/**
* @param HTMLPurifier_Length $max Minimum length, or null for no bound. String is also acceptable.
* @param HTMLPurifier_Length $max Maximum length, or null for no bound. String is also acceptable.
*/
public function __construct($min = null, $max = null) {
$this->min = $min !== null ? HTMLPurifier_Length::make($min) : null;
$this->max = $max !== null ? HTMLPurifier_Length::make($max) : null;
}
public function validate($string, $config, $context) {
$string = $this->parseCDATA($string);
// Optimizations
if ($string === '') return false;
if ($string === '0') return '0';
if (strlen($string) === 1) return false;
$length = HTMLPurifier_Length::make($string);
if (!$length->isValid()) return false;
if ($this->min) {
$c = $length->compareTo($this->min);
if ($c === false) return false;
if ($c < 0) return false;
}
if ($this->max) {
$c = $length->compareTo($this->max);
if ($c === false) return false;
if ($c > 0) return false;
}
return $length->toString();
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,78 @@
<?php
/**
* Validates shorthand CSS property list-style.
* @warning Does not support url tokens that have internal spaces.
*/
class HTMLPurifier_AttrDef_CSS_ListStyle extends HTMLPurifier_AttrDef
{
/**
* Local copy of component validators.
* @note See HTMLPurifier_AttrDef_CSS_Font::$info for a similar impl.
*/
protected $info;
public function __construct($config) {
$def = $config->getCSSDefinition();
$this->info['list-style-type'] = $def->info['list-style-type'];
$this->info['list-style-position'] = $def->info['list-style-position'];
$this->info['list-style-image'] = $def->info['list-style-image'];
}
public function validate($string, $config, $context) {
// regular pre-processing
$string = $this->parseCDATA($string);
if ($string === '') return false;
// assumes URI doesn't have spaces in it
$bits = explode(' ', strtolower($string)); // bits to process
$caught = array();
$caught['type'] = false;
$caught['position'] = false;
$caught['image'] = false;
$i = 0; // number of catches
$none = false;
foreach ($bits as $bit) {
if ($i >= 3) return; // optimization bit
if ($bit === '') continue;
foreach ($caught as $key => $status) {
if ($status !== false) continue;
$r = $this->info['list-style-' . $key]->validate($bit, $config, $context);
if ($r === false) continue;
if ($r === 'none') {
if ($none) continue;
else $none = true;
if ($key == 'image') continue;
}
$caught[$key] = $r;
$i++;
break;
}
}
if (!$i) return false;
$ret = array();
// construct type
if ($caught['type']) $ret[] = $caught['type'];
// construct image
if ($caught['image']) $ret[] = $caught['image'];
// construct position
if ($caught['position']) $ret[] = $caught['position'];
if (empty($ret)) return false;
return implode(' ', $ret);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,58 @@
<?php
/**
* Framework class for strings that involve multiple values.
*
* Certain CSS properties such as border-width and margin allow multiple
* lengths to be specified. This class can take a vanilla border-width
* definition and multiply it, usually into a max of four.
*
* @note Even though the CSS specification isn't clear about it, inherit
* can only be used alone: it will never manifest as part of a multi
* shorthand declaration. Thus, this class does not allow inherit.
*/
class HTMLPurifier_AttrDef_CSS_Multiple extends HTMLPurifier_AttrDef
{
/**
* Instance of component definition to defer validation to.
* @todo Make protected
*/
public $single;
/**
* Max number of values allowed.
* @todo Make protected
*/
public $max;
/**
* @param $single HTMLPurifier_AttrDef to multiply
* @param $max Max number of values allowed (usually four)
*/
public function __construct($single, $max = 4) {
$this->single = $single;
$this->max = $max;
}
public function validate($string, $config, $context) {
$string = $this->parseCDATA($string);
if ($string === '') return false;
$parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n
$length = count($parts);
$final = '';
for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) {
if (ctype_space($parts[$i])) continue;
$result = $this->single->validate($parts[$i], $config, $context);
if ($result !== false) {
$final .= $result . ' ';
$num++;
}
}
if ($final === '') return false;
return rtrim($final);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,69 @@
<?php
/**
* Validates a number as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_CSS_Number extends HTMLPurifier_AttrDef
{
/**
* Bool indicating whether or not only positive values allowed.
*/
protected $non_negative = false;
/**
* @param $non_negative Bool indicating whether negatives are forbidden
*/
public function __construct($non_negative = false) {
$this->non_negative = $non_negative;
}
/**
* @warning Some contexts do not pass $config, $context. These
* variables should not be used without checking HTMLPurifier_Length
*/
public function validate($number, $config, $context) {
$number = $this->parseCDATA($number);
if ($number === '') return false;
if ($number === '0') return '0';
$sign = '';
switch ($number[0]) {
case '-':
if ($this->non_negative) return false;
$sign = '-';
case '+':
$number = substr($number, 1);
}
if (ctype_digit($number)) {
$number = ltrim($number, '0');
return $number ? $sign . $number : '0';
}
// Period is the only non-numeric character allowed
if (strpos($number, '.') === false) return false;
list($left, $right) = explode('.', $number, 2);
if ($left === '' && $right === '') return false;
if ($left !== '' && !ctype_digit($left)) return false;
$left = ltrim($left, '0');
$right = rtrim($right, '0');
if ($right === '') {
return $left ? $sign . $left : '0';
} elseif (!ctype_digit($right)) {
return false;
}
return $sign . $left . '.' . $right;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,40 @@
<?php
/**
* Validates a Percentage as defined by the CSS spec.
*/
class HTMLPurifier_AttrDef_CSS_Percentage extends HTMLPurifier_AttrDef
{
/**
* Instance of HTMLPurifier_AttrDef_CSS_Number to defer number validation
*/
protected $number_def;
/**
* @param Bool indicating whether to forbid negative values
*/
public function __construct($non_negative = false) {
$this->number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative);
}
public function validate($string, $config, $context) {
$string = $this->parseCDATA($string);
if ($string === '') return false;
$length = strlen($string);
if ($length === 1) return false;
if ($string[$length - 1] !== '%') return false;
$number = substr($string, 0, $length - 1);
$number = $this->number_def->validate($number, $config, $context);
if ($number === false) return false;
return "$number%";
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,38 @@
<?php
/**
* Validates the value for the CSS property text-decoration
* @note This class could be generalized into a version that acts sort of
* like Enum except you can compound the allowed values.
*/
class HTMLPurifier_AttrDef_CSS_TextDecoration extends HTMLPurifier_AttrDef
{
public function validate($string, $config, $context) {
static $allowed_values = array(
'line-through' => true,
'overline' => true,
'underline' => true,
);
$string = strtolower($this->parseCDATA($string));
if ($string === 'none') return $string;
$parts = explode(' ', $string);
$final = '';
foreach ($parts as $part) {
if (isset($allowed_values[$part])) {
$final .= $part . ' ';
}
}
$final = rtrim($final);
if ($final === '') return false;
return $final;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,61 @@
<?php
/**
* Validates a URI in CSS syntax, which uses url('http://example.com')
* @note While theoretically speaking a URI in a CSS document could
* be non-embedded, as of CSS2 there is no such usage so we're
* generalizing it. This may need to be changed in the future.
* @warning Since HTMLPurifier_AttrDef_CSS blindly uses semicolons as
* the separator, you cannot put a literal semicolon in
* in the URI. Try percent encoding it, in that case.
*/
class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
{
public function __construct() {
parent::__construct(true); // always embedded
}
public function validate($uri_string, $config, $context) {
// parse the URI out of the string and then pass it onto
// the parent object
$uri_string = $this->parseCDATA($uri_string);
if (strpos($uri_string, 'url(') !== 0) return false;
$uri_string = substr($uri_string, 4);
$new_length = strlen($uri_string) - 1;
if ($uri_string[$new_length] != ')') return false;
$uri = trim(substr($uri_string, 0, $new_length));
if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) {
$quote = $uri[0];
$new_length = strlen($uri) - 1;
if ($uri[$new_length] !== $quote) return false;
$uri = substr($uri, 1, $new_length - 1);
}
$uri = $this->expandCSSEscape($uri);
$result = parent::validate($uri, $config, $context);
if ($result === false) return false;
// extra sanity check; should have been done by URI
$result = str_replace(array('"', "\\", "\n", "\x0c", "\r"), "", $result);
// suspicious characters are ()'; we're going to percent encode
// them for safety.
$result = str_replace(array('(', ')', "'"), array('%28', '%29', '%27'), $result);
// there's an extra bug where ampersands lose their escaping on
// an innerHTML cycle, so a very unlucky query parameter could
// then change the meaning of the URL. Unfortunately, there's
// not much we can do about that...
return "url(\"$result\")";
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,65 @@
<?php
// Enum = Enumerated
/**
* Validates a keyword against a list of valid values.
* @warning The case-insensitive compare of this function uses PHP's
* built-in strtolower and ctype_lower functions, which may
* cause problems with international comparisons
*/
class HTMLPurifier_AttrDef_Enum extends HTMLPurifier_AttrDef
{
/**
* Lookup table of valid values.
* @todo Make protected
*/
public $valid_values = array();
/**
* Bool indicating whether or not enumeration is case sensitive.
* @note In general this is always case insensitive.
*/
protected $case_sensitive = false; // values according to W3C spec
/**
* @param $valid_values List of valid values
* @param $case_sensitive Bool indicating whether or not case sensitive
*/
public function __construct(
$valid_values = array(), $case_sensitive = false
) {
$this->valid_values = array_flip($valid_values);
$this->case_sensitive = $case_sensitive;
}
public function validate($string, $config, $context) {
$string = trim($string);
if (!$this->case_sensitive) {
// we may want to do full case-insensitive libraries
$string = ctype_lower($string) ? $string : strtolower($string);
}
$result = isset($this->valid_values[$string]);
return $result ? $string : false;
}
/**
* @param $string In form of comma-delimited list of case-insensitive
* valid values. Example: "foo,bar,baz". Prepend "s:" to make
* case sensitive
*/
public function make($string) {
if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') {
$string = substr($string, 2);
$sensitive = true;
} else {
$sensitive = false;
}
$values = explode(',', $string);
return new HTMLPurifier_AttrDef_Enum($values, $sensitive);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,28 @@
<?php
/**
* Validates a boolean attribute
*/
class HTMLPurifier_AttrDef_HTML_Bool extends HTMLPurifier_AttrDef
{
protected $name;
public $minimized = true;
public function __construct($name = false) {$this->name = $name;}
public function validate($string, $config, $context) {
if (empty($string)) return false;
return $this->name;
}
/**
* @param $string Name of attribute
*/
public function make($string) {
return new HTMLPurifier_AttrDef_HTML_Bool($string);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,34 @@
<?php
/**
* Implements special behavior for class attribute (normally NMTOKENS)
*/
class HTMLPurifier_AttrDef_HTML_Class extends HTMLPurifier_AttrDef_HTML_Nmtokens
{
protected function split($string, $config, $context) {
// really, this twiddle should be lazy loaded
$name = $config->getDefinition('HTML')->doctype->name;
if ($name == "XHTML 1.1" || $name == "XHTML 2.0") {
return parent::split($string, $config, $context);
} else {
return preg_split('/\s+/', $string);
}
}
protected function filter($tokens, $config, $context) {
$allowed = $config->get('Attr.AllowedClasses');
$forbidden = $config->get('Attr.ForbiddenClasses');
$ret = array();
foreach ($tokens as $token) {
if (
($allowed === null || isset($allowed[$token])) &&
!isset($forbidden[$token]) &&
// We need this O(n) check because of PHP's array
// implementation that casts -0 to 0.
!in_array($token, $ret, true)
) {
$ret[] = $token;
}
}
return $ret;
}
}

View file

@ -0,0 +1,32 @@
<?php
/**
* Validates a color according to the HTML spec.
*/
class HTMLPurifier_AttrDef_HTML_Color extends HTMLPurifier_AttrDef
{
public function validate($string, $config, $context) {
static $colors = null;
if ($colors === null) $colors = $config->get('Core.ColorKeywords');
$string = trim($string);
if (empty($string)) return false;
if (isset($colors[$string])) return $colors[$string];
if ($string[0] === '#') $hex = substr($string, 1);
else $hex = $string;
$length = strlen($hex);
if ($length !== 3 && $length !== 6) return false;
if (!ctype_xdigit($hex)) return false;
if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
return "#$hex";
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,21 @@
<?php
/**
* Special-case enum attribute definition that lazy loads allowed frame targets
*/
class HTMLPurifier_AttrDef_HTML_FrameTarget extends HTMLPurifier_AttrDef_Enum
{
public $valid_values = false; // uninitialized value
protected $case_sensitive = false;
public function __construct() {}
public function validate($string, $config, $context) {
if ($this->valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets');
return parent::validate($string, $config, $context);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,70 @@
<?php
/**
* Validates the HTML attribute ID.
* @warning Even though this is the id processor, it
* will ignore the directive Attr:IDBlacklist, since it will only
* go according to the ID accumulator. Since the accumulator is
* automatically generated, it will have already absorbed the
* blacklist. If you're hacking around, make sure you use load()!
*/
class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
{
// ref functionality disabled, since we also have to verify
// whether or not the ID it refers to exists
public function validate($id, $config, $context) {
if (!$config->get('Attr.EnableID')) return false;
$id = trim($id); // trim it first
if ($id === '') return false;
$prefix = $config->get('Attr.IDPrefix');
if ($prefix !== '') {
$prefix .= $config->get('Attr.IDPrefixLocal');
// prevent re-appending the prefix
if (strpos($id, $prefix) !== 0) $id = $prefix . $id;
} elseif ($config->get('Attr.IDPrefixLocal') !== '') {
trigger_error('%Attr.IDPrefixLocal cannot be used unless '.
'%Attr.IDPrefix is set', E_USER_WARNING);
}
//if (!$this->ref) {
$id_accumulator =& $context->get('IDAccumulator');
if (isset($id_accumulator->ids[$id])) return false;
//}
// we purposely avoid using regex, hopefully this is faster
if (ctype_alpha($id)) {
$result = true;
} else {
if (!ctype_alpha(@$id[0])) return false;
$trim = trim( // primitive style of regexps, I suppose
$id,
'A..Za..z0..9:-._'
);
$result = ($trim === '');
}
$regexp = $config->get('Attr.IDBlacklistRegexp');
if ($regexp && preg_match($regexp, $id)) {
return false;
}
if (/*!$this->ref && */$result) $id_accumulator->add($id);
// if no change was made to the ID, return the result
// else, return the new id if stripping whitespace made it
// valid, or return false.
return $result ? $id : false;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,41 @@
<?php
/**
* Validates the HTML type length (not to be confused with CSS's length).
*
* This accepts integer pixels or percentages as lengths for certain
* HTML attributes.
*/
class HTMLPurifier_AttrDef_HTML_Length extends HTMLPurifier_AttrDef_HTML_Pixels
{
public function validate($string, $config, $context) {
$string = trim($string);
if ($string === '') return false;
$parent_result = parent::validate($string, $config, $context);
if ($parent_result !== false) return $parent_result;
$length = strlen($string);
$last_char = $string[$length - 1];
if ($last_char !== '%') return false;
$points = substr($string, 0, $length - 1);
if (!is_numeric($points)) return false;
$points = (int) $points;
if ($points < 0) return '0%';
if ($points > 100) return '100%';
return ((string) $points) . '%';
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,53 @@
<?php
/**
* Validates a rel/rev link attribute against a directive of allowed values
* @note We cannot use Enum because link types allow multiple
* values.
* @note Assumes link types are ASCII text
*/
class HTMLPurifier_AttrDef_HTML_LinkTypes extends HTMLPurifier_AttrDef
{
/** Name config attribute to pull. */
protected $name;
public function __construct($name) {
$configLookup = array(
'rel' => 'AllowedRel',
'rev' => 'AllowedRev'
);
if (!isset($configLookup[$name])) {
trigger_error('Unrecognized attribute name for link '.
'relationship.', E_USER_ERROR);
return;
}
$this->name = $configLookup[$name];
}
public function validate($string, $config, $context) {
$allowed = $config->get('Attr.' . $this->name);
if (empty($allowed)) return false;
$string = $this->parseCDATA($string);
$parts = explode(' ', $string);
// lookup to prevent duplicates
$ret_lookup = array();
foreach ($parts as $part) {
$part = strtolower(trim($part));
if (!isset($allowed[$part])) continue;
$ret_lookup[$part] = true;
}
if (empty($ret_lookup)) return false;
$string = implode(' ', array_keys($ret_lookup));
return $string;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,41 @@
<?php
/**
* Validates a MultiLength as defined by the HTML spec.
*
* A multilength is either a integer (pixel count), a percentage, or
* a relative number.
*/
class HTMLPurifier_AttrDef_HTML_MultiLength extends HTMLPurifier_AttrDef_HTML_Length
{
public function validate($string, $config, $context) {
$string = trim($string);
if ($string === '') return false;
$parent_result = parent::validate($string, $config, $context);
if ($parent_result !== false) return $parent_result;
$length = strlen($string);
$last_char = $string[$length - 1];
if ($last_char !== '*') return false;
$int = substr($string, 0, $length - 1);
if ($int == '') return '*';
if (!is_numeric($int)) return false;
$int = (int) $int;
if ($int < 0) return false;
if ($int == 0) return '0';
if ($int == 1) return '*';
return ((string) $int) . '*';
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,52 @@
<?php
/**
* Validates contents based on NMTOKENS attribute type.
*/
class HTMLPurifier_AttrDef_HTML_Nmtokens extends HTMLPurifier_AttrDef
{
public function validate($string, $config, $context) {
$string = trim($string);
// early abort: '' and '0' (strings that convert to false) are invalid
if (!$string) return false;
$tokens = $this->split($string, $config, $context);
$tokens = $this->filter($tokens, $config, $context);
if (empty($tokens)) return false;
return implode(' ', $tokens);
}
/**
* Splits a space separated list of tokens into its constituent parts.
*/
protected function split($string, $config, $context) {
// OPTIMIZABLE!
// do the preg_match, capture all subpatterns for reformulation
// we don't support U+00A1 and up codepoints or
// escaping because I don't know how to do that with regexps
// and plus it would complicate optimization efforts (you never
// see that anyway).
$pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start
'((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'.
'(?:(?=\s)|\z)/'; // look ahead for space or string end
preg_match_all($pattern, $string, $matches);
return $matches[1];
}
/**
* Template method for removing certain tokens based on arbitrary criteria.
* @note If we wanted to be really functional, we'd do an array_filter
* with a callback. But... we're not.
*/
protected function filter($tokens, $config, $context) {
return $tokens;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,48 @@
<?php
/**
* Validates an integer representation of pixels according to the HTML spec.
*/
class HTMLPurifier_AttrDef_HTML_Pixels extends HTMLPurifier_AttrDef
{
protected $max;
public function __construct($max = null) {
$this->max = $max;
}
public function validate($string, $config, $context) {
$string = trim($string);
if ($string === '0') return $string;
if ($string === '') return false;
$length = strlen($string);
if (substr($string, $length - 2) == 'px') {
$string = substr($string, 0, $length - 2);
}
if (!is_numeric($string)) return false;
$int = (int) $string;
if ($int < 0) return '0';
// upper-bound value, extremely high values can
// crash operating systems, see <http://ha.ckers.org/imagecrash.html>
// WARNING, above link WILL crash you if you're using Windows
if ($this->max !== null && $int > $this->max) return (string) $this->max;
return (string) $int;
}
public function make($string) {
if ($string === '') $max = null;
else $max = (int) $string;
$class = get_class($this);
return new $class($max);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,73 @@
<?php
/**
* Validates an integer.
* @note While this class was modeled off the CSS definition, no currently
* allowed CSS uses this type. The properties that do are: widows,
* orphans, z-index, counter-increment, counter-reset. Some of the
* HTML attributes, however, find use for a non-negative version of this.
*/
class HTMLPurifier_AttrDef_Integer extends HTMLPurifier_AttrDef
{
/**
* Bool indicating whether or not negative values are allowed
*/
protected $negative = true;
/**
* Bool indicating whether or not zero is allowed
*/
protected $zero = true;
/**
* Bool indicating whether or not positive values are allowed
*/
protected $positive = true;
/**
* @param $negative Bool indicating whether or not negative values are allowed
* @param $zero Bool indicating whether or not zero is allowed
* @param $positive Bool indicating whether or not positive values are allowed
*/
public function __construct(
$negative = true, $zero = true, $positive = true
) {
$this->negative = $negative;
$this->zero = $zero;
$this->positive = $positive;
}
public function validate($integer, $config, $context) {
$integer = $this->parseCDATA($integer);
if ($integer === '') return false;
// we could possibly simply typecast it to integer, but there are
// certain fringe cases that must not return an integer.
// clip leading sign
if ( $this->negative && $integer[0] === '-' ) {
$digits = substr($integer, 1);
if ($digits === '0') $integer = '0'; // rm minus sign for zero
} elseif( $this->positive && $integer[0] === '+' ) {
$digits = $integer = substr($integer, 1); // rm unnecessary plus
} else {
$digits = $integer;
}
// test if it's numeric
if (!ctype_digit($digits)) return false;
// perform scope tests
if (!$this->zero && $integer == 0) return false;
if (!$this->positive && $integer > 0) return false;
if (!$this->negative && $integer < 0) return false;
return $integer;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,73 @@
<?php
/**
* Validates the HTML attribute lang, effectively a language code.
* @note Built according to RFC 3066, which obsoleted RFC 1766
*/
class HTMLPurifier_AttrDef_Lang extends HTMLPurifier_AttrDef
{
public function validate($string, $config, $context) {
$string = trim($string);
if (!$string) return false;
$subtags = explode('-', $string);
$num_subtags = count($subtags);
if ($num_subtags == 0) return false; // sanity check
// process primary subtag : $subtags[0]
$length = strlen($subtags[0]);
switch ($length) {
case 0:
return false;
case 1:
if (! ($subtags[0] == 'x' || $subtags[0] == 'i') ) {
return false;
}
break;
case 2:
case 3:
if (! ctype_alpha($subtags[0]) ) {
return false;
} elseif (! ctype_lower($subtags[0]) ) {
$subtags[0] = strtolower($subtags[0]);
}
break;
default:
return false;
}
$new_string = $subtags[0];
if ($num_subtags == 1) return $new_string;
// process second subtag : $subtags[1]
$length = strlen($subtags[1]);
if ($length == 0 || ($length == 1 && $subtags[1] != 'x') || $length > 8 || !ctype_alnum($subtags[1])) {
return $new_string;
}
if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]);
$new_string .= '-' . $subtags[1];
if ($num_subtags == 2) return $new_string;
// process all other subtags, index 2 and up
for ($i = 2; $i < $num_subtags; $i++) {
$length = strlen($subtags[$i]);
if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) {
return $new_string;
}
if (!ctype_lower($subtags[$i])) {
$subtags[$i] = strtolower($subtags[$i]);
}
$new_string .= '-' . $subtags[$i];
}
return $new_string;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,34 @@
<?php
/**
* Decorator that, depending on a token, switches between two definitions.
*/
class HTMLPurifier_AttrDef_Switch
{
protected $tag;
protected $withTag, $withoutTag;
/**
* @param string $tag Tag name to switch upon
* @param HTMLPurifier_AttrDef $with_tag Call if token matches tag
* @param HTMLPurifier_AttrDef $without_tag Call if token doesn't match, or there is no token
*/
public function __construct($tag, $with_tag, $without_tag) {
$this->tag = $tag;
$this->withTag = $with_tag;
$this->withoutTag = $without_tag;
}
public function validate($string, $config, $context) {
$token = $context->get('CurrentToken', true);
if (!$token || $token->name !== $this->tag) {
return $this->withoutTag->validate($string, $config, $context);
} else {
return $this->withTag->validate($string, $config, $context);
}
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,15 @@
<?php
/**
* Validates arbitrary text according to the HTML spec.
*/
class HTMLPurifier_AttrDef_Text extends HTMLPurifier_AttrDef
{
public function validate($string, $config, $context) {
return $this->parseCDATA($string);
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,77 @@
<?php
/**
* Validates a URI as defined by RFC 3986.
* @note Scheme-specific mechanics deferred to HTMLPurifier_URIScheme
*/
class HTMLPurifier_AttrDef_URI extends HTMLPurifier_AttrDef
{
protected $parser;
protected $embedsResource;
/**
* @param $embeds_resource_resource Does the URI here result in an extra HTTP request?
*/
public function __construct($embeds_resource = false) {
$this->parser = new HTMLPurifier_URIParser();
$this->embedsResource = (bool) $embeds_resource;
}
public function make($string) {
$embeds = (bool) $string;
return new HTMLPurifier_AttrDef_URI($embeds);
}
public function validate($uri, $config, $context) {
if ($config->get('URI.Disable')) return false;
$uri = $this->parseCDATA($uri);
// parse the URI
$uri = $this->parser->parse($uri);
if ($uri === false) return false;
// add embedded flag to context for validators
$context->register('EmbeddedURI', $this->embedsResource);
$ok = false;
do {
// generic validation
$result = $uri->validate($config, $context);
if (!$result) break;
// chained filtering
$uri_def = $config->getDefinition('URI');
$result = $uri_def->filter($uri, $config, $context);
if (!$result) break;
// scheme-specific validation
$scheme_obj = $uri->getSchemeObj($config, $context);
if (!$scheme_obj) break;
if ($this->embedsResource && !$scheme_obj->browsable) break;
$result = $scheme_obj->validate($uri, $config, $context);
if (!$result) break;
// Post chained filtering
$result = $uri_def->postFilter($uri, $config, $context);
if (!$result) break;
// survived gauntlet
$ok = true;
} while (false);
$context->destroy('EmbeddedURI');
if (!$ok) return false;
// back to string
return $uri->toString();
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,17 @@
<?php
abstract class HTMLPurifier_AttrDef_URI_Email extends HTMLPurifier_AttrDef
{
/**
* Unpacks a mailbox into its display-name and address
*/
function unpack($string) {
// needs to be implemented
}
}
// sub-implementations
// vim: et sw=4 sts=4

View file

@ -0,0 +1,21 @@
<?php
/**
* Primitive email validation class based on the regexp found at
* http://www.regular-expressions.info/email.html
*/
class HTMLPurifier_AttrDef_URI_Email_SimpleCheck extends HTMLPurifier_AttrDef_URI_Email
{
public function validate($string, $config, $context) {
// no support for named mailboxes i.e. "Bob <bob@example.com>"
// that needs more percent encoding to be done
if ($string == '') return false;
$string = trim($string);
$result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string);
return $result ? $string : false;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,68 @@
<?php
/**
* Validates a host according to the IPv4, IPv6 and DNS (future) specifications.
*/
class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
{
/**
* Instance of HTMLPurifier_AttrDef_URI_IPv4 sub-validator
*/
protected $ipv4;
/**
* Instance of HTMLPurifier_AttrDef_URI_IPv6 sub-validator
*/
protected $ipv6;
public function __construct() {
$this->ipv4 = new HTMLPurifier_AttrDef_URI_IPv4();
$this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6();
}
public function validate($string, $config, $context) {
$length = strlen($string);
// empty hostname is OK; it's usually semantically equivalent:
// the default host as defined by a URI scheme is used:
//
// If the URI scheme defines a default for host, then that
// default applies when the host subcomponent is undefined
// or when the registered name is empty (zero length).
if ($string === '') return '';
if ($length > 1 && $string[0] === '[' && $string[$length-1] === ']') {
//IPv6
$ip = substr($string, 1, $length - 2);
$valid = $this->ipv6->validate($ip, $config, $context);
if ($valid === false) return false;
return '['. $valid . ']';
}
// need to do checks on unusual encodings too
$ipv4 = $this->ipv4->validate($string, $config, $context);
if ($ipv4 !== false) return $ipv4;
// A regular domain name.
// This breaks I18N domain names, but we don't have proper IRI support,
// so force users to insert Punycode. If there's complaining we'll
// try to fix things into an international friendly form.
// The productions describing this are:
$a = '[a-z]'; // alpha
$an = '[a-z0-9]'; // alphanum
$and = '[a-z0-9-]'; // alphanum | "-"
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
$domainlabel = "$an($and*$an)?";
// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
$toplabel = "$a($and*$an)?";
// hostname = *( domainlabel "." ) toplabel [ "." ]
$match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string);
if (!$match) return false;
return $string;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,39 @@
<?php
/**
* Validates an IPv4 address
* @author Feyd @ forums.devnetwork.net (public domain)
*/
class HTMLPurifier_AttrDef_URI_IPv4 extends HTMLPurifier_AttrDef
{
/**
* IPv4 regex, protected so that IPv6 can reuse it
*/
protected $ip4;
public function validate($aIP, $config, $context) {
if (!$this->ip4) $this->_loadRegex();
if (preg_match('#^' . $this->ip4 . '$#s', $aIP))
{
return $aIP;
}
return false;
}
/**
* Lazy load function to prevent regex from being stuffed in
* cache.
*/
protected function _loadRegex() {
$oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255
$this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})";
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,99 @@
<?php
/**
* Validates an IPv6 address.
* @author Feyd @ forums.devnetwork.net (public domain)
* @note This function requires brackets to have been removed from address
* in URI.
*/
class HTMLPurifier_AttrDef_URI_IPv6 extends HTMLPurifier_AttrDef_URI_IPv4
{
public function validate($aIP, $config, $context) {
if (!$this->ip4) $this->_loadRegex();
$original = $aIP;
$hex = '[0-9a-fA-F]';
$blk = '(?:' . $hex . '{1,4})';
$pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128
// prefix check
if (strpos($aIP, '/') !== false)
{
if (preg_match('#' . $pre . '$#s', $aIP, $find))
{
$aIP = substr($aIP, 0, 0-strlen($find[0]));
unset($find);
}
else
{
return false;
}
}
// IPv4-compatiblity check
if (preg_match('#(?<=:'.')' . $this->ip4 . '$#s', $aIP, $find))
{
$aIP = substr($aIP, 0, 0-strlen($find[0]));
$ip = explode('.', $find[0]);
$ip = array_map('dechex', $ip);
$aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3];
unset($find, $ip);
}
// compression check
$aIP = explode('::', $aIP);
$c = count($aIP);
if ($c > 2)
{
return false;
}
elseif ($c == 2)
{
list($first, $second) = $aIP;
$first = explode(':', $first);
$second = explode(':', $second);
if (count($first) + count($second) > 8)
{
return false;
}
while(count($first) < 8)
{
array_push($first, '0');
}
array_splice($first, 8 - count($second), 8, $second);
$aIP = $first;
unset($first,$second);
}
else
{
$aIP = explode(':', $aIP[0]);
}
$c = count($aIP);
if ($c != 8)
{
return false;
}
// All the pieces should be 16-bit hex strings. Are they?
foreach ($aIP as $piece)
{
if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece)))
{
return false;
}
}
return $original;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,56 @@
<?php
/**
* Processes an entire attribute array for corrections needing multiple values.
*
* Occasionally, a certain attribute will need to be removed and popped onto
* another value. Instead of creating a complex return syntax for
* HTMLPurifier_AttrDef, we just pass the whole attribute array to a
* specialized object and have that do the special work. That is the
* family of HTMLPurifier_AttrTransform.
*
* An attribute transformation can be assigned to run before or after
* HTMLPurifier_AttrDef validation. See HTMLPurifier_HTMLDefinition for
* more details.
*/
abstract class HTMLPurifier_AttrTransform
{
/**
* Abstract: makes changes to the attributes dependent on multiple values.
*
* @param $attr Assoc array of attributes, usually from
* HTMLPurifier_Token_Tag::$attr
* @param $config Mandatory HTMLPurifier_Config object.
* @param $context Mandatory HTMLPurifier_Context object
* @returns Processed attribute array.
*/
abstract public function transform($attr, $config, $context);
/**
* Prepends CSS properties to the style attribute, creating the
* attribute if it doesn't exist.
* @param $attr Attribute array to process (passed by reference)
* @param $css CSS to prepend
*/
public function prependCSS(&$attr, $css) {
$attr['style'] = isset($attr['style']) ? $attr['style'] : '';
$attr['style'] = $css . $attr['style'];
}
/**
* Retrieves and removes an attribute
* @param $attr Attribute array to process (passed by reference)
* @param $key Key of attribute to confiscate
*/
public function confiscateAttr(&$attr, $key) {
if (!isset($attr[$key])) return null;
$value = $attr[$key];
unset($attr[$key]);
return $value;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,23 @@
<?php
/**
* Pre-transform that changes proprietary background attribute to CSS.
*/
class HTMLPurifier_AttrTransform_Background extends HTMLPurifier_AttrTransform {
public function transform($attr, $config, $context) {
if (!isset($attr['background'])) return $attr;
$background = $this->confiscateAttr($attr, 'background');
// some validation should happen here
$this->prependCSS($attr, "background-image:url($background);");
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,19 @@
<?php
// this MUST be placed in post, as it assumes that any value in dir is valid
/**
* Post-trasnform that ensures that bdo tags have the dir attribute set.
*/
class HTMLPurifier_AttrTransform_BdoDir extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
if (isset($attr['dir'])) return $attr;
$attr['dir'] = $config->get('Attr.DefaultTextDir');
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,23 @@
<?php
/**
* Pre-transform that changes deprecated bgcolor attribute to CSS.
*/
class HTMLPurifier_AttrTransform_BgColor extends HTMLPurifier_AttrTransform {
public function transform($attr, $config, $context) {
if (!isset($attr['bgcolor'])) return $attr;
$bgcolor = $this->confiscateAttr($attr, 'bgcolor');
// some validation should happen here
$this->prependCSS($attr, "background-color:$bgcolor;");
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,36 @@
<?php
/**
* Pre-transform that changes converts a boolean attribute to fixed CSS
*/
class HTMLPurifier_AttrTransform_BoolToCSS extends HTMLPurifier_AttrTransform {
/**
* Name of boolean attribute that is trigger
*/
protected $attr;
/**
* CSS declarations to add to style, needs trailing semicolon
*/
protected $css;
/**
* @param $attr string attribute name to convert from
* @param $css string CSS declarations to add to style (needs semicolon)
*/
public function __construct($attr, $css) {
$this->attr = $attr;
$this->css = $css;
}
public function transform($attr, $config, $context) {
if (!isset($attr[$this->attr])) return $attr;
unset($attr[$this->attr]);
$this->prependCSS($attr, $this->css);
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,18 @@
<?php
/**
* Pre-transform that changes deprecated border attribute to CSS.
*/
class HTMLPurifier_AttrTransform_Border extends HTMLPurifier_AttrTransform {
public function transform($attr, $config, $context) {
if (!isset($attr['border'])) return $attr;
$border_width = $this->confiscateAttr($attr, 'border');
// some validation should happen here
$this->prependCSS($attr, "border:{$border_width}px solid;");
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,58 @@
<?php
/**
* Generic pre-transform that converts an attribute with a fixed number of
* values (enumerated) to CSS.
*/
class HTMLPurifier_AttrTransform_EnumToCSS extends HTMLPurifier_AttrTransform {
/**
* Name of attribute to transform from
*/
protected $attr;
/**
* Lookup array of attribute values to CSS
*/
protected $enumToCSS = array();
/**
* Case sensitivity of the matching
* @warning Currently can only be guaranteed to work with ASCII
* values.
*/
protected $caseSensitive = false;
/**
* @param $attr String attribute name to transform from
* @param $enumToCSS Lookup array of attribute values to CSS
* @param $case_sensitive Boolean case sensitivity indicator, default false
*/
public function __construct($attr, $enum_to_css, $case_sensitive = false) {
$this->attr = $attr;
$this->enumToCSS = $enum_to_css;
$this->caseSensitive = (bool) $case_sensitive;
}
public function transform($attr, $config, $context) {
if (!isset($attr[$this->attr])) return $attr;
$value = trim($attr[$this->attr]);
unset($attr[$this->attr]);
if (!$this->caseSensitive) $value = strtolower($value);
if (!isset($this->enumToCSS[$value])) {
return $attr;
}
$this->prependCSS($attr, $this->enumToCSS[$value]);
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,43 @@
<?php
// must be called POST validation
/**
* Transform that supplies default values for the src and alt attributes
* in img tags, as well as prevents the img tag from being removed
* because of a missing alt tag. This needs to be registered as both
* a pre and post attribute transform.
*/
class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
$src = true;
if (!isset($attr['src'])) {
if ($config->get('Core.RemoveInvalidImg')) return $attr;
$attr['src'] = $config->get('Attr.DefaultInvalidImage');
$src = false;
}
if (!isset($attr['alt'])) {
if ($src) {
$alt = $config->get('Attr.DefaultImageAlt');
if ($alt === null) {
// truncate if the alt is too long
$attr['alt'] = substr(basename($attr['src']),0,40);
} else {
$attr['alt'] = $alt;
}
} else {
$attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt');
}
}
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,44 @@
<?php
/**
* Pre-transform that changes deprecated hspace and vspace attributes to CSS
*/
class HTMLPurifier_AttrTransform_ImgSpace extends HTMLPurifier_AttrTransform {
protected $attr;
protected $css = array(
'hspace' => array('left', 'right'),
'vspace' => array('top', 'bottom')
);
public function __construct($attr) {
$this->attr = $attr;
if (!isset($this->css[$attr])) {
trigger_error(htmlspecialchars($attr) . ' is not valid space attribute');
}
}
public function transform($attr, $config, $context) {
if (!isset($attr[$this->attr])) return $attr;
$width = $this->confiscateAttr($attr, $this->attr);
// some validation could happen here
if (!isset($this->css[$this->attr])) return $attr;
$style = '';
foreach ($this->css[$this->attr] as $suffix) {
$property = "margin-$suffix";
$style .= "$property:{$width}px;";
}
$this->prependCSS($attr, $style);
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,40 @@
<?php
/**
* Performs miscellaneous cross attribute validation and filtering for
* input elements. This is meant to be a post-transform.
*/
class HTMLPurifier_AttrTransform_Input extends HTMLPurifier_AttrTransform {
protected $pixels;
public function __construct() {
$this->pixels = new HTMLPurifier_AttrDef_HTML_Pixels();
}
public function transform($attr, $config, $context) {
if (!isset($attr['type'])) $t = 'text';
else $t = strtolower($attr['type']);
if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') {
unset($attr['checked']);
}
if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') {
unset($attr['maxlength']);
}
if (isset($attr['size']) && $t !== 'text' && $t !== 'password') {
$result = $this->pixels->validate($attr['size'], $config, $context);
if ($result === false) unset($attr['size']);
else $attr['size'] = $result;
}
if (isset($attr['src']) && $t !== 'image') {
unset($attr['src']);
}
if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) {
$attr['value'] = '';
}
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,28 @@
<?php
/**
* Post-transform that copies lang's value to xml:lang (and vice-versa)
* @note Theoretically speaking, this could be a pre-transform, but putting
* post is more efficient.
*/
class HTMLPurifier_AttrTransform_Lang extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
$lang = isset($attr['lang']) ? $attr['lang'] : false;
$xml_lang = isset($attr['xml:lang']) ? $attr['xml:lang'] : false;
if ($lang !== false && $xml_lang === false) {
$attr['xml:lang'] = $lang;
} elseif ($xml_lang !== false) {
$attr['lang'] = $xml_lang;
}
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,27 @@
<?php
/**
* Class for handling width/height length attribute transformations to CSS
*/
class HTMLPurifier_AttrTransform_Length extends HTMLPurifier_AttrTransform
{
protected $name;
protected $cssName;
public function __construct($name, $css_name = null) {
$this->name = $name;
$this->cssName = $css_name ? $css_name : $name;
}
public function transform($attr, $config, $context) {
if (!isset($attr[$this->name])) return $attr;
$length = $this->confiscateAttr($attr, $this->name);
if(ctype_digit($length)) $length .= 'px';
$this->prependCSS($attr, $this->cssName . ":$length;");
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,21 @@
<?php
/**
* Pre-transform that changes deprecated name attribute to ID if necessary
*/
class HTMLPurifier_AttrTransform_Name extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
// Abort early if we're using relaxed definition of name
if ($config->get('HTML.Attr.Name.UseCDATA')) return $attr;
if (!isset($attr['name'])) return $attr;
$id = $this->confiscateAttr($attr, 'name');
if ( isset($attr['id'])) return $attr;
$attr['id'] = $id;
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,27 @@
<?php
/**
* Post-transform that performs validation to the name attribute; if
* it is present with an equivalent id attribute, it is passed through;
* otherwise validation is performed.
*/
class HTMLPurifier_AttrTransform_NameSync extends HTMLPurifier_AttrTransform
{
public function __construct() {
$this->idDef = new HTMLPurifier_AttrDef_HTML_ID();
}
public function transform($attr, $config, $context) {
if (!isset($attr['name'])) return $attr;
$name = $attr['name'];
if (isset($attr['id']) && $attr['id'] === $name) return $attr;
$result = $this->idDef->validate($name, $config, $context);
if ($result === false) unset($attr['name']);
else $attr['name'] = $result;
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,41 @@
<?php
// must be called POST validation
/**
* Adds rel="nofollow" to all outbound links. This transform is
* only attached if Attr.Nofollow is TRUE.
*/
class HTMLPurifier_AttrTransform_Nofollow extends HTMLPurifier_AttrTransform
{
private $parser;
public function __construct() {
$this->parser = new HTMLPurifier_URIParser();
}
public function transform($attr, $config, $context) {
if (!isset($attr['href'])) {
return $attr;
}
// XXX Kind of inefficient
$url = $this->parser->parse($attr['href']);
$scheme = $url->getSchemeObj($config, $context);
if (!is_null($url->host) && $scheme !== false && $scheme->browsable) {
if (isset($attr['rel'])) {
$attr['rel'] .= ' nofollow';
} else {
$attr['rel'] = 'nofollow';
}
}
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,15 @@
<?php
class HTMLPurifier_AttrTransform_SafeEmbed extends HTMLPurifier_AttrTransform
{
public $name = "SafeEmbed";
public function transform($attr, $config, $context) {
$attr['allowscriptaccess'] = 'never';
$attr['allownetworking'] = 'internal';
$attr['type'] = 'application/x-shockwave-flash';
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,16 @@
<?php
/**
* Writes default type for all objects. Currently only supports flash.
*/
class HTMLPurifier_AttrTransform_SafeObject extends HTMLPurifier_AttrTransform
{
public $name = "SafeObject";
function transform($attr, $config, $context) {
if (!isset($attr['type'])) $attr['type'] = 'application/x-shockwave-flash';
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,64 @@
<?php
/**
* Validates name/value pairs in param tags to be used in safe objects. This
* will only allow name values it recognizes, and pre-fill certain attributes
* with required values.
*
* @note
* This class only supports Flash. In the future, Quicktime support
* may be added.
*
* @warning
* This class expects an injector to add the necessary parameters tags.
*/
class HTMLPurifier_AttrTransform_SafeParam extends HTMLPurifier_AttrTransform
{
public $name = "SafeParam";
private $uri;
public function __construct() {
$this->uri = new HTMLPurifier_AttrDef_URI(true); // embedded
$this->wmode = new HTMLPurifier_AttrDef_Enum(array('window', 'opaque', 'transparent'));
}
public function transform($attr, $config, $context) {
// If we add support for other objects, we'll need to alter the
// transforms.
switch ($attr['name']) {
// application/x-shockwave-flash
// Keep this synchronized with Injector/SafeObject.php
case 'allowScriptAccess':
$attr['value'] = 'never';
break;
case 'allowNetworking':
$attr['value'] = 'internal';
break;
case 'allowFullScreen':
if ($config->get('HTML.FlashAllowFullScreen')) {
$attr['value'] = ($attr['value'] == 'true') ? 'true' : 'false';
} else {
$attr['value'] = 'false';
}
break;
case 'wmode':
$attr['value'] = $this->wmode->validate($attr['value'], $config, $context);
break;
case 'movie':
case 'src':
$attr['name'] = "movie";
$attr['value'] = $this->uri->validate($attr['value'], $config, $context);
break;
case 'flashvars':
// we're going to allow arbitrary inputs to the SWF, on
// the reasoning that it could only hack the SWF, not us.
break;
// add other cases to support other param name/value pairs
default:
$attr['name'] = $attr['value'] = null;
}
return $attr;
}
}
// vim: et sw=4 sts=4

View file

@ -0,0 +1,16 @@
<?php
/**
* Implements required attribute stipulation for <script>
*/
class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform
{
public function transform($attr, $config, $context) {
if (!isset($attr['type'])) {
$attr['type'] = 'text/javascript';
}
return $attr;
}
}
// vim: et sw=4 sts=4

Some files were not shown because too many files have changed in this diff Show more