Overview
In CVE-2026-32628, AnythingLLM’s 1.11.1 and earlier versions contained a SQL injection flaw in the built-in SQL Agent plugin. The vulnerability stemmed from getTableSchemaSql() in the database connectors (MySQL, PostgreSQL, MSSQL) that constructed SQL queries by concatenating the table_name parameter directly into the query text without sanitization or parameterization. This CWE-89 issue allowed an attacker who could invoke the agent to craft input that executes arbitrary SQL commands on the connected databases, potentially exfiltrating data, altering schema or data, or compromising DB account privileges. The resulting risk is particularly severe given agent-based workflows that expose endpoints to end users or automated clients.
Exploitation typically occurred when user input reached the table_name used to build a schema query. By injecting crafted values (e.g., closing the string and appending malicious SQL), an attacker could escape the intended query context and run unintended statements. The vulnerability affected the MySQL, PostgreSQL, and MSSQL connectors used by AnythingLLM’s SQL Agent plugin, enabling arbitrary SQL execution with the permissions of the DB user associated with the agent. This is a classic dynamic-SQL risk where untrusted input directly shapes SQL text.
Go (Gin) remediation emphasizes avoiding dynamic SQL with user input for identifiers. Since identifiers (like table names) cannot be parameterized in the same way as values, the recommended fixes are input validation via whitelisting or metadata queries with proper parameterization for values. In a Go (Gin) service, apply strict input validation, prefer parameterized queries for data values, and consider querying information_schema (or equivalent) with bound parameters or a validated allowlist. Additionally, enforce least-privilege DB users, enable proper logging, and add tests to guard against injection patterns in endpoints that expose database metadata.
This guide provides concrete, Go (Gin)-oriented remediation guidance, including an example demonstrating the vulnerable pattern and a secure, fixed pattern for retrieving schema information without risking injection.
Affected Versions
AnythingLLM 1.11.1 and earlier (per CVE-2026-32628)
Code Fix Example
Go (Gin) API Security Remediation
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"github.com/gin-gonic/gin"
_ "github.com/go-sql-driver/mysql"
)
type Col struct {
ColumnName string
DataType string
}
// Vulnerable pattern: direct string concatenation with user input
func vulnerableGetTableSchema(db *sql.DB, table string) ([]Col, error) {
// POTENTIAL SQL INJECTION: table is interpolated directly into SQL text
q := fmt.Sprintf("SELECT column_name, data_type FROM information_schema.columns WHERE table_name = '%s'", table)
rows, err := db.Query(q)
if err != nil {
return nil, err
}
defer rows.Close()
var cols []Col
for rows.Next() {
var c Col
if err := rows.Scan(&c.ColumnName, &c.DataType); err != nil {
return nil, err
}
cols = append(cols, c)
}
return cols, nil
}
// Fixed pattern: use parameterized query for values and, for identifiers, use a whitelist
func fixedGetTableSchema(db *sql.DB, table string) ([]Col, error) {
// Whitelist approach to identifiers
allowed := map[string]bool{"users": true, "orders": true, "products": true}
if !allowed[table] {
return nil, fmt.Errorf("invalid table: %s", table)
}
// Parameterized query for the metadata lookup
rows, err := db.Query("SELECT column_name, data_type FROM information_schema.columns WHERE table_name = ?", table)
if err != nil {
return nil, err
}
defer rows.Close()
var cols []Col
for rows.Next() {
var c Col
if err := rows.Scan(&c.ColumnName, &c.DataType); err != nil {
return nil, err
}
cols = append(cols, c)
}
return cols, nil
}
func main() {
dsn := "user:password@tcp(127.0.0.1:3306)/dbname"
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
r := gin.Default()
r.GET("/schema", func(c *gin.Context) {
tbl := c.Query("table")
vuln, err := vulnerableGetTableSchema(db, tbl)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
fix, err := fixedGetTableSchema(db, tbl)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{"vulnerable": vuln, "fixed": fix})
})
r.Run()
}