@rhinostone/swig-twig 2.8.1 → 2.8.3

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/index.js CHANGED
@@ -19,6 +19,16 @@ var utils = require('@rhinostone/swig-core/lib/utils'),
19
19
 
20
20
  exports.name = 'twig';
21
21
 
22
+ /**
23
+ * Package version number as a string. The packages of the family are
24
+ * released together, so they share one version.
25
+ * @example
26
+ * if (twig.version === "2.8.2") { ... }
27
+ *
28
+ * @type {String}
29
+ */
30
+ exports.version = "2.8.3";
31
+
22
32
  /**
23
33
  * Expression-level parser — Pratt-style recursive descent that consumes
24
34
  * Twig lexer tokens and returns swig-core IRExpr nodes.
@@ -75,7 +85,7 @@ function validateOptions(options) {
75
85
  if (!options.hasOwnProperty(key)) {
76
86
  return;
77
87
  }
78
- if (!utils.isArray(options[key]) || options[key].length !== 2) {
88
+ if (!utils.isArray(options[key]) || options[key].length !== 2 || typeof options[key][0] !== 'string' || typeof options[key][1] !== 'string') {
79
89
  throw new Error('Option "' + key + '" must be an array containing 2 different control strings.');
80
90
  }
81
91
  if (options[key][0] === options[key][1]) {
@@ -83,7 +93,7 @@ function validateOptions(options) {
83
93
  }
84
94
  utils.each(options[key], function (a, i) {
85
95
  if (a.length < 2) {
86
- throw new Error('Option "' + key + '" ' + ((i) ? 'open ' : 'close ') + 'control must be at least 2 characters. Saw "' + a + '" instead.');
96
+ throw new Error('Option "' + key + '" ' + ((i) ? 'close ' : 'open ') + 'control must be at least 2 characters. Saw "' + a + '" instead.');
87
97
  }
88
98
  });
89
99
  });
@@ -151,7 +161,14 @@ exports.Twig = function (opts) {
151
161
  filters: _filters,
152
162
  validateOptions: validateOptions,
153
163
  onCompileError: function (err, options) {
154
- utils.throwError(err, null, options.filename);
164
+ var filename = options && options.filename;
165
+ // An error with no file name to add, or one whose message already
166
+ // names the file (a tag's own throwError does), is rethrown
167
+ // unchanged instead of being wrapped a second time.
168
+ if (err instanceof Error && (!filename || err.message.indexOf(' in file ' + filename) !== -1)) {
169
+ throw err;
170
+ }
171
+ utils.throwError(err, null, filename);
155
172
  }
156
173
  });
157
174
 
package/lib/parser.js CHANGED
@@ -85,12 +85,12 @@ exports.parseExpr = function (tokens, filters, _posOut) {
85
85
 
86
86
  function guardSegment(segment) {
87
87
  if (_dangerousProps.indexOf(segment) !== -1) {
88
- bail('Unsafe access to "' + segment + '" is not allowed in templates (CVE-2023-25345)');
88
+ bail('Unsafe access to "' + segment + '" is not allowed in templates (prototype-chain guard)');
89
89
  }
90
90
  }
91
91
  function guardBracketString(value) {
92
92
  if (_dangerousProps.indexOf(value) !== -1) {
93
- bail('Unsafe access to "' + value + '" via bracket notation is not allowed in templates (CVE-2023-25345)');
93
+ bail('Unsafe access to "' + value + '" via bracket notation is not allowed in templates (prototype-chain guard)');
94
94
  }
95
95
  }
96
96
 
package/lib/tags/apply.js CHANGED
@@ -109,7 +109,7 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
109
109
 
110
110
  function checkName(name) {
111
111
  if (_dangerousProps.indexOf(name) !== -1) {
112
- utils.throwError('Unsafe filter name "' + name + '" is not allowed (CVE-2023-25345)', line, opts.filename);
112
+ utils.throwError('Unsafe filter name "' + name + '" is not allowed (prototype-chain guard)', line, opts.filename);
113
113
  }
114
114
  }
115
115
 
package/lib/tags/block.js CHANGED
@@ -66,7 +66,7 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
66
66
  utils.throwError('Block name "' + nameTok.match + '" must be a bare identifier', line, opts.filename);
67
67
  }
68
68
  if (_dangerousProps.indexOf(nameTok.match) !== -1) {
69
- utils.throwError('Unsafe block name "' + nameTok.match + '" is not allowed (CVE-2023-25345)', line, opts.filename);
69
+ utils.throwError('Unsafe block name "' + nameTok.match + '" is not allowed (prototype-chain guard)', line, opts.filename);
70
70
  }
71
71
 
72
72
  pos += 1;
package/lib/tags/for.js CHANGED
@@ -76,7 +76,7 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
76
76
  utils.throwError('Loop variable "' + tok.match + '" must be a bare identifier in "for" tag', line, opts.filename);
77
77
  }
78
78
  if (_dangerousProps.indexOf(tok.match) !== -1) {
79
- utils.throwError('Unsafe loop variable "' + tok.match + '" is not allowed (CVE-2023-25345)', line, opts.filename);
79
+ utils.throwError('Unsafe loop variable "' + tok.match + '" is not allowed (prototype-chain guard)', line, opts.filename);
80
80
  }
81
81
  return tok.match;
82
82
  }
package/lib/tags/from.js CHANGED
@@ -88,7 +88,7 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
88
88
  utils.throwError(role + ' "' + name + '" must be a bare identifier in "from" tag', line, opts.filename);
89
89
  }
90
90
  if (_dangerousProps.indexOf(name) !== -1) {
91
- utils.throwError('Unsafe ' + role.toLowerCase() + ' "' + name + '" is not allowed (CVE-2023-25345)', line, opts.filename);
91
+ utils.throwError('Unsafe ' + role.toLowerCase() + ' "' + name + '" is not allowed (prototype-chain guard)', line, opts.filename);
92
92
  }
93
93
  }
94
94
 
@@ -106,7 +106,7 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
106
106
  utils.throwError('Import alias "' + aliasTok.match + '" must be a bare identifier in "import" tag', line, opts.filename);
107
107
  }
108
108
  if (_dangerousProps.indexOf(aliasTok.match) !== -1) {
109
- utils.throwError('Unsafe import alias "' + aliasTok.match + '" is not allowed (CVE-2023-25345)', line, opts.filename);
109
+ utils.throwError('Unsafe import alias "' + aliasTok.match + '" is not allowed (prototype-chain guard)', line, opts.filename);
110
110
  }
111
111
 
112
112
  if (peek()) {
@@ -28,8 +28,8 @@
28
28
  * `try { ... } catch {}` wrapper that collapses missing-file errors to
29
29
  * the empty string when `ignoreMissing` is set.
30
30
  *
31
- * `resolveFrom` is the template's own filename (backslash-escaped so
32
- * Windows paths round-trip through the JSON-literal emit). The native
31
+ * `resolveFrom` is the template's own filename; the backend escapes it at emit time
32
+ * (GHSA-q89c), so Windows paths and quotes round-trip through the literal. The native
33
33
  * parser's splitter passes it in as a trailing arg; Twig's
34
34
  * tag-dispatch shape carries it via `opts.filename`.
35
35
  */
