@rhinostone/swig 2.9.0 → 2.9.1
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/HISTORY.md +15 -0
- package/README.md +8 -5
- package/ROADMAP.md +6 -0
- package/dist/swig.js +52 -4
- package/dist/swig.min.js +6 -2
- package/dist/swig.min.js.map +1 -1
- package/dist/swig.runtime.js +52 -4
- package/dist/swig.runtime.min.js +6 -2
- package/dist/swig.runtime.min.js.map +1 -1
- package/lib/filters.js +5 -1
- package/lib/runtime.js +1 -1
- package/lib/swig.js +1 -1
- package/package.json +3 -2
package/HISTORY.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
[2.9.1](https://github.com/gina-io/swig/tree/v2.9.1) / 2026-10-23
|
|
2
|
+
-----------------------------------------------------------------
|
|
3
|
+
|
|
4
|
+
* **Fixed** The Express example (examples/express) no longer has its catch-all route, which rendered any .html file a URL named as a template, including files outside the views folder. Its pages link only to the explicit routes, which are unchanged. The example is not part of any package; an application built from it should remove or confine that route.
|
|
5
|
+
|
|
6
|
+
* **Changed** The test suite gains property-based tests, which check each property on many generated inputs: in every flavor the escape filter is idempotent and leaves no raw markup character, `{{ x }}` renders the escaped string, text with no tag renders unchanged apart from line endings, and any template filename reaches the loader unchanged through an include; the membership helper behind the `in` operator matches its definition on lists. They run with the new fast-check dev dependency (MIT, one dependency, pure-rand) under a fixed seed. Development and CI only; the published package is unaffected and its only runtime dependency remains @rhinostone/swig-core.
|
|
7
|
+
|
|
8
|
+
* **Security** swig striptags no longer takes time quadratic in the length of its input. On text with many < and no > after them it ran for about five seconds per 100 000 characters, so a template that applied it to request data could be made to block the event loop. It now runs in linear time with the same output. The regex dates from the original project, so every earlier release has it.
|
|
9
|
+
|
|
10
|
+
* **Security** swig-twig striptags and trim no longer take time quadratic in the length of their input. striptags ran for about five seconds per 100 000 characters on text with many < and no > after them, and trim took about as long on a long run of the trimmed characters inside the text, so a template that applied either to request data could be made to block the event loop. Both now run in linear time with the same output. Every swig-twig release since 2.0.0-alpha.3 has them.
|
|
11
|
+
|
|
12
|
+
* **Security** swig-jinja2 striptags, trim and format no longer take time quadratic in the length of their input. striptags ran for about five seconds per 100 000 characters on text with many < or <!-- and nothing closing them, trim took as long on a long run of the trimmed characters inside the text, and format longer still on a % followed by a long run of zeros. A template that applied one of them to request data could be made to block the event loop. All three now run in linear time with the same output (Jinja2 3.1.6 strips tags and trims these inputs in linear time). Every swig-jinja2 release since 2.5.0 has them.
|
|
13
|
+
|
|
14
|
+
* **Security** swig-django striptags, truncatewords, pluralize, slugify and floatformat no longer take time quadratic in the length of their input. striptags ran for about five seconds per 100 000 characters on text with many < and no > after them, truncatewords and pluralize took about as long on a long run of whitespace inside the value, slugify on a long run of underscores, and floatformat on an argument with a long run of g or u not at its end. A template that applied one of them to request data could be made to block the event loop. All five now run in linear time with the same output (Django 5.2.18 handles these inputs in linear time). Every swig-django release since 2.7.0 has them, except pluralize, which has had it since 2.8.2.
|
|
15
|
+
|
|
1
16
|
[2.9.0](https://github.com/gina-io/swig/tree/v2.9.0) / 2026-10-23
|
|
2
17
|
-----------------------------------------------------------------
|
|
3
18
|
|
package/README.md
CHANGED
|
@@ -43,14 +43,17 @@ Features
|
|
|
43
43
|
* Robust without the bloat — a single runtime dependency ([`@rhinostone/swig-core`](https://www.npmjs.com/package/@rhinostone/swig-core)).
|
|
44
44
|
* Extendable and customizable — register custom filters, tags, and loaders per-instance.
|
|
45
45
|
|
|
46
|
-
What's new in v2.9.
|
|
46
|
+
What's new in v2.9.1
|
|
47
47
|
--------------------
|
|
48
48
|
|
|
49
|
-
> **
|
|
49
|
+
> **Security release: upgrade all five packages to 2.9.1.** `striptags` in every flavor, `trim` in swig-twig and swig-jinja2, swig-jinja2 `format`, and swig-django `truncatewords`, `pluralize`, `slugify` and `floatformat` took time quadratic in the length of their input on some inputs, several seconds for 100 000 characters. A template that applied one of them to request data could be made to block the event loop. They now run in linear time with the same output.
|
|
50
50
|
|
|
51
|
-
* **
|
|
52
|
-
* **
|
|
53
|
-
* **
|
|
51
|
+
* **Security — swig `striptags` runs in linear time.** On text with many `<` and no `>` after them it ran for about five seconds per 100 000 characters. The regex dates from the original project, so every earlier release has it.
|
|
52
|
+
* **Security — swig-twig `striptags` and `trim` run in linear time.** `striptags` was slow on text with many `<` and no `>` after them, and `trim` on a long run of the trimmed characters inside the text. Every swig-twig release since 2.0.0-alpha.3 has them.
|
|
53
|
+
* **Security — swig-jinja2 `striptags`, `trim` and `format` run in linear time.** `striptags` was slow on text with many `<` or `<!--` and nothing closing them, `trim` on a long run of the trimmed characters inside the text, and `format` slower still on a `%` followed by a long run of zeros. Every swig-jinja2 release since 2.5.0 has them.
|
|
54
|
+
* **Security — swig-django `striptags`, `truncatewords`, `pluralize`, `slugify` and `floatformat` run in linear time.** `striptags` was slow on text with many `<` and no `>` after them, `truncatewords` and `pluralize` on a long run of whitespace inside the value, `slugify` on a long run of underscores, and `floatformat` on an argument with a long run of `g` or `u` not at its end. Every swig-django release since 2.7.0 has them, except `pluralize`, which has had it since 2.8.2.
|
|
55
|
+
* **Fixed — the Express example no longer has its catch-all route.** The route rendered any `.html` file a URL named as a template, including files outside the views folder. The example is not part of any package; an application built from it should remove or confine that route.
|
|
56
|
+
* **Changed — property-based tests.** The test suite checks properties on many generated inputs, with the new fast-check dev dependency under a fixed seed. Development and CI only; the only runtime dependency remains `@rhinostone/swig-core`.
|
|
54
57
|
|
|
55
58
|
See the full [HISTORY.md](./HISTORY.md) and [ROADMAP.md](./ROADMAP.md).
|
|
56
59
|
|
package/ROADMAP.md
CHANGED
|
@@ -28,6 +28,12 @@ For bug reports and feature requests, file an issue at [gina-io/swig](https://gi
|
|
|
28
28
|
|
|
29
29
|
## Completed
|
|
30
30
|
|
|
31
|
+
### v2.9.1 (October 2026)
|
|
32
|
+
|
|
33
|
+
- Security: `striptags` in every flavor, `trim` in swig-twig and swig-jinja2, swig-jinja2 `format`, and swig-django `truncatewords`, `pluralize`, `slugify` and `floatformat` run in linear time with the same output. Their regexes took time quadratic in the input length on some inputs, so a template that applied one of them to request data could be made to block the event loop.
|
|
34
|
+
- The Express example no longer has its catch-all route, which rendered any `.html` file a URL named as a template.
|
|
35
|
+
- All five packages released in lockstep at `2.9.1`.
|
|
36
|
+
|
|
31
37
|
### v2.9.0 (October 2026)
|
|
32
38
|
|
|
33
39
|
- `in` tests membership in swig-twig, swig-jinja2 and swig-django, as Twig, Jinja2 and Django do: a list holds an item, a string a substring, and a mapping a key (Jinja2, Django) or a value (Twig). Native swig keeps the JavaScript `in`, which tests an object's keys or an array's indices.
|
package/dist/swig.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! Swig v2.9.
|
|
1
|
+
/*! Swig v2.9.1 | https://github.com/gina-io/swig | @license https://github.com/gina-io/swig/blob/master/LICENSE */
|
|
2
2
|
/*! DateZ (c) 2011 Tomo Universalis | @license https://github.com/tn0ms/DateZ/blob/master/LISENCE */
|
|
3
3
|
(() => {
|
|
4
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -22,8 +22,52 @@
|
|
|
22
22
|
"packages/swig-core/lib/utils.js"(exports) {
|
|
23
23
|
var security = require_security();
|
|
24
24
|
var isArray;
|
|
25
|
+
var whitespace = /\s/;
|
|
25
26
|
exports.strip = function(input) {
|
|
26
|
-
return input.replace(/^\s
|
|
27
|
+
return exports.trimEnd(input.replace(/^\s+/, ""));
|
|
28
|
+
};
|
|
29
|
+
exports.trimEnd = function(input, chars) {
|
|
30
|
+
var end = input.length;
|
|
31
|
+
if (chars === void 0) {
|
|
32
|
+
while (end > 0 && whitespace.test(input.charAt(end - 1))) {
|
|
33
|
+
end -= 1;
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
while (end > 0 && chars.indexOf(input.charAt(end - 1)) !== -1) {
|
|
37
|
+
end -= 1;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return input.slice(0, end);
|
|
41
|
+
};
|
|
42
|
+
exports.stripTags = function(input, allowEmpty) {
|
|
43
|
+
var out = "", from = 0, lt = input.indexOf("<"), gt;
|
|
44
|
+
while (lt !== -1) {
|
|
45
|
+
gt = input.indexOf(">", lt + 1);
|
|
46
|
+
if (gt === -1) {
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
if (gt === lt + 1 && !allowEmpty) {
|
|
50
|
+
lt = input.indexOf("<", lt + 1);
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
out += input.slice(from, lt);
|
|
54
|
+
from = gt + 1;
|
|
55
|
+
lt = input.indexOf("<", from);
|
|
56
|
+
}
|
|
57
|
+
return out + input.slice(from);
|
|
58
|
+
};
|
|
59
|
+
exports.stripComments = function(input) {
|
|
60
|
+
var out = "", from = 0, open = input.indexOf("<!--"), close;
|
|
61
|
+
while (open !== -1) {
|
|
62
|
+
close = input.indexOf("-->", open + 4);
|
|
63
|
+
if (close === -1) {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
out += input.slice(from, open);
|
|
67
|
+
from = close + 3;
|
|
68
|
+
open = input.indexOf("<!--", from);
|
|
69
|
+
}
|
|
70
|
+
return out + input.slice(from);
|
|
27
71
|
};
|
|
28
72
|
exports.startsWith = function(str, prefix) {
|
|
29
73
|
return str.indexOf(prefix) === 0;
|
|
@@ -1183,7 +1227,7 @@
|
|
|
1183
1227
|
if (out !== void 0) {
|
|
1184
1228
|
return out;
|
|
1185
1229
|
}
|
|
1186
|
-
return input.toString()
|
|
1230
|
+
return utils.stripTags(input.toString());
|
|
1187
1231
|
};
|
|
1188
1232
|
exports.title = function(input) {
|
|
1189
1233
|
var out = iterateFilter.apply(exports.title, arguments);
|
|
@@ -4894,7 +4938,7 @@
|
|
|
4894
4938
|
var loaders = require_loaders2();
|
|
4895
4939
|
var preWalker = require_pre_walker();
|
|
4896
4940
|
var engine = require_engine();
|
|
4897
|
-
exports.version = "2.9.
|
|
4941
|
+
exports.version = "2.9.1";
|
|
4898
4942
|
var defaultOptions = {
|
|
4899
4943
|
autoescape: true,
|
|
4900
4944
|
varControls: ["{{", "}}"],
|
|
@@ -5120,6 +5164,10 @@
|
|
|
5120
5164
|
window.swig = swig;
|
|
5121
5165
|
}
|
|
5122
5166
|
})();
|
|
5167
|
+
/*!
|
|
5168
|
+
* What a \s regex atom matches, tested one code unit at a time.
|
|
5169
|
+
* @private
|
|
5170
|
+
*/
|
|
5123
5171
|
/*!
|
|
5124
5172
|
* Resolve a single path segment against a value, Django-style. A plain
|
|
5125
5173
|
* property access (`obj[seg]`) covers dictionary, attribute, and array /
|