Completed
Push — master ( 579af5...b29473 )
by Oleg
07:53
created

XssFilter   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 93
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 1
Metric Value
wmc 13
lcom 0
cbo 1
dl 0
loc 93
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
B pre() 0 18 6
B doXssClean() 0 51 6
A post() 0 4 1
1
<?php /** XssFilterMicro */
2
3
namespace Micro\Filter;
4
5
/**
6
 * Class XssFilter
7
 *
8
 * @author Opeykin A. <andrey.opeykin.ru> &; <[email protected]>
9
 * @package Micro
10
 * @subpackage Filter
11
 * @version 1.0
12
 * @since 1.0
13
 */
14
class XssFilter extends Filter
15
{
16
    /**
17
     * @inheritdoc
18
     */
19
    public function pre(array $params)
0 ignored issues
show
Coding Style introduced by
pre uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
pre uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
pre uses the super-global variable $_COOKIE which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
pre uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
20
    {
21
        $clean = trim(strtoupper(!empty($params['clean']) ? $params['clean'] : '*'));
22
23
        $data = ['GET' => &$_GET, 'POST' => &$_POST, 'COOKIE' => &$_COOKIE, 'FILES' => &$_FILES];
24
        if ($clean === '*') {
25
            $clean = 'GET,POST,COOKIE,FILES';
26
        }
27
        $dataForClean = explode(',', $clean);
28
29
        foreach ($dataForClean as $key => &$value) {
30
            if (!empty($data[$key]) && count($data[$key])) {
31
                $value = $this->doXssClean($data[$key]);
32
            }
33
        }
34
35
        return true;
36
    }
37
38
    /**
39
     * Do XSS Clean
40
     *
41
     * @access private
42
     *
43
     * @param array|mixed $data data for check
44
     *
45
     * @return mixed
46
     */
47
    private function doXssClean($data)
48
    {
49
        if (is_array($data) && count($data)) {
50
            foreach ($data as $k => &$v) {
51
                $v = $this->doXssClean($data[$k]);
52
            }
53
54
            return $data;
55
        }
56
57
        if (trim($data) === '') {
58
            return $data;
59
        }
60
61
        // xss_clean function from Kohana framework 2.3.1
62
        $data = str_replace(['&amp;', '&lt;', '&gt;'], ['&amp;amp;', '&amp;lt;', '&amp;gt;'], $data);
63
        $data = preg_replace('/(&#*\w+)[\x00-\x20]+;/u', '$1;', $data);
64
        $data = preg_replace('/(&#x*[0-9A-F]+);*/iu', '$1;', $data);
65
        $data = html_entity_decode($data, ENT_COMPAT, 'UTF-8');
66
67
        // Remove any attribute starting with "on" or xmlns
68
        $data = preg_replace('#(<[^>]+?[\x00-\x20"\'])(?:on|xmlns)[^>]*+>#iu', '$1>', $data);
69
70
        // Remove javascript: and vbscript: protocols
71
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([`\'"]*)[\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu',
72
            '$1=$2nojavascript...', $data);
73
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iu',
74
            '$1=$2novbscript...', $data);
75
        $data = preg_replace('#([a-z]*)[\x00-\x20]*=([\'"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#u',
76
            '$1=$2nomozbinding...', $data);
77
78
        // Only works in IE: <span style="width: expression(alert('Ping!'));"></span>
79
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?expression[\x00-\x20]*\([^>]*+>#i',
80
            '$1>', $data);
81
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?behaviour[\x00-\x20]*\([^>]*+>#i',
82
            '$1>', $data);
83
        $data = preg_replace('#(<[^>]+?)style[\x00-\x20]*=[\x00-\x20]*[`\'"]*.*?s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*+>#iu',
84
            '$1>', $data);
85
86
        // Remove namespaced elements (we do not need them)
87
        $data = preg_replace('#</*\w+:\w[^>]*+>#i', '', $data);
88
89
        do {
90
            // Remove really unwanted tags
91
            $old_data = $data;
92
            $data = preg_replace('#</*(?:applet|b(?:ase|gsound|link)|embed|frame(?:set)?|i(?:frame|layer)|l(?:ayer|ink)|meta|object|s(?:cript|tyle)|title|xml)[^>]*+>#i',
93
                '', $data);
94
        } while ($old_data !== $data);
95
96
        return $data;
97
    }
98
99
    /**
100
     * @inheritdoc
101
     */
102
    public function post(array $params)
103
    {
104
        return $params['data'];
105
    }
106
}
107