@neverinfamous/mysql-mcp 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +1 -0
- package/.gitattributes +18 -0
- package/.github/workflows/codeql.yml +2 -10
- package/.github/workflows/docker-publish.yml +15 -13
- package/CHANGELOG.md +287 -1
- package/DOCKER_README.md +100 -265
- package/Dockerfile +5 -0
- package/README.md +124 -59
- package/VERSION +1 -1
- package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
- package/dist/__tests__/mocks/adapter.js +2 -0
- package/dist/__tests__/mocks/adapter.js.map +1 -1
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
- package/dist/adapters/DatabaseAdapter.js +50 -9
- package/dist/adapters/DatabaseAdapter.js.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.d.ts +6 -0
- package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.js +8 -0
- package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
- package/dist/adapters/mysql/SchemaManager.js +16 -15
- package/dist/adapters/mysql/SchemaManager.js.map +1 -1
- package/dist/adapters/mysql/prompts/index.js +10 -20
- package/dist/adapters/mysql/prompts/index.js.map +1 -1
- package/dist/adapters/mysql/prompts/proxysqlSetup.js +1 -1
- package/dist/adapters/mysql/resources/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/docstore.js +10 -7
- package/dist/adapters/mysql/resources/docstore.js.map +1 -1
- package/dist/adapters/mysql/resources/events.js +11 -8
- package/dist/adapters/mysql/resources/events.js.map +1 -1
- package/dist/adapters/mysql/resources/indexes.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/indexes.js +12 -15
- package/dist/adapters/mysql/resources/indexes.js.map +1 -1
- package/dist/adapters/mysql/resources/innodb.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/innodb.js +20 -17
- package/dist/adapters/mysql/resources/innodb.js.map +1 -1
- package/dist/adapters/mysql/resources/locks.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/locks.js +9 -6
- package/dist/adapters/mysql/resources/locks.js.map +1 -1
- package/dist/adapters/mysql/resources/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/performance.js +15 -15
- package/dist/adapters/mysql/resources/performance.js.map +1 -1
- package/dist/adapters/mysql/resources/spatial.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/spatial.js +9 -6
- package/dist/adapters/mysql/resources/spatial.js.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.js +12 -9
- package/dist/adapters/mysql/resources/sysschema.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.js +170 -121
- package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.js +106 -57
- package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.js +183 -101
- package/dist/adapters/mysql/tools/admin/monitoring.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.js +164 -120
- package/dist/adapters/mysql/tools/cluster/group-replication.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +212 -145
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.js +6 -4
- package/dist/adapters/mysql/tools/codemode/index.js.map +1 -1
- package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/core.js +152 -29
- package/dist/adapters/mysql/tools/core.js.map +1 -1
- package/dist/adapters/mysql/tools/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/docstore.js +340 -163
- package/dist/adapters/mysql/tools/docstore.js.map +1 -1
- package/dist/adapters/mysql/tools/events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/events.js +284 -198
- package/dist/adapters/mysql/tools/events.js.map +1 -1
- package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/core.js +11 -39
- package/dist/adapters/mysql/tools/json/core.js.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.js +15 -33
- package/dist/adapters/mysql/tools/json/enhanced.js.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.js +13 -24
- package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.js +3 -0
- package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.js +89 -60
- package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.js +151 -127
- package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.js +289 -176
- package/dist/adapters/mysql/tools/proxysql.js.map +1 -1
- package/dist/adapters/mysql/tools/replication.js +75 -49
- package/dist/adapters/mysql/tools/replication.js.map +1 -1
- package/dist/adapters/mysql/tools/roles.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/roles.js +224 -182
- package/dist/adapters/mysql/tools/roles.js.map +1 -1
- package/dist/adapters/mysql/tools/router.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/router.js +168 -67
- package/dist/adapters/mysql/tools/router.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.js +21 -3
- package/dist/adapters/mysql/tools/schema/constraints.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.js +61 -14
- package/dist/adapters/mysql/tools/schema/management.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.js +27 -4
- package/dist/adapters/mysql/tools/schema/routines.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.js +24 -3
- package/dist/adapters/mysql/tools/schema/scheduled_events.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.js +23 -2
- package/dist/adapters/mysql/tools/schema/triggers.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.js +47 -7
- package/dist/adapters/mysql/tools/schema/views.js.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.js +102 -34
- package/dist/adapters/mysql/tools/security/audit.js.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.js +264 -205
- package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.js +137 -104
- package/dist/adapters/mysql/tools/security/encryption.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.js +71 -59
- package/dist/adapters/mysql/tools/shell/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.js +61 -47
- package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.js +19 -5
- package/dist/adapters/mysql/tools/spatial/geometry.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.js +42 -17
- package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.js +109 -57
- package/dist/adapters/mysql/tools/spatial/queries.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.js +103 -50
- package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.js +128 -79
- package/dist/adapters/mysql/tools/stats/comparative.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.js +174 -102
- package/dist/adapters/mysql/tools/stats/descriptive.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.js +50 -25
- package/dist/adapters/mysql/tools/sysschema/activity.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.js +121 -66
- package/dist/adapters/mysql/tools/sysschema/performance.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.js +101 -64
- package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.js +18 -32
- package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
- package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/transactions.js +48 -23
- package/dist/adapters/mysql/tools/transactions.js.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.d.ts +15 -0
- package/dist/adapters/mysql/types/proxysql-types.d.ts.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.js +33 -1
- package/dist/adapters/mysql/types/proxysql-types.js.map +1 -1
- package/dist/adapters/mysql/types/router-types.d.ts +1 -1
- package/dist/adapters/mysql/types/router-types.js +1 -1
- package/dist/adapters/mysql/types/router-types.js.map +1 -1
- package/dist/adapters/mysql/types/shell-types.js +2 -2
- package/dist/adapters/mysql/types/shell-types.js.map +1 -1
- package/dist/adapters/mysql/types.d.ts +485 -21
- package/dist/adapters/mysql/types.d.ts.map +1 -1
- package/dist/adapters/mysql/types.js +546 -19
- package/dist/adapters/mysql/types.js.map +1 -1
- package/dist/auth/scopes.js +1 -1
- package/dist/auth/scopes.js.map +1 -1
- package/dist/codemode/api.d.ts +3 -2
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/api.js +80 -5
- package/dist/codemode/api.js.map +1 -1
- package/dist/codemode/sandbox-factory.js +1 -1
- package/dist/codemode/sandbox-factory.js.map +1 -1
- package/dist/codemode/types.d.ts +26 -0
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/codemode/types.js +2 -0
- package/dist/codemode/types.js.map +1 -1
- package/dist/codemode/worker-sandbox.d.ts +4 -2
- package/dist/codemode/worker-sandbox.d.ts.map +1 -1
- package/dist/codemode/worker-sandbox.js +66 -7
- package/dist/codemode/worker-sandbox.js.map +1 -1
- package/dist/codemode/worker-script.d.ts +3 -0
- package/dist/codemode/worker-script.d.ts.map +1 -1
- package/dist/codemode/worker-script.js +128 -75
- package/dist/codemode/worker-script.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +37 -31
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +1 -1
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +1 -2
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/pool/ConnectionPool.d.ts.map +1 -1
- package/dist/pool/ConnectionPool.js.map +1 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +6 -0
- package/dist/transports/http.js.map +1 -1
- package/dist/utils/validators.d.ts +1 -1
- package/dist/utils/validators.d.ts.map +1 -1
- package/dist/utils/validators.js.map +1 -1
- package/package.json +4 -4
- package/releases/v2.3.0-release-notes.md +20 -20
- package/releases/v2.3.1-release-notes.md +34 -0
- package/releases/v3.0.0-release-notes.md +81 -0
- package/src/__tests__/mocks/adapter.ts +3 -0
- package/src/__tests__/perf.test.ts +6 -6
- package/src/adapters/DatabaseAdapter.ts +58 -9
- package/src/adapters/__tests__/DatabaseAdapter.test.ts +89 -8
- package/src/adapters/mysql/MySQLAdapter.ts +17 -2
- package/src/adapters/mysql/SchemaManager.ts +21 -21
- package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
- package/src/adapters/mysql/prompts/index.ts +12 -22
- package/src/adapters/mysql/prompts/proxysqlSetup.ts +1 -1
- package/src/adapters/mysql/resources/docstore.ts +13 -10
- package/src/adapters/mysql/resources/events.ts +12 -12
- package/src/adapters/mysql/resources/indexes.ts +17 -19
- package/src/adapters/mysql/resources/innodb.ts +23 -22
- package/src/adapters/mysql/resources/locks.ts +9 -7
- package/src/adapters/mysql/resources/performance.ts +23 -18
- package/src/adapters/mysql/resources/spatial.ts +9 -7
- package/src/adapters/mysql/resources/sysschema.ts +12 -11
- package/src/adapters/mysql/tools/__tests__/core.test.ts +126 -55
- package/src/adapters/mysql/tools/__tests__/docstore.test.ts +459 -88
- package/src/adapters/mysql/tools/__tests__/events.test.ts +281 -103
- package/src/adapters/mysql/tools/__tests__/proxysql.test.ts +128 -28
- package/src/adapters/mysql/tools/__tests__/replication.test.ts +48 -2
- package/src/adapters/mysql/tools/__tests__/roles.test.ts +15 -18
- package/src/adapters/mysql/tools/__tests__/router.test.ts +32 -5
- package/src/adapters/mysql/tools/__tests__/security.test.ts +126 -2
- package/src/adapters/mysql/tools/__tests__/security_injection.test.ts +84 -76
- package/src/adapters/mysql/tools/__tests__/security_integration.test.ts +47 -50
- package/src/adapters/mysql/tools/__tests__/spatial.test.ts +11 -10
- package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +54 -38
- package/src/adapters/mysql/tools/__tests__/stats.test.ts +285 -152
- package/src/adapters/mysql/tools/__tests__/transactions.test.ts +13 -13
- package/src/adapters/mysql/tools/admin/__tests__/backup.test.ts +171 -25
- package/src/adapters/mysql/tools/admin/__tests__/maintenance.test.ts +240 -4
- package/src/adapters/mysql/tools/admin/__tests__/monitoring-summary.test.ts +274 -0
- package/src/adapters/mysql/tools/admin/__tests__/monitoring.test.ts +94 -5
- package/src/adapters/mysql/tools/admin/backup.ts +193 -143
- package/src/adapters/mysql/tools/admin/maintenance.ts +118 -69
- package/src/adapters/mysql/tools/admin/monitoring.ts +201 -125
- package/src/adapters/mysql/tools/cluster/__tests__/group-replication.test.ts +69 -0
- package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +141 -0
- package/src/adapters/mysql/tools/cluster/group-replication.ts +172 -132
- package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +231 -157
- package/src/adapters/mysql/tools/codemode/__tests__/codemode-tool.test.ts +227 -0
- package/src/adapters/mysql/tools/codemode/index.ts +5 -3
- package/src/adapters/mysql/tools/core.ts +152 -38
- package/src/adapters/mysql/tools/docstore.ts +422 -205
- package/src/adapters/mysql/tools/events.ts +334 -233
- package/src/adapters/mysql/tools/json/__tests__/core.test.ts +20 -0
- package/src/adapters/mysql/tools/json/__tests__/enhanced.test.ts +82 -50
- package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +42 -3
- package/src/adapters/mysql/tools/json/core.ts +21 -42
- package/src/adapters/mysql/tools/json/enhanced.ts +22 -37
- package/src/adapters/mysql/tools/json/helpers.ts +21 -25
- package/src/adapters/mysql/tools/partitioning.ts +3 -0
- package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +98 -5
- package/src/adapters/mysql/tools/performance/__tests__/optimization-coverage.test.ts +515 -0
- package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +187 -0
- package/src/adapters/mysql/tools/performance/analysis.ts +95 -69
- package/src/adapters/mysql/tools/performance/optimization.ts +182 -153
- package/src/adapters/mysql/tools/proxysql.ts +314 -209
- package/src/adapters/mysql/tools/replication.ts +84 -57
- package/src/adapters/mysql/tools/roles.ts +274 -226
- package/src/adapters/mysql/tools/router.ts +181 -85
- package/src/adapters/mysql/tools/schema/__tests__/constraints.test.ts +13 -0
- package/src/adapters/mysql/tools/schema/__tests__/management.test.ts +60 -25
- package/src/adapters/mysql/tools/schema/__tests__/scheduled_events.test.ts +11 -0
- package/src/adapters/mysql/tools/schema/__tests__/triggers.test.ts +25 -4
- package/src/adapters/mysql/tools/schema/__tests__/views.test.ts +46 -14
- package/src/adapters/mysql/tools/schema/constraints.ts +22 -3
- package/src/adapters/mysql/tools/schema/management.ts +60 -15
- package/src/adapters/mysql/tools/schema/routines.ts +26 -4
- package/src/adapters/mysql/tools/schema/scheduled_events.ts +25 -3
- package/src/adapters/mysql/tools/schema/triggers.ts +27 -2
- package/src/adapters/mysql/tools/schema/views.ts +46 -8
- package/src/adapters/mysql/tools/security/__tests__/audit.test.ts +90 -4
- package/src/adapters/mysql/tools/security/audit.ts +113 -39
- package/src/adapters/mysql/tools/security/data-protection.ts +293 -233
- package/src/adapters/mysql/tools/security/encryption.ts +172 -139
- package/src/adapters/mysql/tools/shell/__tests__/backup.test.ts +29 -0
- package/src/adapters/mysql/tools/shell/backup.ts +90 -73
- package/src/adapters/mysql/tools/shell/restore.ts +62 -48
- package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +22 -14
- package/src/adapters/mysql/tools/spatial/__tests__/queries.test.ts +65 -51
- package/src/adapters/mysql/tools/spatial/geometry.ts +23 -7
- package/src/adapters/mysql/tools/spatial/operations.ts +60 -31
- package/src/adapters/mysql/tools/spatial/queries.ts +142 -65
- package/src/adapters/mysql/tools/spatial/setup.ts +121 -55
- package/src/adapters/mysql/tools/stats/__tests__/comparative.test.ts +12 -10
- package/src/adapters/mysql/tools/stats/comparative.ts +150 -98
- package/src/adapters/mysql/tools/stats/descriptive.ts +204 -127
- package/src/adapters/mysql/tools/sysschema/__tests__/error-paths.test.ts +222 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/performance.test.ts +45 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +6 -3
- package/src/adapters/mysql/tools/sysschema/activity.ts +52 -27
- package/src/adapters/mysql/tools/sysschema/performance.ts +132 -68
- package/src/adapters/mysql/tools/sysschema/resources.ts +105 -67
- package/src/adapters/mysql/tools/text/__tests__/fulltext.test.ts +45 -17
- package/src/adapters/mysql/tools/text/fulltext.ts +27 -38
- package/src/adapters/mysql/tools/transactions.ts +49 -24
- package/src/adapters/mysql/types/proxysql-types.ts +38 -1
- package/src/adapters/mysql/types/router-types.ts +1 -1
- package/src/adapters/mysql/types/shell-types.ts +2 -2
- package/src/adapters/mysql/types.ts +632 -19
- package/src/auth/__tests__/scopes.test.ts +2 -2
- package/src/auth/scopes.ts +1 -1
- package/src/codemode/__tests__/api.test.ts +417 -0
- package/src/codemode/__tests__/sandbox-factory.test.ts +158 -0
- package/src/codemode/__tests__/sandbox.test.ts +301 -0
- package/src/codemode/__tests__/security.test.ts +368 -0
- package/src/codemode/__tests__/worker-sandbox.test.ts +179 -0
- package/src/codemode/__tests__/worker-script.test.ts +226 -0
- package/src/codemode/api.ts +89 -5
- package/src/codemode/sandbox-factory.ts +1 -1
- package/src/codemode/types.ts +34 -0
- package/src/codemode/worker-sandbox.ts +74 -7
- package/src/codemode/worker-script.ts +157 -86
- package/src/constants/ServerInstructions.ts +37 -31
- package/src/filtering/ToolConstants.ts +1 -2
- package/src/filtering/__tests__/ToolFilter.test.ts +9 -9
- package/src/pool/ConnectionPool.ts +4 -1
- package/src/transports/__tests__/http.test.ts +15 -3
- package/src/transports/http.ts +12 -0
- package/src/utils/validators.ts +2 -1
- package/vitest.config.ts +3 -1
- package/CODE_MODE.md +0 -245
|
@@ -204,6 +204,21 @@ describe("Replication Handler Execution", () => {
|
|
|
204
204
|
expect(mockAdapter.executeQuery).toHaveBeenCalled();
|
|
205
205
|
expect(result).toBeDefined();
|
|
206
206
|
});
|
|
207
|
+
|
|
208
|
+
it("should return structured error on query failure", async () => {
|
|
209
|
+
mockAdapter.executeQuery.mockRejectedValue(
|
|
210
|
+
new Error("GTID not supported"),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const tool = tools.find((t) => t.name === "mysql_gtid_status")!;
|
|
214
|
+
const result = (await tool.handler({}, mockContext)) as {
|
|
215
|
+
success: boolean;
|
|
216
|
+
error: string;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
expect(result.success).toBe(false);
|
|
220
|
+
expect(result.error).toContain("Failed to retrieve GTID status");
|
|
221
|
+
});
|
|
207
222
|
});
|
|
208
223
|
|
|
209
224
|
describe("mysql_replication_lag", () => {
|
|
@@ -710,14 +725,18 @@ describe("Replication Fallback Handling", () => {
|
|
|
710
725
|
expect(result).toHaveProperty("status");
|
|
711
726
|
});
|
|
712
727
|
|
|
713
|
-
it("should return error when binary logging is disabled", async () => {
|
|
728
|
+
it("should return structured error when binary logging is disabled", async () => {
|
|
714
729
|
mockAdapter.executeQuery
|
|
715
730
|
.mockRejectedValueOnce(new Error("Unknown command"))
|
|
716
731
|
.mockRejectedValueOnce(new Error("Binary logging not enabled"));
|
|
717
732
|
|
|
718
733
|
const tool = tools.find((t) => t.name === "mysql_master_status")!;
|
|
719
|
-
const result = (await tool.handler({}, mockContext)) as {
|
|
734
|
+
const result = (await tool.handler({}, mockContext)) as {
|
|
735
|
+
success: boolean;
|
|
736
|
+
error: string;
|
|
737
|
+
};
|
|
720
738
|
|
|
739
|
+
expect(result.success).toBe(false);
|
|
721
740
|
expect(result.error).toContain("Binary logging");
|
|
722
741
|
});
|
|
723
742
|
});
|
|
@@ -812,6 +831,33 @@ describe("Replication Fallback Handling", () => {
|
|
|
812
831
|
});
|
|
813
832
|
});
|
|
814
833
|
|
|
834
|
+
describe("mysql_binlog_events limit:0 guard", () => {
|
|
835
|
+
it("should return empty events for limit: 0 without querying MySQL", async () => {
|
|
836
|
+
const tool = tools.find((t) => t.name === "mysql_binlog_events")!;
|
|
837
|
+
const result = (await tool.handler(
|
|
838
|
+
{ logFile: "mysql-bin.000001", limit: 0 },
|
|
839
|
+
mockContext,
|
|
840
|
+
)) as { events: unknown[] };
|
|
841
|
+
|
|
842
|
+
expect(result.events).toEqual([]);
|
|
843
|
+
// Should NOT have called executeQuery — guard returns before any SQL
|
|
844
|
+
expect(mockAdapter.executeQuery).not.toHaveBeenCalled();
|
|
845
|
+
});
|
|
846
|
+
|
|
847
|
+
it("should return structured error for negative limit", async () => {
|
|
848
|
+
const tool = tools.find((t) => t.name === "mysql_binlog_events")!;
|
|
849
|
+
const result = (await tool.handler(
|
|
850
|
+
{ logFile: "mysql-bin.000001", limit: -1 },
|
|
851
|
+
mockContext,
|
|
852
|
+
)) as { success: boolean; error: string };
|
|
853
|
+
|
|
854
|
+
expect(result.success).toBe(false);
|
|
855
|
+
expect(result.error).toBeDefined();
|
|
856
|
+
// Should NOT have called executeQuery — Zod rejects before any SQL
|
|
857
|
+
expect(mockAdapter.executeQuery).not.toHaveBeenCalled();
|
|
858
|
+
});
|
|
859
|
+
});
|
|
860
|
+
|
|
815
861
|
describe("mysql_replication_lag fallback", () => {
|
|
816
862
|
it("should return lag from replica status", async () => {
|
|
817
863
|
mockAdapter.executeQuery.mockResolvedValue(
|
|
@@ -108,11 +108,10 @@ describe("Handler Execution", () => {
|
|
|
108
108
|
expect(call).toContain("CREATE ROLE 'test_role'");
|
|
109
109
|
});
|
|
110
110
|
|
|
111
|
-
it("should
|
|
111
|
+
it("should return structured error for invalid role names", async () => {
|
|
112
112
|
const tool = tools.find((t) => t.name === "mysql_role_create")!;
|
|
113
|
-
await
|
|
114
|
-
|
|
115
|
-
).rejects.toThrow("Invalid role name");
|
|
113
|
+
const result = await tool.handler({ name: "invalid-role" }, mockContext);
|
|
114
|
+
expect(result).toEqual({ success: false, error: "Invalid role name" });
|
|
116
115
|
});
|
|
117
116
|
|
|
118
117
|
it("should return skipped when ifNotExists and role already exists", async () => {
|
|
@@ -170,14 +169,13 @@ describe("Handler Execution", () => {
|
|
|
170
169
|
);
|
|
171
170
|
});
|
|
172
171
|
|
|
173
|
-
it("should
|
|
172
|
+
it("should return structured error for invalid role names", async () => {
|
|
174
173
|
const tool = tools.find((t) => t.name === "mysql_role_grant")!;
|
|
175
|
-
await
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
).rejects.toThrow("Invalid role name");
|
|
174
|
+
const result = await tool.handler(
|
|
175
|
+
{ role: "invalid-role", privileges: ["SELECT"] },
|
|
176
|
+
mockContext,
|
|
177
|
+
);
|
|
178
|
+
expect(result).toEqual({ success: false, error: "Invalid role name" });
|
|
181
179
|
});
|
|
182
180
|
});
|
|
183
181
|
|
|
@@ -218,7 +216,7 @@ describe("Handler Execution", () => {
|
|
|
218
216
|
role: "test_role",
|
|
219
217
|
user: "testuser",
|
|
220
218
|
host: "localhost",
|
|
221
|
-
|
|
219
|
+
error:
|
|
222
220
|
"Role 'test_role' is not assigned to user 'testuser'@'localhost'",
|
|
223
221
|
});
|
|
224
222
|
expect(mockAdapter.rawQuery).not.toHaveBeenCalled();
|
|
@@ -249,11 +247,10 @@ describe("Handler Execution", () => {
|
|
|
249
247
|
expect(call).toContain("DROP ROLE 'test_role'");
|
|
250
248
|
});
|
|
251
249
|
|
|
252
|
-
it("should
|
|
250
|
+
it("should return structured error for invalid role names", async () => {
|
|
253
251
|
const tool = tools.find((t) => t.name === "mysql_role_drop")!;
|
|
254
|
-
await
|
|
255
|
-
|
|
256
|
-
).rejects.toThrow("Invalid role name");
|
|
252
|
+
const result = await tool.handler({ name: "invalid-role" }, mockContext);
|
|
253
|
+
expect(result).toEqual({ success: false, error: "Invalid role name" });
|
|
257
254
|
});
|
|
258
255
|
|
|
259
256
|
it("should return skipped when ifExists and role does not exist", async () => {
|
|
@@ -367,7 +364,7 @@ describe("Handler Execution", () => {
|
|
|
367
364
|
|
|
368
365
|
expect(result).toEqual({
|
|
369
366
|
success: false,
|
|
370
|
-
|
|
367
|
+
error: "Role 'test_role' already exists",
|
|
371
368
|
});
|
|
372
369
|
});
|
|
373
370
|
});
|
|
@@ -386,7 +383,7 @@ describe("Handler Execution", () => {
|
|
|
386
383
|
|
|
387
384
|
expect(result).toEqual({
|
|
388
385
|
success: false,
|
|
389
|
-
|
|
386
|
+
error: "Role 'test_role' does not exist",
|
|
390
387
|
});
|
|
391
388
|
});
|
|
392
389
|
});
|
|
@@ -475,7 +475,7 @@ describe("Error Handling", () => {
|
|
|
475
475
|
|
|
476
476
|
expect(result).toEqual({
|
|
477
477
|
available: false,
|
|
478
|
-
|
|
478
|
+
error: "Router API error: 401 Unauthorized",
|
|
479
479
|
});
|
|
480
480
|
});
|
|
481
481
|
|
|
@@ -510,8 +510,8 @@ describe("Error Handling", () => {
|
|
|
510
510
|
);
|
|
511
511
|
|
|
512
512
|
expect(result).toEqual({
|
|
513
|
-
|
|
514
|
-
|
|
513
|
+
success: false,
|
|
514
|
+
error: "Router API error: 404 Not Found",
|
|
515
515
|
});
|
|
516
516
|
});
|
|
517
517
|
|
|
@@ -535,7 +535,7 @@ describe("Error Handling", () => {
|
|
|
535
535
|
|
|
536
536
|
expect(result).toEqual({
|
|
537
537
|
available: false,
|
|
538
|
-
|
|
538
|
+
error: "Router API request failed: Network error",
|
|
539
539
|
});
|
|
540
540
|
});
|
|
541
541
|
|
|
@@ -561,11 +561,38 @@ describe("Error Handling", () => {
|
|
|
561
561
|
|
|
562
562
|
expect(result).toEqual({
|
|
563
563
|
available: false,
|
|
564
|
-
|
|
564
|
+
error: expect.stringContaining("Connection refused"),
|
|
565
565
|
});
|
|
566
566
|
});
|
|
567
567
|
});
|
|
568
568
|
|
|
569
|
+
describe("Zod Validation Error Handling", () => {
|
|
570
|
+
let tools: ReturnType<typeof getRouterTools>;
|
|
571
|
+
let mockContext: ReturnType<typeof createMockRequestContext>;
|
|
572
|
+
|
|
573
|
+
beforeEach(() => {
|
|
574
|
+
vi.clearAllMocks();
|
|
575
|
+
tools = getRouterTools(createMockMySQLAdapter() as unknown as MySQLAdapter);
|
|
576
|
+
mockContext = createMockRequestContext();
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("should return structured error for invalid routeName type", async () => {
|
|
580
|
+
const tool = tools.find((t) => t.name === "mysql_router_route_status")!;
|
|
581
|
+
const result = await tool.handler({ routeName: 123 }, mockContext);
|
|
582
|
+
|
|
583
|
+
expect(result).toHaveProperty("success", false);
|
|
584
|
+
expect(result).toHaveProperty("error");
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
it("should return structured error for invalid metadataName type", async () => {
|
|
588
|
+
const tool = tools.find((t) => t.name === "mysql_router_metadata_status")!;
|
|
589
|
+
const result = await tool.handler({ metadataName: true }, mockContext);
|
|
590
|
+
|
|
591
|
+
expect(result).toHaveProperty("success", false);
|
|
592
|
+
expect(result).toHaveProperty("error");
|
|
593
|
+
});
|
|
594
|
+
});
|
|
595
|
+
|
|
569
596
|
describe("Authentication and TLS Handling", () => {
|
|
570
597
|
let originalEnv: NodeJS.ProcessEnv;
|
|
571
598
|
|
|
@@ -101,8 +101,19 @@ describe("Security Tools", () => {
|
|
|
101
101
|
const tool = tools.find((t) => t.name === "mysql_security_audit");
|
|
102
102
|
const result = (await tool?.handler({}, mockContext)) as any;
|
|
103
103
|
|
|
104
|
+
expect(result.success).toBe(false);
|
|
105
|
+
expect(result.error).toBe("Connect error");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("should include error field in access-denied response", async () => {
|
|
109
|
+
mockAdapter.executeQuery.mockRejectedValue(new Error("Access denied"));
|
|
110
|
+
|
|
111
|
+
const tool = tools.find((t) => t.name === "mysql_security_audit");
|
|
112
|
+
const result = (await tool?.handler({}, mockContext)) as any;
|
|
113
|
+
|
|
114
|
+
expect(result.success).toBe(false);
|
|
115
|
+
expect(result.error).toContain("Audit logging is not enabled");
|
|
104
116
|
expect(result.available).toBe(false);
|
|
105
|
-
expect(result.message).toContain("Audit logging is not enabled");
|
|
106
117
|
});
|
|
107
118
|
});
|
|
108
119
|
|
|
@@ -153,7 +164,9 @@ describe("Security Tools", () => {
|
|
|
153
164
|
);
|
|
154
165
|
const result = (await tool?.handler({}, mockContext)) as any;
|
|
155
166
|
|
|
167
|
+
expect(result.success).toBe(false);
|
|
156
168
|
expect(result.available).toBe(false);
|
|
169
|
+
expect(result.error).toContain("Firewall tables not accessible");
|
|
157
170
|
});
|
|
158
171
|
});
|
|
159
172
|
|
|
@@ -200,8 +213,9 @@ describe("Security Tools", () => {
|
|
|
200
213
|
);
|
|
201
214
|
const result = (await tool?.handler({}, mockContext)) as any;
|
|
202
215
|
|
|
216
|
+
expect(result.success).toBe(false);
|
|
203
217
|
expect(result.installed).toBe(false);
|
|
204
|
-
expect(result.
|
|
218
|
+
expect(result.error).toContain("Firewall plugin check failed");
|
|
205
219
|
});
|
|
206
220
|
});
|
|
207
221
|
|
|
@@ -501,6 +515,27 @@ describe("Security Tools", () => {
|
|
|
501
515
|
expect(result.sslEnabled).toBe(false);
|
|
502
516
|
expect(result.currentCipher).toBe("None");
|
|
503
517
|
});
|
|
518
|
+
|
|
519
|
+
it("should return 'None' for empty cipher string", async () => {
|
|
520
|
+
// Status with empty Ssl_cipher (SSL disabled but variable present)
|
|
521
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(
|
|
522
|
+
createMockQueryResult([
|
|
523
|
+
{ Variable_name: "Ssl_cipher", Value: "" },
|
|
524
|
+
{ Variable_name: "Ssl_version", Value: "" },
|
|
525
|
+
]),
|
|
526
|
+
);
|
|
527
|
+
// Variables
|
|
528
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
|
|
529
|
+
// Connection
|
|
530
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
|
|
531
|
+
|
|
532
|
+
const tool = tools.find((t) => t.name === "mysql_security_ssl_status");
|
|
533
|
+
const result = (await tool?.handler({}, mockContext)) as any;
|
|
534
|
+
|
|
535
|
+
expect(result.sslEnabled).toBe(false);
|
|
536
|
+
expect(result.currentCipher).toBe("None");
|
|
537
|
+
expect(result.sslVersion).toBe("N/A");
|
|
538
|
+
});
|
|
504
539
|
});
|
|
505
540
|
|
|
506
541
|
describe("mysql_security_user_privileges", () => {
|
|
@@ -788,4 +823,93 @@ describe("Security Tools", () => {
|
|
|
788
823
|
expect(result.encryptionSettings).toEqual({ "": "invalid" });
|
|
789
824
|
});
|
|
790
825
|
});
|
|
826
|
+
|
|
827
|
+
describe("Error handling (try/catch wrapping)", () => {
|
|
828
|
+
it("mysql_security_ssl_status should return structured error on query failure", async () => {
|
|
829
|
+
mockAdapter.executeQuery.mockRejectedValue(new Error("Access denied"));
|
|
830
|
+
|
|
831
|
+
const tool = tools.find((t) => t.name === "mysql_security_ssl_status");
|
|
832
|
+
const result = (await tool?.handler({}, mockContext)) as any;
|
|
833
|
+
|
|
834
|
+
expect(result.success).toBe(false);
|
|
835
|
+
expect(result.error).toBe("Access denied");
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it("mysql_security_encryption_status should return structured error on query failure", async () => {
|
|
839
|
+
mockAdapter.executeQuery.mockRejectedValue(
|
|
840
|
+
new Error("Query failed: Execute failed: Access denied"),
|
|
841
|
+
);
|
|
842
|
+
|
|
843
|
+
const tool = tools.find(
|
|
844
|
+
(t) => t.name === "mysql_security_encryption_status",
|
|
845
|
+
);
|
|
846
|
+
const result = (await tool?.handler({}, mockContext)) as any;
|
|
847
|
+
|
|
848
|
+
expect(result.success).toBe(false);
|
|
849
|
+
expect(result.error).toBe("Access denied");
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it("mysql_security_user_privileges should return structured error on query failure", async () => {
|
|
853
|
+
mockAdapter.executeQuery.mockRejectedValue(
|
|
854
|
+
new Error("Query failed: Access denied to mysql.user"),
|
|
855
|
+
);
|
|
856
|
+
|
|
857
|
+
const tool = tools.find(
|
|
858
|
+
(t) => t.name === "mysql_security_user_privileges",
|
|
859
|
+
);
|
|
860
|
+
const result = (await tool?.handler({}, mockContext)) as any;
|
|
861
|
+
|
|
862
|
+
expect(result.success).toBe(false);
|
|
863
|
+
expect(result.error).toBe("Access denied to mysql.user");
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
it("mysql_security_sensitive_tables should return structured error on query failure", async () => {
|
|
867
|
+
mockAdapter.executeQuery.mockRejectedValue(
|
|
868
|
+
new Error("Execute failed: Connection lost"),
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
const tool = tools.find(
|
|
872
|
+
(t) => t.name === "mysql_security_sensitive_tables",
|
|
873
|
+
);
|
|
874
|
+
const result = (await tool?.handler({}, mockContext)) as any;
|
|
875
|
+
|
|
876
|
+
expect(result.success).toBe(false);
|
|
877
|
+
expect(result.error).toBe("Connection lost");
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
it("mysql_security_user_privileges should use backtick-quoted identifiers in SHOW GRANTS", async () => {
|
|
881
|
+
// Setup: one user returned
|
|
882
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(
|
|
883
|
+
createMockQueryResult([
|
|
884
|
+
{
|
|
885
|
+
User: "test_user",
|
|
886
|
+
Host: "localhost",
|
|
887
|
+
plugin: "caching_sha2_password",
|
|
888
|
+
account_locked: "N",
|
|
889
|
+
},
|
|
890
|
+
]),
|
|
891
|
+
);
|
|
892
|
+
// SHOW GRANTS
|
|
893
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(
|
|
894
|
+
createMockQueryResult([
|
|
895
|
+
{
|
|
896
|
+
"Grants for test_user@localhost":
|
|
897
|
+
"GRANT USAGE ON *.* TO `test_user`@`localhost`",
|
|
898
|
+
},
|
|
899
|
+
]),
|
|
900
|
+
);
|
|
901
|
+
// Role edges
|
|
902
|
+
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
|
|
903
|
+
|
|
904
|
+
const tool = tools.find(
|
|
905
|
+
(t) => t.name === "mysql_security_user_privileges",
|
|
906
|
+
);
|
|
907
|
+
await tool?.handler({}, mockContext);
|
|
908
|
+
|
|
909
|
+
// The SHOW GRANTS call is the second call (index 1)
|
|
910
|
+
const grantsCall = mockAdapter.executeQuery.mock.calls[1][0] as string;
|
|
911
|
+
expect(grantsCall).toContain("`test_user`@`localhost`");
|
|
912
|
+
expect(grantsCall).not.toContain("'test_user'@'localhost'");
|
|
913
|
+
});
|
|
914
|
+
});
|
|
791
915
|
});
|
|
@@ -28,94 +28,100 @@ describe("Security: SQL Injection Prevention", () => {
|
|
|
28
28
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
29
29
|
const exportTool = tools.find((t) => t.name === "mysql_export_table")!;
|
|
30
30
|
|
|
31
|
-
await
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
).
|
|
31
|
+
const result = (await exportTool.handler(
|
|
32
|
+
{
|
|
33
|
+
table: "users; DROP TABLE users",
|
|
34
|
+
format: "SQL",
|
|
35
|
+
},
|
|
36
|
+
mockContext,
|
|
37
|
+
)) as { success: boolean; error: string };
|
|
38
|
+
|
|
39
|
+
expect(result.success).toBe(false);
|
|
40
|
+
expect(result.error).toContain("Invalid table name");
|
|
40
41
|
});
|
|
41
42
|
|
|
42
43
|
it("should reject table name starting with number", async () => {
|
|
43
44
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
44
45
|
const exportTool = tools.find((t) => t.name === "mysql_export_table")!;
|
|
45
46
|
|
|
46
|
-
await
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
).
|
|
47
|
+
const result = (await exportTool.handler(
|
|
48
|
+
{
|
|
49
|
+
table: "123users",
|
|
50
|
+
format: "SQL",
|
|
51
|
+
},
|
|
52
|
+
mockContext,
|
|
53
|
+
)) as { success: boolean; error: string };
|
|
54
|
+
|
|
55
|
+
expect(result.success).toBe(false);
|
|
56
|
+
expect(result.error).toContain("Invalid table name");
|
|
55
57
|
});
|
|
56
58
|
|
|
57
59
|
it("should reject WHERE clause with stacked queries", async () => {
|
|
58
60
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
59
61
|
const exportTool = tools.find((t) => t.name === "mysql_export_table")!;
|
|
60
62
|
|
|
61
|
-
await
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
).
|
|
63
|
+
const result = (await exportTool.handler(
|
|
64
|
+
{
|
|
65
|
+
table: "users",
|
|
66
|
+
format: "SQL",
|
|
67
|
+
where: "1=1; DROP TABLE users",
|
|
68
|
+
},
|
|
69
|
+
mockContext,
|
|
70
|
+
)) as { success: boolean; error: string };
|
|
71
|
+
|
|
72
|
+
expect(result.success).toBe(false);
|
|
73
|
+
expect(result.error).toContain("dangerous SQL patterns");
|
|
71
74
|
});
|
|
72
75
|
|
|
73
76
|
it("should reject WHERE clause with UNION attack", async () => {
|
|
74
77
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
75
78
|
const exportTool = tools.find((t) => t.name === "mysql_export_table")!;
|
|
76
79
|
|
|
77
|
-
await
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
).
|
|
80
|
+
const result = (await exportTool.handler(
|
|
81
|
+
{
|
|
82
|
+
table: "users",
|
|
83
|
+
format: "SQL",
|
|
84
|
+
where: "1=1 UNION SELECT password FROM admin",
|
|
85
|
+
},
|
|
86
|
+
mockContext,
|
|
87
|
+
)) as { success: boolean; error: string };
|
|
88
|
+
|
|
89
|
+
expect(result.success).toBe(false);
|
|
90
|
+
expect(result.error).toContain("dangerous SQL patterns");
|
|
87
91
|
});
|
|
88
92
|
|
|
89
93
|
it("should reject WHERE clause with timing attack (SLEEP)", async () => {
|
|
90
94
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
91
95
|
const exportTool = tools.find((t) => t.name === "mysql_export_table")!;
|
|
92
96
|
|
|
93
|
-
await
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
).
|
|
97
|
+
const result = (await exportTool.handler(
|
|
98
|
+
{
|
|
99
|
+
table: "users",
|
|
100
|
+
format: "SQL",
|
|
101
|
+
where: "1=1 AND SLEEP(5)",
|
|
102
|
+
},
|
|
103
|
+
mockContext,
|
|
104
|
+
)) as { success: boolean; error: string };
|
|
105
|
+
|
|
106
|
+
expect(result.success).toBe(false);
|
|
107
|
+
expect(result.error).toContain("dangerous SQL patterns");
|
|
103
108
|
});
|
|
104
109
|
|
|
105
110
|
it("should reject WHERE clause with unbalanced quotes", async () => {
|
|
106
111
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
107
112
|
const exportTool = tools.find((t) => t.name === "mysql_export_table")!;
|
|
108
113
|
|
|
109
|
-
await
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
).
|
|
114
|
+
const result = (await exportTool.handler(
|
|
115
|
+
{
|
|
116
|
+
table: "users",
|
|
117
|
+
format: "SQL",
|
|
118
|
+
where: "id = 1 OR '1'='1",
|
|
119
|
+
},
|
|
120
|
+
mockContext,
|
|
121
|
+
)) as { success: boolean; error: string };
|
|
122
|
+
|
|
123
|
+
expect(result.success).toBe(false);
|
|
124
|
+
expect(result.error).toContain("unbalanced");
|
|
119
125
|
});
|
|
120
126
|
|
|
121
127
|
it("should accept valid table and WHERE", async () => {
|
|
@@ -142,30 +148,32 @@ describe("Security: SQL Injection Prevention", () => {
|
|
|
142
148
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
143
149
|
const importTool = tools.find((t) => t.name === "mysql_import_data")!;
|
|
144
150
|
|
|
145
|
-
await
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
).
|
|
151
|
+
const result = (await importTool.handler(
|
|
152
|
+
{
|
|
153
|
+
table: "users`; DROP TABLE users; --",
|
|
154
|
+
data: [{ id: 1, name: "test" }],
|
|
155
|
+
},
|
|
156
|
+
mockContext,
|
|
157
|
+
)) as { success: boolean; error: string };
|
|
158
|
+
|
|
159
|
+
expect(result.success).toBe(false);
|
|
160
|
+
expect(result.error).toContain("Invalid table name");
|
|
154
161
|
});
|
|
155
162
|
|
|
156
163
|
it("should reject column name with injection", async () => {
|
|
157
164
|
const tools = getBackupTools(mockAdapter as unknown as MySQLAdapter);
|
|
158
165
|
const importTool = tools.find((t) => t.name === "mysql_import_data")!;
|
|
159
166
|
|
|
160
|
-
await
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
).
|
|
167
|
+
const result = (await importTool.handler(
|
|
168
|
+
{
|
|
169
|
+
table: "users",
|
|
170
|
+
data: [{ "id; DROP TABLE": 1 }],
|
|
171
|
+
},
|
|
172
|
+
mockContext,
|
|
173
|
+
)) as { success: boolean; error: string };
|
|
174
|
+
|
|
175
|
+
expect(result.success).toBe(false);
|
|
176
|
+
expect(result.error).toContain("Invalid column name");
|
|
169
177
|
});
|
|
170
178
|
});
|
|
171
179
|
|