spyne 0.22.4 → 0.23.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/lib/spyne.esm.js +3 -3
- package/lib/spyne.esm.js.map +1 -1
- package/lib/spyne.umd.js +2 -2
- package/lib/spyne.umd.js.LICENSE.txt +2 -2
- package/package.json +1 -1
- package/src/spyne/channels/channel-fetch-class.js +2 -2
- package/src/spyne/spyne-app.js +2 -2
- package/src/spyne/utils/channel-fetch-util.js +5 -2
- package/src/spyne/utils/sanitize-html.js +68 -16
- package/src/spyne/utils/spyne-trait.js +5 -2
- package/src/spyne/views/dom-element-template.js +6 -4
- package/src/spyne/views/view-stream-broadcaster.js +4 -0
package/package.json
CHANGED
|
@@ -46,7 +46,7 @@ export class ChannelFetch extends Channel {
|
|
|
46
46
|
*
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
|
-
constructor(name, props = {}) {
|
|
49
|
+
constructor(name, props = { disableSanitize:false }) {
|
|
50
50
|
// ALLOW FOR GENERIC MAP PROPERTY
|
|
51
51
|
|
|
52
52
|
ChannelFetch.validateMapMethod(props, name)
|
|
@@ -123,7 +123,7 @@ export class ChannelFetch extends Channel {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
consolidateAllFetchProps(options, props = this.props) {
|
|
126
|
-
const propsOptions = pick(['mapFn', 'url', 'header', 'body', 'mode', 'method', 'responseType', 'debug'], props)
|
|
126
|
+
const propsOptions = pick(['mapFn', 'url', 'header', 'body', 'mode', 'disableSanitize', 'method', 'responseType', 'debug'], props)
|
|
127
127
|
const mergeOptions = (o1, o2) => mergeDeepRight(o1, o2)
|
|
128
128
|
const filterOutUndefined = reject(isNil)
|
|
129
129
|
return compose(filterOutUndefined, mergeOptions)(propsOptions, options)
|
package/src/spyne/spyne-app.js
CHANGED
|
@@ -6,7 +6,7 @@ import { sanitizeHTMLConfigure } from './utils/sanitize-html.js'
|
|
|
6
6
|
import { sanitizeDataConfigure } from './utils/sanitize-data.js'
|
|
7
7
|
|
|
8
8
|
const _channels = new ChannelsMap()
|
|
9
|
-
const version = '0.
|
|
9
|
+
const version = '0.23.0'
|
|
10
10
|
|
|
11
11
|
class SpyneApplication {
|
|
12
12
|
/**
|
|
@@ -41,7 +41,7 @@ class SpyneApplication {
|
|
|
41
41
|
init(config = {}, testMode = false) {
|
|
42
42
|
// this.channels = new ChannelsMap();
|
|
43
43
|
/*!
|
|
44
|
-
* Spyne 0.
|
|
44
|
+
* Spyne 0.23.0
|
|
45
45
|
* https://spynejs.org
|
|
46
46
|
*
|
|
47
47
|
* @license
|
|
@@ -59,6 +59,7 @@ export class ChannelFetchUtil {
|
|
|
59
59
|
this._serverOptions = ChannelFetchUtil.setServerOptions(options)
|
|
60
60
|
this._subscriber = subscriber !== undefined ? subscriber : testSubscriber
|
|
61
61
|
this.debug = options.debug !== undefined ? options.debug : false
|
|
62
|
+
this.disableSanitize = options?.disableSanitize
|
|
62
63
|
this.channelName = CHANNEL_NAME
|
|
63
64
|
|
|
64
65
|
const fetchProps = {
|
|
@@ -66,6 +67,7 @@ export class ChannelFetchUtil {
|
|
|
66
67
|
url: this.url,
|
|
67
68
|
serverOptions: this.serverOptions,
|
|
68
69
|
responseType: this.responseType,
|
|
70
|
+
disableSanitize: this.disableSanitize,
|
|
69
71
|
debug: this.debug
|
|
70
72
|
}
|
|
71
73
|
if (testMode !== true) {
|
|
@@ -82,8 +84,8 @@ export class ChannelFetchUtil {
|
|
|
82
84
|
const metadata = { channelName, url, responseType, serverOptions }
|
|
83
85
|
|
|
84
86
|
return (data) => {
|
|
85
|
-
const disableSanitize =
|
|
86
|
-
|
|
87
|
+
const disableSanitize = props?.disableSanitize === true
|
|
88
|
+
/*
|
|
87
89
|
if (disableSanitize) {
|
|
88
90
|
const env = (typeof process !== 'undefined' && process.env && process.env.NODE_ENV)
|
|
89
91
|
? process.env.NODE_ENV
|
|
@@ -100,6 +102,7 @@ export class ChannelFetchUtil {
|
|
|
100
102
|
)
|
|
101
103
|
}
|
|
102
104
|
}
|
|
105
|
+
*/
|
|
103
106
|
|
|
104
107
|
const sanitizedData = disableSanitize ? data : sanitizeData(data)
|
|
105
108
|
return mapMethod(sanitizedData, metadata)
|
|
@@ -3,25 +3,77 @@ import DOMPurify from 'dompurify'
|
|
|
3
3
|
let _sanitizeHTML
|
|
4
4
|
let isConfigured = false
|
|
5
5
|
|
|
6
|
-
const sanitizeHTMLConfigure = (config) => {
|
|
7
|
-
if (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
6
|
+
const sanitizeHTMLConfigure = (config = {}) => {
|
|
7
|
+
if (isConfigured) {
|
|
8
|
+
console.warn('sanitizeHTML is already configured. Reconfiguration is not allowed.')
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
strict = true,
|
|
14
|
+
allowTargetAttr = true,
|
|
15
|
+
addNoopener = true
|
|
16
|
+
} = config
|
|
17
|
+
|
|
18
|
+
if (!strict) {
|
|
19
|
+
_sanitizeHTML = (html) => html
|
|
21
20
|
isConfigured = true
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const domPurifyConfig = {
|
|
25
|
+
RETURN_TRUSTED_TYPE: false,
|
|
26
|
+
ADD_ATTR: allowTargetAttr ? ['target'] : []
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sanitizeWithPolicy = (html) => DOMPurify.sanitize(html, domPurifyConfig)
|
|
30
|
+
|
|
31
|
+
if (allowTargetAttr) {
|
|
32
|
+
DOMPurify.addHook('afterSanitizeAttributes', (node) => {
|
|
33
|
+
if (!node || typeof node.getAttribute !== 'function') return
|
|
34
|
+
if (node.tagName !== 'A') return
|
|
35
|
+
|
|
36
|
+
const href = node.getAttribute('href')
|
|
37
|
+
const target = node.getAttribute('target')
|
|
38
|
+
|
|
39
|
+
// remove target on anchors without href
|
|
40
|
+
if (!href && target) {
|
|
41
|
+
node.removeAttribute('target')
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!target) return
|
|
46
|
+
|
|
47
|
+
const validTargets = ['_blank', '_self', '_parent', '_top']
|
|
48
|
+
if (!validTargets.includes(target)) {
|
|
49
|
+
node.removeAttribute('target')
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (target === '_blank' && addNoopener) {
|
|
54
|
+
const currentRel = node.getAttribute('rel') || ''
|
|
55
|
+
const relParts = currentRel.split(/\s+/).filter(Boolean)
|
|
56
|
+
const relSet = new Set(relParts)
|
|
57
|
+
|
|
58
|
+
relSet.add('noopener')
|
|
59
|
+
relSet.add('noreferrer')
|
|
60
|
+
|
|
61
|
+
node.setAttribute('rel', Array.from(relSet).join(' '))
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (window.trustedTypes) {
|
|
67
|
+
const policy = window.trustedTypes.createPolicy('default', {
|
|
68
|
+
createHTML: (toEscape) => sanitizeWithPolicy(toEscape)
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
_sanitizeHTML = (html) => policy.createHTML(html)
|
|
22
72
|
} else {
|
|
23
|
-
|
|
73
|
+
_sanitizeHTML = (html) => sanitizeWithPolicy(html)
|
|
24
74
|
}
|
|
75
|
+
|
|
76
|
+
isConfigured = true
|
|
25
77
|
}
|
|
26
78
|
|
|
27
79
|
const sanitizeHTML = (html) => {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getAllMethodNames } from './frp-tools.js'
|
|
2
2
|
import { reject, curryN, __, map } from 'ramda'
|
|
3
|
+
import { SpyneAppProperties } from './spyne-app-properties.js'
|
|
3
4
|
|
|
4
5
|
export class SpyneTrait {
|
|
5
6
|
/**
|
|
@@ -44,7 +45,9 @@ export class SpyneTrait {
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
checkForMalformedMethods(methodsArr) {
|
|
47
|
-
|
|
48
|
+
const isDebugMode = SpyneAppProperties?.debug || false
|
|
49
|
+
|
|
50
|
+
if (this.prefix === '' && isDebugMode) {
|
|
48
51
|
console.warn(`SPYNE WARNING: The following SpyneTrait ${this.constructor.name} needs a prefix`)
|
|
49
52
|
return
|
|
50
53
|
}
|
|
@@ -53,7 +56,7 @@ export class SpyneTrait {
|
|
|
53
56
|
const hasPrefix = (str) => str.indexOf(this.prefix) === 0
|
|
54
57
|
|
|
55
58
|
const malformedMethodsArr = reject(hasPrefix, methodsArr)
|
|
56
|
-
if (malformedMethodsArr.length >= 1) {
|
|
59
|
+
if (malformedMethodsArr.length >= 1 && isDebugMode) {
|
|
57
60
|
const warningStr = `Spyne Warning: The following method(s) in ${this.constructor.name} require the prefix, "${this.prefix}": [${malformedMethodsArr.join(', ')}];`
|
|
58
61
|
console.warn(warningStr)
|
|
59
62
|
}
|
|
@@ -51,10 +51,12 @@ export class DomElementTemplate {
|
|
|
51
51
|
this.template = DomElementTemplate.normalizeTripleBrackets(this.template)
|
|
52
52
|
|
|
53
53
|
if (this.isProxyData === true) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
const proxyFormatter = SpyneAppProperties.enableCMSProxies === true
|
|
55
|
+
? SpyneAppProperties.formatTemplateForProxyData
|
|
56
|
+
: DomElementTemplate.formatTemplateForProxyData
|
|
57
|
+
|
|
58
|
+
if (typeof proxyFormatter === 'function') {
|
|
59
|
+
this.template = proxyFormatter(this.template)
|
|
58
60
|
}
|
|
59
61
|
}
|
|
60
62
|
|
|
@@ -85,6 +85,10 @@ export class ViewStreamBroadcaster {
|
|
|
85
85
|
const data = {}// convertDomStringMapToObj(q.dataset);
|
|
86
86
|
data.payload = convertDomStringMapToObj(q.dataset)
|
|
87
87
|
data.payload = omit(['channel'], data.payload)
|
|
88
|
+
if (data.channel === 'ROUTE') {
|
|
89
|
+
data.payload.endPreventDefault = true
|
|
90
|
+
}
|
|
91
|
+
|
|
88
92
|
data.channel = channel
|
|
89
93
|
// payload needs vsid# to pass verification
|
|
90
94
|
|