Function bodies 391 total
get_profile_secrets_mut function · rust · L779-L788 (10 LOC)src/config.rs
pub fn get_profile_secrets_mut(
&mut self,
profile: &str,
) -> &mut IndexMap<String, SecretConfig> {
&mut self
.profiles
.entry(profile.to_string())
.or_default()
.secrets
}get_secrets function · rust · L796-L817 (22 LOC)src/config.rs
pub fn get_secrets(&self, profile: &str) -> Result<IndexMap<String, SecretConfig>> {
if profile == "default" {
return Ok(self.secrets.clone());
}
let mut secrets = if Settings::get().no_defaults {
// Profile-only mode: do not merge top-level secrets.
IndexMap::new()
} else {
// Start with top-level secrets as base
self.secrets.clone()
};
// Get profile-specific secrets and merge/override (if profile exists)
if let Some(profile_config) = self.profiles.get(profile) {
// Profile-specific secrets override top-level ones
secrets.extend(profile_config.secrets.clone());
}
// If profile doesn't exist in [profiles], that's OK - just use top-level secrets
// This allows fnox.$FNOX_PROFILE.toml to work without requiring [profiles.xxx]
Ok(secrets)
}get_secrets_mut function · rust · L820-L826 (7 LOC)src/config.rs
pub fn get_secrets_mut(&mut self, profile: &str) -> &mut IndexMap<String, SecretConfig> {
if profile == "default" {
self.get_default_secrets_mut()
} else {
self.get_profile_secrets_mut(profile)
}
}get_providers function · rust · L829-L839 (11 LOC)src/config.rs
pub fn get_providers(&self, profile: &str) -> IndexMap<String, ProviderConfig> {
let mut providers = self.providers.clone(); // Start with global providers
if profile != "default"
&& let Some(profile_config) = self.profiles.get(profile)
{
providers.extend(profile_config.providers.clone());
}
providers
}get_default_provider function · rust · L843-L925 (83 LOC)src/config.rs
pub fn get_default_provider(&self, profile: &str) -> Result<Option<String>> {
let providers = self.get_providers(profile);
// If no providers configured and this is a root config, return None
if providers.is_empty() && self.root {
return Ok(None);
}
// If no providers configured, that's an error
if providers.is_empty() {
return Err(FnoxError::Config(
"No providers configured. Add at least one provider to fnox.toml".to_string(),
));
}
// Check for profile-specific default provider
if profile != "default"
&& let Some(profile_config) = self.profiles.get(profile)
&& let Some(default_provider_name) = profile_config.default_provider()
{
// Validate that the default provider exists
if !providers.contains_key(default_provider_name) {
// Try to get source info for better error reporting
set_source_paths function · rust · L928-L966 (39 LOC)src/config.rs
fn set_source_paths(&mut self, path: &Path) {
// Set source paths for default profile secrets
for (key, secret) in self.secrets.iter_mut() {
secret.source_path = Some(path.to_path_buf());
self.secret_sources.insert(key.clone(), path.to_path_buf());
}
// Set source paths for default profile providers
for (provider_name, _) in self.providers.iter() {
self.provider_sources
.insert(provider_name.clone(), path.to_path_buf());
}
// Set source path for default_provider if set
if self.default_provider().is_some() {
self.default_provider_source = Some(path.to_path_buf());
}
// Set source paths for named profiles
for (_profile_name, profile) in self.profiles.iter_mut() {
for (key, secret) in profile.secrets.iter_mut() {
secret.source_path = Some(path.to_path_buf());
profile
.scheck_empty_value function · rust · L970-L1001 (32 LOC)src/config.rs
fn check_empty_value(
&self,
key: &str,
secret: &SecretConfig,
profile: &str,
) -> Option<crate::error::ValidationIssue> {
// Early return if value is not an empty string
let Some(value) = secret.value() else {
return None; // No value specified - not an issue
};
if !value.is_empty() {
return None; // Non-empty value - not an issue
}
// At this point, value is an empty string
// Allow empty values for plain provider (empty string is a valid secret value)
if self.is_plain_provider(secret.provider(), profile) {
return None;
}
let message = if profile == "default" {
format!("Secret '{}' has an empty value", key)
} else {
format!(
"Secret '{}' in profile '{}' has an empty value",
key, profile
)
};
Some(crate::error::ValidationIssue::with_help(
Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
is_plain_provider function · rust · L1005-L1040 (36 LOC)src/config.rs
fn is_plain_provider(&self, secret_provider: Option<&str>, profile: &str) -> bool {
// Get providers for this profile first (needed for auto-selection)
let providers = self.get_providers(profile);
// Determine which provider name to use
let provider_name = secret_provider
.map(String::from)
.or_else(|| {
// Try profile's default_provider first (only for non-default profiles)
if profile != "default" {
self.profiles
.get(profile)
.and_then(|p| p.default_provider().map(|s| s.to_string()))
} else {
None
}
})
.or_else(|| self.default_provider().map(|s| s.to_string()))
.or_else(|| {
// Auto-select if exactly one provider exists (matching get_default_provider behavior)
if providers.len() == 1 {
prvalidate function · rust · L1044-L1161 (118 LOC)src/config.rs
pub fn validate(&self) -> Result<()> {
use crate::error::ValidationIssue;
// If root=true and no providers AND no secrets, that's OK (empty config)
if self.root
&& self.providers.is_empty()
&& self.profiles.is_empty()
&& self.secrets.is_empty()
{
return Ok(());
}
let mut issues = Vec::new();
// Check for secrets with empty values (likely a mistake, but allowed for plain provider)
for (key, secret) in &self.secrets {
if let Some(issue) = self.check_empty_value(key, secret, "default") {
issues.push(issue);
}
}
// Check that there's at least one provider if there are any secrets
if self.providers.is_empty() && self.profiles.is_empty() && !self.secrets.is_empty() {
issues.push(ValidationIssue::with_help(
"No providers configured",
"Add at least one provider to fnox.new function · rust · L1192-L1203 (12 LOC)src/config.rs
pub fn new() -> Self {
Self {
description: None,
if_missing: None,
default: None,
provider: None,
value: None,
as_file: false,
json_path: None,
source_path: None,
}
}to_inline_table function · rust · L1206-L1238 (33 LOC)src/config.rs
pub fn to_inline_table(&self) -> toml_edit::InlineTable {
let mut inline = toml_edit::InlineTable::new();
if let Some(provider) = self.provider() {
inline.insert("provider", toml_edit::Value::from(provider));
}
if let Some(value) = self.value() {
inline.insert("value", toml_edit::Value::from(value));
}
if let Some(ref json_path) = self.json_path {
inline.insert("json_path", toml_edit::Value::from(json_path.as_str()));
}
if let Some(ref description) = self.description {
inline.insert("description", toml_edit::Value::from(description.as_str()));
}
if let Some(ref default) = self.default {
inline.insert("default", toml_edit::Value::from(default.as_str()));
}
if let Some(if_missing) = self.if_missing {
let if_missing_str = match if_missing {
IfMissing::Error => "error",
IfMissing::Warn =>new function · rust · L1276-L1285 (10 LOC)src/config.rs
pub fn new() -> Self {
Self {
providers: IndexMap::new(),
default_provider: None,
secrets: IndexMap::new(),
provider_sources: HashMap::new(),
secret_sources: HashMap::new(),
default_provider_source: None,
}
}test_empty_import_not_serialized function · rust · L1329-L1336 (8 LOC)src/config.rs
fn test_empty_import_not_serialized() {
let config = Config::new();
let toml = toml_edit::ser::to_string_pretty(&config).unwrap();
assert!(
!toml.contains("import"),
"Empty import should not be serialized"
);
}test_non_empty_import_is_serialized function · rust · L1339-L1351 (13 LOC)src/config.rs
fn test_non_empty_import_is_serialized() {
let mut config = Config::new();
config.import.push("other.toml".to_string());
let toml = toml_edit::ser::to_string_pretty(&config).unwrap();
assert!(
toml.contains("import"),
"Non-empty import should be serialized"
);
assert!(
toml.contains("other.toml"),
"Import value should be present"
);
}test_empty_profiles_not_serialized function · rust · L1354-L1361 (8 LOC)src/config.rs
fn test_empty_profiles_not_serialized() {
let config = Config::new();
let toml = toml_edit::ser::to_string_pretty(&config).unwrap();
assert!(
!toml.contains("profiles"),
"Empty profiles should not be serialized"
);
}Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
test_non_empty_profiles_is_serialized function · rust · L1364-L1396 (33 LOC)src/config.rs
fn test_non_empty_profiles_is_serialized() {
let mut config = Config::new();
// Add a provider and secret to the prod profile
let mut prod_profile = ProfileConfig::new();
prod_profile
.providers
.insert("plain".to_string(), ProviderConfig::Plain);
let mut secret = SecretConfig::new();
secret.set_value(Some("test-value".to_string()));
prod_profile
.secrets
.insert("TEST_SECRET".to_string(), secret);
config.profiles.insert("prod".to_string(), prod_profile);
let toml = toml_edit::ser::to_string_pretty(&config).unwrap();
// Print the TOML for debugging
eprintln!("Generated TOML:\n{}", toml);
assert!(
toml.contains("profiles"),
"Non-empty profiles should be serialized"
);
assert!(toml.contains("prod"), "Profile name should be present");
// Check that we don't have a standalone [profiles] headetest_empty_profile_not_serialized function · rust · L1399-L1431 (33 LOC)src/config.rs
fn test_empty_profile_not_serialized() {
use std::io::Read;
let mut config = Config::new();
// Add an empty profile (no providers, no secrets)
config
.profiles
.insert("prod".to_string(), ProfileConfig::new());
// Use save() which cleans up empty profiles
let temp_file = std::env::temp_dir().join("fnox_test_empty_profile.toml");
config.save(&temp_file).unwrap();
let mut toml = String::new();
std::fs::File::open(&temp_file)
.unwrap()
.read_to_string(&mut toml)
.unwrap();
std::fs::remove_file(&temp_file).ok();
eprintln!("Generated TOML with empty profile:\n{}", toml);
// Empty profiles should not appear in the output at all
// Because save() cleans them up
assert!(
!toml.contains("[profiles"),
"Empty profile should not be serialized"
);
assert!(
!toml.containtest_no_defaults_profile_only_secrets function · rust · L1434-L1457 (24 LOC)src/config.rs
fn test_no_defaults_profile_only_secrets() {
crate::settings::Settings::reset_for_tests();
crate::settings::Settings::set_cli_snapshot(crate::settings::CliSnapshot {
age_key_file: None,
profile: Some("prod".to_string()),
if_missing: None,
no_defaults: true,
});
let mut config = Config::new();
config
.secrets
.insert("DEFAULT_ONLY".to_string(), SecretConfig::new());
let mut prod_profile = ProfileConfig::new();
prod_profile
.secrets
.insert("PROD_ONLY".to_string(), SecretConfig::new());
config.profiles.insert("prod".to_string(), prod_profile);
let secrets = config.get_secrets("prod").unwrap();
assert!(secrets.contains_key("PROD_ONLY"));
assert!(!secrets.contains_key("DEFAULT_ONLY"));
}test_no_defaults_profile_without_section_is_empty function · rust · L1460-L1476 (17 LOC)src/config.rs
fn test_no_defaults_profile_without_section_is_empty() {
crate::settings::Settings::reset_for_tests();
crate::settings::Settings::set_cli_snapshot(crate::settings::CliSnapshot {
age_key_file: None,
profile: Some("prod".to_string()),
if_missing: None,
no_defaults: true,
});
let mut config = Config::new();
config
.secrets
.insert("DEFAULT_ONLY".to_string(), SecretConfig::new());
let secrets = config.get_secrets("prod").unwrap();
assert!(secrets.is_empty());
}set_var function · rust · L12-L18 (7 LOC)src/env.rs
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, val: V) {
let _lock = ENV_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
// SAFETY: set_var is unsafe in Rust 2024 edition. Access is serialized via ENV_MUTEX.
unsafe {
std::env::set_var(key, val);
}
}is_valid_profile_name function · rust · L61-L88 (28 LOC)src/env.rs
fn is_valid_profile_name(name: &str) -> bool {
// Profile names must be non-empty
if name.is_empty() {
return false;
}
// Reject path separators and other dangerous characters
// Allow: alphanumeric, dash, underscore, dot (but not .. or .)
if name == "." || name == ".." {
return false;
}
// Check for path separators or other dangerous characters
for ch in name.chars() {
match ch {
// Path separators
'/' | '\\' => return false,
// Null byte (could truncate paths)
'\0' => return false,
// Control characters
c if c.is_control() => return false,
// Allow everything else (alphanumeric, dash, underscore, dot)
_ => {}
}
}
true
}test_var_path function · rust · L95-L104 (10 LOC)src/env.rs
fn test_var_path() {
unsafe {
set_var("FNOX_TEST_PATH", "/foo/bar");
assert_eq!(
var_path("FNOX_TEST_PATH").unwrap(),
PathBuf::from("/foo/bar")
);
remove_var("FNOX_TEST_PATH");
}
}test_valid_profile_names function · rust · L107-L116 (10 LOC)src/env.rs
fn test_valid_profile_names() {
// Valid profile names
assert!(is_valid_profile_name("production"));
assert!(is_valid_profile_name("staging"));
assert!(is_valid_profile_name("dev"));
assert!(is_valid_profile_name("test-env"));
assert!(is_valid_profile_name("test_env"));
assert!(is_valid_profile_name("prod-v2.0"));
assert!(is_valid_profile_name("env123"));
}All rows scored by the Repobility analyzer (https://repobility.com)
test_invalid_profile_names function · rust · L119-L140 (22 LOC)src/env.rs
fn test_invalid_profile_names() {
// Path traversal attempts
assert!(!is_valid_profile_name("../../../etc/passwd"));
assert!(!is_valid_profile_name(".."));
assert!(!is_valid_profile_name("."));
assert!(!is_valid_profile_name("../production"));
assert!(!is_valid_profile_name("production/../../etc/passwd"));
// Absolute paths
assert!(!is_valid_profile_name("/etc/passwd"));
assert!(!is_valid_profile_name("/tmp/evil"));
// Windows paths
assert!(!is_valid_profile_name("C:\\Windows\\System32"));
assert!(!is_valid_profile_name("..\\..\\evil"));
// Empty and special characters
assert!(!is_valid_profile_name(""));
assert!(!is_valid_profile_name("prod\0uction")); // null byte
assert!(!is_valid_profile_name("prod\ntest")); // newline
assert!(!is_valid_profile_name("prod\rtest")); // carriage return
}with_help function · rust · L18-L23 (6 LOC)src/error.rs
pub fn with_help(message: impl Into<String>, help: impl Into<String>) -> Self {
Self {
message: message.into(),
help: Some(help.into()),
}
}new function · rust · L54-L112 (59 LOC)src/hook_env.rs
pub fn new(
dir: Option<PathBuf>,
config_path: Option<PathBuf>,
loaded_secrets: HashMap<String, String>,
temp_files: HashMap<String, String>,
) -> Result<Self> {
let config_mtime = if let Some(ref path) = config_path {
std::fs::metadata(path)
.and_then(|m| m.modified())
.ok()
.and_then(|t| t.duration_since(SystemTime::UNIX_EPOCH).ok())
.map(|d| d.as_millis())
} else {
None
};
let env_var_hash = hash_fnox_env_vars();
// Calculate hash of all config files in the hierarchy
let config_files_hash = if let Some(ref d) = dir {
let configs = collect_config_files(d);
hash_config_files(&configs)
} else {
String::new()
};
// Generate a random key for this session's hashes
use blake3::Hasher;
let hash_key = *Hasher::new()
.update(b"fnodecode_session function · rust · L123-L129 (7 LOC)src/hook_env.rs
fn decode_session(encoded: &str) -> Result<HookEnvSession> {
let compressed = data_encoding::BASE64.decode(encoded.as_bytes())?;
let bytes = miniz_oxide::inflate::decompress_to_vec(&compressed)
.map_err(|e| anyhow::anyhow!("failed to decompress session: {:?}", e))?;
let session = rmp_serde::from_slice(&bytes)?;
Ok(session)
}hash_secret_with_key function · rust · L134-L140 (7 LOC)src/hook_env.rs
fn hash_secret_with_key(hash_key: &[u8; 32], key: &str, value: &str) -> String {
let mut hasher = blake3::Hasher::new_keyed(hash_key);
hasher.update(key.as_bytes());
hasher.update(b"\x00"); // separator
hasher.update(value.as_bytes());
hasher.finalize().to_hex().to_string()
}should_exit_early function · rust · L149-L170 (22 LOC)src/hook_env.rs
pub fn should_exit_early() -> bool {
// Check if directory changed
if has_directory_changed() {
tracing::debug!("directory changed, must run hook-env");
return false;
}
// Check if fnox.toml was modified
if has_config_been_modified() {
tracing::debug!("fnox.toml modified, must run hook-env");
return false;
}
// Check if FNOX_* env vars changed
if has_fnox_env_vars_changed() {
tracing::debug!("FNOX_* env vars changed, must run hook-env");
return false;
}
tracing::debug!("no changes detected, exiting early");
true
}has_config_been_modified function · rust · L180-L204 (25 LOC)src/hook_env.rs
fn has_config_been_modified() -> bool {
let current_dir = match std::env::current_dir() {
Ok(dir) => dir,
Err(_) => return true, // If we can't get current dir, force reload
};
// Build a hash of all current config files and their mtimes
let current_configs = collect_config_files(¤t_dir);
let current_hash = hash_config_files(¤t_configs);
// Compare with the stored hash from the previous session
if PREV_SESSION.config_files_hash.is_empty() {
// Old session without config_files_hash - use conservative fallback
// Without the hash, we can't reliably detect changes, so we must be conservative:
// - Reload if there was a previous config (might have changed or been deleted)
// - Reload if there's a current config (might be new or changed)
// - Only skip if neither previous nor current config exists
let had_config = PREV_SESSION.config_path.is_some();
let has_config = !current_concollect_config_files function · rust · L208-L246 (39 LOC)src/hook_env.rs
fn collect_config_files(start_dir: &Path) -> Vec<(PathBuf, u128)> {
use crate::config::Config;
let mut configs = Vec::new();
let mut current = start_dir.to_path_buf();
// Get profile name using Settings system which respects: CLI flag > Env var > Default
let profile_name = crate::settings::Settings::get().profile.clone();
let filenames = crate::config::all_config_filenames(Some(&profile_name));
loop {
// Check all config filenames (fnox.toml, .fnox.toml, fnox.$PROFILE.toml, etc.)
for filename in &filenames {
let config_path = current.join(filename);
if let Ok(metadata) = std::fs::metadata(&config_path)
&& let Ok(modified) = metadata.modified()
&& let Ok(duration) = modified.duration_since(SystemTime::UNIX_EPOCH)
{
configs.push((config_path, duration.as_millis()));
}
}
// Move to parent directory
if !current.pop() {
Repobility · open methodology · https://repobility.com/research/
hash_fnox_env_vars function · rust · L268-L282 (15 LOC)src/hook_env.rs
fn hash_fnox_env_vars() -> String {
use std::collections::BTreeMap;
use std::hash::{Hash, Hasher};
let mut vars: BTreeMap<String, String> = BTreeMap::new();
for (key, value) in std::env::vars() {
if key.starts_with("FNOX_") {
vars.insert(key, value);
}
}
let mut hasher = std::collections::hash_map::DefaultHasher::new();
vars.hash(&mut hasher);
format!("{:x}", hasher.finish())
}find_config function · rust · L287-L316 (30 LOC)src/hook_env.rs
pub fn find_config() -> Option<PathBuf> {
use crate::config::{Config, all_config_filenames};
let profile = crate::settings::Settings::get().profile.clone();
let filenames = all_config_filenames(Some(&profile));
let mut current = std::env::current_dir().ok()?;
loop {
// Check all config files (returns first match)
for filename in &filenames {
let path = current.join(filename);
if path.exists() {
return Some(path);
}
}
if !current.pop() {
break;
}
}
// Check global config as fallback
let global = Config::global_config_path();
if global.is_file() {
return Some(global);
}
None
}main function · rust · L26-L61 (36 LOC)src/main.rs
async fn main() -> miette::Result<()> {
miette::set_panic_hook();
// Initialize rustls crypto provider for GCP SDKs
let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
let cli = Cli::parse();
// Set CLI snapshot for settings system
settings::Settings::set_cli_snapshot(settings::CliSnapshot {
age_key_file: cli.age_key_file.clone(),
profile: cli.profile.clone(),
if_missing: cli.if_missing.clone(),
no_defaults: cli.no_defaults,
});
// Handle --no-color flag
if cli.no_color {
console::set_colors_enabled(false);
console::set_colors_enabled_stderr(false);
}
// Initialize tracing
let log_level = if cli.verbose { "debug" } else { "info" };
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| format!("fnox={}", log_level).into()),
)
.with(tracing_subscriber::fmtnew function · rust · L17-L22 (6 LOC)src/providers/age.rs
pub fn new(recipients: Vec<String>, key_file: Option<String>) -> Self {
Self {
recipients,
key_file: key_file.map(|k| PathBuf::from(shellexpand::tilde(&k).to_string())),
}
}encrypt function · rust · L30-L99 (70 LOC)src/providers/age.rs
async fn encrypt(&self, plaintext: &str) -> Result<String> {
use std::io::Write;
if self.recipients.is_empty() {
return Err(FnoxError::AgeNotConfigured);
}
// Parse recipients - try both SSH and native age formats
let mut parsed_recipients: Vec<Box<dyn age::Recipient + Send + Sync>> = Vec::new();
for recipient in &self.recipients {
// Try parsing as SSH recipient first
if let Ok(ssh_recipient) = recipient.parse::<age::ssh::Recipient>() {
parsed_recipients.push(Box::new(ssh_recipient));
continue;
}
// Fall back to native age recipient
match recipient.parse::<age::x25519::Recipient>() {
Ok(age_recipient) => {
parsed_recipients.push(Box::new(age_recipient));
}
Err(e) => {
return Err(FnoxError::AgeEncryptionFailed {
detaiget_secret function · rust · L100-L209 (110 LOC)src/providers/age.rs
async fn get_secret(&self, value: &str) -> Result<String> {
// value contains the encrypted blob (might be base64 encoded or raw)
// Try to decode as base64 first, if that fails, treat as raw bytes
let encrypted_bytes =
match base64::Engine::decode(&base64::engine::general_purpose::STANDARD, value) {
Ok(bytes) => bytes,
Err(_) => {
// Not base64 encoded, treat as raw bytes
value.as_bytes().to_vec()
}
};
// Priority for key file:
// 1. FNOX_AGE_KEY env var (inline key content)
// 2. self.key_file (from provider config)
// 3. Settings age_key_file (from CLI flag - deprecated)
// 4. Default path (~/.config/fnox/age.txt)
let (identity_content, key_file_path_opt) = if let Some(ref age_key) = *env::FNOX_AGE_KEY {
// Use the key directly from the environment variable
(age_key.cloneaws_kms_error_to_fnox function · rust · L14-L119 (106 LOC)src/providers/aws_kms.rs
fn aws_kms_error_to_fnox<E, R>(
err: &aws_sdk_kms::error::SdkError<E, R>,
operation: &str,
key_id: &str,
) -> FnoxError
where
E: std::fmt::Debug + std::fmt::Display,
R: std::fmt::Debug,
{
use aws_sdk_kms::error::SdkError;
match err {
SdkError::ServiceError(service_err) => {
let err_str = service_err.err().to_string();
if err_str.contains("AccessDenied") || err_str.contains("UnauthorizedAccess") {
FnoxError::ProviderAuthFailed {
provider: "AWS KMS".to_string(),
details: err_str,
hint: format!("Check IAM permissions for kms:{}", operation),
url: URL.to_string(),
}
} else if err_str.contains("NotFoundException") {
FnoxError::ProviderSecretNotFound {
provider: "AWS KMS".to_string(),
secret: key_id.to_string(),
hint: "Check that the Kcreate_client function · rust · L132-L140 (9 LOC)src/providers/aws_kms.rs
async fn create_client(&self) -> Result<Client> {
// Load AWS config with the specified region
let config = aws_config::defaults(BehaviorVersion::latest())
.region(aws_sdk_kms::config::Region::new(self.region.clone()))
.load()
.await;
Ok(Client::new(&config))
}Repobility's GitHub App fixes findings like these · https://github.com/apps/repobility-bot
decrypt function · rust · L143-L185 (43 LOC)src/providers/aws_kms.rs
async fn decrypt(&self, ciphertext_base64: &str) -> Result<String> {
let client = self.create_client().await?;
// Decode from base64
let ciphertext_bytes = base64::Engine::decode(
&base64::engine::general_purpose::STANDARD,
ciphertext_base64,
)
.map_err(|e| FnoxError::ProviderInvalidResponse {
provider: "AWS KMS".to_string(),
details: format!("Failed to decode base64 ciphertext: {}", e),
hint: "The encrypted value appears to be corrupted".to_string(),
url: URL.to_string(),
})?;
let result = client
.decrypt()
.key_id(&self.key_id)
.ciphertext_blob(Blob::new(ciphertext_bytes))
.send()
.await
.map_err(|e| aws_kms_error_to_fnox(&e, "Decrypt", &self.key_id))?;
let plaintext_blob =
result
.plaintext()
.ok_or_else(|| FnoxError::ProviderInencrypt function · rust · L198-L225 (28 LOC)src/providers/aws_kms.rs
async fn encrypt(&self, plaintext: &str) -> Result<String> {
let client = self.create_client().await?;
let result = client
.encrypt()
.key_id(&self.key_id)
.plaintext(Blob::new(plaintext.as_bytes()))
.send()
.await
.map_err(|e| aws_kms_error_to_fnox(&e, "Encrypt", &self.key_id))?;
let ciphertext_blob =
result
.ciphertext_blob()
.ok_or_else(|| FnoxError::ProviderInvalidResponse {
provider: "AWS KMS".to_string(),
details: "Encrypt returned no ciphertext".to_string(),
hint: "This is an unexpected error".to_string(),
url: URL.to_string(),
})?;
// Encode as base64 for storage
Ok(base64::Engine::encode(
&base64::engine::general_purpose::STANDARD,
ciphertext_blob.as_ref(),
))
}test_connection function · rust · L226-L239 (14 LOC)src/providers/aws_kms.rs
async fn test_connection(&self) -> Result<()> {
let client = self.create_client().await?;
// Try to describe the key to verify access
client
.describe_key()
.key_id(&self.key_id)
.send()
.await
.map_err(|e| aws_kms_error_to_fnox(&e, "DescribeKey", &self.key_id))?;
Ok(())
}aws_ps_error_to_fnox function · rust · L14-L118 (105 LOC)src/providers/aws_ps.rs
fn aws_ps_error_to_fnox<E, R>(
err: &aws_sdk_ssm::error::SdkError<E, R>,
param_name: &str,
) -> FnoxError
where
E: std::fmt::Debug + std::fmt::Display,
R: std::fmt::Debug,
{
use aws_sdk_ssm::error::SdkError;
match err {
SdkError::ServiceError(service_err) => {
let err_str = service_err.err().to_string();
if err_str.contains("ParameterNotFound") {
FnoxError::ProviderSecretNotFound {
provider: "AWS Parameter Store".to_string(),
secret: param_name.to_string(),
hint: "Check that the parameter exists in AWS Parameter Store".to_string(),
url: URL.to_string(),
}
} else if err_str.contains("AccessDenied") || err_str.contains("UnauthorizedAccess") {
FnoxError::ProviderAuthFailed {
provider: "AWS Parameter Store".to_string(),
details: err_str,
new function · rust · L127-L133 (7 LOC)src/providers/aws_ps.rs
pub fn new(region: String, profile: Option<String>, prefix: Option<String>) -> Self {
Self {
region,
profile,
prefix,
}
}get_parameter_name function · rust · L134-L140 (7 LOC)src/providers/aws_ps.rs
pub fn get_parameter_name(&self, key: &str) -> String {
match &self.prefix {
Some(prefix) => format!("{}{}", prefix, key),
None => key.to_string(),
}
}create_client function · rust · L143-L153 (11 LOC)src/providers/aws_ps.rs
async fn create_client(&self) -> Result<Client> {
let mut builder = aws_config::defaults(BehaviorVersion::latest())
.region(aws_sdk_ssm::config::Region::new(self.region.clone()));
if let Some(profile) = &self.profile {
builder = builder.profile_name(profile);
}
let config = builder.load().await;
Ok(Client::new(&config))
}get_parameter_value function · rust · L156-L178 (23 LOC)src/providers/aws_ps.rs
async fn get_parameter_value(&self, parameter_name: &str) -> Result<String> {
let client = self.create_client().await?;
let result = client
.get_parameter()
.name(parameter_name)
.with_decryption(true) // Automatically decrypt SecureString parameters
.send()
.await
.map_err(|e| aws_ps_error_to_fnox(&e, parameter_name))?;
// Get the parameter value
result
.parameter()
.and_then(|p| p.value())
.ok_or_else(|| FnoxError::ProviderInvalidResponse {
provider: "AWS Parameter Store".to_string(),
details: format!("Parameter '{}' has no value", parameter_name),
hint: "The parameter exists but has no value set".to_string(),
url: URL.to_string(),
})
.map(|s| s.to_string())
}Citation: Repobility (2026). State of AI-Generated Code. https://repobility.com/research/
put_parameter function · rust · L289-L307 (19 LOC)src/providers/aws_ps.rs
pub async fn put_parameter(&self, parameter_name: &str, parameter_value: &str) -> Result<()> {
let client = self.create_client().await?;
client
.put_parameter()
.name(parameter_name)
.value(parameter_value)
.r#type(aws_sdk_ssm::types::ParameterType::SecureString)
.overwrite(true) // Overwrite if exists
.send()
.await
.map_err(|e| aws_ps_error_to_fnox(&e, parameter_name))?;
tracing::debug!(
"Stored parameter '{}' in AWS Parameter Store",
parameter_name
);
Ok(())
}get_secret function · rust · L315-L325 (11 LOC)src/providers/aws_ps.rs
async fn get_secret(&self, value: &str) -> Result<String> {
let parameter_name = self.get_parameter_name(value);
tracing::debug!(
"Getting parameter '{}' from AWS Parameter Store in region '{}'",
parameter_name,
self.region
);
self.get_parameter_value(¶meter_name).await
}test_connection function · rust · L376-L389 (14 LOC)src/providers/aws_ps.rs
async fn test_connection(&self) -> Result<()> {
let client = self.create_client().await?;
// Try to describe parameters to verify connection
client
.describe_parameters()
.max_results(1)
.send()
.await
.map_err(|e| aws_ps_error_to_fnox(&e, "connection-test"))?;
Ok(())
}