mirror of
https://github.com/danbulant/design
synced 2026-06-19 06:11:28 +00:00
add react spread notes
This commit is contained in:
parent
004d08ad18
commit
a0c424db77
2 changed files with 68 additions and 0 deletions
|
|
@ -4,6 +4,8 @@ tags: [security, javascript]
|
|||
date: 2025-03-29
|
||||
---
|
||||
|
||||
*note that this might have changed silently, though there is still an [open issue](https://github.com/sidorares/node-mysql2/issues/1247) referencing this*
|
||||
|
||||
TLDR object serialization via [sqlstring](https://www.npmjs.com/package/sqlstring), vulnerable if user input is not primitive (e.g. unchecked object).
|
||||
|
||||
```js
|
||||
|
|
|
|||
66
src/routes/notes/react-spread/+page.md
Normal file
66
src/routes/notes/react-spread/+page.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
title: React spread operator is dangerous
|
||||
tags: [cybersec]
|
||||
date: 2025-07-06
|
||||
---
|
||||
|
||||
Using spread operator within react with untrusted user input is a recipe for XSS; embedding raw HTML into website.
|
||||
|
||||
React supports embedding raw HTML via an attribute called `dangerouslySetInnerHTML.__html`.
|
||||
|
||||
```jsx
|
||||
return (
|
||||
<p dangerouslySetInnerHTML={{__html:"<b>hello world</b>"}} />
|
||||
)
|
||||
```
|
||||
|
||||
Which is different from how for example svelte handles it - by using a different tag / template (the equivalent would be `<p>{@html "..."}</p>`).
|
||||
|
||||
JSX supports spread operators just like any JS object, as it creates one under the hood anyway.
|
||||
Which is nice for passing props to another component:
|
||||
|
||||
```jsx
|
||||
return (
|
||||
<Component {...props} />
|
||||
)
|
||||
```
|
||||
|
||||
But this could be coupled to make a mostly hidden bug with no 'danger' in it's sources being explicitly present:
|
||||
|
||||
```jsx
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { parse } from 'qs';
|
||||
|
||||
function useQs() {
|
||||
const searchParams = useSearchParams();
|
||||
const search = searchParams.toString();
|
||||
|
||||
return parse(search, {
|
||||
ignoreQueryPrefix: true,
|
||||
decoder: (str) => {
|
||||
str = decodeURIComponent(str);
|
||||
return str
|
||||
}
|
||||
});
|
||||
}
|
||||
function Page() {
|
||||
// load query string, i.e. parses http://tld/path?key=val into { key: "val" }
|
||||
const qs = useQs();
|
||||
|
||||
return (<Component props={{ }} {...qs} />)
|
||||
}
|
||||
|
||||
function Component({ props, key }) {
|
||||
return (
|
||||
<div {...props}>
|
||||
Your key is {key || "empty"}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
The above app is vulnerable, because the latter spread operator can override `props`, which is then spreaded `div`.
|
||||
This would be safe - if buggy - if all we could do is set `props` to a string value.
|
||||
|
||||
But `qs` also parses objects. So you're just one `?props[dangerouslySetInnerHTML][__html]="<b>pwned</b>"` away from danger.
|
||||
Check your vibe coded apps, and perhaps choose technologies with sane & safe defaults.
|
||||
Loading…
Reference in a new issue