Skip to content

Commit 2215c85

Browse files
committed
escape all output by default, add <?unsafe blocks
1 parent 029b9fb commit 2215c85

File tree

10 files changed

+99
-54
lines changed

10 files changed

+99
-54
lines changed

Makefile

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ SPINCFG = spin.toml
88
endif
99

1010
ifndef SPINVER
11-
SPINVER = v0.4.0
11+
SPINVER = v0.7.1
1212
endif
1313

1414
ifndef ARCH
@@ -34,13 +34,10 @@ run: build
3434
$(SPINFLAGS) spin up --file $(SPINCFG)
3535

3636
watch: build
37-
nodemon --watch cgi-bin --watch www --watch lib --ext pl,html,php --verbose --legacy-watch --signal SIGINT --exec '$(SPINFLAGS) spin up --file $(SPINCFG)'
37+
nodemon --watch cgi-bin --watch . --ext pl,html --verbose --legacy-watch --signal SIGINT --exec '$(SPINFLAGS) spin up --file $(SPINCFG)'
3838

3939
container: build
4040
nixpacks build . --name php --pkgs wget curl \
41-
--install-cmd 'wget -O spin.tar.gz https://github.com/fermyon/spin/releases/download/v0.4.0/spin-v0.4.0-linux-amd64.tar.gz && tar xvf spin.tar.gz && (curl https://get.wasmer.io -sSfL | sh)' \
41+
--install-cmd 'wget -O spin.tar.gz $(SPINBIN) && tar xvf spin.tar.gz && (curl https://get.wasmer.io -sSfL | sh)' \
4242
--build-cmd './spin build' \
4343
--start-cmd './spin up --file spin.toml'
44-
45-
deploy:
46-
ssh [email protected] 'cd php && git pull && sudo systemctl restart php'

README.md

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,34 +66,41 @@ Put PHP templates here and they will show up in your root.
6666

6767
Files ending in `.html` will be interpreted as PHP templates with the following special syntax.
6868

69-
### Queries: `<?- Goal ?>`
69+
### Queries: `<?- Goal. ?>`
7070
```prolog
7171
<?- current_prolog_flag(version, Ver), write(Ver) ?>
7272
<?php ... ?>
7373
```
7474

75-
You can use the `<?php goal ?>` expression to execute Prolog (of course).
75+
You can use the `<?- Goal. ?>` (alias: `<?php Goal. ?>`) expression to execute Prolog (of course).
7676

77-
For example, this renders a table of the current Prolog flags:
77+
By default, all output from these blocks will be escaped to avoid XSS attacks. You can also use unsafe queries, see below.
78+
79+
#### Unsafe queries: `<?unsafe Goal. ?>`
80+
81+
You can use the `<?unsafe Goal. ?>` expression to execute Prolog code without escaping its output (dangerous!).
82+
Avoid using this if you can.
83+
84+
For example, this renders a table of the numbers 1-10 and their squares:
7885

7986
```html
80-
<h3>Prolog flags</h3>
87+
<h3>Math</h3>
8188
<table>
82-
<tr><th>Flag</th><th>Value</th></tr>
83-
<?php
84-
bagof([K, V], current_prolog_flag(K, V), Flags),
89+
<tr><th>N</th><th></th></tr>
90+
<?unsafe
91+
bagof([N, Square], (between(1, 10, N), Square is N^2)), Flags),
8592
maplist(format("<tr><td>~w</td><td>~w</td></tr>"), Flags)
8693
?>
8794
</table>
8895
```
8996

90-
#### findall: `<?* Goal ?>`
97+
#### findall: `<?* Goal. ?>`
9198
```prolog
9299
<?* member(X, [1, 2, 3]) ?>
93100
```
94101
Works the same as query, but findall behavior instead of once behavior.
95102