@@ -125,7 +125,7 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
125
125
  ctxExpr = parser.parseExpr(ctxTokens);
126
126
  }
127
127
 
128
- var resolveFrom = (opts.filename || '').replace(/\\/g, '\\\\');
128
+ var resolveFrom = (opts.filename || '');
129
129
 
130
130
  token.irExpr = ir.include(
131
131
  pathExpr,
package/lib/tags/macro.js CHANGED
@@ -76,7 +76,7 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
76
76
  utils.throwError(role + ' "' + name + '" must be a bare identifier in "macro" tag', line, opts.filename);
77
77
  }
78
78
  if (_dangerousProps.indexOf(name) !== -1) {
79
- utils.throwError('Unsafe ' + role.toLowerCase() + ' "' + name + '" is not allowed (CVE-2023-25345)', line, opts.filename);
79
+ utils.throwError('Unsafe ' + role.toLowerCase() + ' "' + name + '" is not allowed (prototype-chain guard)', line, opts.filename);
80
80
  }
81
81
  }
82
82
 
package/lib/tags/set.js CHANGED
@@ -84,17 +84,18 @@ exports.parse = function (str, line, parser, types, stack, opts, swig, token) {
84
84
  var path = lhsTok.match.split('.');
85
85
  utils.each(path, function (segment) {
86
86
  if (_dangerousProps.indexOf(segment) !== -1) {
87
- utils.throwError('Unsafe assignment to "' + segment + '" is not allowed (CVE-2023-25345)', line, opts.filename);
87
+ utils.throwError('Unsafe assignment to "' + segment + '" is not allowed (prototype-chain guard)', line, opts.filename);
88
88
  }
89
89
  });
90
90
 
91
- // DOTKEY tail — the Twig lexer already folds dotted paths into the
92
- // VAR match, but a defensive DOTKEY consumer here keeps this tag
93
- // robust if a future lexer tightening splits `foo.bar` into VAR + DOTKEY.
91
+ // DOTKEY tail — the Twig lexer folds `foo.bar` into one VAR match, but a
92
+ // dot after whitespace (`foo .bar`) lexes as its own DOTKEY token, so this
93
+ // loop runs today; it also keeps the tag safe if a future lexer splits
94
+ // `foo.bar` into VAR + DOTKEY.
94
95
  while (peek() && peek().type === types.DOTKEY) {
95
96
  var dk = consume();
96
97
  if (_dangerousProps.indexOf(dk.match) !== -1) {
97
- utils.throwError('Unsafe assignment to "' + dk.match + '" is not allowed (CVE-2023-25345)', line, opts.filename);
98
+ utils.throwError('Unsafe assignment to "' + dk.match + '" is not allowed (prototype-chain guard)', line, opts.filename);
98
99
  }
99
100
  path.push(dk.match);
100
101
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhinostone/swig-twig",
3
- "version": "2.8.1",
3
+ "version": "2.8.3",
4
4
  "description": "Twig-syntax frontend for the @rhinostone/swig-core template engine. Part of the @rhinostone/swig multi-flavor family.",
5
5
  "keywords": [
6
6
  "template",
@@ -22,7 +22,7 @@
22
22
  "node": ">=12"
23
23
  },
24
24
  "peerDependencies": {
25
- "@rhinostone/swig-core": "2.8.1"
25
+ "@rhinostone/swig-core": "2.8.3"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"