@suveren/gateway 0.29.0 → 0.30.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/content/integrations/gmail.json +13 -2
- package/content/integrations/mail.json +12 -2
- package/content/integrations/mollie.json +10 -0
- package/dist/control-plane/index.mjs +125 -37
- package/dist/mcp-server/http.mjs +772 -420
- package/dist/ui/assets/index-C2BnlNib.js +123 -0
- package/dist/ui/assets/{index-DUIlMVlP.css → index-DPgewsLG.css} +1 -1
- package/dist/ui/index.html +2 -2
- package/node_modules/qs/.github/THREAT_MODEL.md +4 -2
- package/node_modules/qs/CHANGELOG.md +38 -14
- package/node_modules/qs/README.md +31 -3
- package/node_modules/qs/dist/qs.js +20 -20
- package/node_modules/qs/lib/parse.js +94 -4
- package/node_modules/qs/lib/stringify.js +4 -4
- package/node_modules/qs/lib/utils.js +130 -6
- package/node_modules/qs/package.json +4 -4
- package/node_modules/qs/test/parse.js +174 -0
- package/node_modules/qs/test/stringify.js +263 -33
- package/node_modules/qs/test/utils.js +184 -0
- package/node_modules/sanitize-html/index.js +47 -10
- package/node_modules/sanitize-html/package.json +2 -3
- package/package.json +1 -1
- package/dist/ui/assets/index-C3vTF2Ye.js +0 -123
- package/node_modules/deepmerge/.editorconfig +0 -7
- package/node_modules/deepmerge/.eslintcache +0 -1
- package/node_modules/deepmerge/changelog.md +0 -167
- package/node_modules/deepmerge/dist/cjs.js +0 -133
- package/node_modules/deepmerge/dist/umd.js +0 -139
- package/node_modules/deepmerge/index.d.ts +0 -20
- package/node_modules/deepmerge/index.js +0 -106
- package/node_modules/deepmerge/license.txt +0 -21
- package/node_modules/deepmerge/package.json +0 -42
- package/node_modules/deepmerge/readme.md +0 -264
- package/node_modules/deepmerge/rollup.config.js +0 -22
|
@@ -106,6 +106,146 @@ test('merge()', function (t) {
|
|
|
106
106
|
s2t.end();
|
|
107
107
|
});
|
|
108
108
|
|
|
109
|
+
st.test('with strictMerge, repeated conflicts append instead of nesting a new wrapper', function (s2t) {
|
|
110
|
+
var opts = { strictMerge: true };
|
|
111
|
+
var wrapped = utils.merge({ foo: 'bar' }, 'baz', opts);
|
|
112
|
+
s2t.deepEqual(wrapped, [{ foo: 'bar' }, 'baz'], 'first conflict wraps in an array');
|
|
113
|
+
|
|
114
|
+
var withObject = utils.merge(wrapped, { qux: 'quux' }, opts);
|
|
115
|
+
s2t.deepEqual(
|
|
116
|
+
withObject,
|
|
117
|
+
{ 0: { foo: 'bar' }, 1: 'baz', qux: 'quux' },
|
|
118
|
+
'merging an object converts the wrapper to an object'
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
var second = utils.merge(withObject, 'corge', opts);
|
|
122
|
+
s2t.deepEqual(
|
|
123
|
+
second,
|
|
124
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', qux: 'quux' },
|
|
125
|
+
'a second conflict appends at the next index'
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
var third = utils.merge(second, 'grault', opts);
|
|
129
|
+
s2t.deepEqual(
|
|
130
|
+
third,
|
|
131
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', 3: 'grault', qux: 'quux' },
|
|
132
|
+
'a third conflict appends again, so nesting depth stays constant'
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
s2t.end();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
st.test('with strictMerge, a caller-supplied array is still wrapped, not appended to', function (s2t) {
|
|
139
|
+
var merged = utils.merge({ 0: 'a', 1: 'b' }, 'c', { strictMerge: true });
|
|
140
|
+
s2t.deepEqual(merged, [{ 0: 'a', 1: 'b' }, 'c'], 'an unmarked object with numeric keys is wrapped');
|
|
141
|
+
s2t.end();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
st.test('with strictMerge, throws instead of appending a conflict past arrayLimit with throwOnLimitExceeded', function (s2t) {
|
|
145
|
+
var appendConflict = function (options) {
|
|
146
|
+
var wrapped = utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true });
|
|
147
|
+
var container = utils.merge(wrapped, { qux: 'quux' }, { strictMerge: true });
|
|
148
|
+
return utils.merge(container, 'corge', options);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
s2t['throws'](
|
|
152
|
+
function () { appendConflict({ strictMerge: true, arrayLimit: 1, throwOnLimitExceeded: true }); },
|
|
153
|
+
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
|
154
|
+
'appending to a two-element container past arrayLimit throws'
|
|
155
|
+
);
|
|
156
|
+
s2t['throws'](
|
|
157
|
+
function () { appendConflict({ strictMerge: true, arrayLimit: 2, throwOnLimitExceeded: true }); },
|
|
158
|
+
new RangeError('Array limit exceeded. Only 2 elements allowed in an array.'),
|
|
159
|
+
'uses the plural message when arrayLimit is not 1'
|
|
160
|
+
);
|
|
161
|
+
s2t.deepEqual(
|
|
162
|
+
appendConflict({ strictMerge: true, arrayLimit: 3, throwOnLimitExceeded: true }),
|
|
163
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', qux: 'quux' },
|
|
164
|
+
'appends while the container stays within arrayLimit'
|
|
165
|
+
);
|
|
166
|
+
s2t.deepEqual(
|
|
167
|
+
appendConflict({ strictMerge: true, arrayLimit: 1 }),
|
|
168
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'corge', qux: 'quux' },
|
|
169
|
+
'still appends past arrayLimit without throwOnLimitExceeded, since the container is already an object'
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
s2t.end();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
st.test('with strictMerge, a conflict appends after numeric keys already in its container', function (s2t) {
|
|
176
|
+
var opts = { strictMerge: true };
|
|
177
|
+
|
|
178
|
+
var container = utils.merge(utils.merge({ foo: 'bar' }, 'baz', opts), { qux: 'quux' }, opts);
|
|
179
|
+
var withIndex = utils.merge(container, { 2: 'keep' }, opts);
|
|
180
|
+
s2t.deepEqual(
|
|
181
|
+
utils.merge(withIndex, 'corge', opts),
|
|
182
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'keep', 3: 'corge', qux: 'quux' },
|
|
183
|
+
'a numeric key merged into the container is not overwritten'
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
var sparse = [];
|
|
187
|
+
sparse[2] = 'keep';
|
|
188
|
+
var extended = utils.merge(utils.merge({ foo: 'bar' }, 'baz', opts), sparse, opts);
|
|
189
|
+
s2t.deepEqual(extended, [{ foo: 'bar' }, 'baz', 'keep'], 'an indexed value extends the container while it is an array');
|
|
190
|
+
var converted = utils.merge(extended, { qux: 'quux' }, opts);
|
|
191
|
+
s2t.deepEqual(
|
|
192
|
+
utils.merge(converted, 'corge', opts),
|
|
193
|
+
{ 0: { foo: 'bar' }, 1: 'baz', 2: 'keep', 3: 'corge', qux: 'quux' },
|
|
194
|
+
'converting the container to an object keeps its last index'
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
s2t.end();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
st.test('enforces arrayLimit on the two-element array built for colliding values', function (s2t) {
|
|
201
|
+
s2t['throws'](
|
|
202
|
+
function () { utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true, arrayLimit: 1, throwOnLimitExceeded: true }); },
|
|
203
|
+
new RangeError('Array limit exceeded. Only 1 element allowed in an array.'),
|
|
204
|
+
'a strictMerge conflict throws at arrayLimit 1'
|
|
205
|
+
);
|
|
206
|
+
s2t['throws'](
|
|
207
|
+
function () { utils.merge('a', 'b', { arrayLimit: 0, throwOnLimitExceeded: true }); },
|
|
208
|
+
new RangeError('Array limit exceeded. Only 0 elements allowed in an array.'),
|
|
209
|
+
'two colliding primitives throw at arrayLimit 0'
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
var conflict = utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true, arrayLimit: 1 });
|
|
213
|
+
s2t.ok(utils.isOverflow(conflict), 'without throwOnLimitExceeded, a strictMerge conflict converts to an overflow object');
|
|
214
|
+
s2t.deepEqual(conflict, { 0: { foo: 'bar' }, 1: 'baz' }, 'holding both values');
|
|
215
|
+
|
|
216
|
+
var pair = utils.merge('a', 'b', { arrayLimit: 1 });
|
|
217
|
+
s2t.ok(utils.isOverflow(pair), 'without throwOnLimitExceeded, two colliding primitives convert to an overflow object');
|
|
218
|
+
s2t.deepEqual(pair, { 0: 'a', 1: 'b' }, 'holding both values');
|
|
219
|
+
|
|
220
|
+
s2t.deepEqual(
|
|
221
|
+
utils.merge({ foo: 'bar' }, 'baz', { strictMerge: true, arrayLimit: 2, throwOnLimitExceeded: true }),
|
|
222
|
+
[{ foo: 'bar' }, 'baz'],
|
|
223
|
+
'a strictMerge conflict within arrayLimit is still an array'
|
|
224
|
+
);
|
|
225
|
+
s2t.deepEqual(
|
|
226
|
+
utils.merge('a', 'b', { arrayLimit: 2, throwOnLimitExceeded: true }),
|
|
227
|
+
['a', 'b'],
|
|
228
|
+
'two colliding primitives within arrayLimit are still an array'
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
s2t.end();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
st.test('seeds an overflow container index from the keys it already holds', function (s2t) {
|
|
235
|
+
var overflow = utils.combine(['a'], 'b', 0, false);
|
|
236
|
+
s2t.ok(utils.isOverflow(overflow), 'the source is an overflow object');
|
|
237
|
+
|
|
238
|
+
var merged = utils.merge({ 2: 'keep', z: 'zz' }, overflow);
|
|
239
|
+
s2t.ok(utils.isOverflow(merged), 'the result is marked as overflow');
|
|
240
|
+
s2t.deepEqual(
|
|
241
|
+
utils.merge(merged, 'c'),
|
|
242
|
+
{ 0: 'a', 1: 'b', 2: 'keep', 3: 'c', z: 'zz' },
|
|
243
|
+
'the next value lands after the highest numeric key the container already held'
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
s2t.end();
|
|
247
|
+
});
|
|
248
|
+
|
|
109
249
|
st.test('merges overflow object into primitive', function (s2t) {
|
|
110
250
|
// Create an overflow object via combine: 2 elements (indices 0-1) with limit 0
|
|
111
251
|
var overflow = utils.combine(['a'], 'b', 0, false);
|
|
@@ -441,6 +581,50 @@ test('decode', function (t) {
|
|
|
441
581
|
'decodes a string in iso-8859-1'
|
|
442
582
|
);
|
|
443
583
|
|
|
584
|
+
t.test('strings with no percent sign', function (st) {
|
|
585
|
+
st.equal(utils.decode(''), '', 'empty string');
|
|
586
|
+
st.equal(utils.decode('abc'), 'abc', 'returns a plain string unchanged');
|
|
587
|
+
st.equal(utils.decode('abc', null, 'iso-8859-1'), 'abc', 'returns a plain string unchanged in iso-8859-1');
|
|
588
|
+
st.equal(utils.decode('a++b+'), 'a b ', 'decodes every + to a space');
|
|
589
|
+
st.equal(utils.decode('a++b+', null, 'iso-8859-1'), 'a b ', 'decodes every + to a space in iso-8859-1');
|
|
590
|
+
st.equal(utils.decode('\uD800x\uDC00'), '\uD800x\uDC00', 'leaves lone surrogates unchanged');
|
|
591
|
+
st.equal(utils.decode('😀+'), '😀 ', 'leaves surrogate pairs unchanged');
|
|
592
|
+
st.equal(utils.decode('a=b&c'), 'a=b&c', 'leaves delimiters unchanged');
|
|
593
|
+
|
|
594
|
+
st.end();
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
t.test('strings with a percent sign', function (st) {
|
|
598
|
+
st.equal(utils.decode('a+%20b'), 'a b', 'decodes both + and an escape');
|
|
599
|
+
st.equal(utils.decode('%E9+x', null, 'iso-8859-1'), 'é x', 'decodes both + and an escape in iso-8859-1');
|
|
600
|
+
st.equal(utils.decode('a+%'), 'a %', 'decodes + even when the escape is malformed');
|
|
601
|
+
st.equal(utils.decode('a+%E0%A4%A'), 'a %E0%A4%A', 'decodes + even when the escape is truncated');
|
|
602
|
+
|
|
603
|
+
st.end();
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
t.test('non-strings', function (st) {
|
|
607
|
+
forEach(v.nullPrimitives, function (nullish) {
|
|
608
|
+
st['throws'](
|
|
609
|
+
function () { utils.decode(nullish); },
|
|
610
|
+
TypeError,
|
|
611
|
+
inspect(nullish) + ' is not a string'
|
|
612
|
+
);
|
|
613
|
+
});
|
|
614
|
+
st['throws'](
|
|
615
|
+
function () { utils.decode(['a']); },
|
|
616
|
+
TypeError,
|
|
617
|
+
'an array is not a string'
|
|
618
|
+
);
|
|
619
|
+
|
|
620
|
+
/* eslint no-new-wrappers: 0 */
|
|
621
|
+
st.equal(utils.decode(new String('ab')), 'ab', 'a boxed string decodes to a primitive');
|
|
622
|
+
st.equal(utils.decode(new String('a+b')), 'a b', 'a boxed string with + decodes to a primitive');
|
|
623
|
+
st.equal(utils.decode(new String('a%20b')), 'a b', 'a boxed string with an escape decodes to a primitive');
|
|
624
|
+
|
|
625
|
+
st.end();
|
|
626
|
+
});
|
|
627
|
+
|
|
444
628
|
t.end();
|
|
445
629
|
});
|
|
446
630
|
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const htmlparser = require('htmlparser2');
|
|
2
2
|
const escapeStringRegexp = require('escape-string-regexp');
|
|
3
3
|
const { isPlainObject } = require('is-plain-object');
|
|
4
|
-
const deepmerge = require('deepmerge');
|
|
5
4
|
const parseSrcset = require('parse-srcset');
|
|
6
5
|
const { parse: postcssParse } = require('postcss');
|
|
7
6
|
const { naughtyHref: launderNaughtyHref } = require('launder');
|
|
@@ -53,6 +52,29 @@ function has(obj, key) {
|
|
|
53
52
|
return ({}).hasOwnProperty.call(obj, key);
|
|
54
53
|
}
|
|
55
54
|
|
|
55
|
+
// Combine the tag-specific and wildcard lists of allowed classes
|
|
56
|
+
function mergeClasses(specific, wildcard) {
|
|
57
|
+
if (!Array.isArray(specific) || !Array.isArray(wildcard)) {
|
|
58
|
+
return wildcard;
|
|
59
|
+
}
|
|
60
|
+
return specific.concat(wildcard);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Combine the tag-specific and wildcard allowed styles, concatenating the
|
|
64
|
+
// lists of regular expressions for properties that appear in both. The result
|
|
65
|
+
// has no prototype, so a property named `__proto__` is just another property
|
|
66
|
+
function mergeStyles(specific, wildcard) {
|
|
67
|
+
const merged = Object.create(null);
|
|
68
|
+
for (const rules of [ specific, wildcard ]) {
|
|
69
|
+
for (const prop of Object.keys(rules)) {
|
|
70
|
+
merged[prop] = has(merged, prop)
|
|
71
|
+
? merged[prop].concat(rules[prop])
|
|
72
|
+
: rules[prop];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return merged;
|
|
76
|
+
}
|
|
77
|
+
|
|
56
78
|
// Returns those elements of `a` for which `cb(a)` returns truthy
|
|
57
79
|
function filter(a, cb) {
|
|
58
80
|
const n = [];
|
|
@@ -103,14 +125,23 @@ module.exports = sanitizeHtml;
|
|
|
103
125
|
// attribute name state with an empty attribute name buffer.
|
|
104
126
|
const VALID_HTML_ATTRIBUTE_NAME = /^[^\0\t\n\f\r /<=>]+$/;
|
|
105
127
|
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
//
|
|
128
|
+
// How many discarded <iframe> elements can be nested while their fallback
|
|
129
|
+
// markup is still re-sanitized as HTML (see the `ontext` handler). Deeper
|
|
130
|
+
// fallback is escaped as text, so that crafted input cannot recurse without
|
|
131
|
+
// bound or re-parse the remainder of the input once per iframe
|
|
132
|
+
// (GHSA-c9rx-x947-fp3h).
|
|
133
|
+
const MAX_IFRAME_FALLBACK_DEPTH = 8;
|
|
109
134
|
|
|
110
|
-
|
|
135
|
+
// `_fallback` is internal: it is set when re-sanitizing the fallback markup
|
|
136
|
+
// of a discarded <iframe>, to carry the current nesting depth into the
|
|
137
|
+
// recursive call.
|
|
138
|
+
|
|
139
|
+
function sanitizeHtml(html, options, _fallback) {
|
|
111
140
|
if (html == null) {
|
|
112
141
|
return '';
|
|
113
142
|
}
|
|
143
|
+
const baseDepth = (_fallback && _fallback.depth) || 0;
|
|
144
|
+
const iframeFallbackDepth = (_fallback && _fallback.iframeFallbackDepth) || 0;
|
|
114
145
|
if (typeof html === 'number') {
|
|
115
146
|
html = html.toString();
|
|
116
147
|
}
|
|
@@ -542,7 +573,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
542
573
|
if (allowedSpecificClasses && allowedWildcardClasses) {
|
|
543
574
|
value = filterClasses(
|
|
544
575
|
value,
|
|
545
|
-
|
|
576
|
+
mergeClasses(allowedSpecificClasses, allowedWildcardClasses),
|
|
546
577
|
allowedClassesGlobs
|
|
547
578
|
);
|
|
548
579
|
} else {
|
|
@@ -682,11 +713,17 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
682
713
|
// (including after an unclosed <iframe>) arrives as a single text node.
|
|
683
714
|
// When the iframe is discarded, re-sanitize that fallback markup as HTML
|
|
684
715
|
// instead of escaping it as plain text (issue #5550).
|
|
716
|
+
// The nested call continues from the current depth so that
|
|
717
|
+
// `nestingLimit` applies to the whole output.
|
|
685
718
|
tag === 'iframe' &&
|
|
686
719
|
!tagAllowed(tag) &&
|
|
687
|
-
options.disallowedTagsMode === 'discard'
|
|
720
|
+
options.disallowedTagsMode === 'discard' &&
|
|
721
|
+
iframeFallbackDepth < MAX_IFRAME_FALLBACK_DEPTH
|
|
688
722
|
) {
|
|
689
|
-
result += sanitizeHtml(text, options
|
|
723
|
+
result += sanitizeHtml(text, options, {
|
|
724
|
+
depth,
|
|
725
|
+
iframeFallbackDepth: iframeFallbackDepth + 1
|
|
726
|
+
});
|
|
690
727
|
} else if (!addedText) {
|
|
691
728
|
const escaped = escapeHtml(text, false);
|
|
692
729
|
if (options.textFilter) {
|
|
@@ -819,7 +856,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
819
856
|
|
|
820
857
|
function initializeState() {
|
|
821
858
|
result = '';
|
|
822
|
-
depth =
|
|
859
|
+
depth = baseDepth;
|
|
823
860
|
stack = [];
|
|
824
861
|
skipMap = {};
|
|
825
862
|
transformMap = {};
|
|
@@ -1053,7 +1090,7 @@ function sanitizeHtml(html, options, _recursing) {
|
|
|
1053
1090
|
|
|
1054
1091
|
// Merge global and tag-specific styles into new AST.
|
|
1055
1092
|
if (allowedStyles[astRules.selector] && allowedStyles['*']) {
|
|
1056
|
-
selectedRule =
|
|
1093
|
+
selectedRule = mergeStyles(
|
|
1057
1094
|
allowedStyles[astRules.selector],
|
|
1058
1095
|
allowedStyles['*']
|
|
1059
1096
|
);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sanitize-html",
|
|
3
|
-
"version": "2.18.
|
|
3
|
+
"version": "2.18.1",
|
|
4
4
|
"description": "Clean up user-submitted HTML, preserving allowlisted elements and allowlisted attributes on a per-element basis",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "index.js",
|
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
"node": ">=22.12.0"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"deepmerge": "^4.3.1",
|
|
33
32
|
"escape-string-regexp": "^4.0.0",
|
|
34
33
|
"htmlparser2": "^12.0.0",
|
|
35
34
|
"is-plain-object": "^5.1.0",
|
|
@@ -40,7 +39,7 @@
|
|
|
40
39
|
"devDependencies": {
|
|
41
40
|
"eslint": "^9.39.5",
|
|
42
41
|
"eslint-config-apostrophe": "^6.1.0",
|
|
43
|
-
"mocha": "^
|
|
42
|
+
"mocha": "^12.0.3",
|
|
44
43
|
"sinon": "^9.2.4"
|
|
45
44
|
},
|
|
46
45
|
"apostropheTestConfig": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suveren/gateway",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"description": "Suveren gateway — local agent gateway built in compliance with the Human Agency Protocol (HAP). Runs the UI, control plane, and MCP server in one Node process.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "server.js",
|