VYPR
High severityNVD Advisory· Published Jun 13, 2022· Updated Aug 3, 2024

Improper Privilege Management in nocodb/nocodb

CVE-2022-2063

Description

Improper Privilege Management in GitHub repository nocodb/nocodb prior to 0.91.7+.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

NocoDB before 0.91.7+ exposes user references in webhook context, enabling privilege escalation via crafted requests.

Vulnerability

Overview CVE-2022-2063 is an improper privilege management vulnerability in NocoDB, an open-source Airtable alternative. The issue exists in versions prior to 0.91.7+. The root cause is that the webhook context included user references, which could be leveraged by an attacker to escalate privileges. The fix, implemented in commit 269a19c2ad89a0e8a7596498e3806ff2ec1040c2, removes the user object from the parseBody function and related webhook processing code, preventing unauthorized access to user-specific data in webhook templates [1][3].

Attack

Vector and Prerequisites An attacker needs to be able to trigger or define webhooks within NocoDB. The vulnerability lies in the webhook context, where the user object was previously passed into Handlebars template compilation. By crafting a webhook payload that references the user object, an attacker could potentially extract sensitive information or perform actions with elevated privileges. No authentication is explicitly bypassed, but privilege escalation occurs through the manipulation of webhook templates containing user context [1][3].

Impact

The impact of this vulnerability is privilege escalation, as improper privilege management allows an attacker to gain unintended access to user-related data or functionalities within the webhook processing pipeline. Since NocoDB manages database and user permissions, this could lead to data exposure or unauthorized operations. The NVD entry references the commit and advisories from huntr.dev [4].

Mitigation

The vulnerability is patched in NocoDB version 0.91.7+. Users are strongly advised to update to the latest release. The fix was merged via pull request #2337, which removes the user parameter from parseBody and axiosRequestMake functions, ensuring that webhook templates no longer have access to the user object [1][3].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
nocodbnpm
< 0.91.80.91.8

Affected products

2
  • ghsa-coords
    Range: < 0.91.8
  • nocodb/nocodb/nocodbv5
    Range: unspecified

Patches

1
269a19c2ad89

