@@ -5,6 +5,11 @@ import { Authenticator, onAuthChange } from '@aws-blocks/blocks/ui';
55// For coding agents: Leave this comment in place for future reference.
66// Read node_modules/@aws -blocks/blocks/README.md for Building Block documentation
77
8+ /** Escape user-controlled strings before interpolating into innerHTML */
9+ function escapeHtml ( str : string ) : string {
10+ return str . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) . replace ( / " / g, '"' ) ;
11+ }
12+
813let currentUser : { username : string } | null = null ;
914let currentSort : 'priority' | 'title' | 'createdAt' | undefined ;
1015
@@ -33,20 +38,20 @@ async function refreshTodos() {
3338 todoList . innerHTML = todos . map ( todo => `
3439 <div class="todo-item">
3540 <input type="checkbox" ${ todo . completed ? 'checked' : '' }
36- onchange="toggleTodo('${ todo . todoId } ', this.checked)">
37- <input type="text" class="todo-title" value="${ todo . title } "
38- onblur="updateTitle('${ todo . todoId } ', this.value)"
41+ onchange="toggleTodo('${ escapeHtml ( todo . todoId ) } ', this.checked)">
42+ <input type="text" class="todo-title" value="${ escapeHtml ( todo . title ) } "
43+ onblur="updateTitle('${ escapeHtml ( todo . todoId ) } ', this.value)"
3944 onkeypress="if(event.key==='Enter') this.blur()">
40- <select onchange="changePriority('${ todo . todoId } ', parseInt(this.value))" style="margin-left: auto;">
45+ <select onchange="changePriority('${ escapeHtml ( todo . todoId ) } ', parseInt(this.value))" style="margin-left: auto;">
4146 <option value="1" ${ todo . priority === 1 ? 'selected' : '' } >🔴 High</option>
4247 <option value="2" ${ todo . priority === 2 ? 'selected' : '' } >🟡 Medium</option>
4348 <option value="3" ${ todo . priority === 3 ? 'selected' : '' } >🟢 Low</option>
4449 </select>
45- <button onclick="deleteTodo('${ todo . todoId } ')">Delete</button>
50+ <button onclick="deleteTodo('${ escapeHtml ( todo . todoId ) } ')">Delete</button>
4651 </div>
4752 ` ) . join ( '' ) ;
4853 } catch ( error : any ) {
49- if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ error . message } </span>` ;
54+ if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ escapeHtml ( error . message ) } </span>` ;
5055 }
5156}
5257
@@ -65,7 +70,7 @@ async function refreshTodos() {
6570 input . value = '' ;
6671 await refreshTodos ( ) ;
6772 } catch ( error : any ) {
68- if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ error . message } </span>` ;
73+ if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ escapeHtml ( error . message ) } </span>` ;
6974 }
7075} ;
7176
@@ -84,7 +89,7 @@ async function refreshTodos() {
8489 await api . updateTodo ( todoId , { completed } ) ;
8590 await refreshTodos ( ) ;
8691 } catch ( error : any ) {
87- if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ error . message } </span>` ;
92+ if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ escapeHtml ( error . message ) } </span>` ;
8893 await refreshTodos ( ) ;
8994 }
9095} ;
@@ -98,7 +103,7 @@ async function refreshTodos() {
98103 await api . updateTodo ( todoId , { priority } ) ;
99104 await refreshTodos ( ) ;
100105 } catch ( error : any ) {
101- if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ error . message } </span>` ;
106+ if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ escapeHtml ( error . message ) } </span>` ;
102107 await refreshTodos ( ) ;
103108 }
104109} ;
@@ -118,7 +123,7 @@ async function refreshTodos() {
118123 await api . updateTodo ( todoId , { title : trimmedTitle } ) ;
119124 await refreshTodos ( ) ;
120125 } catch ( error : any ) {
121- if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ error . message } </span>` ;
126+ if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ escapeHtml ( error . message ) } </span>` ;
122127 await refreshTodos ( ) ;
123128 }
124129} ;
@@ -132,7 +137,7 @@ async function refreshTodos() {
132137 await api . deleteTodo ( todoId ) ;
133138 await refreshTodos ( ) ;
134139 } catch ( error : any ) {
135- if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ error . message } </span>` ;
140+ if ( errorDiv ) errorDiv . innerHTML = `<span class="error">${ escapeHtml ( error . message ) } </span>` ;
136141 }
137142} ;
138143
@@ -161,15 +166,15 @@ document.addEventListener('DOMContentLoaded', async () => {
161166
162167 await api . setCookie ( name , value ) ;
163168 document . getElementById ( 'cookie-result' ) ! . innerHTML =
164- `<span class="success">✓ Set cookie ${ name } = ${ value } </span>` ;
169+ `<span class="success">✓ Set cookie ${ escapeHtml ( name ) } = ${ escapeHtml ( value ) } </span>` ;
165170} ;
166171
167172( window as any ) . testGetCookie = async ( ) => {
168173 const name = ( document . getElementById ( 'cookieName' ) as HTMLInputElement ) . value ;
169174
170175 const value = await api . getCookie ( name ) ;
171176 document . getElementById ( 'cookie-result' ) ! . innerHTML =
172- value ? `<span class="success">✓ Got cookie: ${ value } </span>`
177+ value ? `<span class="success">✓ Got cookie: ${ escapeHtml ( value ) } </span>`
173178 : `<span class="error">✗ Cookie not found</span>` ;
174179} ;
175180
@@ -178,7 +183,7 @@ document.addEventListener('DOMContentLoaded', async () => {
178183
179184 await api . deleteCookie ( name ) ;
180185 document . getElementById ( 'cookie-result' ) ! . innerHTML =
181- `<span class="success">✓ Deleted cookie ${ name } </span>` ;
186+ `<span class="success">✓ Deleted cookie ${ escapeHtml ( name ) } </span>` ;
182187} ;
183188
184189// KV Store test functions
@@ -189,7 +194,7 @@ document.addEventListener('DOMContentLoaded', async () => {
189194 const result = await api . setValue ( key , value ) ;
190195
191196 document . getElementById ( 'kv-result' ) ! . innerHTML =
192- `<span class="success">✓ Set ${ key } = ${ value } </span>` ;
197+ `<span class="success">✓ Set ${ escapeHtml ( key ) } = ${ escapeHtml ( value ) } </span>` ;
193198} ;
194199
195200( window as any ) . testGetValue = async ( ) => {
@@ -198,7 +203,7 @@ document.addEventListener('DOMContentLoaded', async () => {
198203 const value = await api . getValue ( key ) ;
199204
200205 document . getElementById ( 'kv-result' ) ! . innerHTML =
201- value ? `<span class="success">✓ Got value: ${ value } </span>`
206+ value ? `<span class="success">✓ Got value: ${ escapeHtml ( value ) } </span>`
202207 : `<span class="error">✗ Key not found</span>` ;
203208} ;
204209
@@ -221,7 +226,7 @@ document.addEventListener('DOMContentLoaded', async () => {
221226 document . getElementById ( 'test-results' ) ! . innerHTML = results . join ( '<br>' ) ;
222227 } catch ( error : any ) {
223228 document . getElementById ( 'test-results' ) ! . innerHTML =
224- `<span class="error">✗ Tests failed: ${ error . message } </span>` ;
229+ `<span class="error">✗ Tests failed: ${ escapeHtml ( error . message ) } </span>` ;
225230 }
226231} ;
227232
0 commit comments