VYPR
High severityNVD Advisory· Published Jan 19, 2021· Updated Sep 17, 2024

Denial of Service (DoS)

CVE-2020-28479

Description

JointJS <3.3.0 allows denial of service via prototype pollution in unsetByPath, corrupting object behavior.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

JointJS <3.3.0 allows denial of service via prototype pollution in unsetByPath, corrupting object behavior.

Vulnerability

CVE-2020-28479 is a denial-of-service (DoS) vulnerability in the JointJS JavaScript diagramming library, affecting versions before 3.3.0 [1]. The root cause lies in the unsetByPath function, which failed to properly validate property keys when deleting nested object properties. This allowed an attacker to traverse up the prototype chain by providing a path that includes keys like __proto__ or constructor, leading to prototype pollution [3][4].

Exploitation

Exploitation requires no authentication and can be triggered by any untrusted input that reaches the unsetByPath function. The Snyk advisory demonstrates a proof-of-concept where calling jointjs.util.unsetByPath({}, '__proto__/toString', '/') deletes the toString method from Object.prototype [4]. After such an operation, standard object methods become undefined, causing crashes in any code that relies on them [4].

Impact

A successful attack results in a complete denial of service of the application using JointJS. Because the pollution affects the global Object.prototype, it can break not only diagram-related functionality but any part of the application that depends on built-in object methods like toString, valueOf, or hasOwnProperty [3][4]. The vulnerability is classified with a CVSS score determined by the vendor (NVD lists severity information, though it was not fully provided in the input).

Mitigation

The issue was fixed in JointJS version 3.3.0, which introduced the isGetSafe helper to reject dangerous keys (constructor, __proto__) in both setByPath and unsetByPath [2][3]. Users of JointJS should upgrade to v3.3.0 or later. There is no viable workaround that preserves all functionality. The vulnerability is tracked by Snyk as SNYK-JS-JOINTJS-1062038 [4].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
jointjsnpm
< 3.3.03.3.0

Affected products

2

Patches

1
ec7ab01b512a

util.unsetByPath(): prevent prototype modification (#1406)

https://github.com/clientIO/jointRoman BrucknerDec 18, 2020via ghsa
2 files changed · +34 24
  • src/util/util.mjs+25 24 modified
    @@ -137,6 +137,18 @@ export const getByPath = function(obj, path, delimiter) {
         return obj;
     };
     
    +const isGetSafe = function(obj, key) {
    +    // Prevent prototype pollution
    +    // https://snyk.io/vuln/SNYK-JS-JSON8MERGEPATCH-1038399
    +    if (key === 'constructor' && typeof obj[key] === 'function') {
    +        return false;
    +    }
    +    if (key === '__proto__') {
    +        return false;
    +    }
    +    return true;
    +};
    +
     export const setByPath = function(obj, path, value, delimiter) {
     
         const keys = Array.isArray(path) ? path : path.split(delimiter || '/');
    @@ -146,15 +158,8 @@ export const setByPath = function(obj, path, value, delimiter) {
     
         for (; i < last; i++) {
             const key = keys[i];
    +        if (!isGetSafe(diver, key)) return obj;
             const value = diver[key];
    -        // Prevent prototype pollution
    -        // https://snyk.io/vuln/SNYK-JS-JSON8MERGEPATCH-1038399
    -        if (key === 'constructor' && typeof value === 'function') {
    -            return obj;
    -        }
    -        if (key === '__proto__') {
    -            return obj;
    -        }
             // diver creates an empty object if there is no nested object under such a key.
             // This means that one can populate an empty nested object with setByPath().
             diver = value || (diver[key] = {});
    @@ -167,25 +172,21 @@ export const setByPath = function(obj, path, value, delimiter) {
     
     export const unsetByPath = function(obj, path, delimiter) {
     
    -    delimiter || (delimiter = '/');
    -
    -    var pathArray = Array.isArray(path) ? path.slice() : path.split(delimiter);
    -
    -    var propertyToRemove = pathArray.pop();
    -    if (pathArray.length > 0) {
    -
    -        // unsetting a nested attribute
    -        var parent = getByPath(obj, pathArray, delimiter);
    -        if (parent) {
    -            delete parent[propertyToRemove];
    -        }
    -
    -    } else {
    +    const keys = Array.isArray(path) ? path : path.split(delimiter || '/');
    +    const last = keys.length - 1;
    +    let diver = obj;
    +    let i = 0;
     
    -        // unsetting a primitive attribute
    -        delete obj[propertyToRemove];
    +    for (; i < last; i++) {
    +        const key = keys[i];
    +        if (!isGetSafe(diver, key)) return obj;
    +        const value = diver[key];
    +        if (!value) return obj;
    +        diver = value;
         }
     
    +    delete diver[keys[last]];
    +
         return obj;
     };
     
    
  • test/jointjs/core/util.js+9 0 modified
    @@ -401,6 +401,15 @@ QUnit.module('util', function(hooks) {
                 joint.util.unsetByPath(obj, ['c', 'd']);
                 assert.deepEqual(obj, { a: 1 }, 'Attempt to delete non-existing attribute doesn\'t affect object.');
             });
    +
    +        ['__proto__/toString', 'constructor/prototype/toString'].forEach(function(path) {
    +            QUnit.test('unsetting "' + path + '" does not modify prototype' , function(assert) {
    +                var obj = {};
    +                assert.equal(typeof obj.toString, 'function');
    +                joint.util.unsetByPath({}, path, '/');
    +                assert.equal(typeof obj.toString, 'function');
    +            });
    +        });
         });
     
         QUnit.test('util.normalizeSides()', function(assert) {
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

7

News mentions

0

No linked articles in our index yet.