Surfaced by the full-codebase SQL injection audit (2026-05-26). The audit found zero SQLi vulnerabilities but one suspicious LIKE-wildcard escape gap.
Defect
internal/config/store_postgres_registrations.go:101-107:
if filter.Search != "" {
conditions = append(conditions, fmt.Sprintf(
"(account_name ILIKE $%d OR external_id ILIKE $%d OR contact_email ILIKE $%d)",
idx, idx, idx,
))
args = append(args, "%"+filter.Search+"%")
idx++
}
filter.Search is bound as $N so this is not SQL injection — the value never enters SQL text. BUT % / _ / \ in filter.Search are not escaped. A user passing %@gmail.com matches every email containing @gmail.com rather than literally containing %@gmail.com. Enumeration / minor information-disclosure risk.
Fix
Mirror the working pattern in internal/config/store_postgres.go:1974 (ListCloudAccounts search filter): escape %, _, \ before wrapping with %...% and add ESCAPE '\\' to the ILIKE.
Add a regression test asserting that filter.Search = "%foo" matches the literal %foo substring, not any foo-containing string.
Surfaced by the full-codebase SQL injection audit (2026-05-26). The audit found zero SQLi vulnerabilities but one suspicious LIKE-wildcard escape gap.
Defect
internal/config/store_postgres_registrations.go:101-107:filter.Searchis bound as$Nso this is not SQL injection — the value never enters SQL text. BUT%/_/\infilter.Searchare not escaped. A user passing%@gmail.commatches every email containing@gmail.comrather than literally containing%@gmail.com. Enumeration / minor information-disclosure risk.Fix
Mirror the working pattern in
internal/config/store_postgres.go:1974(ListCloudAccountssearch filter): escape%,_,\before wrapping with%...%and addESCAPE '\\'to the ILIKE.Add a regression test asserting that
filter.Search = "%foo"matches the literal%foosubstring, not anyfoo-containing string.