Back to jointjs.com.
CHANGELOG
-
apps.RichTextEditor - fix the pinch-to-zoom direction
Placing two fingers on the touch screen, moving them apart will zoom in, and moving them closer together will zoom out.
paper.on('paper:pinch', (evt, ox, oy, scale) => { scroller.zoom(scale - 1, { min: 0.2, max: 5, ox, oy }); // How to change the sensitivity: // scroller.zoom((scale - 1) * 2, { min: 0.2, max: 5, ox, oy }); });
-
ui.TreeLayoutView - fix support for touch devices
Make sure that drag & drop interactions (reconnecting, translating) work on touch devices.
-
dia.Paper - preserve
contextmenu
events hierarchyPreserve the event hierarchy to allow
stopPropagation()
in theelement:magnet:contextmenu
event handler to prevent theelement:contextmenu
andcell:contextmenu
events from firing.paper.on('element:magnet:contextmenu', (elementView, evt) => { evt.stopPropagation(); }); paper.on('element:contextmenu', () => { // Assert: the event bubbling was stopped. // The callback is not invoked. });
-
dia.Paper - fix element
user-drag
propertyReplace
user-drag
property with-webkit-user-drag
since the unprefixed version is not supported by any browsers..joint-element * { -webkit-user-drag: none; }
-
Vectorizer & Geometry - Fix RegEx in code to avoid potential ReDoS attacks. See the CodeQL reference for more information.
JavaScript’s RegEx engine uses backtracking which means that its worst-case time complexity can be polynomial or even exponential. This can happen when the regex fails to find any matches in the queried string, when the regex is written in such a way that forces the engine to investigate each character in the queried string multiple times.
Polynomial Worst-Case Complexity
The polynomial worst-case complexity happens in cases such as
/\w+\(/
. Here, the intention is to find a string of letters terminated with a(
. If the queried string does end with a(
, all is good. However, if the queried string doesn’t end with a(
, the engine needs to do a lot of backtracking, which leads to a potential ReDoS vector.The engine needs to do a pattern that looks like this: start with first letter, collect all following letters, reach end of string without finding
(
, backtrack, start again with second letter, collect all following letters again, reach end of string without finding(
again, backtrack, start again with third letter… If the string is millions of characters long, the engine can freeze and bring down the whole application.There are several ways to prevent this problem:
- Add an anchor (e.g.
/\b\w+\(/
) before each unbounded repetition, if it makes sense. The generic advice is to add a negative lookbehind before the repetition (e.g./(?<!\w)\w*\(/
in our example) which acts as a generic anchor, but this is not available in JavaScript. - Replace unbounded repetitions - either by restructuring the code and/or the regex to avoid the need for them, or by replacing the repetition with an alternation listing all possible options (e.g.
/(?:scale|translate|rotate|skewx|skewy|matrix)\(/
in our example). - Limit backtracking by specifying the end condition (e.g.
/[^)]+\(/
is better than/.+?\(/
is better than/.+\(
). - Ensure that repetitions inside the regex cannot match the constant parts of the regex that precede them (e.g.
\btranslate\([^()]+\
) where the added(
in the square bracket group prevents infinite recursion inside the square bracket group).
Note: Repetitions at the very end of a regex (e.g.
/\w+/
or/\w+|abc/
) are okay because they succeed/fail immediately, without triggering the backtracking which is the problem.Another case with polynomial worst-case complexity is
/^\w+,?\w+\(/
in absence of the,
and(
. In this situation, the second/w+
subregex becomes unanchored, forcing the regex engine to traverse all letters for each letter, as above. This problem can be prevented by ensuring that there are no overlaps between the two subregexes - that in every possible situation, each subregex is anchored (e.g./\w+(,\w+)?\(/
).Exponential Worst-Case Complexity
Exponential worst-case complexity happens when the polynomial worst-case complexity items happen inside a repetition and without an anchor. Building on from the previous example, if
(\w+,?\w+)*
, in absence of the,
. The problem is that not only does the regex engine check every possibility for each letter, it also checks every combination of possible arrangements of the two subregexes, which leads to the exponential factor.The surest way to prevent these is to avoid repetitions inside repetitions as much as possible - and, if such a construct is necessary, to make sure that the possibilities (e.g. in an alternation) can never overlap.
- Add an anchor (e.g.
-
linkTools.Anchor - fix the area bounding box when the link is connected to a link
The visual aid for displaying the available anchor position was incorrectly calculated for links (the current paper transformations were applied on the area bounding box twice).
The change also makes sure that the
Anchor
tool fails silently if thearea
markup is not provided.const CustomAnchor = linkTools.TargetAnchor.extend({ children: [ { tagName: 'circle', selector: 'anchor', attributes: { 'cursor': 'pointer' } }, /* { tagName: 'rect', selector: 'area', attributes: { 'pointer-events': 'none', 'fill': 'none', 'stroke': '#33334F', 'stroke-dasharray': '2,4', 'rx': 5, 'ry': 5 } } */ ] }); const anchor = new CustomAnchor(); // Assert: it's ok to create an anchor without the area visual aid