Skip to content

Commit 2d79d05

Browse files
committed
fix(conan): auth-gated login with anonymous fallback
Refs: SCA-296
1 parent 724cf66 commit 2d79d05

1 file changed

Lines changed: 39 additions & 13 deletions

File tree

module/conan/conan_cmd.go

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,23 +119,27 @@ func ExecuteConanInfoCmd(ctx context.Context, cmdInfo *CmdInfo, dir string) (str
119119
logger.Sugar().Infof("Conan detected: path=%s version=%s major=%d", cmdInfo.Path, cmdInfo.Version, major)
120120
logger.Sugar().Infof("Conan verbose mode: -v %s", conanVerboseArg)
121121
logConanRemoteConfigPaths(logger, major)
122-
if e := ensureConanRemoteLogin(ctx, cmdInfo.Path, major); e != nil {
123-
return "", "", e
124-
}
122+
loginWarnings := ensureConanRemoteLogin(ctx, cmdInfo.Path, major)
125123
logger.Sugar().Debugf("temp file: %s", jsonP)
126124
if major >= 2 {
127125
if e := ensureConan2DefaultProfile(ctx, cmdInfo.Path); e != nil {
128126
return "", "", e
129127
}
130128
logger.Info("Conan mode selected: graph")
131129
if e := executeConanGraphInfoCmd(ctx, cmdInfo.Path, dir, jsonP); e != nil {
130+
if len(loginWarnings) > 0 {
131+
return "", "", fmt.Errorf("%w; login warnings: %s", e, strings.Join(loginWarnings, " | "))
132+
}
132133
return "", "", e
133134
}
134135
logger.Info("Conan command completed with graph mode")
135136
return jsonP, ConanJsonKindGraph, nil
136137
}
137138
logger.Info("Conan mode selected: info")
138139
if e := executeConanInfoCmd(ctx, cmdInfo.Path, dir, jsonP); e != nil {
140+
if len(loginWarnings) > 0 {
141+
return "", "", fmt.Errorf("%w; login warnings: %s", e, strings.Join(loginWarnings, " | "))
142+
}
139143
return "", "", e
140144
}
141145
logger.Info("Conan command completed with info mode")
@@ -233,24 +237,28 @@ type conanRemoteCredential struct {
233237
Password string
234238
}
235239

236-
func ensureConanRemoteLogin(ctx context.Context, conanPath string, major int) error {
240+
func ensureConanRemoteLogin(ctx context.Context, conanPath string, major int) []string {
241+
warnings := make([]string, 0)
237242
if major < 2 {
238-
return nil
243+
return warnings
239244
}
240245
logger := logctx.Use(ctx)
241246
creds, err := getConanRemoteCredentialsFromConfig(major)
242247
if err != nil {
243248
logger.Warn("Conan remote login precheck failed when reading config", zap.Error(err))
244-
return nil
249+
return warnings
245250
}
246251
if len(creds) == 0 {
247252
logger.Info("Conan remote login skipped: no credentials found in remote URLs")
248-
return nil
253+
return warnings
249254
}
250255
for _, cred := range creds {
251-
authenticated, authErr := isConanRemoteAuthenticated(ctx, conanPath, cred.Name)
256+
authenticated, authOutput, authErr := isConanRemoteAuthenticated(ctx, conanPath, cred.Name, cred.Username)
252257
if authErr != nil {
253258
logger.Sugar().Warnf("Conan remote auth probe failed for %s: %v", cred.Name, authErr)
259+
} else {
260+
logger.Sugar().Infof("Conan remote auth probe: remote=%s user=%s authenticated=%t output=%q",
261+
cred.Name, cred.Username, authenticated, strings.TrimSpace(authOutput))
254262
}
255263
if authenticated {
256264
logger.Sugar().Infof("Conan remote auth already valid: remote=%s", cred.Name)
@@ -266,22 +274,40 @@ func ensureConanRemoteLogin(ctx context.Context, conanPath string, major int) er
266274
c.Stderr = io.MultiWriter(sb, logPipe)
267275
if runErr := c.Run(); runErr != nil {
268276
logPipe.Close()
269-
return fmt.Errorf("conan remote login failed for %s: %w, details: %s", cred.Name, runErr, strings.TrimSpace(string(sb.Bytes())))
277+
msg := fmt.Sprintf("conan remote login failed for %s(user=%s): %v, details: %s", cred.Name, cred.Username, runErr, strings.TrimSpace(string(sb.Bytes())))
278+
logger.Warn(msg)
279+
warnings = append(warnings, msg)
280+
continue
270281
}
271282
logPipe.Close()
272283
logger.Sugar().Infof("Conan remote login completed: remote=%s user=%s", cred.Name, cred.Username)
273284
}
274-
return nil
285+
return warnings
275286
}
276287

277-
func isConanRemoteAuthenticated(ctx context.Context, conanPath string, remoteName string) (bool, error) {
288+
func isConanRemoteAuthenticated(ctx context.Context, conanPath string, remoteName string, expectedUser string) (bool, string, error) {
278289
c := exec.CommandContext(ctx, conanPath, conanArgs("remote", "auth", remoteName, "--with-user")...)
279290
c.Env = getEnvForConan()
280291
out, err := c.CombinedOutput()
292+
output := strings.TrimSpace(string(out))
281293
if err != nil {
282-
return false, fmt.Errorf("auth check failed: %w, output: %s", err, strings.TrimSpace(string(out)))
294+
return false, output, fmt.Errorf("auth check failed: %w, output: %s", err, output)
295+
}
296+
lower := strings.ToLower(output)
297+
if output == "" {
298+
return false, output, nil
299+
}
300+
if strings.Contains(lower, "anonymous") || strings.Contains(lower, "anonymously") {
301+
return false, output, nil
302+
}
303+
if strings.Contains(lower, "not authenticated") || strings.Contains(lower, "authenticated: false") {
304+
return false, output, nil
305+
}
306+
if expectedUser != "" && strings.Contains(lower, strings.ToLower(expectedUser)) {
307+
return true, output, nil
283308
}
284-
return true, nil
309+
// Conan output format may vary by version; if we cannot infer a user match, treat as unauthenticated.
310+
return false, output, nil
285311
}
286312

287313
type conanRemotesFile struct {

0 commit comments

Comments
 (0)