Fix: Remove user reference from webhook context (#2337)

https://github.com/nocodb/nocodbPranav CJun 13, 2022via ghsa
3 files changed · +18 64
  • packages/nocodb/src/lib/db/sql-data-mapper/lib/sql/BaseModelSqlv2.ts+2 3 modified
    @@ -34,8 +34,7 @@ import Hook from '../../../../models/Hook';
     import NcPluginMgrv2 from '../../../../meta/helpers/NcPluginMgrv2';
     import {
       _transformSubmittedFormDataForEmail,
    -  invokeWebhook,
    -  parseBody
    +  invokeWebhook
     } from '../../../../meta/helpers/webhookHelpers';
     import Validator from 'validator';
     import { customValidators } from './customValidators';
    @@ -1791,7 +1790,7 @@ class BaseModelSqlv2 {
               // todo: notification template
               (await NcPluginMgrv2.emailAdapter())?.mailSend({
                 to: emails.join(','),
    -            subject: parseBody('NocoDB Form', req, data, {}),
    +            subject: 'NocoDB Form',
                 html: ejs.render(formSubmissionEmailTemplate, {
                   data: transformedData,
                   tn: this.model.table_name,
    
  • packages/nocodb/src/lib/meta/helpers/webhookHelpers.ts+15 54 modified
    @@ -7,21 +7,13 @@ import Filter from '../../models/Filter';
     import HookLog from '../../models/HookLog';
     import { HookLogType } from 'nocodb-sdk';
     
    -export function parseBody(
    -  template: string,
    -  user: any,
    -  data: any,
    -  payload: any
    -): string {
    +export function parseBody(template: string, data: any): string {
       if (!template) {
         return template;
       }
     
       return Handlebars.compile(template, { noEscape: true })({
    -    data,
    -    user,
    -    payload,
    -    env: process.env
    +    data
       });
     }
     
    @@ -121,52 +113,43 @@ export async function handleHttpWebHook(apiMeta, user, data) {
       // }
     }
     
    -export function axiosRequestMake(_apiMeta, user, data) {
    +export function axiosRequestMake(_apiMeta, _user, data) {
       const apiMeta = { ..._apiMeta };
       if (apiMeta.body) {
         try {
           apiMeta.body = JSON.parse(apiMeta.body, (_key, value) => {
    -        return typeof value === 'string'
    -          ? parseBody(value, user, data, apiMeta)
    -          : value;
    +        return typeof value === 'string' ? parseBody(value, data) : value;
           });
         } catch (e) {
    -      apiMeta.body = parseBody(apiMeta.body, user, data, apiMeta);
    +      apiMeta.body = parseBody(apiMeta.body, data);
         }
       }
       if (apiMeta.auth) {
         try {
           apiMeta.auth = JSON.parse(apiMeta.auth, (_key, value) => {
    -        return typeof value === 'string'
    -          ? parseBody(value, user, data, apiMeta)
    -          : value;
    +        return typeof value === 'string' ? parseBody(value, data) : value;
           });
         } catch (e) {
    -      apiMeta.auth = parseBody(apiMeta.auth, user, data, apiMeta);
    +      apiMeta.auth = parseBody(apiMeta.auth, data);
         }
       }
       apiMeta.response = {};
       const req = {
         params: apiMeta.parameters
           ? apiMeta.parameters.reduce((paramsObj, param) => {
               if (param.name && param.enabled) {
    -            paramsObj[param.name] = parseBody(param.value, user, data, apiMeta);
    +            paramsObj[param.name] = parseBody(param.value, data);
               }
               return paramsObj;
             }, {})
           : {},
    -    url: parseBody(apiMeta.path, user, data, apiMeta),
    +    url: parseBody(apiMeta.path, data),
         method: apiMeta.method,
         data: apiMeta.body,
         headers: apiMeta.headers
           ? apiMeta.headers.reduce((headersObj, header) => {
               if (header.name && header.enabled) {
    -            headersObj[header.name] = parseBody(
    -              header.value,
    -              user,
    -              data,
    -              apiMeta
    -            );
    +            headersObj[header.name] = parseBody(header.value, data);
               }
               return headersObj;
             }, {})
    @@ -208,24 +191,9 @@ export async function invokeWebhook(
           case 'Email':
             {
               const res = await (await NcPluginMgrv2.emailAdapter())?.mailSend({
    -            to: parseBody(
    -              notification?.payload?.to,
    -              user,
    -              data,
    -              notification?.payload
    -            ),
    -            subject: parseBody(
    -              notification?.payload?.subject,
    -              user,
    -              data,
    -              notification?.payload
    -            ),
    -            html: parseBody(
    -              notification?.payload?.body,
    -              user,
    -              data,
    -              notification?.payload
    -            )
    +            to: parseBody(notification?.payload?.to, data),
    +            subject: parseBody(notification?.payload?.subject, data),
    +            html: parseBody(notification?.payload?.body, data)
               });
               hookLog = {
                 ...hook,
    @@ -258,16 +226,9 @@ export async function invokeWebhook(
               const res = await (
                 await NcPluginMgrv2.webhookNotificationAdapters(notification.type)
               ).sendMessage(
    -            parseBody(
    -              notification?.payload?.body,
    -              user,
    -              data,
    -              notification?.payload
    -            ),
    +            parseBody(notification?.payload?.body, data),
                 JSON.parse(JSON.stringify(notification?.payload), (_key, value) => {
    -              return typeof value === 'string'
    -                ? parseBody(value, user, data, notification?.payload)
    -                : value;
    +              return typeof value === 'string' ? parseBody(value, data) : value;
                 })
               );
     
    
  • packages/noco-docs/content/en/developer-resources/webhooks.md+1 7 modified
    @@ -66,15 +66,9 @@ For INSERT/ UPDATE based triggers, use following handlebars to access correspond
     Note that, for Update trigger - all the fields in the ROW will be accessible, not just the field updated.
     For DELETE based triggers, **only** {{ data.id }} is accessible representing ID of the column deleted.
       
    -For all trigger, following **user** information associated with person trigger can be accessed.
    -
    --   {{ **user**.id }} : Unique auto incremented NocoDB system value
    --   {{ **user**.email }} : User E-mail.
    --   {{ **user**.roles }} : User Role amongst [Owner, Creator, Editor, Commenter, Viewer].
    -
     ### JSON format
     
    -Use {{ json data }} {{ json user }} to dump complete data & user information available in JSON format
    +Use {{ json data }} to dump complete data & user information available in JSON format
     
     ### Additional references:
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.