From 7662a8271c8c709a1d59ea8137b60c7a04c1a145 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 8 Apr 2026 01:25:33 +0000 Subject: [PATCH] Sanitize API-derived values before writing to AWS config INI Values from ConductorOne API responses (account ID, role name, account name, profile name, app ID, entitlement ID) are now passed through sanitizeINIValue before interpolation into ~/.aws/config. The function strips newlines, carriage returns, null bytes, square brackets, double quotes, and backslashes to prevent INI structure injection and shell command injection via credential_process. --- cmd/cone/aws.go | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/cmd/cone/aws.go b/cmd/cone/aws.go index b5c821c0..4239c4fe 100644 --- a/cmd/cone/aws.go +++ b/cmd/cone/aws.go @@ -194,6 +194,9 @@ func createAWSProfile(entitlement *shared.AppEntitlement, resource *shared.AppRe return "", fmt.Errorf("entitlement is nil") } + appID = sanitizeINIValue(appID) + entitlementID = sanitizeINIValue(entitlementID) + var accountID string for _, value := range entitlement.SourceConnectorIds { parts := strings.Split(value, "|") @@ -202,6 +205,7 @@ func createAWSProfile(entitlement *shared.AppEntitlement, resource *shared.AppRe break } } + accountID = sanitizeINIValue(accountID) if accountID == "" { return "", fmt.Errorf("could not extract AWS account ID from sourceConnectorIds") } @@ -209,13 +213,13 @@ func createAWSProfile(entitlement *shared.AppEntitlement, resource *shared.AppRe if entitlement.DisplayName == nil { return "", fmt.Errorf("entitlement has no display name") } - roleName := strings.Split(*entitlement.DisplayName, " ")[0] + roleName := sanitizeINIValue(strings.Split(*entitlement.DisplayName, " ")[0]) accountName := "aws" if resource != nil && resource.DisplayName != nil { - accountName = strings.ToLower(strings.ReplaceAll(*resource.DisplayName, " ", "-")) + accountName = sanitizeINIValue(strings.ToLower(strings.ReplaceAll(*resource.DisplayName, " ", "-"))) } - profileName := fmt.Sprintf("%s-%s", accountName, strings.ToLower(roleName)) + profileName := sanitizeINIValue(fmt.Sprintf("%s-%s", accountName, strings.ToLower(roleName))) awsConfigDir := filepath.Join(os.Getenv("HOME"), ".aws") if err := os.MkdirAll(awsConfigDir, 0700); err != nil { @@ -513,6 +517,22 @@ func extractDuplicateTaskID(errMsg string) string { return "" } +// sanitizeINIValue strips characters that could break INI structure or enable +// injection when API-derived values are interpolated into ~/.aws/config. +func sanitizeINIValue(s string) string { + var b strings.Builder + b.Grow(len(s)) + for _, r := range s { + switch r { + case '\n', '\r', '\x00', '[', ']', '"', '\\': + continue + default: + b.WriteRune(r) + } + } + return b.String() +} + func extractProfileConfig(config, profileSection string) string { lines := strings.Split(config, "\n") var profileLines []string