ini icon indicating copy to clipboard operation
ini copied to clipboard

IgnoreInlineComment doesn't stop it adding `'s

Open apenney opened this issue 1 year ago • 2 comments

Version

1.67.0

Describe the bug

I have some code that parses the output of an AWS command. I use:

	cfg, err := ini.LoadSources(ini.LoadOptions{
		InsensitiveSections:     true,
		IgnoreInlineComment:     true,
		SkipUnrecognizableLines: true, // Ignore the initial output pre-sections
	}, output)

output is something like:

[profile Environment-Blah.PowerUserAccess]
sso_start_url = https://blah.awsapps.com/start#/
sso_region = us-east-1
sso_account_name = [Environment] Blah
sso_account_id = 111111
sso_role_name = PowerUserAccess
region = us-east-1
credential_process = aws-sso-util credential-process --profile blah
sso_auto_populated = true

Later when setting up certain profiles I do:

		profileSection.Key("sso_start_url").SetValue(fmt.Sprintf("https://%s/start#/", envConfig.SSOStart))

When I save the .aws/config file however, I always get:

[profile blah-develop-admin]
sso_start_url      = `'https://blah.awsapps.com/start#/'`

It will always wrap the output in because of the # in the url. No matter what I do, """ quoting, single quotes, double quotes, anything, it still wraps that entire value in. It's as if this code from go-ini is ignored:

				if strings.ContainsAny(val, "\n`") {
					val = `"""` + val + `"""`
				} else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") {
					val = "`" + val + "`"

I'd appreciate any ideas or help you could suggest, I cannot make this stop wrapping it in `. I did find a related bug, https://github.com/go-ini/ini/issues/282, but it seemed like the solution maybe worked for @5nafu.

To reproduce

.

Expected behavior

No `'s in the output.

Additional context

No response

Code of Conduct

  • [X] I agree to follow this project's Code of Conduct

apenney avatar Nov 05 '24 01:11 apenney

I spent some time trying to make test cases to understand this issue better. This was written with the help of Claude.ai, but I was able to reproduce it with minimal code. Here's a bug report fresh out of the AI:

Backticks incorrectly added around URLs containing '#' when using ini.Empty()

Description

When using ini.Empty() to create a new configuration and setting URLs containing the # character, backticks are incorrectly added around the URL value in the output file. This does not occur when loading and copying existing configurations.

Reproduction Steps

package main

import (
    "fmt"
    "gopkg.in/ini.v1"
)

func main() {
    // Case 1: Using ini.Empty() (produces backticks)
    cfg1 := ini.Empty()
    section1, _ := cfg1.NewSection("test-section")
    section1.Key("sso_start_url").SetValue("https://example.com/start#/")
    
    // Case 2: Loading from source (works correctly)
    sourceData := `[test-section]
sso_start_url = https://example.com/start#/`
    cfg2, _ := ini.LoadSources(ini.LoadOptions{}, []byte(sourceData))
}

Expected Behavior

The URL should be written to the file without surrounding backticks:

[test-section]
sso_start_url = https://example.com/start#/

Actual Behavior

The URL is written with surrounding backticks:

[test-section]
sso_start_url = `https://example.com/start#/`

Test Cases Results

We've tested various scenarios:

  1. Loading from source and copying sections (WORKS):
[test-section]
sso_start_url = https://example.com/start#/
  1. Using ini.Empty() (FAILS):
[test-section]
sso_start_url = `https://example.com/start#/`
  1. Using NewKey instead of Key().SetValue() (FAILS):
[test-section]
sso_start_url = `https://example.com/start#/`
  1. Different URL formats:
[test-section]
url1 = `https://example.com/start#/`    # With # (fails)
url2 = http://example.com/start         # Without # (works)
url3 = https://example.com/start?param=value  # With ? (works)

This suggests the issue is specifically related to the '#' character when using ini.Empty().

Environment

  • go-ini version: v1.67.0
  • Go version: 1.23.2
  • OS: Linux

Additional Notes

  • The issue appears to be related specifically to the '#' character in URLs
  • The problem occurs regardless of whether using Key().SetValue() or NewKey()
  • Various escape options (UnescapeValueCommentSymbols, PreserveSurroundedQuote, etc.) do not prevent the issue
  • The issue does not occur when loading from source and copying sections, only when creating new sections in an empty config

