Skip to content
This repository has been archived by the owner on Apr 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #95 from fredemmott/unsaferenderable-alwaysvalidchild
Browse files Browse the repository at this point in the history
Add XHPUnsafeRenderable, XHPAlwaysValidChild, MIGRATING.md
  • Loading branch information
fredemmott committed Feb 2, 2015
2 parents 5d30cc7 + 4c786dd commit 3626c13
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 0 deletions.
92 changes: 92 additions & 0 deletions MIGRATING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
Ideally your entire HTML tree should be generated with XHP, however this is
often impractical when migrating an existing project to XHP.

To work around this, we provide two interfaces that allow you to embed
arbitrary HTML code at any point in the tree; using these bypasses XHPs
security and/or correctness checks, so be very careful about using them. For this
reason, we have only provided interfaces, instead of concrete implementations.

The Interfaces
==============

XHPUnsafeRenderable
-------------------

**This is incredibly dangerous!**

```PHP
interface XHPUnsafeRenderable {
public function toHTMLString();
}
```

This marks an object as being able to provide a raw HTML string. It is up to
you to make sure that the string is safe - eg no XSS vulnerabilties, and so on.

XHPAlwaysValidChild
-------------------

This makes an object pass any child element validation rules except for
"no children". While this is less likely to lead to a security issue, it may
break the assumptions of the parent object, so should be used as little as
possible.

Usage
=====

To actually embed arbitrary HTML anywhere:

```PHP
final class POTENTIAL_XSS_SECURITY_HOLE
implements XHPAlwaysValidChild, XHPUnsafeRenderable {
private $html;

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

public function toHTMLString() {
return $this->html;
}
}

// The function previously known as HTML()
function POTENTIAL_XSS_SECURITY_HOLE($html) {
return new POTENTIAL_XSS_SECURITY_HOLE($html);
}

$xhp =
<div>
Hello, world!
{POTENTIAL_XSS_SECURITY_HOLE('<b>herp derp</b>')}
</div>;
```

We **strongly** recommend making much stricter interfaces instead - for example,
instead of writing:

```
POTENTIAL_XSS_SECURITY_HOLE(render_markdown($markdown));
```

We suggest not implementing POTENTIAL_XSS_SECURITY_HOLE at all, and instead
doing something like the following:

```PHP
// Probably don't need XHPAlwaysValidChild - this is likely to be in a <div />
// or other similarly liberal container
final class XHPMarkdown implements XHPUnsafeRenderable {
private $markdown;
public function __construct($markdown) {
$this->markdown = $markdown;
}

public function toHTMLString() {
return render_markdown($this->markdown);
}
}

function xhp_markdown($markdown) {
return new XHPMarkdown($markdown);
}
```
29 changes: 29 additions & 0 deletions src/core.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ final public function __toString() {
final protected static function renderChild($child) {
if ($child instanceof :xhp) {
return $child->toString();
} else if ($child instanceof XHPUnsafeRenderable) {
return $child->toHTMLString();
} else if (is_array($child)) {
throw new XHPRenderArrayException('Can not render array!');
} else {
Expand Down Expand Up @@ -683,6 +685,10 @@ final protected function validateChildren() {
$ii = 0;
if (!$this->validateChildrenExpression($decl, $ii) ||
$ii < count($this->children)) {
if (isset($this->children[$ii])
&& $this->children[$ii] instanceof XHPAlwaysValidChild) {
return;
}
throw new XHPInvalidChildrenException($this, $ii);
}
}
Expand Down Expand Up @@ -1036,3 +1042,26 @@ public function __construct($that, $index) {
);
}
}

/**
* INCREDIBLY DANGEROUS: Marks an object as a valid child of *any* element,
* ignoring any child rules.
*
* This is useful when migrating to XHP as it allows you to embed non-XHP
* content, usually in combination with XHPUnsafeRenderable; see MIGRATING.md
* for more information.
*/
interface XHPAlwaysValidChild {
}

/**
* INCREDIBLY DANGEROUS: Marks an object as being able to provide an HTML
* string.
*
* This is useful when migrating to XHP as it allows you to embed non-XHP
* content, usually in combination with XHPAlwaysValidChild; see MIGRATING.md
* for more information.
*/
interface XHPUnsafeRenderable {
public function toHTMLString();
}

0 comments on commit 3626c13

Please sign in to comment.