caldav-adapter 8.2.3 → 8.2.4

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/common/tags.js CHANGED
@@ -7,6 +7,24 @@ const cal = 'urn:ietf:params:xml:ns:caldav';
7
7
  const cs = 'http://calendarserver.org/ns/';
8
8
  const ical = 'http://apple.com/ns/ical/';
9
9
 
10
+ /**
11
+ * Encode special characters for XML content to prevent parsing errors
12
+ * @param {string} str - String to encode
13
+ * @returns {string} - XML-safe encoded string
14
+ */
15
+ function encodeXMLEntities(str) {
16
+ if (typeof str !== 'string') {
17
+ return str;
18
+ }
19
+
20
+ return str
21
+ .replaceAll('&', '&') // Must be first to avoid double-encoding
22
+ .replaceAll('<', '&lt;')
23
+ .replaceAll('>', '&gt;')
24
+ .replaceAll('"', '&quot;')
25
+ .replaceAll("'", '&#39;');
26
+ }
27
+
10
28
  module.exports = function (options) {
11
29
  const log = winston({ ...options, label: 'tags' });
12
30
  const tags = {
@@ -59,13 +77,13 @@ module.exports = function (options) {
59
77
 
60
78
  if (resource === 'calendar')
61
79
  return {
62
- [buildTag(dav, 'displayname')]: calendar.name
80
+ [buildTag(dav, 'displayname')]: encodeXMLEntities(calendar.name)
63
81
  };
64
82
 
65
83
  if (resource === 'calendarProppatch')
66
84
  return response(ctx.url, status[200], [
67
85
  {
68
- [buildTag(dav, 'displayname')]: calendar.name
86
+ [buildTag(dav, 'displayname')]: encodeXMLEntities(calendar.name)
69
87
  }
70
88
  ]);
71
89
  }
@@ -216,11 +234,11 @@ module.exports = function (options) {
216
234
  doc: 'https://tools.ietf.org/html/rfc4791#section-9.6',
217
235
  async resp({ event, ctx, calendar }) {
218
236
  const ics = await options.data.buildICS(ctx, event, calendar);
219
- // Wrap iCalendar data in CDATA to prevent XML parsing issues
220
- // This ensures that the iCalendar content is treated as character data
221
- // and not parsed as XML, preventing malformed XML errors
237
+ // Use entity encoding instead of CDATA for better API compatibility
238
+ // This ensures special characters are properly encoded while maintaining
239
+ // compatibility with tsdav and other CalDAV clients that expect string content
222
240
  return {
223
- [buildTag(cal, 'calendar-data')]: { $cdata: ics }
241
+ [buildTag(cal, 'calendar-data')]: encodeXMLEntities(ics)
224
242
  };
225
243
  }
226
244
  },
@@ -241,13 +259,17 @@ module.exports = function (options) {
241
259
  async resp({ resource, ctx, calendar }) {
242
260
  if (resource === 'calendar')
243
261
  return {
244
- [buildTag(cal, 'calendar-description')]: calendar.description
262
+ [buildTag(cal, 'calendar-description')]: encodeXMLEntities(
263
+ calendar.description
264
+ )
245
265
  };
246
266
 
247
267
  if (resource === 'calendarProppatch')
248
268
  return response(ctx.url, status[200], [
249
269
  {
250
- [buildTag(cal, 'calendar-description')]: calendar.description
270
+ [buildTag(cal, 'calendar-description')]: encodeXMLEntities(
271
+ calendar.description
272
+ )
251
273
  }
252
274
  ]);
253
275
  }
@@ -257,13 +279,17 @@ module.exports = function (options) {
257
279
  async resp({ resource, ctx, calendar }) {
258
280
  if (resource === 'calendar')
259
281
  return {
260
- [buildTag(cal, 'calendar-timezone')]: calendar.timezone
282
+ [buildTag(cal, 'calendar-timezone')]: encodeXMLEntities(
283
+ calendar.timezone
284
+ )
261
285
  };
262
286
 
263
287
  if (resource === 'calendarProppatch')
264
288
  return response(ctx.url, status[200], [
265
289
  {
266
- [buildTag(cal, 'calendar-timezone')]: calendar.timezone
290
+ [buildTag(cal, 'calendar-timezone')]: encodeXMLEntities(
291
+ calendar.timezone
292
+ )
267
293
  }
268
294
  ]);
269
295
  }
@@ -389,13 +415,17 @@ module.exports = function (options) {
389
415
  async resp({ resource, ctx, calendar }) {
390
416
  if (resource === 'calendar')
391
417
  return {
392
- [buildTag(ical, 'calendar-color')]: calendar.color
418
+ [buildTag(ical, 'calendar-color')]: encodeXMLEntities(
419
+ calendar.color
420
+ )
393
421
  };
394
422
 
395
423
  if (resource === 'calendarProppatch')
396
424
  return response(ctx.url, status[200], [
397
425
  {
398
- [buildTag(ical, 'calendar-color')]: calendar.color
426
+ [buildTag(ical, 'calendar-color')]: encodeXMLEntities(
427
+ calendar.color
428
+ )
399
429
  }
400
430
  ]);
401
431
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "caldav-adapter",
3
3
  "description": "CalDAV server for Node.js and Koa. Modernized and maintained for Forward Email.",
4
- "version": "8.2.3",
4
+ "version": "8.2.4",
5
5
  "author": "Sanders DeNardi and Forward Email LLC",
6
6
  "contributors": [
7
7
  "Sanders DeNardi <sedenardi@gmail.com> (http://www.sandersdenardi.com/)",
@@ -3,6 +3,24 @@ const xml = require('../../../common/xml');
3
3
  const { response, status } = require('../../../common/x-build');
4
4
  const winston = require('../../../common/winston');
5
5
 
6
+ /**
7
+ * Encode special characters for XML content to prevent parsing errors
8
+ * @param {string} str - String to encode
9
+ * @returns {string} - XML-safe encoded string
10
+ */
11
+ function encodeXMLEntities(str) {
12
+ if (typeof str !== 'string') {
13
+ return str;
14
+ }
15
+
16
+ return str
17
+ .replaceAll('&', '&amp;') // Must be first to avoid double-encoding
18
+ .replaceAll('<', '&lt;')
19
+ .replaceAll('>', '&gt;')
20
+ .replaceAll('"', '&quot;')
21
+ .replaceAll("'", '&#39;');
22
+ }
23
+
6
24
  module.exports = function (options) {
7
25
  const log = winston({
8
26
  ...options,
@@ -37,7 +55,7 @@ module.exports = function (options) {
37
55
  'D:getetag': options.data.getETag(ctx, event)
38
56
  },
39
57
  {
40
- 'CAL:calendar-data': { $cdata: ics }
58
+ 'CAL:calendar-data': encodeXMLEntities(ics)
41
59
  }
42
60
  ]);
43
61
  });
@@ -2,6 +2,24 @@ const { setMissingMethod } = require('../../../common/response');
2
2
  const winston = require('../../../common/winston');
3
3
  const { response, status } = require('../../../common/x-build');
4
4
 
5
+ /**
6
+ * Encode special characters for XML content to prevent parsing errors
7
+ * @param {string} str - String to encode
8
+ * @returns {string} - XML-safe encoded string
9
+ */
10
+ function encodeXMLEntities(str) {
11
+ if (typeof str !== 'string') {
12
+ return str;
13
+ }
14
+
15
+ return str
16
+ .replaceAll('&', '&amp;') // Must be first to avoid double-encoding
17
+ .replaceAll('<', '&lt;')
18
+ .replaceAll('>', '&gt;')
19
+ .replaceAll('"', '&quot;')
20
+ .replaceAll("'", '&#39;');
21
+ }
22
+
5
23
  module.exports = function (options) {
6
24
  const log = winston({ ...options, label: 'calendar/get' });
7
25
 
@@ -23,7 +41,7 @@ module.exports = function (options) {
23
41
  'D:getetag': options.data.getETag(ctx, calendar)
24
42
  },
25
43
  {
26
- 'CAL:calendar-data': { $cdata: ics }
44
+ 'CAL:calendar-data': encodeXMLEntities(ics)
27
45
  }
28
46
  ]);
29
47
  }
@@ -52,7 +70,7 @@ module.exports = function (options) {
52
70
  'D:getetag': options.data.getETag(ctx, calendar)
53
71
  },
54
72
  {
55
- 'CAL:calendar-data': { $cdata: ics }
73
+ 'CAL:calendar-data': encodeXMLEntities(ics)
56
74
  }
57
75
  ]);
58
76
  };