{"record":{"id":"15985230498f1f4a","repo":"dani-garcia/vaultwarden","slug":"cannot-set-prototype-values","errorCode":null,"errorMessage":"Cannot set prototype values","messagePattern":"Cannot set prototype values","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/static/scripts/datatables.js","lineNumber":1161,"sourceCode":"    }\n    else if (typeof dataPoint === 'function') {\n        return function (data, val, meta) {\n            dataPoint(data, 'set', val, meta);\n        };\n    }\n    else if (typeof dataPoint === 'string' &&\n        (dataPoint.indexOf('.') !== -1 ||\n            dataPoint.indexOf('[') !== -1 ||\n            dataPoint.indexOf('(') !== -1)) {\n        // Like the get, we need to get data from a nested object\n        let setData = function (data, val, src) {\n            let a = splitObjNotation(src), b;\n            let aLast = a[a.length - 1];\n            let arrayNotation, funcNotation, o, innerSrc;\n            for (let i = 0, iLen = a.length - 1; i < iLen; i++) {\n                // Protect against prototype pollution\n                if (a[i] === '__proto__' || a[i] === 'constructor') {\n                    throw new Error('Cannot set prototype values');\n                }\n                // Check if we are dealing with an array notation request\n                arrayNotation = a[i].match(__reArray);\n                funcNotation = a[i].match(__reFn);\n                if (arrayNotation) {\n                    a[i] = a[i].replace(__reArray, '');\n                    data[a[i]] = [];\n                    // Get the remainder of the nested object to set so we can recurse\n                    b = a.slice();\n                    b.splice(0, i + 1);\n                    innerSrc = b.join('.');\n                    // Traverse each entry in the array setting the properties requested\n                    if (Array.isArray(val)) {\n                        for (let j = 0, jLen = val.length; j < jLen; j++) {\n                            o = {};\n                            setData(o, val[j], innerSrc);\n                            data[a[i]].push(o);\n                        }","sourceCodeStart":1143,"sourceCodeEnd":1179,"githubUrl":"https://github.com/dani-garcia/vaultwarden/blob/6729e835218edb29b644a99f65a3b76cde9a341d/src/static/scripts/datatables.js#L1143-L1179","documentation":"DataTables' data accessor (the set() helper behind `columns.data` / `row.data()`) resolves dotted string paths like 'user.name' or '[0].id' to read/write nested values. Before walking the path it checks every segment and throws 'Cannot set prototype values' if a segment is `__proto__` or `constructor`. This is an intentional security guard against prototype pollution: without it, hostile data or config could overwrite Object.prototype and corrupt every object in the page.","triggerScenarios":"Passing a `columns.data` string option containing '__proto__' or 'constructor' as a path segment (e.g. columns: [{data: '__proto__.x'}]); calling table.row().data() / row().set() with a dotted source naming those keys; rendering JSON payloads that contain '__proto__' or 'constructor' properties when DataTables resolves them via splitObjNotation.","commonSituations":"Rendering untrusted third-party JSON (API responses, uploaded files) whose objects were crafted with __proto__/constructor keys; a typo or over-broad dotted path in column definitions; server-side code echoing user input into keys; older DataTables versions lacked this check, so payloads that 'worked' before may now throw after upgrading.","solutions":["Sanitize incoming JSON/data before handing it to DataTables: strip or rename any '__proto__' or 'constructor' keys (e.g. JSON.parse with a reviver that rejects them).","Fix the columns.data / dataSrc dotted path so it targets real fields instead of __proto__ or constructor.","If you legitimately have fields with those names, access them via a function data source — columns.data as a function receives the raw row and avoids the path walker.","Use a safe deep-parse (e.g. parse with Object.create(null) or a hardened JSON.parse) so polluted keys never reach the table.","Keep DataTables updated; the prototype-pollution guard was added as a security fix, so downgrading reintroduces the vulnerability."],"exampleFix":"// before\ncolumns: [\n  { data: '__proto__.name' } // throws: Cannot set prototype values\n]\n// after\ncolumns: [\n  { data: 'name' } // correct dotted path to a real field\n]\n// or, for genuinely hostile keys, use a function accessor:\ncolumns: [\n  { data: row => row['constructor'] ?? '' }\n]","handlingStrategy":"validation","validationCode":"const UNSAFE_PATH = /(^|\\.|\\[)(__proto__|constructor|prototype)(\\.|\\]|$)/;\nfunction isSafeDataPath(path) {\n  return typeof path === 'string' && !UNSAFE_PATH.test(path);\n}\n// before building columns:\n// columns.forEach(c => { if (typeof c.data === 'string' && !isSafeDataPath(c.data)) throw new Error('unsafe columns.data: ' + c.data); })\n// and sanitize row data:\nfunction sanitize(obj) {\n  for (const k of Object.keys(obj)) {\n    if (k === '__proto__' || k === 'constructor') { delete obj[k]; continue; }\n    if (obj[k] && typeof obj[k] === 'object') sanitize(obj[k]);\n  }\n  return obj;\n}","typeGuard":"function isSafeColumnData(data) {\n  return typeof data !== 'string' ||\n    !/(^|\\.|\\[)(__proto__|constructor|prototype)(\\.|\\]|$)/.test(data);\n}","tryCatchPattern":"try {\n  table.rows.add(rows).draw();\n} catch (e) {\n  if (e.message === 'Cannot set prototype values') {\n    console.error('Rejected data containing __proto__/constructor keys', e);\n    rows = rows.map(sanitize);\n    table.rows.add(rows).draw();\n  } else { throw e; }\n}","preventionTips":["Parse untrusted JSON with a reviver that rejects __proto__ and constructor keys before it reaches DataTables.","Never build columns.data paths by concatenating user input.","Prefer function-based data accessors for rows that may contain unusual keys.","Freeze Object.prototype (Object.freeze(Object.prototype)) in hardened pages to blunt pollution attempts.","Test column definitions against sample payloads with hostile keys in CI."],"tags":["datatables","prototype-pollution","security","configuration"],"backgroundTag":"prototype-pollution","analyzedSha":"6729e835218edb29b644a99f65a3b76cde9a341d","analyzedAt":"2026-09-02T22:55:15.880Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}