@budibase/bbui 3.46.0 → 3.48.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/dist/bbui.mjs +2 -2
- package/dist/{easymde-BLIQXh8H.mjs → easymde-ChtNhWXp.mjs} +1 -1
- package/dist/{index-tE3FZQPr.mjs → index-DhX0y_e_.mjs} +3050 -2972
- package/package.json +2 -2
- package/src/Form/Core/Picker.svelte +7 -0
- package/src/Form/Select.svelte +2 -0
- package/src/Markdown/renderMarkdown.spec.ts +43 -0
- package/src/Markdown/renderMarkdown.ts +75 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budibase/bbui",
|
|
3
3
|
"description": "A UI solution used in the different Budibase projects.",
|
|
4
|
-
"version": "v3.
|
|
4
|
+
"version": "v3.48.0",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"module": "dist/bbui.mjs",
|
|
7
7
|
"exports": {
|
|
@@ -108,5 +108,5 @@
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
},
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "0d896a86074936025b8ef6e3400ab650faeb8d06"
|
|
112
112
|
}
|
|
@@ -228,6 +228,7 @@
|
|
|
228
228
|
{id}
|
|
229
229
|
class="spectrum-Picker spectrum-Picker--size{size}"
|
|
230
230
|
class:has-border={bordered}
|
|
231
|
+
class:has-subtitle={!!fieldSubtitle}
|
|
231
232
|
class:spectrum-Picker--quiet={quiet}
|
|
232
233
|
{disabled}
|
|
233
234
|
class:is-open={open}
|
|
@@ -447,6 +448,12 @@
|
|
|
447
448
|
border: 1px solid var(--spectrum-global-color-gray-200);
|
|
448
449
|
border-radius: 6px;
|
|
449
450
|
}
|
|
451
|
+
.spectrum-Picker.has-subtitle {
|
|
452
|
+
height: auto;
|
|
453
|
+
align-items: center;
|
|
454
|
+
padding-top: var(--spacing-s);
|
|
455
|
+
padding-bottom: var(--spacing-s);
|
|
456
|
+
}
|
|
450
457
|
.spectrum-Picker--quiet {
|
|
451
458
|
padding: 0 8px;
|
|
452
459
|
}
|
package/src/Form/Select.svelte
CHANGED
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
export let onOptionMouseleave = () => {}
|
|
54
54
|
export let loading: boolean | undefined = false
|
|
55
55
|
export let searchPlaceholder: string | undefined = undefined
|
|
56
|
+
export let searchTerm: string | undefined = undefined
|
|
56
57
|
export let required: boolean | undefined = false
|
|
57
58
|
export let wrapText: boolean = false
|
|
58
59
|
export let description: string | undefined = undefined
|
|
@@ -114,6 +115,7 @@
|
|
|
114
115
|
{tooltipMessage}
|
|
115
116
|
{searchPlaceholder}
|
|
116
117
|
{wrapText}
|
|
118
|
+
bind:searchTerm
|
|
117
119
|
on:change={onChange}
|
|
118
120
|
on:click
|
|
119
121
|
on:loadMore
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest"
|
|
2
|
+
|
|
3
|
+
import { renderMarkdown } from "./renderMarkdown"
|
|
4
|
+
|
|
5
|
+
describe("renderMarkdown", () => {
|
|
6
|
+
it("preserves safe HTML used for email layouts", () => {
|
|
7
|
+
const html = renderMarkdown(
|
|
8
|
+
'<div style="color:red;padding:4px"><span>Alert</span><table width="600" cellpadding="4"><tr><td colspan="2">Body</td></tr></table></div>'
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
expect(html).toBe(
|
|
12
|
+
'<div style="color:red;padding:4px"><span>Alert</span><table width="600" cellpadding="4"><tr><td colspan="2">Body</td></tr></table></div>'
|
|
13
|
+
)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it("blocks the stored XSS advisory payload", () => {
|
|
17
|
+
const html = renderMarkdown(
|
|
18
|
+
'<iframe srcdoc="<script src=https://example.com/payload.mjs></script>"></iframe>'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
expect(html).toBe("")
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it("removes executable attributes, URLs, and styles", () => {
|
|
25
|
+
const html = renderMarkdown(
|
|
26
|
+
'<div class="spectrum-Modal" onclick="alert(1)" style="color:red;position:fixed;background-image:url(https://example.com/pixel)"><img src="https://example.com/image.png" onerror="alert(1)"><a href="javascript:alert(1)">Link</a></div>'
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
expect(html).toBe(
|
|
30
|
+
'<div style="color:red"><img src="https://example.com/image.png" /><a>Link</a></div>'
|
|
31
|
+
)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it("removes unsafe values from allowed style properties", () => {
|
|
35
|
+
const html = renderMarkdown(
|
|
36
|
+
'<div style="color:url(https://example.com/x);width:expression(alert(1));padding:4px">Direct</div><div style="color:u\\72l(https://example.com/x);width:e\\78pression(alert(1));font-family:u/**/rl(https://example.com/x);padding:4px">Obfuscated</div>'
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
expect(html).toBe(
|
|
40
|
+
'<div style="padding:4px">Direct</div><div style="padding:4px">Obfuscated</div>'
|
|
41
|
+
)
|
|
42
|
+
})
|
|
43
|
+
})
|
|
@@ -28,8 +28,81 @@ const MARKDOWN_TAGS = [
|
|
|
28
28
|
"thead",
|
|
29
29
|
"tr",
|
|
30
30
|
"ul",
|
|
31
|
+
"div",
|
|
32
|
+
"span",
|
|
31
33
|
]
|
|
32
34
|
|
|
35
|
+
const MARKDOWN_ATTRIBUTES = {
|
|
36
|
+
"*": [
|
|
37
|
+
"align",
|
|
38
|
+
"bgcolor",
|
|
39
|
+
"dir",
|
|
40
|
+
"height",
|
|
41
|
+
"lang",
|
|
42
|
+
"role",
|
|
43
|
+
"style",
|
|
44
|
+
"valign",
|
|
45
|
+
"width",
|
|
46
|
+
],
|
|
47
|
+
a: ["href", "title"],
|
|
48
|
+
img: ["alt", "src", "title"],
|
|
49
|
+
li: ["value"],
|
|
50
|
+
ol: ["reversed", "start", "type"],
|
|
51
|
+
table: ["border", "cellpadding", "cellspacing"],
|
|
52
|
+
td: ["colspan", "headers", "nowrap", "rowspan"],
|
|
53
|
+
th: ["colspan", "headers", "nowrap", "rowspan", "scope"],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const SAFE_STYLE_VALUE =
|
|
57
|
+
/^(?![\s\S]*(?:\\|\/\*|(?:expression|url)\s*\())[\s\S]*$/i
|
|
58
|
+
const MARKDOWN_STYLE_PROPERTIES = [
|
|
59
|
+
"background-color",
|
|
60
|
+
"border",
|
|
61
|
+
"border-bottom",
|
|
62
|
+
"border-collapse",
|
|
63
|
+
"border-color",
|
|
64
|
+
"border-left",
|
|
65
|
+
"border-right",
|
|
66
|
+
"border-spacing",
|
|
67
|
+
"border-style",
|
|
68
|
+
"border-top",
|
|
69
|
+
"border-width",
|
|
70
|
+
"color",
|
|
71
|
+
"display",
|
|
72
|
+
"font",
|
|
73
|
+
"font-family",
|
|
74
|
+
"font-size",
|
|
75
|
+
"font-style",
|
|
76
|
+
"font-weight",
|
|
77
|
+
"height",
|
|
78
|
+
"letter-spacing",
|
|
79
|
+
"line-height",
|
|
80
|
+
"margin",
|
|
81
|
+
"margin-bottom",
|
|
82
|
+
"margin-left",
|
|
83
|
+
"margin-right",
|
|
84
|
+
"margin-top",
|
|
85
|
+
"max-height",
|
|
86
|
+
"max-width",
|
|
87
|
+
"min-height",
|
|
88
|
+
"min-width",
|
|
89
|
+
"padding",
|
|
90
|
+
"padding-bottom",
|
|
91
|
+
"padding-left",
|
|
92
|
+
"padding-right",
|
|
93
|
+
"padding-top",
|
|
94
|
+
"text-align",
|
|
95
|
+
"text-decoration",
|
|
96
|
+
"vertical-align",
|
|
97
|
+
"width",
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
const MARKDOWN_STYLES = {
|
|
101
|
+
"*": Object.fromEntries(
|
|
102
|
+
MARKDOWN_STYLE_PROPERTIES.map(property => [property, [SAFE_STYLE_VALUE]])
|
|
103
|
+
),
|
|
104
|
+
}
|
|
105
|
+
|
|
33
106
|
export const renderMarkdown = (markdown: string | undefined) => {
|
|
34
107
|
if (!markdown) {
|
|
35
108
|
return ""
|
|
@@ -39,10 +112,8 @@ export const renderMarkdown = (markdown: string | undefined) => {
|
|
|
39
112
|
|
|
40
113
|
return sanitizeHtml(html, {
|
|
41
114
|
allowedTags: MARKDOWN_TAGS,
|
|
42
|
-
allowedAttributes:
|
|
43
|
-
|
|
44
|
-
img: ["alt", "src", "title"],
|
|
45
|
-
},
|
|
115
|
+
allowedAttributes: MARKDOWN_ATTRIBUTES,
|
|
116
|
+
allowedStyles: MARKDOWN_STYLES,
|
|
46
117
|
allowedSchemes: ["http", "https", "mailto", "tel"],
|
|
47
118
|
allowedSchemesByTag: {
|
|
48
119
|
img: ["http", "https"],
|