96-
#### Echo: `<?=Var Goal ?>`
103+
#### Echo: `<?=Var Goal. ?>`
97104
```html
98105
1+1 = <?=X X is 1+1 ?>
99106
<input type="text" name="ask" value="<?=Param query_param(ask, Param) ?>">
@@ -112,7 +119,8 @@ good_enough(X) :- between(1, 640, X).
112119
:- echo "hello!".
113120
:- succ(68, X), write(X).
114121
?>
115-
<?prolog ... ?>
122+
123+
The web framework of the future is <?=Framework best_web_framework(Framework). ?>
116124
```
117125

118126
Assert facts and rules as if consulting a Prolog program. Directive syntax will call the given goal.

spin.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ spin_version = "1"
22
name = "prolog-php"
33
description = "Prolog Home Page"
44
trigger = { type = "http", base = "/" }
5-
version = "0.2.1"
5+
version = "0.3.0"
66

77
[[component]]
88
id = "prolog"
99
description = "PHP: Prolog Home Page"
10-
source = "wapm_packages/guregu/[email protected].26/tpl.wasm"
10+
source = "wapm_packages/guregu/[email protected].27/tpl.wasm"
1111
files = [{ source = "www/", destination = "/"}]
1212
[component.trigger]
1313
route = "/..."

wapm.lock

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
# Lockfile v4
22
# This file is automatically generated by Wapm.
33
# It is not intended for manual editing. The schema of this file may change.
4-
[modules."guregu/trealla"."0.11.26".tpl]
4+
[modules."guregu/trealla"."0.11.27".tpl]
55
name = "tpl"
6-
package_version = "0.11.26"
6+
package_version = "0.11.27"
77
package_name = "guregu/trealla"
8-
package_path = "guregu/[email protected].26"
9-
resolved = "https://registry-cdn.wapm.io/packages/guregu/trealla/trealla-0.11.26-b8da2854-802b-11ed-90e2-c6aeb50490de.tar.gz"
8+
package_path = "guregu/[email protected].27"
9+
resolved = "https://registry-cdn.wapm.io/packages/guregu/trealla/trealla-0.11.27-9ebbecea-8685-11ed-90e2-c6aeb50490de.tar.gz"
1010
resolved_source = "registry+tpl"
1111
abi = "wasi"
1212
source = "tpl.wasm"
1313
[commands.tpl]
1414
name = "tpl"
1515
package_name = "guregu/trealla"
16-
package_version = "0.11.26"
16+
package_version = "0.11.27"
1717
module = "tpl"
1818
is_top_level_dependency = true

www/lib/php.pl

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
:- module(php, [php//1, render/1, phpinfo/0, pretty_version/1, echo/1, htmlspecialchars//1, html_escape/2]).
1+
:- module(php, [php//1, render/1, phpinfo/0, pretty_version/1, echo/1, htmlspecialchars//1, html_escape/2, op(901, fy, echo)]).
22
:- use_module(cgi).
33
:- use_module(dcgs).
44

@@ -23,49 +23,66 @@
2323
clauses([]) --> term(end_of_file).
2424
term(T) --> read_term_from_chars_(T).
2525

26+
exec(Block) :-
27+
\+unsafe_block(Block),
28+
'$capture_output',
29+
ignore(exec_(Block)),
30+
'$capture_output_to_chars'(Cs),
31+
ignore(echo(Cs)), % escapes output
32+
!.
33+
exec(Block) :-
34+
unsafe_block(Block),
35+
ignore(exec_(Block)).
36+
2637
% <?- ... ?> (query)
2738
% <?php ... ?>
28-
exec(php("-", Code)) :-
29-
exec(php("php", Code)),
39+
exec_(php("-", Code)) :-
40+
exec_(php("php", Code)),
3041
!.
31-
exec(php("php", Code)) :-
42+
exec_(php("php", Code)) :-
3243
read_term_from_chars(Code, Goal, []),
3344
ignore(Goal),
3445
!.
46+
exec_(php("unsafe", Code)) :-
47+
exec_(php("php", Code)),
48+
!.
3549
% <?* ... ?> (findall)
36-
exec(php("*", Code)) :-
50+
exec_(php("*", Code)) :-
3751
read_term_from_chars(Code, Goal, []),
3852
ignore(findall(_, call(Goal), _)),
3953
!.
4054
% <?prolog ... ?> (clauses)
4155
% <? ... ?>
42-
exec(php("prolog", Code)) :-
56+
exec_(php("prolog", Code)) :-
4357
( once(phrase(clauses(Cs), Code))
4458
; throw(error(invalid_template(prolog, Code)))
4559
),
4660
ignore(maplist(prolog_call, Cs)),
4761
!.
48-
exec(php([], Code)) :-
49-
exec(php("prolog", Code)),
62+
exec_(php([], Code)) :-
63+
exec_(php("prolog", Code)),
5064
!.
5165
% <?=Var ... ?> (echo)
52-
exec(php([=|Var], Code)) :-
66+
exec_(php([=|Var], Code)) :-
5367
read_term_from_chars(Code, Goal, [variable_names(Vars)]),
5468
atom_chars(Key, Var),
5569
( member(Key=X, Vars)
5670
-> true
5771
; throw(error(var_not_found(var(Key), goal(Goal))))
5872
),
5973
( call(Goal)
60-
-> echo(X)
74+
-> echo_unsafe(X)
6175
; true
6276
),
6377
!.
64-
exec(text(Text)) :-
78+
exec_(text(Text)) :-
6579
'$put_chars'(Text),
6680
!.
67-
exec(X) :-
68-
throw(error(unknown_opcode(X))).
81+
exec_(X) :-
82+
throw(error(unknown_block(X))).
83+
84+
unsafe_block(text(_)).
85+
unsafe_block(php("unsafe", _)).
6986

7087
render(File) :-
7188
read_file_to_string(File, Cs, []),
@@ -77,7 +94,7 @@
7794
echo "Error: ", echo Error
7895
)).
7996

80-
prolog_call(:-(Goal)) :- ignore(Goal).
97+
prolog_call(':-'(Goal)) :- ignore(Goal).
8198
prolog_call(Goal) :- user:assertz(Goal).
8299

83100
phpinfo :- render('lib/phpinfo.html').
@@ -88,26 +105,36 @@
88105
atomic_list_concat([Head, Min, Patch], '.', Version).
89106

90107
htmlspecialchars([]) --> [].
91-
htmlspecialchars([C|Cs]) --> { danger_subtitute(C, Sub) }, Sub, htmlspecialchars(Cs).
92-
htmlspecialchars([C|Cs]) --> { \+danger_subtitute(C, _) }, [C], htmlspecialchars(Cs).
108+
htmlspecialchars([C|Cs]) --> { danger_substitute(C, Sub) }, Sub, htmlspecialchars(Cs).
109+
htmlspecialchars([C|Cs]) --> { \+danger_substitute(C, _) }, [C], htmlspecialchars(Cs).
93110

94111
html_escape(Raw, Sanitized) :- once(phrase(htmlspecialchars(Raw), Sanitized)).
95112

96-
danger_subtitute(&, "&amp;").
97-
danger_subtitute('"', "&quot;").
98-
danger_subtitute('\'', "&apos;").
99-
danger_subtitute(<, "&lt;").
100-
danger_subtitute(>, "&gt;").
113+
danger_substitute(&, "&amp;").
114+
danger_substitute('"', "&quot;").
115+
danger_substitute('\'', "&apos;").
116+
danger_substitute(<, "&lt;").
117+
danger_substitute(>, "&gt;").
101118

102119
echo([]) :- !.
103120
echo('') :- !.
104121
echo(String) :-
105-
can_be(chars, String),
122+
string(String),
106123
html_escape(String, Sanitized),
107124
'$put_chars'(Sanitized),
108125
!.
109126
echo(X) :-
110127
write_term_to_chars(X, [], Cs),
111128
echo(Cs),
112129
!.
113-
130+
131+
echo_unsafe([]) :- !.
132+
echo_unsafe('') :- !.
133+
echo_unsafe(String) :-
134+
string(String),
135+
'$put_chars'(String),
136+
!.
137+
echo_unsafe(X) :-
138+
write_term_to_chars(X, [], Cs),
139+
echo_unsafe(Cs),
140+
!.

www/lib/phpinfo.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ <h1>phpinfo/0<br>
1010
<h3>Envrionment</h3>
1111
<table>
1212
<tr><th>Name</th><th>Value</th></tr>
13-
<?php
13+
<?unsafe
1414
bagof([K, V], env(K, V), Env),
1515
maplist(format("<tr><td>~w</td><td>~w</td></tr>"), Env)
1616
?>
1717
</table>
1818
<h3>Prolog flags</h3>
1919
<table>
2020
<tr><th>Flag</th><th>Value</th></tr>
21-
<?php
21+
<?unsafe
2222
current_prolog_flag(argv, Argv),
2323
format("<tr><td>~w</td><td>~w</td></tr>", [argv, Argv]),
2424
current_prolog_flag(version_git, GitVer),
@@ -30,7 +30,7 @@ <h3>Prolog flags</h3>
3030
<h3>Query params</h3>
3131
<table>
3232
<tr><th>Key</th><th>Value</th></tr>
33-
<?-
33+
<?unsafe
3434
bagof([K, V], K0^V0^K1^V1^K^V^(
3535
query_param(K0, V0),
3636
atom_chars(K0, K1),

www/public_html/index.html

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,15 @@ <h3>examples</h3>
3333
</ul>
3434
</nav>
3535

36-
<h3>Trealla Prolog playground</h3>
37-
<p>Run <a href="trealla.html">Trealla straight from your browser</a>!</p>
36+
<section>
37+
<h3>Trealla Prolog playground</h3>
38+
<p>✨&nbsp;Run <a href="trealla.html">Trealla straight from your browser</a>!</p>
39+
</section>
40+
41+
<h3>USER TESTIMONIALS</h3>
42+
<ul>
43+
<li>"Dear god", "I'm so happy they left my beloved Python undefiled" — <a href="https://news.ycombinator.com/item?id=34287270">hacker news</a></li>
44+
</ul>
3845

3946
<hr>
4047

www/public_html/tacos.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
h1 {
1010
font-style: italic;
1111
}
12+
figcaption {
13+
text-align: right;
14+
}
1215
</style>
1316
</head>
1417
<body>
@@ -21,6 +24,8 @@ <h1>So I made this girl quit her job today.</h1>
2124
<p>She looks at my face for a few seconds with a blank stare before saying, "Fuck this" and just walks to the back where the customer can't see, and then like 15 seconds later walks down the hallway and out the door and gets into her car. Then some guy comes out from the back, who I assume is the manager, with a concerned look on his face, apologizes and takes my order. Other employees were really confused.
2225

2326
<p>Tacos were okay.
27+
28+
<figcaption><cite>— some guy on an internet forum long ago</cite></figcaption>
2429
</blockquote>
2530
<?
2631

@@ -49,7 +54,8 @@ <h1>So I made this girl quit her job today.</h1>
4954
?>
5055

5156
<ol>
52-
<?* tacos(Tacos), member(taco(Shell, Meat, Toppings), Tacos), format("<li>~a ~a ~w</li>", [Shell, Meat, Toppings]). ?>
57+
<!-- TODO: make this less horrible -->
58+
<?unsafe findall(_, (tacos(Tacos), member(taco(Shell, Meat, Toppings), Tacos), format("<li>~a ~a ~w</li>", [Shell, Meat, Toppings])), _). ?>
5359
</ol>
5460

5561
<section>

www/public_html/test.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<!doctype html>
22
<html>
3-
<?php phpinfo ?>
3+
<?unsafe phpinfo ?>
44
</html>

www/public_html/trealla.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ <h2>More Info</h2>
6767
</footer>
6868

6969
<script type="module">
70-
import { load, Prolog, toJSON } from 'https://esm.sh/[email protected].34';
70+
import { load, Prolog, toJSON } from 'https://esm.sh/[email protected].40';
7171

7272
try {
7373
load().then(init);

0 commit comments

Comments
 (0)