fix(secure-cli): resolve ambiguous column in LookupByBinary JOIN query

LookupByBinary uses LEFT JOIN with secure_cli_user_credentials but
SELECT columns lacked table alias prefix, causing PostgreSQL error:
"column reference 'id' is ambiguous (SQLSTATE 42702)"

This silently broke ALL credentialed CLI exec — commands fell through
to regular shell exec without injected env vars.

Fix: use b.-prefixed column names for JOIN queries.
Also add diagnostic logging to lookupCredentialedBinary for future debugging.
This commit is contained in:
viettranx committed 2026-04-02 14:56:49 +07:00
1 parent 62724780b8
commit ec05c80e90
2 files changed
+15 -2

No files matched your search

+7 -1
View File
@@ -26,6 +26,11 @@ func NewPGSecureCLIStore(db *sql.DB, encryptionKey string) *PGSecureCLIStore {
const secureCLISelectCols = `id, binary_name, binary_path, description, encrypted_env,
deny_args, deny_verbose, timeout_seconds, tips, agent_id, enabled, created_by, created_at, updated_at`
// secureCLISelectColsAliased is the same as secureCLISelectCols but prefixed with table alias "b."
// Required for LookupByBinary which uses LEFT JOIN (ambiguous column names without prefix).
const secureCLISelectColsAliased = `b.id, b.binary_name, b.binary_path, b.description, b.encrypted_env,
b.deny_args, b.deny_verbose, b.timeout_seconds, b.tips, b.agent_id, b.enabled, b.created_by, b.created_at, b.updated_at`
func (s *PGSecureCLIStore) Create(ctx context.Context, b *store.SecureCLIBinary) error {
if err := store.ValidateUserID(b.CreatedBy); err != nil {
return err
@@ -271,7 +276,8 @@ func (s *PGSecureCLIStore) LookupByBinary(ctx context.Context, binaryName string
}
// Build query with optional LEFT JOIN for per-user credentials.
selectCols := secureCLISelectCols
// Use aliased columns (b.) to avoid ambiguous column reference with JOIN.
selectCols := secureCLISelectColsAliased
joinClause := ""
if userID != "" {
selectCols += ", uc.encrypted_env AS user_env"
+8 -1
View File
@@ -281,6 +281,7 @@ func formatCredentialedResult(binary string, args []string,
// Returns the credential config and parsed args, or nil if not credentialed.
func (t *ExecTool) lookupCredentialedBinary(ctx context.Context, command string) (*store.SecureCLIBinary, string, []string) {
if t.secureCLIStore == nil {
slog.Warn("secure_cli.lookup: store is nil, skipping credentialed exec", "command", command)
return nil, "", nil
}
binary, args, err := parseCommandBinary(command)
@@ -296,9 +297,15 @@ func (t *ExecTool) lookupCredentialedBinary(ctx context.Context, command string)
// Pass userID for per-user credential resolution (LEFT JOIN, zero extra queries).
userID := store.UserIDFromContext(ctx)
cred, err := t.secureCLIStore.LookupByBinary(ctx, binary, agentIDPtr, userID)
if err != nil || cred == nil {
if err != nil {
slog.Warn("secure_cli.lookup: query failed", "binary", binary, "agent_id", agentID, "error", err)
return nil, "", nil
}
if cred == nil {
slog.Warn("secure_cli.lookup: no credential found", "binary", binary, "agent_id", agentID)
return nil, "", nil
}
slog.Info("secure_cli.lookup: found credential", "binary", binary, "cred_id", cred.ID, "env_size", len(cred.EncryptedEnv))
return cred, binary, args
}