alchemy_cms 8.3.7 → 8.3.8
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.
- checksums.yaml +4 -4
- data/app/assets/builds/alchemy/admin.css +1 -1
- data/app/assets/builds/alchemy/alchemy_admin.min.js +1 -1
- data/app/assets/builds/alchemy/alchemy_admin.min.js.map +1 -1
- data/app/javascript/alchemy_admin/components/attachment_select.js +5 -2
- data/app/javascript/alchemy_admin/components/node_select.js +6 -3
- data/app/javascript/alchemy_admin/components/page_select.js +8 -5
- data/app/javascript/alchemy_admin/components/remote_select.js +35 -1
- data/app/stylesheets/alchemy/admin/_tom-select.scss +1 -0
- data/app/stylesheets/alchemy/admin/forms.scss +2 -1
- data/app/views/alchemy/admin/nodes/_page_nodes.html.erb +4 -2
- data/lib/alchemy/version.rb +1 -1
- metadata +1 -1
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
RemoteSelect,
|
|
3
|
+
escapeHtml
|
|
4
|
+
} from "alchemy_admin/components/remote_select"
|
|
2
5
|
|
|
3
6
|
export class AttachmentSelect extends RemoteSelect {
|
|
4
7
|
_renderResult(item) {
|
|
@@ -15,7 +18,7 @@ export class AttachmentSelect extends RemoteSelect {
|
|
|
15
18
|
_renderListEntry(attachment, term) {
|
|
16
19
|
return `
|
|
17
20
|
<div class="attachment-select--attachment">
|
|
18
|
-
<alchemy-icon name="${attachment.icon_css_class}"></alchemy-icon>
|
|
21
|
+
<alchemy-icon name="${escapeHtml(attachment.icon_css_class)}"></alchemy-icon>
|
|
19
22
|
<span class="attachment-select--attachment-name">${this._hightlightTerm(attachment.name, term)}</span>
|
|
20
23
|
</div>
|
|
21
24
|
`
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
RemoteSelect,
|
|
3
|
+
escapeHtml
|
|
4
|
+
} from "alchemy_admin/components/remote_select"
|
|
2
5
|
|
|
3
6
|
class NodeSelect extends RemoteSelect {
|
|
4
7
|
_searchQuery(term, page) {
|
|
@@ -23,7 +26,7 @@ class NodeSelect extends RemoteSelect {
|
|
|
23
26
|
* @private
|
|
24
27
|
*/
|
|
25
28
|
_renderListEntry(node, term) {
|
|
26
|
-
const ancestors = node.ancestors.map((a) => a.name)
|
|
29
|
+
const ancestors = node.ancestors.map((a) => escapeHtml(a.name))
|
|
27
30
|
const seperator = `<alchemy-icon name="arrow-right-s"></alchemy-icon>`
|
|
28
31
|
|
|
29
32
|
return `
|
|
@@ -38,7 +41,7 @@ class NodeSelect extends RemoteSelect {
|
|
|
38
41
|
</span>
|
|
39
42
|
</div>
|
|
40
43
|
<div class="node-select--node-url">
|
|
41
|
-
${node.url || ""}
|
|
44
|
+
${escapeHtml(node.url || "")}
|
|
42
45
|
</div>
|
|
43
46
|
</div>
|
|
44
47
|
`
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
RemoteSelect,
|
|
3
|
+
escapeHtml
|
|
4
|
+
} from "alchemy_admin/components/remote_select"
|
|
2
5
|
|
|
3
6
|
export class PageSelect extends RemoteSelect {
|
|
4
7
|
get pageId() {
|
|
@@ -30,7 +33,7 @@ export class PageSelect extends RemoteSelect {
|
|
|
30
33
|
* @private
|
|
31
34
|
*/
|
|
32
35
|
_renderResult(page) {
|
|
33
|
-
return page.text || page.name
|
|
36
|
+
return escapeHtml(page.text || page.name)
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
/**
|
|
@@ -46,11 +49,11 @@ export class PageSelect extends RemoteSelect {
|
|
|
46
49
|
<div class="page-select--top">
|
|
47
50
|
<alchemy-icon name="file-3"></alchemy-icon>
|
|
48
51
|
<span class="page-select--page-name">${this._hightlightTerm(page.name, term)}</span>
|
|
49
|
-
<span class="page-select--site-name">${page.site.name}</span>
|
|
52
|
+
<span class="page-select--site-name">${escapeHtml(page.site.name)}</span>
|
|
50
53
|
</div>
|
|
51
54
|
<div class="page-select--bottom">
|
|
52
|
-
<span class="page-select--page-urlname">${page.url_path}</span>
|
|
53
|
-
<span class="page-select--language-code">${page.language_code}</span>
|
|
55
|
+
<span class="page-select--page-urlname">${escapeHtml(page.url_path)}</span>
|
|
56
|
+
<span class="page-select--language-code">${escapeHtml(page.language_code)}</span>
|
|
54
57
|
</div>
|
|
55
58
|
</div>
|
|
56
59
|
`
|
|
@@ -1,7 +1,41 @@
|
|
|
1
1
|
import { setupSelectLocale } from "alchemy_admin/i18n"
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Escapes a string for use within HTML. Select2 hands the return value of
|
|
5
|
+
* `formatResult`/`formatSelection` straight to jQuery, so everything woven into
|
|
6
|
+
* those templates has to be escaped here.
|
|
7
|
+
* @param {string} value
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
export function escapeHtml(value) {
|
|
11
|
+
return `${value}`
|
|
12
|
+
.replace(/&/g, "&")
|
|
13
|
+
.replace(/</g, "<")
|
|
14
|
+
.replace(/>/g, ">")
|
|
15
|
+
.replace(/"/g, """)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Escapes a string for literal use inside a `RegExp`, so a term with
|
|
20
|
+
* metacharacters (e.g. `(`) matches itself instead of being compiled as a
|
|
21
|
+
* pattern, which could match the wrong text or throw a SyntaxError.
|
|
22
|
+
* @param {string} value
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
export function escapeRegExp(value) {
|
|
26
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
27
|
+
}
|
|
28
|
+
|
|
3
29
|
export function hightlightTerm(name, term) {
|
|
4
|
-
|
|
30
|
+
// The result is rendered raw as the entry's name, so the name must be escaped
|
|
31
|
+
// before the term markup is woven in — otherwise a crafted name (e.g. a menu
|
|
32
|
+
// node title) is a stored XSS sink.
|
|
33
|
+
const escapedName = escapeHtml(name)
|
|
34
|
+
if (!term) return escapedName
|
|
35
|
+
return escapedName.replace(
|
|
36
|
+
new RegExp(escapeRegExp(term), "gi"),
|
|
37
|
+
(match) => `<em>${match}</em>`
|
|
38
|
+
)
|
|
5
39
|
}
|
|
6
40
|
|
|
7
41
|
export class RemoteSelect extends HTMLElement {
|
|
@@ -7,11 +7,13 @@
|
|
|
7
7
|
<th class="tools"></th>
|
|
8
8
|
</tr>
|
|
9
9
|
<% nodes = @page.nodes.select(&:persisted?) %>
|
|
10
|
-
<% seperator = '<alchemy-icon name="arrow-right-s" style="vertical-align: text-bottom"></alchemy-icon>' %>
|
|
10
|
+
<% seperator = '<alchemy-icon name="arrow-right-s" style="vertical-align: text-bottom"></alchemy-icon>'.html_safe %>
|
|
11
11
|
<% if nodes.length > 0 %>
|
|
12
12
|
<% nodes.each do |node| %>
|
|
13
13
|
<tr class="even">
|
|
14
|
-
<td
|
|
14
|
+
<td>
|
|
15
|
+
<%= safe_join(node.ancestors.map(&:name), seperator) %><%= seperator %><strong><%= node.name %></strong>
|
|
16
|
+
</td>
|
|
15
17
|
<td class="tools">
|
|
16
18
|
<sl-tooltip content="<%= Alchemy.t("delete_node") %>">
|
|
17
19
|
<%= link_to render_icon(:minus),
|
data/lib/alchemy/version.rb
CHANGED