secret

package
v0.24.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 11, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// ServiceName for keyring entries
	ServiceName       = "mcpproxy"
	SecretTypeKeyring = "keyring"
	RegistryKey       = "_mcpproxy_secret_registry"
)
View Source
const (
	SecretTypeEnv = "env"
)

Variables

View Source
var ErrKeyringTimeout = errors.New("keyring operation timed out (backend unresponsive — likely waiting on a user prompt)")

ErrKeyringTimeout is returned when a keyring operation exceeds keyringOpTimeout. Callers SHOULD treat this identically to "keyring unavailable" and fall back to an alternative secret store (in-memory, config-file, etc.) rather than propagating the error.

View Source
var ErrKeyringUnavailable = errors.New("keyring unavailable (backend not usable on this system — refusing to write to avoid OS modal prompts)")

ErrKeyringUnavailable is returned by write operations (Store/Delete) when a previous IsAvailable() call determined the keyring backend is not usable on this system. We refuse to call keyring.Set in that state because on macOS it pops a system modal ("Keychain Not Found") whose default action is destructive. Callers MUST fall back to an alternative secret store.

Functions

func DetectPotentialSecret

func DetectPotentialSecret(value, fieldName string) (isSecret bool, confidence float64)

DetectPotentialSecret analyzes a string to determine if it might be a secret

func IsSecretRef

func IsSecretRef(input string) bool

IsSecretRef returns true if the string looks like a secret reference

func MaskSecretValue

func MaskSecretValue(value string) string

MaskSecretValue masks a secret value for safe display

Types

type ConfigSecretsResponse

type ConfigSecretsResponse struct {
	Secrets         []KeyringSecretStatus `json:"secrets"`
	EnvironmentVars []EnvVarStatus        `json:"environment_vars"`
	TotalSecrets    int                   `json:"total_secrets"`
	TotalEnvVars    int                   `json:"total_env_vars"`
}

ConfigSecretsResponse contains secrets and environment variables referenced in config

type EnvProvider

type EnvProvider struct{}

EnvProvider resolves secrets from environment variables

func NewEnvProvider

func NewEnvProvider() *EnvProvider

NewEnvProvider creates a new environment variable provider

func (*EnvProvider) CanResolve

func (p *EnvProvider) CanResolve(secretType string) bool

CanResolve returns true if this provider can handle the given secret type

func (*EnvProvider) Delete

func (p *EnvProvider) Delete(_ context.Context, _ Ref) error

Delete is not supported for environment variables

func (*EnvProvider) IsAvailable

func (p *EnvProvider) IsAvailable() bool

IsAvailable always returns true as environment variables are always available

func (*EnvProvider) List

func (p *EnvProvider) List(_ context.Context) ([]Ref, error)

List returns all environment variables that look like secrets

func (*EnvProvider) Resolve

func (p *EnvProvider) Resolve(_ context.Context, ref Ref) (string, error)

Resolve retrieves the secret value from environment variables

func (*EnvProvider) Store

func (p *EnvProvider) Store(_ context.Context, _ Ref, _ string) error

Store is not supported for environment variables

type EnvVarStatus

type EnvVarStatus struct {
	Ref   Ref  `json:"secret_ref"`
	IsSet bool `json:"is_set"`
}

EnvVarStatus represents the status of an environment variable reference

type KeyringProvider

type KeyringProvider struct {
	// contains filtered or unexported fields
}

KeyringProvider resolves secrets from OS keyring (Keychain, Secret Service, WinCred)

func NewKeyringProvider

func NewKeyringProvider() *KeyringProvider

NewKeyringProvider creates a new keyring provider

func (*KeyringProvider) CanResolve

func (p *KeyringProvider) CanResolve(secretType string) bool

CanResolve returns true if this provider can handle the given secret type

func (*KeyringProvider) Delete

func (p *KeyringProvider) Delete(_ context.Context, ref Ref) error

Delete removes a secret from the OS keyring and updates the registry. Same three-layer guard as Store: known-unavailable short-circuit, first-time probe, and failure cache.

func (*KeyringProvider) DeleteWithRegistry

func (p *KeyringProvider) DeleteWithRegistry(ctx context.Context, ref Ref) error

DeleteWithRegistry deletes a secret and updates the registry

