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

Commit 3626c13

Browse files
committed
Merge pull request #95 from fredemmott/unsaferenderable-alwaysvalidchild
Add XHPUnsafeRenderable, XHPAlwaysValidChild, MIGRATING.md
2 parents 5d30cc7 + 4c786dd commit 3626c13

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

MIGRATING.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
Ideally your entire HTML tree should be generated with XHP, however this is
2+
often impractical when migrating an existing project to XHP.
3+
4+
To work around this, we provide two interfaces that allow you to embed
5+
arbitrary HTML code at any point in the tree; using these bypasses XHPs
6+
security and/or correctness checks, so be very careful about using them. For this
7+
reason, we have only provided interfaces, instead of concrete implementations.
8+
9+
The Interfaces
10+
==============
11+
12+
XHPUnsafeRenderable
13+
-------------------
14+
15+
**This is incredibly dangerous!**
16+
17+
```PHP
18+
interface XHPUnsafeRenderable {
19+
public function toHTMLString();
20+
}
21+
```
22+
23+
This marks an object as being able to provide a raw HTML string. It is up to
24+
you to make sure that the string is safe - eg no XSS vulnerabilties, and so on.
25+
26+
XHPAlwaysValidChild
27+
-------------------
28+
29+
This makes an object pass any child element validation rules except for
30+
"no children". While this is less likely to lead to a security issue, it may
31+
break the assumptions of the parent object, so should be used as little as
32+
possible.
33+
34+
Usage
35+
=====
36+
37+
To actually embed arbitrary HTML anywhere:
38+
39+
```PHP
40+
final class POTENTIAL_XSS_SECURITY_HOLE
41+
implements XHPAlwaysValidChild, XHPUnsafeRenderable {
42+
private $html;
43+
44+
public function __construct($html) {
45+
$this->html = $html;
46+
}
47+
48+
public function toHTMLString() {
49+
return $this->html;
50+
}
51+
}
52+
53+
// The function previously known as HTML()
54+
function POTENTIAL_XSS_SECURITY_HOLE($html) {
55+
return new POTENTIAL_XSS_SECURITY_HOLE($html);
56+
}
57+
58+
$xhp =
59+
<div>
60+
Hello, world!
61+
{POTENTIAL_XSS_SECURITY_HOLE('<b>herp derp</b>')}
62+
</div>;
63+
```
64+
65+
We **strongly** recommend making much stricter interfaces instead - for example,
66+
instead of writing:
67+
68+
```
69+
POTENTIAL_XSS_SECURITY_HOLE(render_markdown($markdown));
70+
```
71+
72+
We suggest not implementing POTENTIAL_XSS_SECURITY_HOLE at all, and instead
73+
doing something like the following:
74+
75+
```PHP
76+
// Probably don't need XHPAlwaysValidChild - this is likely to be in a <div />
77+
// or other similarly liberal container
78+
final class XHPMarkdown implements XHPUnsafeRenderable {
79+
private $markdown;
80+
public function __construct($markdown) {
81+
$this->markdown = $markdown;
82+
}
83+
84+
public function toHTMLString() {
85+
return render_markdown($this->markdown);
86+
}
87+
}
88+
89+
function xhp_markdown($markdown) {
90+
return new XHPMarkdown($markdown);
91+
}
92+
```

src/core.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ final public function __toString() {
4747
final protected static function renderChild($child) {
4848
if ($child instanceof :xhp) {
4949
return $child->toString();
50+
} else if ($child instanceof XHPUnsafeRenderable) {
51+
return $child->toHTMLString();
5052
} else if (is_array($child)) {
5153
throw new XHPRenderArrayException('Can not render array!');
5254
} else {
@@ -683,6 +685,10 @@ final protected function validateChildren() {
683685
$ii = 0;
684686
if (!$this->validateChildrenExpression($decl, $ii) ||
685687
$ii < count($this->children)) {
688+
if (isset($this->children[$ii])
689+
&& $this->children[$ii] instanceof XHPAlwaysValidChild) {
690+
return;
691+
}
686692
throw new XHPInvalidChildrenException($this, $ii);
687693
}
688694
}
@@ -1036,3 +1042,26 @@ public function __construct($that, $index) {
10361042
);
10371043
}
10381044
}
1045+
1046+
/**
1047+
* INCREDIBLY DANGEROUS: Marks an object as a valid child of *any* element,
1048+
* ignoring any child rules.
1049+
*
1050+
* This is useful when migrating to XHP as it allows you to embed non-XHP
1051+
* content, usually in combination with XHPUnsafeRenderable; see MIGRATING.md
1052+
* for more information.
1053+
*/
1054+
interface XHPAlwaysValidChild {
1055+
}
1056+
1057+
/**
1058+
* INCREDIBLY DANGEROUS: Marks an object as being able to provide an HTML
1059+
* string.
1060+
*
1061+
* This is useful when migrating to XHP as it allows you to embed non-XHP
1062+
* content, usually in combination with XHPAlwaysValidChild; see MIGRATING.md
1063+
* for more information.
1064+
*/
1065+
interface XHPUnsafeRenderable {
1066+
public function toHTMLString();
1067+
}

0 commit comments

Comments
 (0)