Impact

This causes issues when generating AWS configuration files, as the backticks make the configuration invalid for AWS tools. The URLs need to be written without backticks to be valid.

apenney avatar Nov 05 '24 14:11 apenney

Also the tests I ran were:

package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"

	"gopkg.in/ini.v1"
)

func printConfig(name string, cfg *ini.File) {
	tempFile := filepath.Join(os.TempDir(), fmt.Sprintf("ini-test-%s.tmp", name))
	err := cfg.SaveTo(tempFile)
	if err != nil {
		fmt.Printf("Error saving %s: %v\n", name, err)
		return
	}

	content, err := os.ReadFile(tempFile)
	if err != nil {
		fmt.Printf("Error reading %s: %v\n", name, err)
		return
	}

	fmt.Printf("\n=== %s ===\n", name)
	fmt.Println(string(content))

	// Clean up temp file
	os.Remove(tempFile)
}

func main() {
	// Initial test data
	sampleData := `[profile Environment-Blah.PowerUserAccess]
sso_start_url = https://blah.awsapps.com/start#/
sso_region = us-east-1
sso_account_name = [Environment] Blah
sso_account_id = 111111
sso_role_name = PowerUserAccess
region = us-east-1
credential_process = aws-sso-util credential-process --profile blah
sso_auto_populated = true`

	// Get this as command output for consistency with original
	cmd := exec.Command("echo", sampleData)
	var output bytes.Buffer
	cmd.Stdout = &output
	_ = cmd.Run()

	// Test 1: Original approach (load from source and copy)
	fmt.Println("\nTest 1: Original approach (load and copy)")
	cfg1, _ := ini.LoadSources(ini.LoadOptions{
		InsensitiveSections:     true,
		IgnoreInlineComment:     true,
		SkipUnrecognizableLines: true,
	}, output.Bytes())

	oldSection := cfg1.Section("profile Environment-Blah.PowerUserAccess")
	newSection, _ := cfg1.NewSection("new-section")
	for _, key := range oldSection.Keys() {
		newSection.Key(key.Name()).SetValue(key.Value())
	}
	newSection.Key("sso_start_url").SetValue("https://blah2/start#/")
	printConfig("test1", cfg1)

	// Test 2: Using ini.Empty()
	fmt.Println("\nTest 2: Using ini.Empty()")
	cfg2 := ini.Empty()
	section2, _ := cfg2.NewSection("test-section")
	section2.Key("sso_start_url").SetValue("https://blah2/start#/")
	section2.Key("sso_region").SetValue("us-east-1")
	printConfig("test2", cfg2)

	// Test 3: Using NewKey instead of Key().SetValue()
	fmt.Println("\nTest 3: Using NewKey")
	cfg3 := ini.Empty()
	section3, _ := cfg3.NewSection("test-section")
	_, _ = section3.NewKey("sso_start_url", "https://blah2/start#/")
	_, _ = section3.NewKey("sso_region", "us-east-1")
	printConfig("test3", cfg3)

	// Test 4: Using different URL formats
	fmt.Println("\nTest 4: Different URL formats")
	cfg4 := ini.Empty()
	section4, _ := cfg4.NewSection("test-section")
	section4.Key("url1").SetValue("https://blah2/start#/")
	section4.Key("url2").SetValue("http://blah2/start")
	section4.Key("url3").SetValue("https://blah2/start?param=value")
	printConfig("test4", cfg4)

	// Test 5: Load and modify existing
	fmt.Println("\nTest 5: Load and modify existing")
	cfg5, _ := ini.LoadSources(ini.LoadOptions{}, []byte(`[section]
sso_start_url = https://original/start#/`))
	section5 := cfg5.Section("section")
	section5.Key("sso_start_url").SetValue("https://blah2/start#/")
	printConfig("test5", cfg5)

	// Test 6: Empty with escape disabled
	fmt.Println("\nTest 6: Empty with escape disabled")
	cfg6 := ini.Empty(ini.LoadOptions{
		UnescapeValueCommentSymbols: true,
	})
	section6, _ := cfg6.NewSection("test-section")
	section6.Key("sso_start_url").SetValue("https://blah2/start#/")
	printConfig("test6", cfg6)
}

apenney avatar Nov 05 '24 14:11 apenney