Debouncing in React + XSS attack.md
-
In order to debounce but also avoid 2 request to the API, we need 2 pieces of state (term and denounced term)
-
Using dangerouslySetInnerHTML could lead to a xss attack
Debouncing in React + XSS attack.md
In order to debounce but also avoid 2 request to the API, we need 2 pieces of state (term and denounced term)
Using dangerouslySetInnerHTML could lead to a xss attack
// libs | |
import React, { useState, useEffect } from 'react'; | |
import axios from 'axios'; | |
const Search = () => { | |
const [term, setTerm] = useState('programming'); | |
const [debouncedTerm, setDebouncedTerm] = useState(term); | |
const [results, setResults] = useState([]); | |
useEffect(() => { | |
const timerId = setTimeout(() => { | |
setDebouncedTerm(term); | |
}, 1000); | |
return () => { | |
clearTimeout(timerId); | |
}; | |
}, [term]); | |
useEffect(() => { | |
const search = async () => { | |
const { data } = await axios.get('https://en.wikipedia.org/w/api.php', { | |
params: { | |
action: 'query', | |
list: 'search', | |
origin: '*', | |
format: 'json', | |
srsearch: debouncedTerm, | |
}, | |
}); | |
setResults(data.query.search); | |
}; | |
search(); | |
}, [debouncedTerm]); | |
const renderedResults = results.map((result) => { | |
return ( | |
<div key={result.pageid} className="item"> | |
<div className="right floated content"> | |
<a | |
className="ui button" | |
href={`https://en.wikipedia.org?curid=${result.pageid}`} | |
> | |
Go | |
</a> | |
</div> | |
<div className="content"> | |
<div className="header">{result.title}</div> | |
<span dangerouslySetInnerHTML={{ __html: result.snippet }}></span> | |
</div> | |
</div> | |
); | |
}); | |
return ( | |
<div> | |
<div className="ui form"> | |
<div className="field"> | |
<label>Enter Search Term</label> | |
<input | |
value={term} | |
onChange={(e) => setTerm(e.target.value)} | |
className="input" | |
/> | |
</div> | |
</div> | |
<div className="ui celled list">{renderedResults}</div> | |
</div> | |
); | |
}; | |
export default Search; |
const express = require('express'); | |
const cors = require('cors'); | |
const app = express(); | |
app.use(express.static('public')); | |
app.use(cors()); | |
app.get('/', (req, res) => { | |
if (req.query.srsearch === 't') { | |
res.send({ | |
query: { | |
search: [ | |
{ | |
snippet: ` | |
<img src="asdf" onerror="document.body.innerHTML = '<h1>HAHAHA, I control this app now!!!</h1>';"></img> | |
`, | |
}, | |
], | |
}, | |
}); | |
} else { | |
res.send({ | |
query: { | |
search: [], | |
}, | |
}); | |
} | |
}); | |
app.listen(3001); |
Note:
In order to debounce but also avoid 2 request to the API, we need 2 pieces of state (term and denounced term)
Using
dangerouslySetInnerHTML
could lead to a xss attack