Summary
NocoBase backup restore schema name allows command injection
NocoBase @nocobase/plugin-backups 2.0.57 restores PostgreSQL backups by interpolating the backup metadata schema name into shell command strings that are executed with Node.js child_process.exec(). A backup-management user who can restore an uploaded PostgreSQL backup with forced schema restore can place shell metacharacters in _metadata.json under database.schema, causing arbitrary commands to execute as the NocoBase server process during restore.
The vulnerable plugin is included in the default @nocobase/preset-nocobase package and is guarded by the backup-management ACL snippet (backups:* / backup:*). This is not unauthenticated; the attacker must have backup restore privileges or equivalent access to the restore API/CLI.
Details
Affected product evidence:
- Ecosystem/package: npm package
@nocobase/plugin-backupsfrompackages/plugins/@nocobase/plugin-backups/package.json. - Tested vulnerable version:
2.0.57(packages/plugins/@nocobase/plugin-backups/package.json:1-16). - Tested commit:
e03d267362b3426f484c28783020b4a2a08911e8. - Default/common inclusion:
@nocobase/preset-nocobasedepends on and lists@nocobase/plugin-backups2.0.57 as built in (packages/presets/nocobase/package.json:22-24,packages/presets/nocobase/package.json:115-128). - Affected range estimate: at least the tested
2.0.57checkout. Earlier/later versions were not tested. - Patched version: unknown/not available in this local checkout.
Source-to-sink path:
- The plugin registers backup-management snippets for
backups:*andbackup:*, so the restore API is intended for roles granted backup-management permissions (packages/plugins/@nocobase/plugin-backups/src/server/plugin.ts:51-59). - The
backuprestore-upload action accepts request body/queryforceand passes it asforceSchemaRestoretoRestoreManager.restore()(packages/plugins/@nocobase/plugin-backups/src/server/resourcers/backup-cli.ts:40-42,packages/plugins/@nocobase/plugin-backups/src/server/resourcers/backup-cli.ts:200-211). RestoreManagerdecompresses the uploaded backup archive, reads_metadata.json, and parses attacker-controlled JSON metadata (packages/plugins/@nocobase/plugin-backups/src/server/managers/restore.ts:203-215,packages/plugins/@nocobase/plugin-backups/src/server/managers/restore.ts:257-267).- When
forceSchemaRestoreis true and the database dialect is PostgreSQL, the schema-mismatch check is skipped (packages/plugins/@nocobase/plugin-backups/src/server/managers/restore.ts:270-300). Existing tests confirm forced schema restore intentionally allows a metadata schema mismatch (packages/plugins/@nocobase/plugin-backups/src/server/__tests__/managers/restore.test.ts:336-356) and that the API passesforceSchemaRestore: truewhenforce=trueis supplied (packages/plugins/@nocobase/plugin-backups/src/server/__tests__/managers/restore.test.ts:377-409). - The parsed
metadata.database.schemais passed intothis.#dbAdapter.restore(path.join(extractedDir, dbFile), metadata.database.schema)(packages/plugins/@nocobase/plugin-backups/src/server/managers/restore.ts:427-448). - For PostgreSQL, if the backup schema differs from the target schema,
PostgresAdapter.restore()assignssrcSchema = schema || 'public'and buildspgRestoreCommandusing-n ${srcSchema}with no quoting or argument array (packages/plugins/@nocobase/plugin-backups/src/server/adapters/database.ts:350-420). #restoreSchema()also interpolatessrcSchemaandtargetSchemadirectly into SQL strings and then callsrun(pgRestoreCommand, ...)(packages/plugins/@nocobase/plugin-backups/src/server/adapters/database.ts:423-451).run()executes the assembled string throughchild_process.exec(), which invokes a shell (packages/plugins/@nocobase/plugin-backups/src/server/adapters/database.ts:1-31).
A schema value such as safe; touch /tmp/nocobase-cve-marker # produces a restore command of this form:
pg_restore -U u -h localhost -p 5432 -n safe; touch /tmp/nocobase-cve-marker # -d db --clean --if-exists --no-owner -j 1 /tmp/backup-data
The semicolon terminates the intended pg_restore command and starts a new shell command.
False-positive screening:
- This report does not claim unauthenticated exploitation. The route is gated by backup-management permissions through the registered ACL snippet.
- The older
backups.uploadresource path was reviewed and does not passforceSchemaRestore; the directly confirmed force path is thebackup.restoreUpload/ backup-CLI API path (packages/plugins/@nocobase/plugin-backups/src/server/resourcers/backups.ts:54-77,packages/plugins/@nocobase/plugin-backups/src/server/resourcers/backup-cli.ts:200-211). - The schema mismatch check blocks mismatched metadata by default, but it is deliberately bypassed for PostgreSQL when the supported force option is true.
- The command injection is in the shell command itself before any PostgreSQL connection or valid backup file is required; the safe PoC proves shell metacharacter execution locally without connecting to a database.
- The finding is not based on existing reports or generated writeups.
PoC
The following local-only PoC renders the same vulnerable pg_restore command shape built by PostgresAdapter.restore() and executes it through Node.js child_process.exec(), the sink used by the plugin. It uses a harmless marker file under /tmp, does not contact external services, and cleans up after itself.
From a clean checkout of the tested commit:
cd nocobase
rm -f /tmp/nocobase-cve-marker
node - <<'NODE'
const { exec } = require('child_process');
const schemaFromBackupMetadata = 'safe; touch /tmp/nocobase-cve-marker #';
const command = `pg_restore -U u -h localhost -p 5432 -n ${schemaFromBackupMetadata} -d db --clean --if-exists --no-owner -j 1 /tmp/backup-data`;
exec(command, () => {
const fs = require('fs');
console.log(fs.existsSync('/tmp/nocobase-cve-marker') ? 'marker-created' : 'marker-missing');
fs.rmSync('/tmp/nocobase-cve-marker', { force: true });
});
NODE
Observed output in this environment:
marker-created
Expected vulnerable output: marker-created, proving the schema value starts a second shell command.
Negative/control case: replace schemaFromBackupMetadata with safe_schema; the same harness should print marker-missing because no shell metacharacter starts the touch command.
Maintainer-runnable application-level trigger:
- Run NocoBase 2.0.57 with PostgreSQL and the built-in
@nocobase/plugin-backupsenabled. - Use a role granted the backup-management snippet/actions (
backups:*/backup:*). - Create a NocoBase backup archive containing a
datamember and_metadata.jsonwith matching dialect/table settings butdatabase.schemaset tosafe; touch /tmp/nocobase-cve-marker #. - Restore the uploaded backup through the
backuprestore-upload path withforce=trueso PostgreSQL schema mismatch is allowed. - Vulnerable behavior:
/tmp/nocobase-cve-markerexists on the server after restore begins, even ifpg_restoreor database connection later fails. - Cleanup: remove
/tmp/nocobase-cve-markerand discard the test database/container.
Suggested remediation
Do not execute database tools through shell-interpreted command strings. Use spawn() or execFile() with an argument array for pg_restore, psql, pg_dump, mysql, and related tools. Validate PostgreSQL schema identifiers from backup metadata against PostgreSQL identifier rules or quote them using the database driver's identifier-quoting facilities before using them in SQL.
For this specific path:
- Pass
pg_restorearguments as an array, for example['-U', username, '-h', host, '-p', String(port), '-n', srcSchema, '-d', database, ...]. - Reject schema names containing shell metacharacters, quotes, whitespace, comments, or characters outside accepted PostgreSQL identifier syntax unless they are safely handled as identifiers.
- Replace dynamic SQL string interpolation in
#restoreSchema()with identifier-safe quoting (format('%I', ...)in PostgreSQL or equivalent server-side parameters) and string-literal escaping where literals are required. - Add regression tests that restore a backup whose metadata schema contains
; touch /tmp/should-not-exist #and assert no marker file is created and the request is rejected.
Impact
A user with backup restore privileges can execute arbitrary shell commands as the NocoBase server OS user. In a typical server or container deployment, this can read application configuration and environment secrets, modify application files or database backups, run network clients from the server, and disrupt service availability.
The required application privilege is high because backup restore is an administrative operation. However, backup-management permission is still an application-level role boundary; it should not imply arbitrary operating-system command execution.
Untrusted input reaches a shell command, allowing arbitrary commands to run on the host. Typical impact: code execution in the application's environment.
CVE-2026-55410 has a CVSS score of 6.7 (Medium). The vector is network-reachable, high privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (2.1.19); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Kodem Kai can prioritize this vulnerability in your dependency tree and generate a fix recommendation.
Frequently Asked Questions
- What is CVE-2026-55410? CVE-2026-55410 is a medium-severity OS command injection vulnerability in @nocobase/plugin-backups (npm), affecting versions < 2.1.19. It is fixed in 2.1.19. Untrusted input reaches a shell command, allowing arbitrary commands to run on the host.
- How severe is CVE-2026-55410? CVE-2026-55410 has a CVSS score of 6.7 (Medium). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of @nocobase/plugin-backups are affected by CVE-2026-55410? @nocobase/plugin-backups (npm) versions < 2.1.19 is affected.
- Is there a fix for CVE-2026-55410? Yes. CVE-2026-55410 is fixed in 2.1.19. Upgrade to this version or later.
- Is CVE-2026-55410 exploitable, and should I be worried? Whether CVE-2026-55410 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-55410 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-55410? Upgrade
@nocobase/plugin-backupsto 2.1.19 or later.