Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions agent/configmgr/fleet.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ func (fleetManager *fleetConfigManager) Start(cfg config.Config, backends map[st
ClientID: cfg.OrbAgent.ConfigManager.Sources.Fleet.ClientID,
Zone: jwtClaims.Zone,
}
configYaml, err := yaml.Marshal(cfg)
configYaml, err := fleetManager.configToSafeString(cfg)
if err != nil {
return fmt.Errorf("failed to marshal agent config: %w", err)
return fmt.Errorf("failed to convert config to safe string: %w", err)
}
err = fleetManager.connection.Connect(ctx, connectionDetails, backends, cfg.OrbAgent.Labels, string(configYaml))
if err != nil {
Expand Down Expand Up @@ -116,6 +116,18 @@ func (fleetManager *fleetConfigManager) Start(cfg config.Config, backends map[st
return nil
}

func (fleetManager *fleetConfigManager) configToSafeString(cfg config.Config) (string, error) {
if cfg.OrbAgent.ConfigManager.Sources.Fleet.ClientSecret != "" {
cfg.OrbAgent.ConfigManager.Sources.Fleet.ClientSecret = "******"
}

configYaml, err := yaml.Marshal(cfg)
if err != nil {
return "", fmt.Errorf("failed to marshal agent config: %w", err)
}
return string(configYaml), nil
}

func (fleetManager *fleetConfigManager) GetContext(ctx context.Context) context.Context {
// Empty implementation for now - just return the context as-is
return ctx
Expand Down
112 changes: 112 additions & 0 deletions agent/configmgr/fleet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,115 @@ func TestFleetConfigManager_Start_WithJWTTopicGeneration(t *testing.T) {
strings.Contains(errorMsg, "deadline"),
"Expected connection-related error, got: %s", err.Error())
}

func TestFleetConfigManager_configToSafeString(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
mockPMgr := &mockPolicyManagerForFleet{}
fleetManager := newFleetConfigManager(logger, mockPMgr, &mockBackendState{})

tests := []struct {
name string
clientSecret string
wantSecret string
wantErr bool
checkInYAML bool
}{
{
name: "sanitizes non-empty client secret",
clientSecret: "my-super-secret-password",
wantSecret: "******",
wantErr: false,
checkInYAML: true,
},
{
name: "empty client secret remains empty",
clientSecret: "",
wantSecret: "",
wantErr: false,
checkInYAML: false,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Arrange
cfg := config.Config{
Version: 1.0,
OrbAgent: config.OrbAgent{
ConfigManager: config.ManagerConfig{
Active: "orb",
Sources: config.Sources{
Fleet: config.FleetManager{
TokenURL: "https://example.com/token",
ClientID: "test-client-id",
ClientSecret: tt.clientSecret,
SkipTLS: false,
},
},
},
},
}

// Act
result, err := fleetManager.configToSafeString(cfg)

// Assert
if tt.wantErr {
assert.Error(t, err)
return
}

require.NoError(t, err)
assert.NotEmpty(t, result)

// Verify the original secret is not in the output
if tt.clientSecret != "" {
assert.NotContains(t, result, tt.clientSecret, "original secret should not be in output")
}

// Verify the expected secret is in the YAML output
if tt.checkInYAML {
assert.Contains(t, result, tt.wantSecret, "sanitized secret should be in output")
// YAML can use either single or double quotes, so check for either
assert.True(t,
strings.Contains(result, "client_secret: '******'") ||
strings.Contains(result, "client_secret: \"******\"") ||
strings.Contains(result, "client_secret: ******"),
"client_secret should be masked in YAML output")
}
})
}
}

func TestFleetConfigManager_configToSafeString_DoesNotModifyOriginal(t *testing.T) {
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))
mockPMgr := &mockPolicyManagerForFleet{}
fleetManager := newFleetConfigManager(logger, mockPMgr, &mockBackendState{})

// Arrange
originalSecret := "my-secret-password"
cfg := config.Config{
Version: 1.0,
OrbAgent: config.OrbAgent{
ConfigManager: config.ManagerConfig{
Sources: config.Sources{
Fleet: config.FleetManager{
TokenURL: "https://example.com/token",
ClientID: "test-client-id",
ClientSecret: originalSecret,
},
},
},
},
}

// Act
_, err := fleetManager.configToSafeString(cfg)

// Assert
require.NoError(t, err)
// The original config should not be modified (we're modifying a copy)
// Note: Due to Go's pass-by-value semantics, the original is preserved
assert.Equal(t, originalSecret, cfg.OrbAgent.ConfigManager.Sources.Fleet.ClientSecret,
"original config should not be modified")
}