@dbx-app/node-core 0.4.5 → 0.4.6

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.
@@ -10,6 +10,7 @@ export interface ConnectionConfig {
10
10
  database?: string;
11
11
  url_params?: string;
12
12
  ssh_enabled: boolean;
13
+ ssh_tunnels?: SshTunnelConfig[];
13
14
  proxy_enabled?: boolean;
14
15
  proxy_type?: "socks5" | "http";
15
16
  proxy_host?: string;
@@ -26,6 +27,19 @@ export interface ConnectionConfig {
26
27
  redis_sentinel_password?: string;
27
28
  redis_sentinel_tls?: boolean;
28
29
  }
30
+ export interface SshTunnelConfig {
31
+ id: string;
32
+ name?: string;
33
+ enabled?: boolean;
34
+ host: string;
35
+ port: number;
36
+ user: string;
37
+ password?: string;
38
+ key_path?: string;
39
+ key_passphrase?: string;
40
+ connect_timeout_secs?: number;
41
+ expose_lan?: boolean;
42
+ }
29
43
  export interface ConnectionStoreOptions {
30
44
  path?: string;
31
45
  }
@@ -143,6 +143,7 @@ export async function addConnection(config) {
143
143
  ssh_key_path: "",
144
144
  ssh_key_passphrase: "",
145
145
  ssh_expose_lan: false,
146
+ ssh_tunnels: normalized.ssh_tunnels ?? [],
146
147
  proxy_enabled: normalized.proxy_enabled ?? false,
147
148
  proxy_type: normalized.proxy_type ?? "socks5",
148
149
  proxy_host: normalized.proxy_host ?? "",
package/dist/database.js CHANGED
@@ -731,7 +731,7 @@ function readChainedIntegerArgument(chain, method, fallback) {
731
731
  return Number(arg.trim());
732
732
  }
733
733
  function normalizeJsonArgument(arg) {
734
- const value = (arg.trim() || "{}").replace(/ObjectId\s*\(\s*["']([^"']+)["']\s*\)/g, '{"$oid":"$1"}');
734
+ const value = quoteUnquotedObjectKeys(convertSingleQuotedStrings((arg.trim() || "{}").replace(/ObjectId\s*\(\s*["']([^"']+)["']\s*\)/g, '{"$oid":"$1"}')));
735
735
  try {
736
736
  JSON.parse(value);
737
737
  return value;
@@ -740,6 +740,100 @@ function normalizeJsonArgument(arg) {
740
740
  return null;
741
741
  }
742
742
  }
743
+ function convertSingleQuotedStrings(source) {
744
+ let result = "";
745
+ let copiedUntil = 0;
746
+ let quote = null;
747
+ let start = 0;
748
+ let value = "";
749
+ let escaped = false;
750
+ for (let i = 0; i < source.length; i += 1) {
751
+ const char = source[i];
752
+ if (!quote) {
753
+ if (char === "'") {
754
+ quote = char;
755
+ start = i;
756
+ value = "";
757
+ escaped = false;
758
+ }
759
+ else if (char === '"') {
760
+ quote = char;
761
+ }
762
+ continue;
763
+ }
764
+ if (quote === '"') {
765
+ if (escaped)
766
+ escaped = false;
767
+ else if (char === "\\")
768
+ escaped = true;
769
+ else if (char === '"')
770
+ quote = null;
771
+ continue;
772
+ }
773
+ if (escaped) {
774
+ value += char;
775
+ escaped = false;
776
+ }
777
+ else if (char === "\\") {
778
+ escaped = true;
779
+ }
780
+ else if (char === "'") {
781
+ result += source.slice(copiedUntil, start) + JSON.stringify(value);
782
+ copiedUntil = i + 1;
783
+ quote = null;
784
+ }
785
+ else {
786
+ value += char;
787
+ }
788
+ }
789
+ return quote === "'" ? source : result + source.slice(copiedUntil);
790
+ }
791
+ function quoteUnquotedObjectKeys(source) {
792
+ let result = "";
793
+ let quote = null;
794
+ let escaped = false;
795
+ for (let i = 0; i < source.length; i += 1) {
796
+ const char = source[i];
797
+ if (quote) {
798
+ result += char;
799
+ if (escaped)
800
+ escaped = false;
801
+ else if (char === "\\")
802
+ escaped = true;
803
+ else if (char === quote)
804
+ quote = null;
805
+ continue;
806
+ }
807
+ if (char === '"' || char === "'") {
808
+ quote = char;
809
+ result += char;
810
+ continue;
811
+ }
812
+ if (/[A-Za-z_$]/.test(char) && shouldQuoteObjectKey(source, i)) {
813
+ let end = i + 1;
814
+ while (/[\w$]/.test(source[end] || ""))
815
+ end += 1;
816
+ result += `"${source.slice(i, end)}"`;
817
+ i = end - 1;
818
+ continue;
819
+ }
820
+ result += char;
821
+ }
822
+ return result;
823
+ }
824
+ function shouldQuoteObjectKey(source, index) {
825
+ let before = index - 1;
826
+ while (/\s/.test(source[before] || ""))
827
+ before -= 1;
828
+ if (source[before] !== "{" && source[before] !== ",")
829
+ return false;
830
+ let after = index + 1;
831
+ while (/[\w$]/.test(source[after] || ""))
832
+ after += 1;
833
+ while (/\s/.test(source[after] || ""))
834
+ after += 1;
835
+ return source[after] === ":";
836
+ }
743
837
  function isEmptyJsonObject(json) {
744
838
  try {
745
839
  const parsed = JSON.parse(json);
@@ -1,7 +1,7 @@
1
1
  export declare const DIRECT_QUERY_TYPES: readonly ["postgres", "redshift", "mysql", "doris", "starrocks", "sqlite", "gaussdb", "opengauss"];
2
2
  export type DirectQueryType = (typeof DIRECT_QUERY_TYPES)[number];
3
3
  export declare function isDirectQueryType(dbType: string): dbType is DirectQueryType;
4
- export declare const BRIDGE_REQUIRED_TYPES: readonly ["redis", "mongodb", "duckdb", "clickhouse", "sqlserver", "oracle", "elasticsearch", "dameng", "kingbase", "highgo", "vastbase", "goldendb", "yashandb", "databricks", "saphana", "teradata", "vertica", "firebird", "exasol", "oceanbase-oracle", "gbase", "tdengine", "h2", "snowflake", "trino", "hive", "db2", "informix", "neo4j", "cassandra", "bigquery", "kylin", "sundb", "xugu", "jdbc", "access"];
4
+ export declare const BRIDGE_REQUIRED_TYPES: readonly ["redis", "mongodb", "duckdb", "clickhouse", "sqlserver", "oracle", "elasticsearch", "dameng", "kingbase", "highgo", "vastbase", "goldendb", "yashandb", "databricks", "saphana", "teradata", "vertica", "firebird", "exasol", "oceanbase-oracle", "gbase", "tdengine", "h2", "snowflake", "trino", "hive", "db2", "informix", "iris", "neo4j", "cassandra", "bigquery", "kylin", "sundb", "xugu", "jdbc", "access"];
5
5
  export interface DbxDiagnostics {
6
6
  appDataDir: string;
7
7
  dbPath: string;
@@ -1,7 +1,16 @@
1
1
  import { access, readFile } from "node:fs/promises";
2
2
  import { bridgePortFilePath, dbPath, appDataDir } from "./paths.js";
3
3
  import { inspectConnectionStore } from "./connections.js";
4
- export const DIRECT_QUERY_TYPES = ["postgres", "redshift", "mysql", "doris", "starrocks", "sqlite", "gaussdb", "opengauss"];
4
+ export const DIRECT_QUERY_TYPES = [
5
+ "postgres",
6
+ "redshift",
7
+ "mysql",
8
+ "doris",
9
+ "starrocks",
10
+ "sqlite",
11
+ "gaussdb",
12
+ "opengauss",
13
+ ];
5
14
  const DIRECT_QUERY_TYPE_SET = new Set(DIRECT_QUERY_TYPES);
6
15
  export function isDirectQueryType(dbType) {
7
16
  return DIRECT_QUERY_TYPE_SET.has(dbType);
@@ -35,6 +44,7 @@ export const BRIDGE_REQUIRED_TYPES = [
35
44
  "hive",
36
45
  "db2",
37
46
  "informix",
47
+ "iris",
38
48
  "neo4j",
39
49
  "cassandra",
40
50
  "bigquery",
@@ -1,5 +1,15 @@
1
1
  const READ_KEYWORDS = new Set(["select", "with", "show", "describe", "desc", "explain"]);
2
2
  const DANGEROUS_KEYWORDS = new Set(["drop", "truncate", "alter"]);
3
+ function parseBooleanEnv(value) {
4
+ if (value === undefined)
5
+ return undefined;
6
+ const normalized = value.trim().toLowerCase();
7
+ if (normalized === "1" || normalized === "true")
8
+ return true;
9
+ if (normalized === "0" || normalized === "false")
10
+ return false;
11
+ return undefined;
12
+ }
3
13
  export function evaluateSqlSafety(sql, options = {}) {
4
14
  const statements = splitSqlStatements(sql);
5
15
  if (statements.length === 0)
@@ -33,7 +43,7 @@ function evaluateSingleSqlStatementSafety(sql, options = {}) {
33
43
  if (!options.allowWrites && !READ_KEYWORDS.has(firstKeyword)) {
34
44
  return {
35
45
  allowed: false,
36
- reason: "MCP SQL execution is read-only by default. Set DBX_MCP_ALLOW_WRITES=1 to allow write statements.",
46
+ reason: "MCP SQL execution is read-only for this session. Set DBX_MCP_ALLOW_WRITES=1 to allow write statements.",
37
47
  };
38
48
  }
39
49
  if (options.allowWrites && !options.allowDangerous) {
@@ -47,9 +57,11 @@ function evaluateSingleSqlStatementSafety(sql, options = {}) {
47
57
  return { allowed: true };
48
58
  }
49
59
  export function sqlSafetyFromEnv(env = process.env) {
60
+ const allowWrites = parseBooleanEnv(env.DBX_MCP_ALLOW_WRITES);
61
+ const allowDangerous = parseBooleanEnv(env.DBX_MCP_ALLOW_DANGEROUS_SQL);
50
62
  return {
51
- allowWrites: env.DBX_MCP_ALLOW_WRITES === "1" || env.DBX_MCP_ALLOW_WRITES === "true",
52
- allowDangerous: env.DBX_MCP_ALLOW_DANGEROUS_SQL === "1" || env.DBX_MCP_ALLOW_DANGEROUS_SQL === "true",
63
+ allowWrites: allowWrites ?? true,
64
+ allowDangerous: allowDangerous ?? false,
53
65
  };
54
66
  }
55
67
  export function splitSqlStatements(sql) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dbx-app/node-core",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
4
4
  "description": "Shared Node.js database and DBX connection utilities for DBX CLI and MCP server",
5
5
  "type": "module",
6
6
  "engines": {