genai-toolbox
- Type
- MCP server
- Scanned
- Oct 29, 2026
- Version
- 494092a9bd98
- Files read
- 2015
MCP Toolbox for Databases is a server by Google that connects AI agents to enterprise databases, cloud storage, and analytics services using prebuilt or custom tool configurations. The biggest risks are tools that allow unrestricted reading or writing of local files, database tools vulnerable to query injection and read-only bypasses, and permissive default web settings that let websites interact with a locally running server.
What it can reach
- Network
- Listens as a local HTTP or STDIO server, connects to configured databases and cloud APIs (including Google Cloud services, PostgreSQL, MySQL, SQLite, MongoDB, Neo4j, Snowflake, and Looker), contacts OAuth and telemetry endpoints, and checks GitHub releases on startup.
- Files
- Reads YAML configuration files, TLS certificates, local SQLite databases, and configured file templates, and can read or write local files through Cloud Storage upload and download tools.
- Shell commands
- Executes the external dataform CLI binary when the local Dataform compilation tool is used, and includes maintainer skill files that instruct AI agents to run git, gh, hugo, and go test commands.
- Secrets
- Reads database passwords, API keys, Google Application Default Credentials, and OAuth or JWT tokens from environment variables, configuration files, and HTTP headers.
Findings
-
HighUnrestricted local file read and write in Cloud Storage upload and download tools
internal/sources/cloudstorage/cloudstorage.go:121
The
cloud-storage-upload-objectandcloud-storage-download-objecttools accept arbitrary absolute local filesystem paths via thesourceanddestinationtool arguments. Unless the user explicitly configuresallowedLocalRootson the source (ordestination_diron the download tool),validateLocalPathpermits any absolute path on the machine. Through prompt injection, an attacker can upload sensitive local files (such as~/.ssh/id_rsaor cloud credentials) to a bucket to read them back, or write/overwrite arbitrary files on the user's filesystem.Fix: Require
allowedLocalRoots(ordestination_dir/source_dir) to be configured before enabling local filesystem upload and download tools, or reject local file operations when no root directory allowlist is set. -
HighURI template prefix bypass in file resource templates when allowedPaths is omitted
internal/resources/file/file.go:627
When a URI is matched against a
fileresourceTemplate (such asfile:///var/log/{path}),MatchResourceTemplateURIextracts{path}using(.*). If a caller requests a URI with a leading slash in the{path}segment (for example,file:///var/log//etc/config.yaml),pathStris/etc/config.yaml. BecauseFileTemplate.Readonly interpolatespathStrintor.URITemplatewhen!filepath.IsAbs(pathStr), it discards the template's base path prefix/var/log/and resolves/etc/config.yamlfrom the filesystem root. IfallowedPathswas not explicitly set, any non-hidden file matching the allowed extensions (.yaml,.json,.py,.sql,.txt,.csv, etc.) on the machine can be read.Fix: Always resolve
{path}relative to the path prefix defined inr.URITemplate(or strip leading slashes before interpolating intor.URITemplate), and requirepathStrto remain within the template's base directory even whenallowedPathsis not specified. -
HighRead-only Cypher classifier bypass via comments inside string literals and quoted procedure names
internal/tools/neo4j/neo4jexecutecypher/classifier/classifier.go:199
When
neo4j-execute-cypheris configured withreadOnly: true, it relies onQueryClassifier.Classifyto reject write queries. However,ClassifycallsnormalizeQuery(which strips//and/* ... */comments) before stripping string literals. An attacker can place/*and*/inside two string literals around a destructive clause (for example,MATCH (n) WHERE n.name <> '/*' DETACH DELETE n RETURN '*/'); the classifier strips theDETACH DELETEas a comment and classifies the query asREAD, while Neo4j executes the deletion. Additionally,procedureCallPatternonly matches unquoted procedure identifiers (\bCALL\s+([a-zA-Z0-9_.]+)), so backtick-quoted procedure calls such as `CALLapoc.create.node(...)` bypass write-procedure detection.Fix: Tokenize string literals, backtick-quoted identifiers, and comments in a single lexical pass rather than running regex comment removal before string-literal removal, and enforce read-only access using Neo4j's native
neo4j.AccessModeReadsession configuration. -
HighSQL injection in prebuilt SQLite list_tables tool via unescaped template parameters
internal/prebuiltconfigs/tools/sqlite.yaml:101
The prebuilt
list_tablestool for SQLite declaresoutput_formatandtable_namesastemplateParametersinstead of bound SQLparameters, and interpolates them directly inside single quotes ('{{.output_format}}'and'{{.table_names}}'). Becausemodernc.org/sqlitesupports multi-statement execution, an attacker who controls tool arguments via prompt injection can break out of the single-quoted literal inlist_tablesand execute arbitrary SQL statements, includingDROP TABLEorATTACH DATABASEto write files to disk.Fix: Replace
templateParameterswith standard boundparameters(?) ininternal/prebuiltconfigs/tools/sqlite.yaml, matching the MySQL, Postgres, and MSSQLlist_tablesimplementations. -
HighSQL injection in prebuilt MindsDB parameterized_sql tool via template parameters
internal/prebuiltconfigs/tools/mindsdb.yaml:39
The prebuilt
parameterized_sqltool inmindsdb.yamlinterpolates{{.table_name}}and{{.condition_column}}as unescaped stringtemplateParametersdirectly into the SQL statement. An attacker can supply arbitrary SQL expressions orUNION SELECTpayloads intable_nameorcondition_columnto read or modify unauthorized data.Fix: Validate identifier template parameters or apply identifier escaping on
table_nameandcondition_column. -
MediumPath traversal across stores and datasets in Cloud Healthcare tools
internal/sources/cloudhealthcare/cloud_healthcare.go:486
In
GetFHIRResource,FHIRPatientEverything,RetrieveRenderedDICOMInstance, andSearchDICOM, tool arguments such asresourceType,resourceID,patientID, and DICOM UIDs are concatenated directly into the resource name string without URL-escaping or rejecting..and/characters. Because the Google Cloud Healthcare client expands{+name}without encoding slashes, a prompt-injected argument containing../segments can traverse out of the intended resource path and bypassallowedFhirStoresorallowedDicomStoresrestrictions.Fix: Validate that resource IDs, types, store IDs, and DICOM UIDs do not contain
/or..segments and applyurl.PathEscapeto each individual path segment. -
MediumUnrestricted directory path and argument injection in dataform-compile-local tool
internal/tools/dataform/dataformcompilelocal/dataformcompilelocal.go:103
The
dataform-compile-localtool passes the agent-suppliedproject_dirargument directly toexec.CommandContext(ctx, "dataform", "compile", projectDir, "--json"). There is no check preventingproject_dirfrom starting with-(allowing CLI flag injection) or confiningproject_dirto an allowed workspace directory. Furthermore, becausedataform compileexecutes JavaScript files within the target project directory, pointing it at an untrusted directory can execute arbitrary code.Fix: Ensure
project_dirdoes not start with-(or pass"--"beforeprojectDir) and restrictproject_dirto a configured base directory. -
MediumRDF injection via unescaped parameter substitution in Dgraph mutations
internal/sources/dgraph/dgraph.go:420
When executing a Dgraph mutation (
isQuery: false),embedParamsIntoMutationsubstitutes user parameters into the mutation payload usingstrings.ReplaceAll(mutation, key, fmt.Sprintf("%v", value))without escaping double quotes, backslashes, or newlines. An attacker controlling a tool parameter can break out of the quoted value and inject arbitrary RDF triples into the Dgraph database.Fix: Properly escape string values (e.g., using
strconv.Quoteor JSON/RDF string escaping) before embedding them into the mutation payload. -
MediumPermissive default CORS and Host allowlists in HTTP server mode
When running as an HTTP server (the default mode on
127.0.0.1:5000), both--allowed-originsand--allowed-hostsdefault to*while CORSAllowCredentialsis set totrue. If authentication is not configured, any website visited by the user in a local browser can send cross-origin requests or use DNS rebinding to invoke MCP tools onlocalhost:5000.Fix: Default
--allowed-hoststo127.0.0.1/localhostand restrict--allowed-originsby default instead of using wildcard*. -
MediumPrebuilt PostgreSQL and AlloyDB get_query_plan tools interpolate unescaped SQL via templateParameters
internal/prebuiltconfigs/tools/alloydb-postgres.yaml:188
In
alloydb-postgres.yaml(as well asalloydb-omni.yaml,cloud-sql-postgres.yaml, andpostgres.yaml), theget_query_plantool interpolates{{.query}}directly intoEXPLAIN (FORMAT JSON) {{.query}};usingtemplateParameters, and is included in read-only monitoring groups (monitorandperformance). Inalloydb-postgres.yaml, the description explicitly states 'Use in production safely', even though{{.query}}is raw text substitution that allows multi-statement execution ifsimple_protocolis used or arbitraryEXPLAIN ANALYZEoptions if injected before the query.Fix: Implement a dedicated
postgres-get-query-plantool that validates single-statement input and runs inside a read-only transaction, and remove the claim that raw template interpolation is safe for production. -
MediumSecond-order Cypher injection in Neo4j schema extraction when without APOC
internal/tools/neo4j/neo4jschema/neo4jschema.go:368
In
GetSchemaWithoutAPOC, node labels and relationship types retrieved from the database are interpolated directly into Cypher queries usingfmt.Sprintfwithout backtick quoting or escaping (MATCH (n:%s)andMATCH ()-[r:%s]->()). Unlikefalkordbschema.go, which escapes backticks and wraps identifiers in backticks,neo4jschema.goembeds the raw string in a write-mode session, allowing labels containing spaces or Cypher syntax to break or inject Cypher commands.Fix: Escape backticks by doubling them (
strings.ReplaceAll(label, "", "`")) and wrap the identifier in backticks (`MATCH (n:%s)andMATCH ()-[r:%s]->()`). -
MediumMissing operator precedence parentheses in Snowflake prebuilt list_tables query leaks cross-schema metadata
internal/prebuiltconfigs/tools/snowflake.yaml:55
In
snowflake.yaml, thelist_tablesquery filtersINFORMATION_SCHEMA.TABLESwithWHERE t.TABLE_TYPE = 'BASE TABLE' AND t.TABLE_SCHEMA NOT IN ('INFORMATION_SCHEMA') AND t.TABLE_SCHEMA = '${SNOWFLAKE_SCHEMA}' AND is_all_tables OR LOWER(T.TABLE_NAME) IN (SELECT table_name FROM filtered_table_names). BecauseANDbinds tighter thanORin SQL, passing anytable_namesargument (is_all_tables = false) bypasses thet.TABLE_SCHEMA = '${SNOWFLAKE_SCHEMA}'restriction and returns metadata for matching tables across all schemas in the database.Fix: Wrap the
ORcondition in parentheses:AND (is_all_tables OR LOWER(T.TABLE_NAME) IN (SELECT table_name FROM filtered_table_names)). -
MediumPermissive default CORS origins and allowed hosts expose HTTP server to browser-based attacks
By default, when Toolbox runs in HTTP mode (
127.0.0.1:5000), both--allowed-origins(also declared with a default of*inserver.json) and--allowed-hostsdefault to*. Unless the user explicitly overrides these flags, any malicious website visited in a local browser can make cross-origin requests or perform DNS rebinding attacks against the local MCP server to list and invoke database tools.Fix: Change the default values of
--allowed-hoststolocalhost,127.0.0.1and--allowed-originsto an empty list (disabling cross-origin browser access unless explicitly configured). -
MediumPR review skill instructs the agent to fetch and execute untrusted pull request code locally
skills/maintainer/review-prs/SKILL.md:209
The
review-prsskill instructs the coding agent to fetch the pull request branch (git fetch origin pull/<n>/head:pr<n>), check it out into a worktree, and run a scratchprobe_test.goinside the package under review. Because pull requests can be submitted by arbitrary strangers, runninggo teston the PR's package executes untrusted code (such asinit()functions or modified package functions) directly on the reviewer's machine with their local credentials.Fix: Instruct the agent never to execute code from an untrusted pull request branch on the host machine unless inside an isolated, credential-free sandbox or container, or require explicit user confirmation before running any code from the PR.
-
MediumPermissive wildcard CORS origin default in server manifest
The MCP server manifest sets the default value of the --allowed-origins runtime argument to '*'. When the server runs in HTTP mode on localhost without authentication, any website open in the user's browser can send cross-origin requests to the local MCP endpoint and invoke configured database tools.
Fix: Change the default for --allowed-origins to restrict cross-origin browser requests by default, or require explicit origin allowlisting when running in HTTP mode.
-
MediumWildcard CORS origin default in MCP server manifest
The MCP server manifest defines the default value for
--allowed-originsas*. When the server runs in HTTP mode on localhost, permitting all origins allows any website open in the user's browser to send cross-origin requests to the local MCP server and invoke database tools.Fix: Change the default value of
--allowed-originsto restrict cross-origin requests by default (for example, to an empty list or localhost-only origins) and require explicit opt-in for wildcard origins. -
MediumPR review skill instructs agent to execute untrusted pull request code locally
skills/maintainer/review-prs/SKILL.md:210
The
review-prsskill instructs the coding agent to fetch the pull request branch into a local worktree and run aprobe_test.gotest inside the package under review rather than just reading the code. Runninggo teston an unreviewed pull request executes anyinit()functions, modified package code, or build directives submitted by an untrusted contributor with the reviewer's local privileges and credentials.Fix: Instruct the agent to only checkout and execute code from a pull request after verifying that the diff does not introduce untrusted code execution, or run untrusted PR code only inside an isolated sandbox/CI environment.
-
LowMissing collection path validation after template substitution in firestore-query
internal/tools/firestore/firestorequery/firestorequery.go:177
Unlike
firestore-query-collectionand the other Firestore tools,firestore-querypopulatescollectionPathfrom a template usingparameters.PopulateTemplatebut does not validate the resulting path withfsUtil.ValidateCollectionPath. If a tool configuration parameterizes part ofcollectionPath, a caller can inject/or..segments to traverse into unintended collections.Fix: Call
fsUtil.ValidateCollectionPath(collectionPath)afterparameters.PopulateTemplateand return anAgentErrorif validation fails. -
LowUnbounded blocking sleep in wait tool ignores context cancellation and configured timeout
internal/tools/utility/wait/wait.go:95
The
waittool parses the caller-supplieddurationparameter and callstime.Sleep(totalDuration)directly without enforcing an upper bound, checkingt.Cfg.Timeout, or selecting onctx.Done(). An attacker via prompt injection can pass a very large duration (such as87600h) to tie up server goroutines even after the client disconnects or the request context is canceled.Fix: Cap
totalDurationagainstt.Cfg.Timeoutand useselect { case <-time.After(totalDuration): case <-ctx.Done(): return nil, ... }instead oftime.Sleep.