XssBehavior   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 184
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 14
lcom 1
cbo 4
dl 0
loc 184
rs 10
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A events() 0 6 1
D clearByXssAttributes() 0 138 13
1
<?php
2
3
namespace yiicod\base\models\behaviors;
4
5
use HTMLPurifier;
6
use HTMLPurifier_Config;
7
use yii\base\Behavior;
8
use yii\base\Event;
9
use yii\db\ActiveRecord;
10
11
/**
12
 * Class XssBehavior
13
 *
14
 * @package yiicod\base\models\behaviors
15
 */
16
class XssBehavior extends Behavior
17
{
18
    /**
19
     * Exlude from purify attributes
20
     *
21
     * @var array
22
     */
23
    public $attributesExclude = [];
24
25
    /**
26
     * Allowed purify filters
27
     *
28
     * @var array
29
     */
30
    public $allowedFilter = [];
31
32
    /**
33
     * Config filter
34
     *
35
     * @var array
36
     */
37
    public $configFilter = [];
38
39
    /**
40
     * Def filter
41
     *
42
     * @var array
43
     */
44
    public $defFilter = [];
45
46
    /**
47
     * Behavior event
48
     *
49
     * @return array
50
     */
51
    public function events()
52
    {
53
        return [
54
            ActiveRecord::EVENT_BEFORE_VALIDATE => 'clearByXssAttributes',
55
        ];
56
    }
57
58
    /**
59
     * Clear model attributes
60
     */
61
    public function clearByXssAttributes()
62
    {
63
        // EDIT: modify this to whatever you need.
64
        $allowedAttrs = ['id', 'class'];
65
        $allowed = [
66
            'img[src|alt|title|width|height|style|data-mce-src|data-mce-json]',
67
            'figure', 'figcaption', 'small[style]',
68
            'video[src|type|width|height|poster|preload|controls]', 'source[src|type]',
69
            'a[href|target]',
70
            'iframe[width|height|src|frameborder|allowfullscreen]',
71
            'strong', 'b', 'i', 'u', 'em', 'br', 'font',
72
            'h1[style]', 'h2[style]', 'h3[style]', 'h4[style]', 'h5[style]', 'h6[style]',
73
            'p[style]', 'div[style]', 'center', 'address[style]',
74
            'span[style]', 'pre[style]',
75
            'ul[style|class]', 'ol[style|class]', 'li[style|class]',
76
            'table[width|height|border|style]', 'th[width|height|border|style]',
77
            'tr[width|height|border|style]', 'td[width|height|border|style]',
78
            'hr[style|class]', 'section[style|class]', 'nav[style|class]', 'article[style|class]',
79
            'aside[style|class]', 'header[style|class]', 'footer[style|class]',
80
            'address', 'hgroup', 'figure', 'figcaption',
81
            'video[src|type|width|height|poster|preload|controls|loop|autoplay]',
82
            's', 'var', 'sub', 'sup', 'mark', 'wbr', 'ins', 'del', 'blockquote', 'q', '*[style|class|id|width|height|alt|title|target|src]',
83
        ];
84
        foreach ($allowed as $key => $element) {
85
            foreach ($allowedAttrs as $attr) {
86
                if (false === strpos($element, $attr . '|') && false === strpos($element, '|' . $attr)) {
87
                    $allowed[$key] = $element = str_replace(']', '|' . $attr . ']', $element);
88
                }
89
            }
90
        }
91
92
        if (is_callable($this->allowedFilter)) {
93
            $allowed = call_user_func_array($this->allowedFilter, ['self' => $this, 'allowed' => $allowed]);
94
        }
95
96
        $config = HTMLPurifier_Config::createDefault();
97
        $config->set('HTML.Doctype', 'HTML 4.01 Transitional');
98
        $config->set('CSS.AllowTricky', true);
99
        $config->set('Cache.SerializerPath', '/tmp');
100
        // Allow iframes from:
101
        // o YouTube.com
102
        // o Vimeo.com
103
        $config->set('HTML.SafeIframe', true);
104
        $config->set('URI.SafeIframeRegexp', '%^(https?:)?(http?:)?//(www.youtube.|player.vimeo.|maps.google.|www.slideshare.)%');
105
//        $config->set('URI.SafeIframeRegexp', '%^(http:|https:)?//(www.youtube(?:-nocookie)?.com/embed/|player.vimeo.com/video/)%');
106
        $config->set('Attr.AllowedFrameTargets', [
107
            '_blank',
108
            '_self',
109
            '_parent',
110
            '_top',
111
        ]);
112
        $config->set('URI.AllowedSchemes', [
113
            'http' => true,
114
            'https' => true,
115
            'mailto' => true,
116
            'target' => true,
117
            'ftp' => true,
118
        ]);
119
        $config->set('Attr.EnableID', true);
120
        $config->set('HTML.Allowed', implode(',', $allowed));
121
        // Set some HTML5 properties
122
        if ($def = $config->getHTMLDefinition(true)) {
123
            // http://developers.whatwg.org/sections.html
124
            $def->addElement('section', 'Block', 'Flow', 'Common');
125
            $def->addElement('nav', 'Block', 'Flow', 'Common');
126
            $def->addElement('article', 'Block', 'Flow', 'Common');
127
            $def->addElement('aside', 'Block', 'Flow', 'Common');
128
            $def->addElement('header', 'Block', 'Flow', 'Common');
129
            $def->addElement('footer', 'Block', 'Flow', 'Common');
130
            // Content model actually excludes several tags, not modelled here
131
            $def->addElement('address', 'Block', 'Flow', 'Common');
132
            $def->addElement('hgroup', 'Block', 'Required: h1 | h2 | h3 | h4 | h5 | h6', 'Common');
133
            // http://developers.whatwg.org/grouping-content.html
134
            $def->addElement('figure', 'Block', 'Optional: (figcaption, Flow) | (Flow, figcaption) | Flow', 'Common');
135
            $def->addElement('figcaption', 'Inline', 'Flow', 'Common');
136
            // http://developers.whatwg.org/the-video-element.html#the-video-element
137
            $def->addElement('video', 'Block', 'Optional: (source, Flow) | (Flow, source) | Flow', 'Common', [
138
                'src' => 'URI',
139
                'type' => 'Text',
140
                'width' => 'Length',
141
                'height' => 'Length',
142
                'poster' => 'URI',
143
                'preload' => 'Enum#auto,metadata,none',
144
                'controls' => 'Bool',
145
            ]);
146
            $def->addElement('source', 'Block', 'Flow', 'Common', [
147
                'src' => 'URI',
148
                'type' => 'Text',
149
            ]);
150
            // http://developers.whatwg.org/text-level-semantics.html
151
            $def->addElement('s', 'Inline', 'Inline', 'Common');
152
            $def->addElement('var', 'Inline', 'Inline', 'Common');
153
            $def->addElement('sub', 'Inline', 'Inline', 'Common');
154
            $def->addElement('sup', 'Inline', 'Inline', 'Common');
155
            $def->addElement('mark', 'Inline', 'Inline', 'Common');
156
            $def->addElement('wbr', 'Inline', 'Empty', 'Core');
157
            // http://developers.whatwg.org/edits.html
158
            $def->addElement('ins', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
159
            $def->addElement('del', 'Block', 'Flow', 'Common', ['cite' => 'URI', 'datetime' => 'CDATA']);
160
            // TinyMCE
161
            $def->addAttribute('img', 'data-mce-src', 'Text');
162
            $def->addAttribute('img', 'data-mce-json', 'Text');
163
            //video
164
            $def->addAttribute('video', 'loop', 'Text');
165
            $def->addAttribute('video', 'autoplay', 'Text');
166
            // Others
167
            $def->addAttribute('iframe', 'allowfullscreen', 'Bool');
168
            $def->addAttribute('table', 'height', 'Text');
169
            $def->addAttribute('td', 'border', 'Text');
170
            $def->addAttribute('th', 'border', 'Text');
171
            $def->addAttribute('tr', 'width', 'Text');
172
            $def->addAttribute('tr', 'height', 'Text');
173
            $def->addAttribute('tr', 'border', 'Text');
174
175
            if (is_callable($this->defFilter)) {
176
                $def = call_user_func_array($this->defFilter, ['self' => $this, 'def' => $def]);
0 ignored issues
show
Unused Code introduced by
$def is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
177
            }
178
        }
179
        if (is_callable($this->configFilter)) {
180
            $config = call_user_func_array($this->configFilter, ['self' => $this, 'config' => $config]);
181
        }
182
        $p = new HTMLPurifier($config);
183
184
        $attributes = [];
185
        foreach ($this->owner->getAttributes() as $key => $value) {
186
            if (!in_array($key, $this->attributesExclude)) {
187
                if (null !== $value) {
188
                    if (is_array($value)) {
189
                        $attributes[$key] = @unserialize($p->purify(serialize($value)));
190
                    } else {
191
                        $attributes[$key] = $p->purify($value);
192
                    }
193
                }
194
            }
195
        }
196
197
        $this->owner->setAttributes($attributes);
198
    }
199
}
200