func (*KeyringProvider) IsAvailable

func (p *KeyringProvider) IsAvailable() bool

IsAvailable checks if the keyring is available on the current system, and caches the result. Subsequent calls to Store/Delete will short-circuit to ErrKeyringUnavailable WITHOUT calling keyring.Set if the probe decided the keyring is not usable — this is the key property that prevents the macOS "Keychain Not Found" modal from ever being shown to the user.

This is deliberately conservative and NEVER calls keyring.Set as a probe: on macOS, Set triggers the "Keychain Not Found" system modal when the user's default keychain is missing/locked/corrupted, whose default button is the destructive "Reset To Defaults" action.

Instead, we:

  1. Skip the probe entirely in obvious headless/CI environments.
  2. Do a single read-only Get for a non-existent key and treat ErrNotFound as "available, no secret yet". Any other error (including ErrUnsupportedPlatform and backend communication failures) is treated as "unavailable".
  3. Run the Get inside a goroutine with a 2s hard timeout so a hung keychain backend cannot stall the caller.
  4. Cache the result via markAvailable/markUnavailable so Store/Delete can short-circuit on the fast path.

func (*KeyringProvider) List

func (p *KeyringProvider) List(_ context.Context) ([]Ref, error)

List returns all secret references stored in the keyring Note: go-keyring doesn't provide a list function, so we'll track them differently

func (*KeyringProvider) Resolve

func (p *KeyringProvider) Resolve(_ context.Context, ref Ref) (string, error)

Resolve retrieves the secret value from the OS keyring

func (*KeyringProvider) SetWritesEnabled added in v0.24.0

func (p *KeyringProvider) SetWritesEnabled(enabled bool)

SetWritesEnabled is a test-only helper that forces the opt-in state regardless of env vars. Pass true to allow keyring.Set, false to disallow. It is safe to call at any time — Store checks the override synchronously.

func (*KeyringProvider) Store

func (p *KeyringProvider) Store(_ context.Context, ref Ref, value string) error

Store saves a secret to the OS keyring and updates the registry.

By default on macOS this method does NOT actually call keyring.Set — it returns ErrKeyringUnavailable immediately. Rationale: on macOS keyring.Set wraps Security.framework under the hood; if the user's default keychain is missing/locked/in an unusual state, Security.framework pops a system modal ("Keychain Not Found" with a destructive default button). The underlying call is blocking and the goroutine we'd wrap it in cannot be cancelled once started — it keeps running until Security. framework finally responds, which may involve the user clicking buttons. No wrapper / timeout / probe can prevent the modal from appearing, so the only safe option is to not call keyring.Set at all.

Users who want keyring-backed storage on macOS can opt in by setting the environment variable MCPPROXY_KEYRING_WRITE=1 or calling EnableKeyringWrites(true) on the provider. When opted in, Store runs a three-layer guard (known-unavailable cache, first-time probe, failure cache) plus a 3s runWithTimeout wrapper — but note that the wrapper only protects the CALLING goroutine from blocking; it cannot prevent the modal from appearing on the user's screen once keyring.Set is called.

On non-macOS platforms (Linux Secret Service, Windows Credential Manager), Store goes through the three-layer guard without requiring the opt-in, because those backends don't have the same modal issue.

Callers MUST treat ErrKeyringUnavailable as "fall back to in-config / in-memory storage" and surface a clear log message.

func (*KeyringProvider) StoreWithRegistry

func (p *KeyringProvider) StoreWithRegistry(ctx context.Context, ref Ref, value string) error

StoreWithRegistry stores a secret and updates the registry

type KeyringSecretStatus

type KeyringSecretStatus struct {
	Ref   Ref  `json:"secret_ref"`
	IsSet bool `json:"is_set"`
}

KeyringSecretStatus represents the status of a keyring secret reference

type MigrationAnalysis

type MigrationAnalysis struct {
	Candidates []MigrationCandidate `json:"candidates"`
	TotalFound int                  `json:"total_found"`
}

MigrationAnalysis contains analysis of potential secrets to migrate

type MigrationCandidate

type MigrationCandidate struct {
	Field      string  `json:"field"`      // Field path in config
	Value      string  `json:"value"`      // Current plaintext value (masked in responses)
	Suggested  string  `json:"suggested"`  // Suggested Ref
	Confidence float64 `json:"confidence"` // Confidence this is a secret (0-1)
}

