SafeObject.php 3.9 KB

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