VYPR
Medium severity6.5NVD Advisory· Published Apr 2, 2026· Updated Apr 10, 2026

CVE-2026-34825

CVE-2026-34825

Description

NocoBase is an AI-powered no-code/low-code platform for building business applications and enterprise solutions. Prior to version 2.0.30, NocoBase plugin-workflow-sql substitutes template variables directly into raw SQL strings via getParsedValue() without parameterization or escaping. Any user who triggers a workflow containing a SQL node with template variables from user-controlled data can inject arbitrary SQL. This issue has been patched in version 2.0.30.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
@nocobase/plugin-workflow-sqlnpm
< 2.0.302.0.30

Affected products

1

Patches

1
75da3dddc4ab

fix(plugin-workflow-sql): fix security issue (#8989)

https://github.com/nocobase/nocobaseJunyiMar 30, 2026via ghsa
23 files changed · +750 44
  • packages/plugins/@nocobase/plugin-workflow-sql/src/client/SQLInstruction.tsx+144 5 modified
    @@ -8,14 +8,86 @@
      */
     
     import { DEFAULT_DATA_SOURCE_KEY, css } from '@nocobase/client';
    +import { ArrayItems } from '@formily/antd-v5';
    +import { useForm } from '@formily/react';
    +import { Space, Input, Alert, Button } from 'antd';
    +import { parse } from '@nocobase/utils/client';
    +import { set } from 'lodash';
     
    -import { Instruction, WorkflowVariableRawTextArea, defaultFieldNames } from '@nocobase/plugin-workflow/client';
    +import {
    +  Instruction,
    +  WorkflowVariableInput,
    +  WorkflowVariableRawTextArea,
    +  defaultFieldNames,
    +} from '@nocobase/plugin-workflow/client';
     
     import React from 'react';
     import { ConsoleSqlOutlined } from '@ant-design/icons';
    -import { Trans } from 'react-i18next';
    +import { Trans, useTranslation } from 'react-i18next';
     import { NAMESPACE } from '../locale';
     
    +function SQLTextArea(props) {
    +  const { values } = useForm();
    +
    +  return values.unsafeInjection ? <WorkflowVariableRawTextArea {...props} /> : <Input.TextArea {...props} />;
    +}
    +
    +function UnsafeInjectionWarning() {
    +  const { t } = useTranslation(NAMESPACE);
    +  const form = useForm();
    +  const { values } = form;
    +
    +  if (!values.unsafeInjection || form.disabled) {
    +    return null;
    +  }
    +
    +  const onMigrate = () => {
    +    const sql = values.sql || '';
    +    const template = parse(sql);
    +    const parameters = template.parameters || [];
    +
    +    // Deduplicate and assign new names
    +    const uniqueKeys: string[] = [
    +      ...new Set(
    +        parameters.map((p: { key: string }) => p.key).filter((key) => key && typeof key === 'string') as string[],
    +      ),
    +    ];
    +    const context = {};
    +    uniqueKeys.forEach((key, index) => {
    +      set(context, key, `:var${index}`);
    +    });
    +
    +    // Build variables config
    +    const variables = uniqueKeys.map((key, index) => ({
    +      name: `var${index}`,
    +      value: `{{${key}}}`,
    +    }));
    +
    +    // Replace {{...}} in SQL with :name placeholders
    +    const newSql = template(context);
    +
    +    form.setValues({
    +      sql: newSql,
    +      variables,
    +      unsafeInjection: false,
    +    });
    +  };
    +
    +  return (
    +    <Alert
    +      type="error"
    +      showIcon
    +      message={t('Current node is using unsafe injection mode (legacy), which has SQL injection risks.')}
    +      action={
    +        <Button size="small" type="primary" onClick={onMigrate}>
    +          {t('Migrate to safe mode')}
    +        </Button>
    +      }
    +      style={{ marginBottom: 16 }}
    +    />
    +  );
    +}
    +
     export default class extends Instruction {
       title = `{{t("SQL action", { ns: "${NAMESPACE}" })}}`;
       type = 'sql';
    @@ -38,13 +110,17 @@ export default class extends Instruction {
           },
           default: 'main',
         },
    +    unsafeInjection: {
    +      type: 'void',
    +      'x-component': 'UnsafeInjectionWarning',
    +    },
         sql: {
           type: 'string',
           required: true,
           title: 'SQL',
           description: '{{sqlDescription()}}',
           'x-decorator': 'FormItem',
    -      'x-component': 'WorkflowVariableRawTextArea',
    +      'x-component': 'SQLTextArea',
           'x-component-props': {
             rows: 20,
             className: css`
    @@ -53,6 +129,65 @@ export default class extends Instruction {
             `,
           },
         },
    +    variables: {
    +      type: 'array',
    +      title: `{{t("Parameters", { ns: "${NAMESPACE}" })}}`,
    +      description: `{{t("SQL parameters. Use :name as placeholders in SQL and provide values here.", { ns: "${NAMESPACE}" })}}`,
    +      'x-decorator': 'FormItem',
    +      'x-component': 'ArrayItems',
    +      'x-reactions': [
    +        {
    +          dependencies: ['unsafeInjection'],
    +          fulfill: {
    +            state: {
    +              visible: '{{!$deps[0]}}',
    +            },
    +          },
    +        },
    +      ],
    +      items: {
    +        type: 'object',
    +        properties: {
    +          space1: {
    +            type: 'void',
    +            'x-component': 'Space',
    +            properties: {
    +              name: {
    +                type: 'string',
    +                'x-decorator': 'FormItem',
    +                'x-component': 'Input',
    +                'x-component-props': {
    +                  placeholder: `{{t("Name", { ns: "${NAMESPACE}" })}}`,
    +                },
    +                required: true,
    +              },
    +              value: {
    +                type: 'string',
    +                'x-decorator': 'FormItem',
    +                'x-component': 'WorkflowVariableInput',
    +                'x-component-props': {
    +                  rows: 1,
    +                  placeholder: `{{t("Value", { ns: "${NAMESPACE}" })}}`,
    +                },
    +                required: true,
    +              },
    +              remove: {
    +                type: 'void',
    +                'x-decorator': 'FormItem',
    +                'x-component': 'ArrayItems.Remove',
    +              },
    +            },
    +          },
    +        },
    +      },
    +      properties: {
    +        add: {
    +          type: 'void',
    +          'x-component': 'ArrayItems.Addition',
    +          title: `{{t("Add parameter", { ns: "${NAMESPACE}" })}}`,
    +        },
    +      },
    +    },
         withMeta: {
           type: 'boolean',
           'x-decorator': 'FormItem',
    @@ -68,13 +203,17 @@ export default class extends Instruction {
               <a href="https://docs-cn.nocobase.com/handbook/workflow-json-query" target="_blank" rel="noreferrer">
                 {'JSON query node'}
               </a>
    -          {' (Commercial plugin).'}
    +          {'.'}
             </Trans>
           );
         },
       };
       components = {
    -    WorkflowVariableRawTextArea,
    +    SQLTextArea,
    +    UnsafeInjectionWarning,
    +    WorkflowVariableInput,
    +    ArrayItems,
    +    Space,
       };
       useVariables({ key, title }, { types, fieldNames = defaultFieldNames }) {
         return {
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/de-DE.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Führt eine SQL-Anweisung in der Datenbank aus.",
       "Include meta information of this query in result": "Metainformationen dieser Abfrage im Ergebnis einschließen",
       "SQL action": "SQL-Aktion",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL-Abfrageergebnisse können über <1>JSON-Abfrageknoten</1> verwendet werden (kommerzielles Plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL-Abfrageergebnisse können über <1>JSON-Abfrageknoten</1> verwendet werden.",
       "Select a data source to execute SQL.": "Wählen Sie eine Datenquelle zur Ausführung von SQL aus",
    -  "Usage of SQL query result is not supported yet.": "Die Verwendung von SQL-Abfrageergebnissen wird noch nicht unterstützt."
    +  "Usage of SQL query result is not supported yet.": "Die Verwendung von SQL-Abfrageergebnissen wird noch nicht unterstützt.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Der aktuelle Knoten verwendet den unsicheren Injektionsmodus (Legacy), der SQL-Injection-Risiken birgt.",
    +  "Migrate to safe mode": "In sicheren Modus migrieren",
    +  "Parameters": "Parameter",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL-Parameter. Verwenden Sie :name als Platzhalter in SQL und geben Sie hier die Werte an.",
    +  "Name": "Name",
    +  "Value": "Wert",
    +  "Add parameter": "Parameter hinzufügen"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/en-US.json+9 1 modified
    @@ -3,7 +3,15 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use $1, $2, etc. as placeholders in SQL and provide values here in order.": "SQL parameters. Use $1, $2, etc. as placeholders in SQL and provide values here in order.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
       "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/es-ES.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/fr-FR.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/hu-HU.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "SQL utasítás végrehajtása az adatbázisban.",
       "Include meta information of this query in result": "A lekérdezés metainformációinak belefoglalása az eredménybe",
       "SQL action": "SQL művelet",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "Az SQL lekérdezés eredménye a <1>JSON lekérdezési csomóponton</1> keresztül használható (Kereskedelmi bővítmény).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "Az SQL lekérdezés eredménye a <1>JSON lekérdezési csomóponton</1> keresztül használható.",
       "Select a data source to execute SQL.": "Válasszon adatforrást az SQL végrehajtásához.",
    -  "Usage of SQL query result is not supported yet.": "Az SQL lekérdezési eredmény használata még nem támogatott."
    +  "Usage of SQL query result is not supported yet.": "Az SQL lekérdezési eredmény használata még nem támogatott.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "A jelenlegi csomópont nem biztonságos injektálási módot (örökölt) használ, amely SQL injektálási kockázatot jelent.",
    +  "Migrate to safe mode": "Migrálás biztonságos módba",
    +  "Parameters": "Paraméterek",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL paraméterek. Használja a :name-t helyőrzőként az SQL-ben, és adja meg itt az értékeket.",
    +  "Name": "Név",
    +  "Value": "Érték",
    +  "Add parameter": "Paraméter hozzáadása"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/id-ID.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Jalankan pernyataan SQL dalam database.",
       "Include meta information of this query in result": "Sertakan informasi meta kueri ini dalam hasil",
       "SQL action": "Tindakan SQL",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "Hasil kueri SQL dapat digunakan melalui <1>node kueri JSON</1> (Plugin Komersial).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "Hasil kueri SQL dapat digunakan melalui <1>node kueri JSON</1>.",
       "Select a data source to execute SQL.": "Pilih sumber data untuk menjalankan SQL.",
    -  "Usage of SQL query result is not supported yet.": "Penggunaan hasil kueri SQL belum didukung."
    +  "Usage of SQL query result is not supported yet.": "Penggunaan hasil kueri SQL belum didukung.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Node saat ini menggunakan mode injeksi tidak aman (legacy), yang memiliki risiko injeksi SQL.",
    +  "Migrate to safe mode": "Migrasi ke mode aman",
    +  "Parameters": "Parameter",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "Parameter SQL. Gunakan :name sebagai placeholder di SQL dan berikan nilainya di sini.",
    +  "Name": "Nama",
    +  "Value": "Nilai",
    +  "Add parameter": "Tambah parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/it-IT.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Esegui un'istruzione SQL nel database.",
       "Include meta information of this query in result": "Includi meta informazioni di questa query nel risultato",
       "SQL action": "Azione SQL",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "Il risultato della query SQL potrebbe essere utilizzato tramite il <1>nodo JSON query</1>(plugin commerciale).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "Il risultato della query SQL potrebbe essere utilizzato tramite il <1>nodo JSON query</1>.",
       "Select a data source to execute SQL.": "Seleziona un origine dati per eseguire SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Il nodo corrente utilizza la modalità di iniezione non sicura (legacy), che comporta rischi di SQL injection.",
    +  "Migrate to safe mode": "Migra alla modalità sicura",
    +  "Parameters": "Parametri",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "Parametri SQL. Usa :name come segnaposto in SQL e fornisci i valori qui.",
    +  "Name": "Nome",
    +  "Value": "Valore",
    +  "Add parameter": "Aggiungi parametro"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/ja-JP.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "データベース内でSQL文を実行します。",
       "Include meta information of this query in result": "結果にこのクエリのメタ情報を含める",
       "SQL action": "SQL操作",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQLクエリ結果は<1>JSONクエリノード</1>を介して使用できます(商用プラグイン)。",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQLクエリ結果は<1>JSONクエリノード</1>を介して使用できます。",
       "Select a data source to execute SQL.": "SQLを実行するためのデータソースを選択します。",
    -  "Usage of SQL query result is not supported yet.": "SQLクエリ結果はまだサポートされていません"
    +  "Usage of SQL query result is not supported yet.": "SQLクエリ結果はまだサポートされていません",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "現在のノードは安全でないインジェクションモード(レガシー)を使用しており、SQLインジェクションのリスクがあります。",
    +  "Migrate to safe mode": "安全モードに移行",
    +  "Parameters": "パラメータ",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQLパラメータ。SQLで :name をプレースホルダとして使用し、ここで値を指定します。",
    +  "Name": "名前",
    +  "Value": "値",
    +  "Add parameter": "パラメータを追加"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/ko-KR.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "데이터베이스에서 SQL 문을 실행합니다.",
       "Include meta information of this query in result": "결과에 쿼리 메타 정보 포함",
       "SQL action": "SQL 작업",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL 쿼리 결과는 <1>JSON 쿼리 노드</1>(상용 플러그인)로 사용할 수 있습니다.",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL 쿼리 결과는 <1>JSON 쿼리 노드</1>를 통해 사용할 수 있습니다.",
       "Select a data source to execute SQL.": "SQL을 실행할 데이터 소스 선택",
    -  "Usage of SQL query result is not supported yet.": "SQL 쿼리 결과 사용은 아직 지원되지 않습니다."
    +  "Usage of SQL query result is not supported yet.": "SQL 쿼리 결과 사용은 아직 지원되지 않습니다.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "현재 노드는 안전하지 않은 주입 모드(레거시)를 사용하고 있으며 SQL 주입 위험이 있습니다.",
    +  "Migrate to safe mode": "안전 모드로 마이그레이션",
    +  "Parameters": "매개변수",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL 매개변수. SQL에서 :name을 자리 표시자로 사용하고 여기에 값을 제공하세요.",
    +  "Name": "이름",
    +  "Value": "값",
    +  "Add parameter": "매개변수 추가"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/nl-NL.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/pt-BR.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/ru-RU.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Выполнить SQL-запрос в базе данных.",
       "Include meta information of this query in result": "Включить метаинформацию этого запроса в результат",
       "SQL action": "Действие SQL",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "Результат SQL-запроса может быть использован через узел <1>JSON-запроса</1> (коммерческий плагин).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "Результат SQL-запроса может быть использован через узел <1>JSON-запроса</1>.",
       "Select a data source to execute SQL.": "Выберите источник данных для выполнения SQL.",
    -  "Usage of SQL query result is not supported yet.": "Использование результата SQL-запроса пока не поддерживается."
    +  "Usage of SQL query result is not supported yet.": "Использование результата SQL-запроса пока не поддерживается.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Текущий узел использует небезопасный режим инъекции (устаревший), который имеет риски SQL-инъекции.",
    +  "Migrate to safe mode": "Перейти в безопасный режим",
    +  "Parameters": "Параметры",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "Параметры SQL. Используйте :name в качестве заполнителей в SQL и укажите значения здесь.",
    +  "Name": "Имя",
    +  "Value": "Значение",
    +  "Add parameter": "Добавить параметр"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/tr-TR.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/uk-UA.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/vi-VN.json+9 2 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "Current node is using unsafe injection mode (legacy), which has SQL injection risks.",
    +  "Migrate to safe mode": "Migrate to safe mode",
    +  "Parameters": "Parameters",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL parameters. Use :name as placeholders in SQL and provide values here.",
    +  "Name": "Name",
    +  "Value": "Value",
    +  "Add parameter": "Add parameter"
     }
    \ No newline at end of file
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/zh-CN.json+10 2 modified
    @@ -3,7 +3,15 @@
       "Execute a SQL statement in database.": "在数据库中执行一个 SQL 语句",
       "Include meta information of this query in result": "在结果中包含此查询的元信息",
       "SQL action": "SQL 操作",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL 执行的结果可在 <1>JSON 解析节点</1> 中使用(商业插件)。",
    +  "Parameters": "参数",
    +  "SQL parameters. Use $1, $2, etc. as placeholders in SQL and provide values here in order.": "SQL 参数。在 SQL 中使用 $1, $2 等作为占位符,并按顺序在此提供对应的值。",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "当前节点使用了不安全注入模式(遗留),存在 SQL 注入风险。",
    +  "Migrate to safe mode": "迁移至安全模式",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL 参数。在 SQL 中使用 :name 作为占位符,并在此提供对应的值。",
    +  "Name": "名称",
    +  "Value": "值",
    +  "Add parameter": "添加参数",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL 执行的结果可在 <1>JSON 解析节点</1> 中使用。",
       "Select a data source to execute SQL.": "选择一个数据源来执行 SQL",
       "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    -}
    \ No newline at end of file
    +}
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/locale/zh-TW.json+10 3 modified
    @@ -3,7 +3,14 @@
       "Execute a SQL statement in database.": "Execute a SQL statement in database.",
       "Include meta information of this query in result": "Include meta information of this query in result",
       "SQL action": "SQL action",
    -  "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).": "SQL query result could be used through <1>JSON query node</1> (Commercial plugin).",
    +  "SQL query result could be used through <1>JSON query node</1>.": "SQL query result could be used through <1>JSON query node</1>.",
       "Select a data source to execute SQL.": "Select a data source to execute SQL.",
    -  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet."
    -}
    \ No newline at end of file
    +  "Usage of SQL query result is not supported yet.": "Usage of SQL query result is not supported yet.",
    +  "Current node is using unsafe injection mode (legacy), which has SQL injection risks.": "當前節點使用了不安全注入模式(遺留),存在 SQL 注入風險。",
    +  "Migrate to safe mode": "遷移至安全模式",
    +  "SQL parameters. Use :name as placeholders in SQL and provide values here.": "SQL 參數。在 SQL 中使用 :name 作為佔位符,並在此提供對應的值。",
    +  "Parameters": "參數",
    +  "Name": "名稱",
    +  "Value": "值",
    +  "Add parameter": "添加參數"
    +}
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/server/migrations/20260327120000-add-unsafe-injection-flag.ts+46 0 added
    @@ -0,0 +1,46 @@
    +/**
    + * This file is part of the NocoBase (R) project.
    + * Copyright (c) 2020-2024 NocoBase Co., Ltd.
    + * Authors: NocoBase Team.
    + *
    + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
    + * For more information, please refer to: https://www.nocobase.com/agreement.
    + */
    +
    +import { Migration } from '@nocobase/server';
    +import { parse } from '@nocobase/utils';
    +
    +export default class extends Migration {
    +  appVersion = '<2.0.30';
    +  async up() {
    +    const { db } = this.context;
    +
    +    const NodeRepo = db.getRepository('flow_nodes');
    +    await db.sequelize.transaction(async (transaction) => {
    +      const nodes = await NodeRepo.find({
    +        filter: {
    +          type: 'sql',
    +        },
    +        transaction,
    +      });
    +
    +      await nodes.reduce(
    +        (promise, node) =>
    +          promise.then(() => {
    +            const sql = node.config?.sql || '';
    +            const template = parse(sql);
    +            if (!template.parameters?.length) {
    +              return;
    +            }
    +            node.set('config', { ...node.config, unsafeInjection: true });
    +            node.changed('config', true);
    +            return node.save({
    +              silent: true,
    +              transaction,
    +            });
    +          }),
    +        Promise.resolve(),
    +      );
    +    });
    +  }
    +}
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/server/SQLInstruction.ts+43 4 modified
    @@ -14,6 +14,8 @@ export type SQLInstructionConfig = {
       dataSource?: string;
       sql?: string;
       withMeta?: boolean;
    +  unsafeInjection?: boolean;
    +  variables?: Array<{ name: string; value: any }>;
     };
     
     export default class extends Instruction {
    @@ -23,7 +25,23 @@ export default class extends Instruction {
         if (!(collectionManager instanceof SequelizeCollectionManager)) {
           throw new Error(`type of data source "${node.config.dataSource}" is not database`);
         }
    -    const sql = processor.getParsedValue(node.config.sql || '', node.id).trim();
    +
    +    const { unsafeInjection = false, variables: variablesConfig = [] } = node.config;
    +
    +    let sql = '';
    +    let replacements = null;
    +    if (unsafeInjection) {
    +      sql = processor.getParsedValue(node.config.sql || '', node.id).trim();
    +    } else {
    +      sql = (node.config.sql || '').trim();
    +      replacements = {};
    +      for (const { name, value } of variablesConfig) {
    +        if (name) {
    +          replacements[name] = processor.getParsedValue(value, node.id);
    +        }
    +      }
    +    }
    +
         if (!sql) {
           return {
             status: JOB_STATUS.RESOLVED,
    @@ -33,6 +51,7 @@ export default class extends Instruction {
         const [result = null, meta = null] =
           (await collectionManager.db.sequelize.query(sql, {
             transaction: this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction),
    +        replacements,
             // plain: true,
             // model: db.getCollection(node.config.collection).model
           })) ?? [];
    @@ -43,8 +62,14 @@ export default class extends Instruction {
         };
       }
     
    -  async test({ dataSource, sql, withMeta }: SQLInstructionConfig = {}) {
    -    if (!sql) {
    +  async test({
    +    dataSource,
    +    sql: sqlConfig,
    +    withMeta,
    +    unsafeInjection = false,
    +    variables: variablesConfig = [],
    +  }: SQLInstructionConfig = {}) {
    +    if (!sqlConfig) {
           return {
             result: null,
             status: JOB_STATUS.RESOLVED,
    @@ -58,7 +83,21 @@ export default class extends Instruction {
         }
     
         try {
    -      const [result = null, meta = null] = (await collectionManager.db.sequelize.query(sql)) ?? [];
    +      let sql = '';
    +      let replacements = null;
    +      if (unsafeInjection) {
    +        sql = sqlConfig.trim();
    +      } else {
    +        sql = sqlConfig.trim();
    +        replacements = {};
    +        for (const { name, value } of variablesConfig) {
    +          if (name) {
    +            replacements[name] = value;
    +          }
    +        }
    +      }
    +
    +      const [result = null, meta = null] = (await collectionManager.db.sequelize.query(sql, { replacements })) ?? [];
     
           return {
             result: withMeta ? [result, meta] : result,
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/server/__tests__/instruction.test.ts+46 1 modified
    @@ -111,6 +111,7 @@ describe('workflow > instructions > sql', () => {
             type: 'sql',
             config: {
               sql: `select '{{$system.now}}' as a`,
    +          unsafeInjection: true,
             },
           });
     
    @@ -123,14 +124,57 @@ describe('workflow > instructions > sql', () => {
           expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
         });
     
    -    it('update', async () => {
    +    it('update with unsafeInjection: true', async () => {
           const queryInterface = db.sequelize.getQueryInterface();
           const n1 = await workflow.createNode({
             type: 'sql',
             config: {
               sql: `update ${PostCollection.quotedTableName()} set ${queryInterface.quoteIdentifier(
                 'read',
               )}={{$context.data.id}} where ${queryInterface.quoteIdentifier('id')}={{$context.data.id}}`,
    +          unsafeInjection: true,
    +        },
    +      });
    +
    +      const n2 = await workflow.createNode({
    +        type: 'query',
    +        config: {
    +          collection: 'posts',
    +          params: {
    +            filter: {
    +              id: '{{ $context.data.id }}',
    +            },
    +          },
    +        },
    +        upstreamId: n1.id,
    +      });
    +
    +      await n1.setDownstream(n2);
    +
    +      const post = await PostRepo.create({ values: { title: 't1' } });
    +
    +      await sleep(500);
    +
    +      const [execution] = await workflow.getExecutions();
    +      const [sqlJob, queryJob] = await execution.getJobs({ order: [['id', 'ASC']] });
    +      expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
    +      expect(queryJob.status).toBe(JOB_STATUS.RESOLVED);
    +      expect(queryJob.result.read).toBe(post.id);
    +    });
    +
    +    it('update with replacements (unsafeInjection: false)', async () => {
    +      const queryInterface = db.sequelize.getQueryInterface();
    +      const n1 = await workflow.createNode({
    +        type: 'sql',
    +        config: {
    +          sql: `update ${PostCollection.quotedTableName()} set ${queryInterface.quoteIdentifier(
    +            'read',
    +          )}=:val1 where ${queryInterface.quoteIdentifier('id')}=:val2`,
    +          unsafeInjection: false,
    +          variables: [
    +            { name: 'val1', value: '{{$context.data.id}}' },
    +            { name: 'val2', value: '{{$context.data.id}}' },
    +          ],
             },
           });
     
    @@ -168,6 +212,7 @@ describe('workflow > instructions > sql', () => {
               sql: `delete from ${PostCollection.quotedTableName()} where ${queryInterface.quoteIdentifier(
                 'id',
               )}={{$context.data.id}};`,
    +          unsafeInjection: true,
             },
           });
     
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/server/__tests__/migrations/20260327120000-add-unsafe-injection-flag.test.ts+141 0 added
    @@ -0,0 +1,141 @@
    +/**
    + * This file is part of the NocoBase (R) project.
    + * Copyright (c) 2020-2024 NocoBase Co., Ltd.
    + * Authors: NocoBase Team.
    + *
    + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
    + * For more information, please refer to: https://www.nocobase.com/agreement.
    + */
    +
    +import Database from '@nocobase/database';
    +import { Application } from '@nocobase/server';
    +import { getApp } from '@nocobase/plugin-workflow-test';
    +
    +import Plugin from '../..';
    +import Migration from '../../migrations/20260327120000-add-unsafe-injection-flag';
    +
    +describe('migration - add unsafe injection flag', () => {
    +  let app: Application;
    +  let db: Database;
    +  let WorkflowModel;
    +  let workflow;
    +
    +  beforeEach(async () => {
    +    app = await getApp({
    +      plugins: [Plugin],
    +    });
    +    db = app.db;
    +    WorkflowModel = db.getCollection('workflows').model;
    +
    +    workflow = await WorkflowModel.create({
    +      enabled: true,
    +      type: 'collection',
    +      config: {
    +        mode: 1,
    +        collection: 'posts',
    +      },
    +    });
    +  });
    +
    +  afterEach(() => app.destroy());
    +
    +  it('should mark node with variables as unsafeInjection', async () => {
    +    const n1 = await workflow.createNode({
    +      type: 'sql',
    +      config: {
    +        sql: `select * from posts where id = {{$context.data.id}}`,
    +      },
    +    });
    +
    +    const migration = new Migration({ db: app.db, app } as any);
    +    await migration.up();
    +
    +    await n1.reload();
    +    expect(n1.config.unsafeInjection).toBe(true);
    +  });
    +
    +  it('should not mark node without variables', async () => {
    +    const n1 = await workflow.createNode({
    +      type: 'sql',
    +      config: {
    +        sql: `select 1 as a`,
    +      },
    +    });
    +
    +    const migration = new Migration({ db: app.db, app } as any);
    +    await migration.up();
    +
    +    await n1.reload();
    +    expect(n1.config.unsafeInjection).toBeUndefined();
    +  });
    +
    +  it('should not mark node with empty sql', async () => {
    +    const n1 = await workflow.createNode({
    +      type: 'sql',
    +      config: {
    +        sql: '',
    +      },
    +    });
    +
    +    const migration = new Migration({ db: app.db, app } as any);
    +    await migration.up();
    +
    +    await n1.reload();
    +    expect(n1.config.unsafeInjection).toBeUndefined();
    +  });
    +
    +  it('should handle node without sql config', async () => {
    +    const n1 = await workflow.createNode({
    +      type: 'sql',
    +      config: {},
    +    });
    +
    +    const migration = new Migration({ db: app.db, app } as any);
    +    await migration.up();
    +
    +    await n1.reload();
    +    expect(n1.config.unsafeInjection).toBeUndefined();
    +  });
    +
    +  it('should mark node with multiple variables', async () => {
    +    const n1 = await workflow.createNode({
    +      type: 'sql',
    +      config: {
    +        sql: `update posts set read = {{$context.data.id}} where id = {{$context.data.id}}`,
    +      },
    +    });
    +
    +    const migration = new Migration({ db: app.db, app } as any);
    +    await migration.up();
    +
    +    await n1.reload();
    +    expect(n1.config.unsafeInjection).toBe(true);
    +  });
    +
    +  it('should only mark nodes with variables among mixed nodes', async () => {
    +    const n1 = await workflow.createNode({
    +      type: 'sql',
    +      config: {
    +        sql: `select 1 as a`,
    +      },
    +    });
    +
    +    const n2 = await workflow.createNode({
    +      type: 'sql',
    +      config: {
    +        sql: `select * from posts where id = {{$context.data.id}}`,
    +      },
    +      upstreamId: n1.id,
    +    });
    +
    +    await n1.setDownstream(n2);
    +
    +    const migration = new Migration({ db: app.db, app } as any);
    +    await migration.up();
    +
    +    await n1.reload();
    +    await n2.reload();
    +    expect(n1.config.unsafeInjection).toBeUndefined();
    +    expect(n2.config.unsafeInjection).toBe(true);
    +  });
    +});
    
  • packages/plugins/@nocobase/plugin-workflow-sql/src/server/__tests__/security.test.ts+175 0 added
    @@ -0,0 +1,175 @@
    +/**
    + * This file is part of the NocoBase (R) project.
    + * Copyright (c) 2020-2024 NocoBase Co., Ltd.
    + * Authors: NocoBase Team.
    + *
    + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
    + * For more information, please refer to: https://www.nocobase.com/agreement.
    + */
    +
    +import Database from '@nocobase/database';
    +import { Application } from '@nocobase/server';
    +import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
    +import { getApp, sleep } from '@nocobase/plugin-workflow-test';
    +
    +import Plugin from '..';
    +
    +describe('workflow > instructions > sql > security', () => {
    +  let app: Application;
    +  let db: Database;
    +  let PostRepo;
    +  let PostCollection;
    +  let WorkflowModel;
    +  let workflow;
    +
    +  beforeEach(async () => {
    +    app = await getApp({
    +      plugins: [Plugin],
    +    });
    +    db = app.db;
    +    WorkflowModel = db.getCollection('workflows').model;
    +    PostCollection = db.getCollection('posts');
    +    PostRepo = PostCollection.repository;
    +
    +    workflow = await WorkflowModel.create({
    +      enabled: true,
    +      type: 'collection',
    +      config: {
    +        mode: 1,
    +        collection: 'posts',
    +      },
    +    });
    +  });
    +
    +  afterEach(() => app.destroy());
    +
    +  describe('sql injection prevention (safe mode)', () => {
    +    it('tautology in WHERE clause (1 OR 1=1)', async () => {
    +      const queryInterface = db.sequelize.getQueryInterface();
    +      await PostRepo.create({ values: { title: 't1' }, hooks: false });
    +      await PostRepo.create({ values: { title: 't2' }, hooks: false });
    +
    +      const n1 = await workflow.createNode({
    +        type: 'sql',
    +        config: {
    +          sql: `select * from ${PostCollection.quotedTableName()} where ${queryInterface.quoteIdentifier(
    +            'title',
    +          )} = :a`,
    +          unsafeInjection: false,
    +          variables: [{ name: 'a', value: '1 OR 1=1' }],
    +        },
    +      });
    +
    +      const post = await PostRepo.create({ values: { title: 't3' } });
    +
    +      await sleep(500);
    +
    +      const [execution] = await workflow.getExecutions();
    +      const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
    +      expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
    +      // "1 OR 1=1" is treated as a literal string, not as SQL expression
    +      expect(sqlJob.result.length).toBe(0);
    +    });
    +
    +    it('UNION SELECT injection', async () => {
    +      const queryInterface = db.sequelize.getQueryInterface();
    +      await PostRepo.create({ values: { title: 'normal' }, hooks: false });
    +
    +      const n1 = await workflow.createNode({
    +        type: 'sql',
    +        config: {
    +          sql: `select ${queryInterface.quoteIdentifier(
    +            'title',
    +          )} from ${PostCollection.quotedTableName()} where ${queryInterface.quoteIdentifier('title')} = :a`,
    +          unsafeInjection: false,
    +          variables: [{ name: 'a', value: "' UNION SELECT usename FROM pg_user --" }],
    +        },
    +      });
    +
    +      const post = await PostRepo.create({ values: { title: 't1' } });
    +
    +      await sleep(500);
    +
    +      const [execution] = await workflow.getExecutions();
    +      const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
    +      expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
    +      expect(sqlJob.result.length).toBe(0);
    +    });
    +
    +    it('semicolon with DROP TABLE', async () => {
    +      const queryInterface = db.sequelize.getQueryInterface();
    +      await PostRepo.create({ values: { title: 'keep' }, hooks: false });
    +
    +      const n1 = await workflow.createNode({
    +        type: 'sql',
    +        config: {
    +          sql: `select * from ${PostCollection.quotedTableName()} where ${queryInterface.quoteIdentifier(
    +            'title',
    +          )} = :a`,
    +          unsafeInjection: false,
    +          variables: [{ name: 'a', value: `'; DROP TABLE ${PostCollection.quotedTableName()}; --` }],
    +        },
    +      });
    +
    +      const post = await PostRepo.create({ values: { title: 't1' } });
    +
    +      await sleep(500);
    +
    +      const [execution] = await workflow.getExecutions();
    +      const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
    +      expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
    +
    +      // Table should still exist and data should be intact
    +      const remaining = await PostRepo.find();
    +      expect(remaining.length).toBeGreaterThanOrEqual(2);
    +    });
    +
    +    it('comment bypass (-- )', async () => {
    +      const queryInterface = db.sequelize.getQueryInterface();
    +      await PostRepo.create({ values: { title: 'secret' }, hooks: false });
    +
    +      const n1 = await workflow.createNode({
    +        type: 'sql',
    +        config: {
    +          sql: `select * from ${PostCollection.quotedTableName()} where ${queryInterface.quoteIdentifier(
    +            'title',
    +          )} = :a AND ${queryInterface.quoteIdentifier('published')} = true`,
    +          unsafeInjection: false,
    +          variables: [{ name: 'a', value: "secret' --" }],
    +        },
    +      });
    +
    +      const post = await PostRepo.create({ values: { title: 't1' } });
    +
    +      await sleep(500);
    +
    +      const [execution] = await workflow.getExecutions();
    +      const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
    +      expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
    +      expect(sqlJob.result.length).toBe(0);
    +    });
    +
    +    it('numeric parameter with dynamic variable', async () => {
    +      const queryInterface = db.sequelize.getQueryInterface();
    +
    +      const n1 = await workflow.createNode({
    +        type: 'sql',
    +        config: {
    +          sql: `select * from ${PostCollection.quotedTableName()} where ${queryInterface.quoteIdentifier('id')} = :a`,
    +          unsafeInjection: false,
    +          variables: [{ name: 'a', value: '{{$context.data.id}}' }],
    +        },
    +      });
    +
    +      const post = await PostRepo.create({ values: { title: 't1' } });
    +
    +      await sleep(500);
    +
    +      const [execution] = await workflow.getExecutions();
    +      const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
    +      expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
    +      expect(sqlJob.result.length).toBe(1);
    +      expect(sqlJob.result[0].id).toBe(post.id);
    +    });
    +  });
    +});
    

Vulnerability mechanics

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

References

5

News mentions

0

No linked articles in our index yet.