MigrationCandidate represents a potential secret that could be migrated

type Provider

type Provider interface {
	// CanResolve returns true if this provider can handle the given secret type
	CanResolve(secretType string) bool

	// Resolve retrieves the actual secret value
	Resolve(ctx context.Context, ref Ref) (string, error)

	// Store saves a secret (if supported by the provider)
	Store(ctx context.Context, ref Ref, value string) error

	// Delete removes a secret (if supported by the provider)
	Delete(ctx context.Context, ref Ref) error

	// List returns all secret references handled by this provider
	List(ctx context.Context) ([]Ref, error)

	// IsAvailable checks if the provider is available on the current system
	IsAvailable() bool
}

Provider interface for secret resolution

type Ref

type Ref struct {
	Type     string `json:"type"`     // env, keyring, op, age
	Name     string `json:"name"`     // environment variable name, keyring alias, etc.
	Original string `json:"original"` // original reference string
}

Ref represents a reference to a secret

func FindSecretRefs

func FindSecretRefs(input string) []*Ref

FindSecretRefs finds all secret references in a string

func ParseSecretRef

func ParseSecretRef(input string) (*Ref, error)

ParseSecretRef parses a string that may contain secret references

type ResolveResult

type ResolveResult struct {
	Ref      Ref
	Value    string
	Error    error
	Resolved bool
}

ResolveResult contains the result of secret resolution

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver manages secret resolution using multiple providers

func NewResolver

func NewResolver() *Resolver

NewResolver creates a new secret resolver

func (*Resolver) AnalyzeForMigration

func (r *Resolver) AnalyzeForMigration(v interface{}) *MigrationAnalysis

AnalyzeForMigration analyzes a struct for potential secrets that could be migrated

func (*Resolver) Delete

func (r *Resolver) Delete(ctx context.Context, ref Ref) error

Delete deletes a secret using the appropriate provider

func (*Resolver) ExpandSecretRefs

func (r *Resolver) ExpandSecretRefs(ctx context.Context, input string) (string, error)

ExpandSecretRefs replaces all secret references in a string with resolved values

func (*Resolver) ExpandStructSecrets

func (r *Resolver) ExpandStructSecrets(ctx context.Context, v interface{}) error

ExpandStructSecrets recursively expands secret references in a struct

func (*Resolver) ExpandStructSecretsCollectErrors added in v0.21.2

func (r *Resolver) ExpandStructSecretsCollectErrors(ctx context.Context, v interface{}) []SecretExpansionError

ExpandStructSecretsCollectErrors expands secret references in all string fields of v. Unlike ExpandStructSecrets, it does not fail fast: it collects all expansion errors and continues processing remaining fields. On error, the field retains its original value. v must be a non-nil pointer to a struct.

func (*Resolver) ExtractConfigSecrets

func (r *Resolver) ExtractConfigSecrets(ctx context.Context, v interface{}) (*ConfigSecretsResponse, error)

ExtractConfigSecrets extracts all secret and environment references from a config structure

func (*Resolver) GetAvailableProviders

func (r *Resolver) GetAvailableProviders() []string

GetAvailableProviders returns a list of available providers

func (*Resolver) ListAll

func (r *Resolver) ListAll(ctx context.Context) ([]Ref, error)

ListAll lists all secret references from all providers

func (*Resolver) RegisterProvider

func (r *Resolver) RegisterProvider(secretType string, provider Provider)

RegisterProvider registers a new secret provider

func (*Resolver) Resolve

func (r *Resolver) Resolve(ctx context.Context, ref Ref) (string, error)

Resolve resolves a single secret reference

func (*Resolver) Store

func (r *Resolver) Store(ctx context.Context, ref Ref, value string) error

Store stores a secret using the appropriate provider

type SecretExpansionError added in v0.21.2

type SecretExpansionError struct {
	FieldPath string // e.g. "WorkingDir", "Isolation.WorkingDir", "Args[0]", "Env[MY_VAR]"
	Reference string // the original unresolved reference pattern, e.g. "${env:HOME}"
	Err       error
}

SecretExpansionError records a failure to resolve a single secret reference during struct expansion.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL