layerpro 3.4.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -36
- package/SECURITY-HARDENING.md +265 -0
- package/SECURITY.md +8 -0
- package/global.cjs +974 -0
- package/global.d.ts +27 -0
- package/global.js +974 -0
- package/index.cjs +415 -383
- package/index.css +4 -5
- package/index.js +415 -383
- package/llms.txt +3 -1
- package/markdown/GUIDE.md +14 -15
- package/package.json +10 -4
- package/types/layerpro.d.ts +134 -143
package/README.md
CHANGED
|
@@ -184,12 +184,13 @@ popup.open({
|
|
|
184
184
|
maxWidth: number,
|
|
185
185
|
maxHeight: number,
|
|
186
186
|
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
// Remote content (same-origin only, enforced). Rendered as raw HTML;
|
|
188
|
+
// sanitize server-side if the endpoint is not fully trusted.
|
|
189
|
+
source: string,
|
|
189
190
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
191
|
+
// React adapter (via reattach)
|
|
192
|
+
inheritContext: boolean,
|
|
193
|
+
resilient: boolean,
|
|
193
194
|
|
|
194
195
|
// Position (numbers are px; strings applied as-is, e.g. "10%")
|
|
195
196
|
top: number | string,
|
|
@@ -210,8 +211,7 @@ popup.open({
|
|
|
210
211
|
iconize: boolean, // allow minimize to dock
|
|
211
212
|
dockable: boolean, // show an icon in the dock bar
|
|
212
213
|
close: boolean, // show the close button
|
|
213
|
-
|
|
214
|
-
modal: boolean, // alias of raised
|
|
214
|
+
modal: boolean, // full-screen overlay that blocks pointer events beneath
|
|
215
215
|
|
|
216
216
|
// Buttons
|
|
217
217
|
buttons: {
|
|
@@ -221,44 +221,59 @@ popup.open({
|
|
|
221
221
|
}
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
> **`
|
|
224
|
+
> **`modal`** injects a full-viewport overlay (`popup_modal_<id>`) that intercepts every pointer event - the page underneath becomes unclickable until the popup closes. Use `modal: true` for alert/confirm/prompt/message pop ups.
|
|
225
225
|
|
|
226
226
|
> **Duplicate id** - opening a popup whose `id` already exists brings that window to the front and refreshes its `body`/`buttons` instead of doing nothing.
|
|
227
227
|
|
|
228
|
-
## Global Overrides
|
|
228
|
+
## Global Overrides (Opt-in in 4.0)
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
In 4.0, `import "layerpro"` no longer overrides the native `alert`, `confirm`,
|
|
231
|
+
and `prompt` globals. The library exposes its own methods on `window.layerpro`
|
|
232
|
+
and provides Promise-based alternatives. To override the browser globals, use
|
|
233
|
+
the **explicit entrypoint**:
|
|
231
234
|
|
|
232
235
|
```javascript
|
|
236
|
+
// Default entry - library installed, globals NOT overridden
|
|
233
237
|
import "layerpro";
|
|
234
238
|
|
|
235
|
-
alert("Hello World!"); //
|
|
236
|
-
confirm("Sure?"
|
|
237
|
-
prompt("Name?"
|
|
238
|
-
message("Done!");
|
|
239
|
+
layerpro.alert("Hello World!"); // Promise<void>
|
|
240
|
+
const confirmed = await layerpro.confirm("Sure?"); // Promise<boolean>
|
|
241
|
+
const name = await layerpro.prompt("Name?"); // Promise<string>
|
|
242
|
+
layerpro.message("Done!"); // Promise<void>
|
|
239
243
|
```
|
|
240
244
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
### Override browser globals (opt-in)
|
|
246
|
+
|
|
247
|
+
If you need `window.alert` / `window.confirm` / `window.prompt` / `window.message`
|
|
248
|
+
replaced with LayerPro's Promise-based versions, import the global entrypoint:
|
|
249
|
+
|
|
250
|
+
```javascript
|
|
251
|
+
import "layerpro/global"; // installs Promise-based overrides on window
|
|
252
|
+
|
|
253
|
+
// These now return Promises instead of native values:
|
|
254
|
+
await alert("Hello World!"); // Promise<void>
|
|
255
|
+
const ok = await confirm("Sure?"); // Promise<boolean>
|
|
256
|
+
const val = await prompt("Name?"); // Promise<string>
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
> **⚠️ Important:** overridden `confirm()` returns `Promise<boolean>`, not a
|
|
260
|
+
> synchronous `boolean`. Code that does `if (confirm("..."))` must be
|
|
261
|
+
> `await`-ed. Only use `layerpro/global` if you control all call sites.
|
|
247
262
|
|
|
248
263
|
## Methods
|
|
249
264
|
|
|
250
265
|
| Method | Description |
|
|
251
266
|
|--------|-------------|
|
|
252
|
-
| `popup.open(props)` | Open a modal; returns
|
|
267
|
+
| `popup.open(props)` | Open a modal; returns `Promise<GenLayerProps>` - resolves when the popup closes. |
|
|
253
268
|
| `popup.close(id)` | Close (and unmount) a modal. |
|
|
254
269
|
| `popup.center(id, offset?)` | Center a modal in the viewport; optional pixel offset. |
|
|
255
270
|
| `popup.maximize(props)` | Toggle maximize (pure CSS). |
|
|
256
271
|
| `popup.iconize(id)` | Toggle minimize to the dock. |
|
|
257
272
|
| `popup.index(id)` | Bring a modal to the front (select). |
|
|
258
|
-
| `popup.zIndex()` | Get the next z-index counter value. |
|
|
259
273
|
| `popup.movable(props)` | Enable dragging. |
|
|
260
274
|
| `popup.resizable(props)` | Enable resizing. |
|
|
261
|
-
| `layerpro.
|
|
275
|
+
| `layerpro.installGlobals()` | Override `window.alert/confirm/prompt/message` with LayerPro's Promise-based versions. |
|
|
276
|
+
| `layerpro.config(opts)` | Runtime config. Currently supports `{ nonce: string }` for CSP; sets `window.__layerproNonce` consumed by the injected `<style>`. Call before importing LayerPro for the nonce to be present at injection. |
|
|
262
277
|
|
|
263
278
|
## Styling & z-index
|
|
264
279
|
|
|
@@ -280,25 +295,15 @@ The title bar, window surface, internal area, and body all read from CSS custom
|
|
|
280
295
|
| `--window-border-radius` | popup border radius |
|
|
281
296
|
| `--internal-background` | body wrapper |
|
|
282
297
|
| `--body-color` | body text color |
|
|
283
|
-
| `--
|
|
298
|
+
| `--modal-background` | modal overlay |
|
|
284
299
|
| `--popup-z-index` | base z-index for the stack |
|
|
285
300
|
|
|
286
301
|
## Security
|
|
287
302
|
|
|
288
303
|
- **Same-origin fetch guard** - the `source` prop only accepts same-origin URLs; cross-origin requests are rejected before any network call is made, preventing SSRF and data exfiltration.
|
|
289
304
|
- **HTML escaping** - all user-provided text content (button labels, body content, titles) is automatically escaped via `escapeHtml()` to prevent XSS.
|
|
290
|
-
- **CSP-friendly** - no inline scripts or `eval()` are used. React components are rendered via `reattach.mount()` (backed by `createRoot()`).
|
|
291
|
-
- **
|
|
292
|
-
|
|
293
|
-
## Browser Support
|
|
294
|
-
|
|
295
|
-
Chrome 80+ • Firefox 75+ • Safari 13+ • Edge 80+
|
|
296
|
-
|
|
297
|
-
## Contributing
|
|
298
|
-
|
|
299
|
-
LayerPro's source is currently private, but contributions and discussion are welcome. Get in touch:
|
|
300
|
-
|
|
301
|
-
- **Dario Passariello** - https://dario.passariello.ca/ - dariopassariello@gmail.com
|
|
305
|
+
- **CSP-friendly** - no inline scripts or `eval()` are used. React components are rendered via `reattach.mount()` (backed by `createRoot()`). For strict CSP environments, the injected stylesheet auto-detects the nonce from `window.__layerproNonce`, existing `<style>`/`<script>` elements, or `document.body.dataset.nonce`. Alternatively, import the stylesheet as a static file via `import 'layerpro/style.css'` to avoid inline style injection entirely. Use `layerpro.config({ nonce })` for explicit nonce control.
|
|
306
|
+
- **No global side effects** - `import 'layerpro'` does NOT override `window.alert`/`confirm`/`prompt`. Use `import 'layerpro/global'` or `layerpro.installGlobals()` to opt in to the global overrides.
|
|
302
307
|
|
|
303
308
|
## License
|
|
304
309
|
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
# Security Hardening Guide
|
|
2
|
+
|
|
3
|
+
This document is a security reference for LayerPro maintainers and consumers.
|
|
4
|
+
It documents every threat surface in the library, the defense-in-depth controls
|
|
5
|
+
that mitigate it, and the residual risks that must be managed at the application
|
|
6
|
+
level.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Threat Surface Summary
|
|
11
|
+
|
|
12
|
+
| Threat | Vector | Mitigation | Residual Risk |
|
|
13
|
+
|--------|--------|------------|---------------|
|
|
14
|
+
| Global override side effects | `import 'layerpro/global'` rewrites `window.alert/confirm/prompt` | Globals are opt-in (not auto-applied) in 4.0; `layerpro.installGlobals()` for programmatic opt-in; native globals untouched by default | Consumers who import `layerpro/global` accept Promise-based globals |
|
|
15
|
+
| XSS via popup body | `body: '<script>...'` or `buttons.confirm.text: '<img onerror=...>'` | `escapeHtml()` on all string bodies, button labels, names, icons | Does **not** protect against stored XSS from same-origin `source` (see §5) |
|
|
16
|
+
| SSRF via `source` prop | `source: 'https://evil.example.com/...'` | Same-origin URL check (`url.origin !== window.location.origin`) + `fetch` mode `same-origin` | Same-origin XSS (server compromise or stored content on own domain) is still possible |
|
|
17
|
+
| CSP bypass via inline `<style>` | CSS injected via `document.head.appendChild(<style>)` | Nonce auto-detection (`window.__layerproNonce`, `<style[nonce]>`, `<script[nonce]>`, `document.body.dataset.nonce`) | If no nonce is detectable, strict CSP will silently drop the stylesheet |
|
|
18
|
+
| React DOM injection | `body: <Component />` executes arbitrary component code | React adapter loaded lazily via dynamic `import('layerpro/react')` - `react-dom` is never in the core bundle | Consumer is fully responsible for the React components they pass |
|
|
19
|
+
| Prototype pollution | `setProps` iterates `Reflect.ownKeys` and reassigns descriptors | `setProps` only operates on the frozen `layerpro` object (non-enumerable, non-configurable, non-writable) | Low - `layerpro` is frozen in `index.ts:21` |
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 2. CSP (Content Security Policy) Support
|
|
24
|
+
|
|
25
|
+
LayerPro supports three strategies for strict CSP environments, in order
|
|
26
|
+
of preference:
|
|
27
|
+
|
|
28
|
+
### Strategy A - Static stylesheet import (recommended)
|
|
29
|
+
|
|
30
|
+
```js
|
|
31
|
+
import 'layerpro/style.css'
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This serves the CSS as a static file under `style-src 'self'`. No inline
|
|
35
|
+
`<style>` element is created at all, so no nonce or hash is needed.
|
|
36
|
+
|
|
37
|
+
In `package.json`:
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"exports": {
|
|
42
|
+
"./style.css": "./dist/index.css"
|
|
43
|
+
},
|
|
44
|
+
"files": ["dist/index.css", "..."]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Strategy B - Nonce auto-detection
|
|
49
|
+
|
|
50
|
+
When the CSS-in-JS injection path is used (i.e., `import 'layerpro'` without
|
|
51
|
+
the static stylesheet), the injected `<style>` element reads the nonce from:
|
|
52
|
+
|
|
53
|
+
1. `window.__layerproNonce` - set explicitly via `layerpro.config({ nonce })`
|
|
54
|
+
2. Any existing `<style>` or `<script>` element with a `nonce` attribute
|
|
55
|
+
3. `document.body.dataset.nonce`
|
|
56
|
+
|
|
57
|
+
This is implemented in `tsup.plugin.injectCss.ts`:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
const n = window.__layerproNonce
|
|
61
|
+
|| window.layerpro?.nonce
|
|
62
|
+
|| document.querySelector('style[nonce],script[nonce]')?.nonce
|
|
63
|
+
|| document.body?.dataset?.nonce
|
|
64
|
+
if (n) s.setAttribute('nonce', n)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Under a strict `style-src` policy with no detectable nonce, the stylesheet
|
|
68
|
+
is **silently dropped** (the browser rejects it). Popups will render unstyled.
|
|
69
|
+
|
|
70
|
+
### Strategy C - Explicit nonce configuration
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// Call BEFORE importing LayerPro so the nonce is set at injection time
|
|
74
|
+
window.__layerproNonce = 'your-base64-nonce-here'
|
|
75
|
+
// OR
|
|
76
|
+
import 'layerpro'
|
|
77
|
+
layerpro.config({ nonce: 'your-base64-nonce-here' })
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
> **Important**: `layerpro.config({ nonce })` must be called **before** the
|
|
81
|
+
> CSS is first injected (i.e., before `import 'layerpro'`). If the module
|
|
82
|
+
> was already imported, the nonce will not be retroactively applied to the
|
|
83
|
+
> already-injected `<style>`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 3. Global Override Security Model
|
|
88
|
+
|
|
89
|
+
### Risk (legacy 3.x pattern)
|
|
90
|
+
|
|
91
|
+
In LayerPro 3.x, `import 'layerpro'` unconditionally replaced
|
|
92
|
+
`window.alert`, `window.confirm`, `window.prompt`, and added `window.message`.
|
|
93
|
+
The overridden versions were **callback-based and asynchronous** - they did not
|
|
94
|
+
return native values:
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
import 'layerpro'
|
|
98
|
+
|
|
99
|
+
confirm('Are you sure?') // returns undefined, NOT a boolean
|
|
100
|
+
if (confirm('Proceed?')) { ... } // ALWAYS falsey - silently breaks!
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Any third-party code in your `node_modules` that relied on synchronous return
|
|
104
|
+
values would break **silently** (no error, no warning, just the falsy branch).
|
|
105
|
+
|
|
106
|
+
### 4.0 Mitigation: opt-in globals
|
|
107
|
+
|
|
108
|
+
LayerPro 4.0 **removes the auto-override**. The main entry (`import 'layerpro'`)
|
|
109
|
+
no longer touches browser globals at all. To opt in to global overrides, use:
|
|
110
|
+
|
|
111
|
+
```js
|
|
112
|
+
import 'layerpro/global' // explicitly overrides window.alert/confirm/prompt/message
|
|
113
|
+
|
|
114
|
+
const confirmed = await confirm('Are you sure?') // Promise<boolean>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Or programmatically:
|
|
118
|
+
|
|
119
|
+
```js
|
|
120
|
+
import 'layerpro'
|
|
121
|
+
layerpro.installGlobals() // same effect, explicit call
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Recommendation
|
|
125
|
+
|
|
126
|
+
In 4.0, simply **do not import `layerpro/global`** if you need the native
|
|
127
|
+
synchronous `alert`/`confirm`/`prompt`. Use the Promise-based methods on the
|
|
128
|
+
`layerpro` object directly instead:
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
import 'layerpro'
|
|
132
|
+
|
|
133
|
+
const confirmed = await layerpro.confirm('Are you sure?')
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## 4. XSS Prevention
|
|
139
|
+
|
|
140
|
+
All user-provided string content is escaped via `escapeHtml()` (regex-based
|
|
141
|
+
replacement of `& < > " '` → HTML entities) before being inserted into the DOM:
|
|
142
|
+
|
|
143
|
+
- **Popup body (text/HTML strings)**: `core/genLayer.ts:escapeHtml()`
|
|
144
|
+
- **Button labels**: `core/genLayer.ts:escapeHtml()` (confirm/cancel text)
|
|
145
|
+
- **Popup name/icon**: `core/genLayer.ts:escapeHtml()`
|
|
146
|
+
- **`alert()`/`confirm()`/`prompt()`/`message()` bodies**: `core/message.ts:escapeHtml()`
|
|
147
|
+
|
|
148
|
+
This is **not** a sanitization library - it does not allow any HTML through.
|
|
149
|
+
Raw HTML rendering is deliberately not supported for string bodies. If you
|
|
150
|
+
need raw HTML, use the React adapter with a component that explicitly
|
|
151
|
+
renders `dangerouslySetInnerHTML`.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 5. `source` Prop (Remote Content Loading)
|
|
156
|
+
|
|
157
|
+
The `source` prop fetches same-origin HTML and injects it into the popup body.
|
|
158
|
+
|
|
159
|
+
### Same-origin enforcement
|
|
160
|
+
|
|
161
|
+
- `core/genLayer.ts:loadSource()` constructs a `URL` and checks
|
|
162
|
+
`url.origin !== window.location.origin`
|
|
163
|
+
- The `fetch()` call also uses `mode: 'same-origin'` as a second layer
|
|
164
|
+
- The same-origin guard is the **primary defense** against SSRF
|
|
165
|
+
|
|
166
|
+
### What is rendered
|
|
167
|
+
|
|
168
|
+
Remote HTML is injected as **raw, unescaped HTML** (as of 3.4.1 - previously
|
|
169
|
+
it was double-escaped, rendering all tags as visible text). This means:
|
|
170
|
+
|
|
171
|
+
- `<p>Hello</p>` renders as a real paragraph (not visible tags)
|
|
172
|
+
- `<script>` tags in the response are **executed** when the HTML is parsed
|
|
173
|
+
- Event handlers (`onclick="..."`) in the response are **activated**
|
|
174
|
+
|
|
175
|
+
### Consumer responsibility
|
|
176
|
+
|
|
177
|
+
The same-origin guard protects against cross-origin SSRF, but **not** against
|
|
178
|
+
same-origin XSS. If an attacker can inject malicious HTML into an endpoint
|
|
179
|
+
on your own domain (stored XSS), the `source` prop will render it.
|
|
180
|
+
|
|
181
|
+
**Always sanitize server-side content before serving it as a `source` URL.**
|
|
182
|
+
|
|
183
|
+
### Abort safety
|
|
184
|
+
|
|
185
|
+
An `AbortController` is associated with each `source` fetch and is aborted
|
|
186
|
+
when the popup closes (`layerpro.popup.abortSource(id)`), preventing
|
|
187
|
+
callbacks from targeting a destroyed layer.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 6. React Adapter Isolation
|
|
192
|
+
|
|
193
|
+
The React adapter (`layerpro/react`) is loaded via dynamic `import('layerpro/react')`
|
|
194
|
+
only when a React element is passed as the `body` prop:
|
|
195
|
+
|
|
196
|
+
- `react-dom` is **never** imported in the core bundle (verified by `verify-fixes.mjs`)
|
|
197
|
+
- The adapter is a separate entry point declared in `package.json` `exports`
|
|
198
|
+
- `reattach` is bundled **inside** the adapter, not as a separate dependency
|
|
199
|
+
- The adapter externalizes only `react` and `react-dom` as peer dependencies
|
|
200
|
+
|
|
201
|
+
This ensures consumers who don't use React bodies never load `react-dom`.
|
|
202
|
+
|
|
203
|
+
### Risk
|
|
204
|
+
|
|
205
|
+
The `import('layerpro/react')` bare-specifier requires a bundler that
|
|
206
|
+
understands the `exports` field in `package.json`. In plain `<script type="module">`
|
|
207
|
+
environments or old bundlers, this dynamic import will fail. The failure is
|
|
208
|
+
caught and logged:
|
|
209
|
+
|
|
210
|
+
```js
|
|
211
|
+
import('layerpro/react').then(adapter => { ... }).catch(error => {
|
|
212
|
+
console.error('LayerPro Error: React adapter failed to load.')
|
|
213
|
+
console.debug(error)
|
|
214
|
+
})
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
No error is surfaced to the user - the popup renders with an empty body.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 7. Accessibility & Security Interaction
|
|
222
|
+
|
|
223
|
+
LayerPro renders popups with the following ARIA attributes (as of 3.4.1):
|
|
224
|
+
|
|
225
|
+
- `role="dialog"` on the popup window container
|
|
226
|
+
- `aria-modal="true"` on modal popups
|
|
227
|
+
- `aria-label` (from `props.name`) or `aria-labelledby` if a named title exists
|
|
228
|
+
- `aria-hidden="true"` on all `<body>` children except `#popup` while a modal is open
|
|
229
|
+
- `tabindex="-1"` on the popup window (focusable via script)
|
|
230
|
+
|
|
231
|
+
### Focus management
|
|
232
|
+
|
|
233
|
+
- The currently focused element is saved when a popup opens
|
|
234
|
+
- Focus is moved into the popup via `requestAnimationFrame` + `.focus()`
|
|
235
|
+
- A `Tab` key listener traps focus within the popup container (cycles to
|
|
236
|
+
first/last focusable element)
|
|
237
|
+
- On close, focus is restored to the previously active element and
|
|
238
|
+
`aria-hidden` is removed from page content
|
|
239
|
+
|
|
240
|
+
### Limitations
|
|
241
|
+
|
|
242
|
+
- This is a **lightweight** focus trap (Tab key only). It does not handle
|
|
243
|
+
screen reader virtual cursor navigation, nested modals, or `inert` polyfill.
|
|
244
|
+
- `aria-hidden` on page content is not equivalent to `inert` - focus can
|
|
245
|
+
still technically reach page elements via direct `.focus()` calls from
|
|
246
|
+
application code.
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 8. Build-time Security
|
|
251
|
+
|
|
252
|
+
- `tsup.plugin.injectCss.ts` injects CSS at build time with nonce support
|
|
253
|
+
- `tsup.plugin.copyStatic.ts` rewrites the dist `package.json` exports map
|
|
254
|
+
- `tsup.plugin.types.ts` generates `react.d.ts` and patches `index.d.ts`
|
|
255
|
+
with a `dphelper` reference (tsc strips `/// <reference>` from `.ts` files)
|
|
256
|
+
- `react.d.ts` is included in the npm `files` array so `import layerpro/react`
|
|
257
|
+
resolves types under `moduleResolution: bundler`
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## 9. Reporting
|
|
262
|
+
|
|
263
|
+
If you have a potential security vulnerability to report, please email
|
|
264
|
+
[@passariello](https://github.com/passariello) or see
|
|
265
|
+
<https://dario.passariello.ca/contact/>.
|
package/SECURITY.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
# Security
|
|
2
2
|
|
|
3
3
|
Please email [@passariello](https://github.com/passariello) or see <https://dario.passariello.ca/contact/> if you have a potential security vulnerability to report.
|
|
4
|
+
|
|
5
|
+
## Source feature (same-origin fetch)
|
|
6
|
+
|
|
7
|
+
The `source` prop fetches HTML from a same-origin URL and renders it as-is.
|
|
8
|
+
The same-origin guard prevents SSRF to other hosts, but the fetched markup is
|
|
9
|
+
NOT sanitized by LayerPro. Any same-origin endpoint used as a `source` must
|
|
10
|
+
sanitize its output server-side. Consumers needing client-side sanitization
|
|
11
|
+
should pass a React element in `body` instead.
|