1pub mod auto_refresh;
2pub mod current;
3pub mod login;
4pub mod output;
5pub mod refresh;
6pub mod remove;
7pub mod save;
8pub mod sync;
9pub mod use_secret;
10
11use anyhow::Result;
12use std::path::Path;
13
14pub fn identity_from_auth_file(path: &Path) -> Result<Option<String>> {
15 crate::runtime::auth::identity_from_auth_file(path).map_err(anyhow::Error::from)
16}
17
18pub fn email_from_auth_file(path: &Path) -> Result<Option<String>> {
19 crate::runtime::auth::email_from_auth_file(path).map_err(anyhow::Error::from)
20}
21
22pub fn account_id_from_auth_file(path: &Path) -> Result<Option<String>> {
23 crate::runtime::auth::account_id_from_auth_file(path).map_err(anyhow::Error::from)
24}
25
26pub fn last_refresh_from_auth_file(path: &Path) -> Result<Option<String>> {
27 crate::runtime::auth::last_refresh_from_auth_file(path).map_err(anyhow::Error::from)
28}
29
30pub fn identity_key_from_auth_file(path: &Path) -> Result<Option<String>> {
31 crate::runtime::auth::identity_key_from_auth_file(path).map_err(anyhow::Error::from)
32}
33
34pub fn is_invalid_secret_target(target: &str) -> bool {
35 target.contains('/') || target.contains('\\') || target.contains("..")
36}
37
38pub fn normalize_secret_file_name(target: &str) -> String {
39 if target.ends_with(".json") {
40 return target.to_string();
41 }
42 format!("{target}.json")
43}
44
45#[cfg(test)]
46mod tests {
47 use super::{is_invalid_secret_target, normalize_secret_file_name};
48
49 #[test]
50 fn secret_target_validation_rejects_paths_and_traversal() {
51 assert!(is_invalid_secret_target("../a.json"));
52 assert!(is_invalid_secret_target("a/b.json"));
53 assert!(is_invalid_secret_target(r"a\b.json"));
54 assert!(!is_invalid_secret_target("alpha"));
55 assert!(!is_invalid_secret_target("alpha.json"));
56 }
57
58 #[test]
59 fn normalize_secret_file_name_appends_json_suffix_only_once() {
60 assert_eq!(normalize_secret_file_name("alpha"), "alpha.json");
61 assert_eq!(normalize_secret_file_name("alpha.json"), "alpha.json");
62 }
63}