NocoDB: Stored Cross-Site Scripting via Secure Attachment
With NC_SECURE_ATTACHMENTS=true, an authenticated uploader could deliver .html or .svg attachments that the browser rendered inline from the NocoDB origin instead of forcing a download.
With NC_SECURE_ATTACHMENTS=true, an authenticated uploader could deliver .html or .svg attachments that the browser rendered inline from the NocoDB origin instead of forcing a download.
The spreadsheet-import endpoint axiosRequestMake could be used as a generic HTTP proxy. Before the fix it was reachable unauthenticated, and its URL-extension allowlist was a regex tested against the full URL string, so URLs whose query string ended in .csv (for example https://example.com/robots.txt?.csv) satisfied the gate even though the underlying request was for robots.txt.
The spreadsheet-fetch endpoint (axiosRequestMake) accepted URLs whose path contained a permitted extension anywhere in the string, and applied a hand-rolled regex blocklist that omitted 127.0.0.0/8 and 169.254.0.0/16, allowing the cloud-metadata endpoint to be reached with a crafted URL.
The base-migration endpoint accepted a caller-supplied URL that the migration worker dereferenced without enforcing protocol or destination, allowing scheme abuse (file:, ftp:, etc.) and probing of internal HTTP destinations.
A stolen refresh token survived a password-forgot flow and could be used to mint fresh JWTs even after the user reset their password.
Sign-in response timing differed between known and unknown email addresses because the unknown-user branch returned without performing a password hash comparison.
An authenticated commenter could store HTML in row comments that executed as script when other users hovered over the comment in the expanded form view.
The shared form-view submit handler in NocoDB writes the form's redirect_url to window.location.href after a same-host check that does not validate the URL scheme. A user with editor role (or above) on any base can plant a javascript: URL in the form's redirect_url; when an authenticated viewer opens the share-link and submits the form, the payload executes in the NocoDB origin and can read the session token from localStorage["nocodb-gui-v2"].
An authenticated user with column-create permission can inject SQL into the bulk groupBy endpoint by setting a column's title to a SQL fragment.
The connection-test endpoint opened a raw TCP socket to the user-supplied database host without resolving and range-checking the destination, so private and link-local addresses (including IPv4-mapped IPv6 forms and localhost) reached the driver.
The password-reset page rendered the URL token directly into a JavaScript string literal in a server-rendered EJS template. EJS <%= %> HTML-entity-encodes a fixed set of characters but does not escape single quotes or backslashes, so a crafted token could break out of the JS string context and execute attacker-controlled script in the NocoDB origin. Triggering required only that a victim follow a malicious password-reset link.
An authenticated user with columnAdd permission on a Postgres-backed base can inject arbitrary SQL into the formula engine via the optional direction argument of ARRAYSORT(…). The value is unrestricted by formula validation and embedded into a knex.raw ORDER BY clause, executing during column creation and on every subsequent record read of the formula column.
The shared-view password check fell back to strict-equality (===) comparison for legacy plaintext passwords, leaking the password's length and per-character prefix through response timing.
An authenticated user with base-create permission can attach a SQLite source pointing at an arbitrary file on the NocoDB host, including NocoDB's own internal databases.
The client-side hashRedirect plugin called window.location.replace() on a path extracted from the URL hash fragment after only checking hashPath.startsWith('/'). Protocol-relative URLs (//attacker.com/…) also satisfy that check, so a crafted link such as https://nocodb.example/#//attacker.com/phishing silently redirected visitors to an attacker-controlled origin.
OAuth access and refresh tokens were not revoked when the user changed, reset, or recovered their password, leaving an attacker-issued OAuth grant valid after the user believed they had locked the attacker out.
Two concurrent token-exchange requests using the same OAuth authorization code could each mint a distinct valid (access_token, refresh_token) pair, breaking the single-use guarantee that PKCE relies on.
A low-privilege MCP token holder with knowledge of an attachment path could read any file in shared storage, including attachments belonging to other bases and workspaces, because the MCP readAttachment tool did not verify the file's ownership.
The public shared-view relation endpoints accepted a caller-supplied column ID without verifying that the column was visible in the shared view, so anyone holding a share UUID could read links from any LTAR column on the view's table — including columns the view owner had hidden.
Public shared-view endpoints exposed values from columns that the view owner had hidden, via three independent paths: groupBy returned raw values for any column named in the request, filter and sort arrays operated on hidden columns enabling boolean-blind extraction, and the related-data list accepted arbitrary link-column IDs from other tables in the same base.
A user in one workspace could exercise another workspace's integration through the testConnection endpoint by supplying its ID, because the integration was fetched in a bypass scope and the caller's permission check matched any base in any workspace.
Deleted API tokens continued to authenticate requests until their cache entry expired, because the auth cache was not invalidated by token value at deletion time.
The request-filtering-agent SSRF protection was non-functional in the four notification webhook plugins (Slack, Discord, Mattermost, Teams) because httpAgent / httpsAgent were passed as part of the request body rather than the axios config. An authenticated user with hook-creation permission could direct outbound POST requests to arbitrary internal hosts.
Shared-base sessions were granted the same base-member capabilities as authenticated viewers. Using only the shared-base UUID (xc-shared-base-id), an attacker could enumerate base members and invite an arbitrary email into the base as a real member. The invited user could then redeem the invite via the normal signup flow and retain authenticated access even after the owner revoked the shared link.
The refresh-token cookie was set with httpOnly: true but missing both the secure flag and the sameSite attribute. Over plain HTTP the cookie could be intercepted on the network; without sameSite, browsers attached it to cross-site POSTs, enabling CSRF against the token-refresh endpoint.
A reflected XSS vulnerability exists in the Page Leaving Warning page. The ncRedirectUrl and ncBackUrl query parameters are used in window.location.href and <a> tag bindings without validation, allowing javascript: URI injection.
The OAuth token strategy attached oauth_scope and oauth_granted_resources to the request user, but the ACL middleware never consulted either. An OAuth token issued with a restricted scope (e.g. MCP-only) therefore inherited the full permissions of the underlying user across all routes; the granted_resources.base_id restriction was bypassed on org-level endpoints that don't populate req.context.base_id.
The uploadViaURL path in the v1/v2 attachment API did not enforce NC_ATTACHMENT_FIELD_SIZE against the remote content-length or against the response stream. An authenticated user (Editor+) could direct the server to download arbitrarily large files, exhausting disk space and causing denial of service.
The upload-by-URL path did not enforce NC_ATTACHMENT_FIELD_SIZE against either the remote file's advertised Content-Length or the decoded length of a data: URI, allowing an authenticated user to bypass the configured per-file size limit.
Rich text cell content rendered via v-html without sanitization, enabling stored XSS.
User-controlled content in comments and rich text cells was rendered via v-html without sanitization, enabling stored XSS.
Comments rendered via v-html without sanitization, enabling stored XSS.
An authenticated user with Creator role can inject arbitrary SQL via the DATEADD formula's unit parameter.
The password reset flow did not revoke existing refresh tokens, allowing an attacker with a previously stolen refresh token to continue minting valid JWTs after the victim resets their password.
The password forgot endpoint returned different responses for registered and unregistered emails, allowing user enumeration.
An authenticated user with Editor role can inject arbitrary HTML into Rich Text cells by bypassing the TipTap editor and sending raw HTML via the API.
The MCP token service did not validate token ownership, allowing a Creator within the same base to read, regenerate, or delete another user's MCP tokens if the token ID was known.
A stored XSS vulnerability exists in the Formula virtual cell. Formula results containing URI::() patterns are rendered via v-html without sanitization, allowing injected HTML to execute.
Shared view passwords were stored in plaintext in the database and compared using direct string equality.
A stored Cross-site Scripting (XSS) vulnerability exists in NocoDB’s attachment handling mechanism. Authenticated users can upload malicious SVG files containing embedded JavaScript, which are later rendered inline and executed in the browsers of other users who view the attachment. Because the malicious payload is stored server-side and executed under the application’s origin, successful exploitation can lead to account compromise, data exfiltration and unauthorized actions performed on behalf of affected users.
An unvalidated redirect (open redirect) vulnerability exists in NocoDB’s login flow due to missing validation of the continueAfterSignIn parameter. During authentication, NocoDB processes a user-controlled redirect value and conditionally performs client-side navigation without enforcing any restrictions on the destination’s origin, domain or protocol. This allows attackers to redirect authenticated users to arbitrary external websites after login.
An authenticated user with org-level-creator permissions can exploit prototype pollution in the /api/v2/meta/connection/test endpoint, causing all database write operations to fail application-wide until server restart. While the pollution technically bypasses SUPER_ADMIN authorization checks, no practical privileged actions can be performed because database operations fail immediately after pollution.
A blind Server-Side Request Forgery (SSRF) vulnerability exists in the uploadViaURL functionality due to an unprotected HEAD request. While the subsequent file retrieval logic correctly enforces SSRF protections, the initial metadata request executes without validation. This allows limited outbound requests to arbitrary URLs before SSRF controls are applied.
The API endpoint related to the password reset function is vulnerable to Reflected Cross-Site-Scripting.
A stored cross-site scripting vulnerability exists within the Formula virtual cell comments functionality.
An authenticated attacker with create access could conduct a SQL Injection attack on MySQL DB using unescaped table_name.
Attacker can upload a html file with malicious content. If user tries to open that file in browser malicious scripts can be executed leading Stored XSS(Cross-Site Script) attack.
Nocodb is an open source Airtable alternative. Affected versions of nocodb contain a SQL injection vulnerability, that allows an authenticated attacker with creator access to query the underlying database. By supplying a specially crafted payload to the given an attacker can inject arbitrary SQL queries to be executed. Since this is a blind SQL injection, an attacker may need to use time-based payloads which would include a function to delay …
Improper Input Validation in GitHub repository nocodb/nocodb prior to 0.96.0.
Denial of Service in GitHub repository nocodb/nocodb prior to 0.92.0.
Cross-site Scripting (XSS) - Stored in GitHub repository nocodb/nocodb prior to 0.91.7+.
Insufficient Session Expiration in GitHub repository nocodb/nocodb prior to 0.91.7+.
Improper Privilege Management in GitHub repository nocodb/nocodb prior to 0.91.7+.
Generation of Error Message Containing Sensitive Information in GitHub repository nocodb/nocodb prior to 0.91.7+.