javascript.lang.security.audit.prototype-pollution.prototype-pollution-assignment.prototype-pollution-assignment

profile photo of semgrepsemgrep
Author
137
Download Count*

Possibility of prototype polluting assignment detected. By adding or modifying attributes of an object prototype, it is possible to create attributes that exist on every object, or replace critical attributes with malicious ones. This can be problematic if the software depends on existence or non-existence of certain attributes, or uses pre-defined attributes of object prototype (such as hasOwnProperty, toString or valueOf). Possible mitigations might be: freezing the object prototype, using an object without prototypes (via Object.create(null) ), blocking modifications of attributes that resolve to object prototype, using Map instead of object.

Run Locally

Run in CI

Defintion

rules:
  - id: prototype-pollution-assignment
    message: "Possibility of prototype polluting assignment detected. By adding or
      modifying attributes of an object prototype, it is possible to create
      attributes that exist on every object, or replace critical attributes with
      malicious ones. This can be problematic if the software depends on
      existence or non-existence of certain attributes, or uses pre-defined
      attributes of object prototype (such as hasOwnProperty, toString or
      valueOf). Possible mitigations might be: freezing the object prototype,
      using an object without prototypes (via Object.create(null) ), blocking
      modifications of attributes that resolve to object prototype, using Map
      instead of object."
    metadata:
      cwe:
        - "CWE-915: Improperly Controlled Modification of Dynamically-Determined
          Object Attributes"
      owasp:
        - A08:2021 - Software and Data Integrity Failures
      category: security
      references:
        - https://github.com/HoLyVieR/prototype-pollution-nsec18/blob/master/paper/JavaScript_prototype_pollution_attack_in_NodeJS.pdf
      technology:
        - javascript
      subcategory:
        - audit
      likelihood: LOW
      impact: LOW
      confidence: LOW
      license: Commons Clause License Condition v1.0[LGPL-2.1-only]
      vulnerability_class:
        - Mass Assignment
    languages:
      - javascript
      - typescript
    severity: WARNING
    patterns:
      - pattern: |
          $X[$B] = ...
      - pattern-not: |
          $X[$B] = '...'
      - pattern-inside: |
          $X = $SMTH[$A]
          ...
      - pattern-not-inside: |
          if (<...'constructor' ...>) {
            ...
          }
          ...
      - pattern-not-inside: |
          if (<...'__proto__' ...>) {
            ...
          }
          ...
      - pattern-not-inside: |
          for(var $B = $S; ...; ...) {...}
      - pattern-not-inside: |
          for($B = $S; ...; ...) {...}
      - pattern-not-inside: |
          $X.forEach(function $NAME($OBJ, $B,...) {...})
      - metavariable-pattern:
          patterns:
            - pattern-not: '"..."'
            - pattern-not: |
                `...${...}...`
            - pattern-not: |
                ($A: float)
          metavariable: $A
      - metavariable-pattern:
          patterns:
            - pattern-not: '"..."'
            - pattern-not: |
                `...${...}...`
            - pattern-not: |
                ($B: float)
          metavariable: $B

Examples

prototype-pollution-assignment.js

app.get('/test/:id', (req, res) => {
    let id = req.params.id;
    let items = req.session.todos[id];
    if (!items) {
        items = req.session.todos[id] = {};
    }
    // ruleid: prototype-pollution-assignment
    items[req.query.name] = req.query.text;
    res.end(200);
});

app.post('/testOk/:id', (req, res) => {
    let id = req.params.id;
    if (id !== 'constructor' && id !== '__proto__') {
        let items = req.session.todos[id];
        if (!items) {
            items = req.session.todos[id] = {};
        }
        // ok: prototype-pollution-assignment
        items[req.query.name] = req.query.text;
    }
    res.end(200);
});

function ok1(req, res) {
    let items = req.session.todos["id"];
    if (!items) {
        items = req.session.todos["id"] = {};
    }
    // ok: prototype-pollution-assignment
    items[req.query.name] = req.query.text;
    res.end(200);
}

function ok2(req, res) {
    let id = req.params.id;
    let items = req.session.todos[id];
    if (!items) {
        items = req.session.todos[id] = {};
    }
    // ok: prototype-pollution-assignment
    items["name"] = req.query.text;
    res.end(200);
}

function ok3(req, res) {
    let items = req.session.todos["id"];
    if (!items) {
        items = req.session.todos["id"] = {};
    }
    // ok: prototype-pollution-assignment
    items["name"] = req.query.text;
    res.end(200);
}

function ok4(req, res) {
    let id = req.params.id;
    let items = req.session.todos[id];
    // ok: prototype-pollution-assignment
    items[0] = req.query.text;
    res.end(200);
}

app.get('/testOk5/:id', (req, res) => {
    let id = req.params.id;
    let items = req.session.todos[id];
    if (!items) {
        items = req.session.todos[id] = [];
    }
    // ok: prototype-pollution-assignment
    for (let i = 0; i < items.length; i++) {
        items[i] = req.query.text;
    }
    res.end(200);
});