package main
import (
"context"
"fmt"
"strconv"
"strings"
"github.com/jackc/pgx/v4/pgxpool"
)
type User struct {
ID int
Name string
Email string
}
// SECURE: Parameterized query with $1 placeholder
func getUserByID(pool *pgxpool.Pool, userIDStr string) (*User, error) {
// Validate input
userID, err := strconv.Atoi(userIDStr)
if err != nil {
return nil, fmt.Errorf("invalid user ID")
}
// Use $1 placeholder - pgx handles escaping
query := "SELECT id, name, email FROM users WHERE id = $1"
row := pool.QueryRow(context.Background(), query, userID)
var user User
err = row.Scan(&user.ID, &user.Name, &user.Email)
return &user, err
}
// SECURE: Parameterized query with $1, $2 placeholders
func searchUsers(pool *pgxpool.Pool, name, status string) ([]User, error) {
// Validate inputs
name = strings.TrimSpace(name)
if len(name) > 100 {
return nil, fmt.Errorf("name too long")
}
// Validate status against allowlist
validStatuses := map[string]bool{"active": true, "inactive": true}
if !validStatuses[status] {
return nil, fmt.Errorf("invalid status")
}
// Use $1, $2 placeholders
query := "SELECT id, name, email FROM users WHERE name ILIKE $1 AND status = $2 LIMIT 100"
rows, err := pool.Query(context.Background(), query, "%"+name+"%", status)
if err != nil {
return nil, err
}
defer rows.Close()
var users []User
for rows.Next() {
var user User
rows.Scan(&user.ID, &user.Name, &user.Email)
users = append(users, user)
}
return users, nil
}