1: <?php
2:
3: /**
4: * Adds important param elements to inside of object in order to make
5: * things safe.
6: */
7: class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
8: {
9: /**
10: * @type string
11: */
12: public $name = 'SafeObject';
13:
14: /**
15: * @type array
16: */
17: public $needed = array('object', 'param');
18:
19: /**
20: * @type array
21: */
22: protected $objectStack = array();
23:
24: /**
25: * @type array
26: */
27: protected $paramStack = array();
28:
29: /**
30: * Keep this synchronized with AttrTransform/SafeParam.php.
31: * @type array
32: */
33: protected $addParam = array(
34: 'allowScriptAccess' => 'never',
35: 'allowNetworking' => 'internal',
36: );
37:
38: /**
39: * These are all lower-case keys.
40: * @type array
41: */
42: protected $allowedParam = array(
43: 'wmode' => true,
44: 'movie' => true,
45: 'flashvars' => true,
46: 'src' => true,
47: 'allowfullscreen' => true, // if omitted, assume to be 'false'
48: );
49:
50: /**
51: * @param HTMLPurifier_Config $config
52: * @param HTMLPurifier_Context $context
53: * @return void
54: */
55: public function prepare($config, $context)
56: {
57: parent::prepare($config, $context);
58: }
59:
60: /**
61: * @param HTMLPurifier_Token $token
62: */
63: public function handleElement(&$token)
64: {
65: if ($token->name == 'object') {
66: $this->objectStack[] = $token;
67: $this->paramStack[] = array();
68: $new = array($token);
69: foreach ($this->addParam as $name => $value) {
70: $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value));
71: }
72: $token = $new;
73: } elseif ($token->name == 'param') {
74: $nest = count($this->currentNesting) - 1;
75: if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') {
76: $i = count($this->objectStack) - 1;
77: if (!isset($token->attr['name'])) {
78: $token = false;
79: return;
80: }
81: $n = $token->attr['name'];
82: // We need this fix because YouTube doesn't supply a data
83: // attribute, which we need if a type is specified. This is
84: // *very* Flash specific.
85: if (!isset($this->objectStack[$i]->attr['data']) &&
86: ($token->attr['name'] == 'movie' || $token->attr['name'] == 'src')
87: ) {
88: $this->objectStack[$i]->attr['data'] = $token->attr['value'];
89: }
90: // Check if the parameter is the correct value but has not
91: // already been added
92: if (!isset($this->paramStack[$i][$n]) &&
93: isset($this->addParam[$n]) &&
94: $token->attr['name'] === $this->addParam[$n]) {
95: // keep token, and add to param stack
96: $this->paramStack[$i][$n] = true;
97: } elseif (isset($this->allowedParam[strtolower($n)])) {
98: // keep token, don't do anything to it
99: // (could possibly check for duplicates here)
100: // Note: In principle, parameters should be case sensitive.
101: // But it seems they are not really; so accept any case.
102: } else {
103: $token = false;
104: }
105: } else {
106: // not directly inside an object, DENY!
107: $token = false;
108: }
109: }
110: }
111:
112: public function handleEnd(&$token)
113: {
114: // This is the WRONG way of handling the object and param stacks;
115: // we should be inserting them directly on the relevant object tokens
116: // so that the global stack handling handles it.
117: if ($token->name == 'object') {
118: array_pop($this->objectStack);
119: array_pop($this->paramStack);
120: }
121: }
122: }
123:
124: // vim: et sw=4 sts=4
125: