Skip to content

Commit 2a7bf6d

Browse files
committed
refactor(examples): improve security when rendering HTML strings [TRI-003] (#14)
1 parent 680554d commit 2a7bf6d

File tree

7 files changed

+99
-80
lines changed

7 files changed

+99
-80
lines changed

examples/api/src/App.svelte

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -90,25 +90,28 @@
9090
let selected = views[0];
9191
9292
let responses = writable([]);
93-
let response = ''
9493
9594
function select(view) {
9695
selected = view;
9796
}
9897
9998
function onMessage(value) {
100-
responses.update(r => [`[${new Date().toLocaleTimeString()}]` + ': ' + (typeof value === "string" ? value : JSON.stringify(value)), ...r])
99+
responses.update(r => [{ text: `[${new Date().toLocaleTimeString()}]` + ': ' + (typeof value === "string" ? value : JSON.stringify(value)) }, ...r])
100+
}
101+
102+
// this function is renders HTML without sanitizing it so it's insecure
103+
// we only use it with our own input data
104+
function insecureRenderHtml(html) {
105+
responses.update(r => [{ html }, ...r])
106+
}
107+
108+
function clear() {
109+
responses.update(() => []);
101110
}
102111
103112
function onLogoClick() {
104113
open("https://tauri.studio/");
105114
}
106-
107-
onMount(() => {
108-
responses.subscribe(r => {
109-
response = r.join('\n')
110-
})
111-
})
112115
</script>
113116

114117
<main>
@@ -136,16 +139,20 @@
136139
{/each}
137140
</div>
138141
<div class="content">
139-
<svelte:component this={selected.component} {onMessage} />
142+
<svelte:component this={selected.component} {onMessage} {insecureRenderHtml} />
140143
</div>
141144
</div>
142145
<div id="response" style="white-space: pre-line">
143146
<p class="flex row just-around">
144147
<strong>Tauri Console</strong>
145-
<span class="nv" on:click={()=> {
146-
responses.update(() => []);
147-
}}>clear</span>
148+
<span class="nv" on:click={clear}>clear</span>
148149
</p>
149-
{@html response}
150+
{#each $responses as r}
151+
{#if r.text}
152+
<p>{r.text}</p>
153+
{:else}
154+
{@html r.html}
155+
{/if}
156+
{/each}
150157
</div>
151158
</main>

examples/api/src/components/Dialog.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { readBinaryFile } from "@tauri-apps/api/fs";
44
55
export let onMessage;
6+
export let insecureRenderHtml;
67
let defaultPath = null;
78
let filter = null;
89
let multiple = false;
@@ -51,7 +52,7 @@
5152
new Uint8Array(response),
5253
function (base64) {
5354
var src = "data:image/png;base64," + base64;
54-
onMessage('<img src="' + src + '"></img>');
55+
insecureRenderHtml('<img src="' + src + '"></img>');
5556
}
5657
);
5758
} else {

examples/api/src/components/FileSystem.svelte

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { convertFileSrc } from "@tauri-apps/api/tauri";
44
55
export let onMessage;
6+
export let insecureRenderHtml;
67
78
let pathToRead = "";
89
let img;
@@ -42,11 +43,11 @@
4243
if (pathToRead.includes(".png") || pathToRead.includes(".jpg")) {
4344
arrayBufferToBase64(new Uint8Array(response), function (base64) {
4445
const src = "data:image/png;base64," + base64;
45-
onMessage('<img src="' + src + '"></img>');
46+
insecureRenderHtml('<img src="' + src + '"></img>');
4647
});
4748
} else {
4849
const value = String.fromCharCode.apply(null, response);
49-
onMessage(
50+
insecureRenderHtml(
5051
'<textarea id="file-response" style="height: 400px"></textarea><button id="file-save">Save</button>'
5152
);
5253
setTimeout(() => {

examples/multiwindow/index.html

Lines changed: 71 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,84 @@
11
<!DOCTYPE html>
22
<html>
3-
<body>
4-
<div id="window-label"></div>
5-
<div id="container"></div>
6-
<div id="response"></div>
73

8-
<script>
9-
var WebviewWindow = window.__TAURI__.window.WebviewWindow
10-
var thisTauriWindow = window.__TAURI__.window.getCurrent()
11-
var windowLabel = thisTauriWindow.label
12-
var windowLabelContainer = document.getElementById('window-label')
13-
windowLabelContainer.innerHTML = 'This is the ' + windowLabel + ' window.'
4+
<head>
5+
<style>
6+
#response {
7+
white-space: pre-wrap;
8+
}
9+
</style>
10+
</head>
1411

15-
var container = document.getElementById('container')
12+
<body>
13+
<div id="window-label"></div>
14+
<div id="container"></div>
15+
<div id="response"></div>
1616

17-
function createWindowMessageBtn(label) {
18-
var tauriWindow = WebviewWindow.getByLabel(label)
19-
var button = document.createElement('button')
20-
button.innerHTML = 'Send message to ' + label
21-
button.addEventListener('click', function () {
22-
tauriWindow.emit('clicked', 'message from ' + windowLabel)
23-
})
24-
container.appendChild(button)
25-
}
17+
<script>
18+
var WebviewWindow = window.__TAURI__.window.WebviewWindow
19+
var thisTauriWindow = window.__TAURI__.window.getCurrent()
20+
var windowLabel = thisTauriWindow.label
21+
var windowLabelContainer = document.getElementById('window-label')
22+
windowLabelContainer.innerText = 'This is the ' + windowLabel + ' window.'
2623

27-
// global listener
28-
window.__TAURI__.event.listen('clicked', function (event) {
29-
responseContainer.innerHTML +=
30-
'Got ' + JSON.stringify(event) + ' on global listener<br><br>'
31-
})
32-
window.__TAURI__.event.listen('tauri://window-created', function (event) {
33-
createWindowMessageBtn(event.payload.label)
34-
})
24+
var container = document.getElementById('container')
3525

36-
var responseContainer = document.getElementById('response')
37-
// listener tied to this window
38-
thisTauriWindow.listen('clicked', function (event) {
39-
responseContainer.innerHTML +=
40-
'Got ' + JSON.stringify(event) + ' on window listener<br><br>'
26+
function createWindowMessageBtn(label) {
27+
var tauriWindow = WebviewWindow.getByLabel(label)
28+
var button = document.createElement('button')
29+
button.innerText = 'Send message to ' + label
30+
button.addEventListener('click', function () {
31+
tauriWindow.emit('clicked', 'message from ' + windowLabel)
4132
})
33+
container.appendChild(button)
34+
}
4235

43-
var createWindowButton = document.createElement('button')
44-
createWindowButton.innerHTML = 'Create window'
45-
createWindowButton.addEventListener('click', function () {
46-
var webviewWindow = new WebviewWindow(Math.random().toString())
47-
webviewWindow.once('tauri://created', function () {
48-
responseContainer.innerHTML += 'Created new webview'
49-
})
50-
webviewWindow.once('tauri://error', function () {
51-
responseContainer.innerHTML += 'Error creating new webview'
52-
})
53-
})
54-
container.appendChild(createWindowButton)
36+
// global listener
37+
window.__TAURI__.event.listen('clicked', function (event) {
38+
responseContainer.innerHTML +=
39+
'Got ' + JSON.stringify(event) + ' on global listener\n\n'
40+
})
41+
window.__TAURI__.event.listen('tauri://window-created', function (event) {
42+
createWindowMessageBtn(event.payload.label)
43+
})
5544

56-
var globalMessageButton = document.createElement('button')
57-
globalMessageButton.innerHTML = 'Send global message'
58-
globalMessageButton.addEventListener('click', function () {
59-
// emit to all windows
60-
window.__TAURI__.event.emit('clicked', 'message from ' + windowLabel)
45+
var responseContainer = document.getElementById('response')
46+
// listener tied to this window
47+
thisTauriWindow.listen('clicked', function (event) {
48+
responseContainer.innerText +=
49+
'Got ' + JSON.stringify(event) + ' on window listener\n\n'
50+
})
51+
52+
var createWindowButton = document.createElement('button')
53+
createWindowButton.innerHTML = 'Create window'
54+
createWindowButton.addEventListener('click', function () {
55+
var webviewWindow = new WebviewWindow(Math.random().toString())
56+
webviewWindow.once('tauri://created', function () {
57+
responseContainer.innerHTML += 'Created new webview'
58+
})
59+
webviewWindow.once('tauri://error', function () {
60+
responseContainer.innerHTML += 'Error creating new webview'
6161
})
62-
container.appendChild(globalMessageButton)
62+
})
63+
container.appendChild(createWindowButton)
6364

64-
var allWindows = window.__TAURI__.window.getAll()
65-
for (var index in allWindows) {
66-
var label = allWindows[index].label
67-
if (label === windowLabel) {
68-
continue
69-
}
70-
createWindowMessageBtn(label)
65+
var globalMessageButton = document.createElement('button')
66+
globalMessageButton.innerHTML = 'Send global message'
67+
globalMessageButton.addEventListener('click', function () {
68+
// emit to all windows
69+
window.__TAURI__.event.emit('clicked', 'message from ' + windowLabel)
70+
})
71+
container.appendChild(globalMessageButton)
72+
73+
var allWindows = window.__TAURI__.window.getAll()
74+
for (var index in allWindows) {
75+
var label = allWindows[index].label
76+
if (label === windowLabel) {
77+
continue
7178
}
72-
</script>
73-
</body>
74-
</html>
79+
createWindowMessageBtn(label)
80+
}
81+
</script>
82+
</body>
83+
84+
</html>

examples/resources/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
const div = document.querySelector('div')
1414
window.__TAURI__.event.listen('message', (event) => {
1515
const p = document.createElement('p')
16-
p.innerHTML = event.payload
16+
p.innerText = event.payload
1717
div.appendChild(p)
1818
})
1919
</script>

examples/sidecar/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
const div = document.querySelector('div')
1414
window.__TAURI__.event.listen('message', (event) => {
1515
const p = document.createElement('p')
16-
p.innerHTML = event.payload
16+
p.innerText = event.payload
1717
div.appendChild(p)
1818
})
1919
</script>

examples/state/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ <h3>Database</h3>
3131
const responseContainer = document.querySelector('#response')
3232

3333
function updateResponse(response) {
34-
responseContainer.innerHTML =
34+
responseContainer.innerText =
3535
typeof response === 'string' ? response : JSON.stringify(response)
3636
}
3737

0 commit comments

Comments
 (0)