//! `flowrsc` resolves a graph's `secret:NAME` from FLOWRS_SECRET_NAME and the file //! FLOWRS_SECRETS names, refuses a credential file others can read, and prints no //! credential: the readings and the report show the marker where one was. use std::path::{Path, PathBuf}; use std::process::Command; fn canary() -> String { format!("{}-{}-{}", "flowrs", "canary", "cli-1b7e3d") } fn scratch(name: &str) -> PathBuf { let d = std::env::temp_dir().join(format!("flowrsc-secrets-{}-{name}", std::process::id())); std::fs::create_dir_all(&d).unwrap(); d } /// A Particle call holding `token`, its `auth` checked by `flowrsc test`. fn graph(dir: &Path, token: &str) -> (PathBuf, PathBuf) { let mut g = flowrs_core::Graph::new(); let n = g.add_node("cloud.particle.function", 0.0, 0.0); g.nodes .get_mut(&n) .unwrap() .properties .insert("token".into(), token.into()); let doc = dir.join("g.flowrs"); std::fs::write(&doc, flowrs_core::graph::document::save(&g)).unwrap(); let expect = dir.join("expect.json"); std::fs::write( &expect, format!( "{{\"{}.auth\": {:?}, \"{}.url\": \"https://api.particle.io/v1/devices/my_device/led\"}}", n.0, flowrs_core::graph::secret::MARKER, n.0 ), ) .unwrap(); (doc, expect) } fn flowrsc(args: &[&Path], env: &[(&str, &str)]) -> (i32, String) { let mut c = Command::new(env!("CARGO_BIN_EXE_flowrsc")); c.arg("test").arg(args[0]).arg("--expect").arg(args[1]); c.env_remove("FLOWRS_SECRETS"); for (k, v) in env { c.env(k, v); } let out = c.output().unwrap(); ( out.status.code().unwrap_or(-1), format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ), ) } #[test] fn flowrsc_resolves_a_reference_and_prints_no_credential() { let dir = scratch("env"); let (doc, expect) = graph(&dir, "secret:PARTICLE"); let value = canary(); let (code, text) = flowrsc(&[&doc, &expect], &[("FLOWRS_SECRET_PARTICLE", &value)]); assert_eq!(code, 0, "{text}"); assert!( !flowrs_core::graph::secret::holds(&text, &[value]), "{text}" ); let _ = std::fs::remove_dir_all(&dir); } #[test] fn a_credential_file_others_can_read_is_refused() { let dir = scratch("file"); let (doc, expect) = graph(&dir, "secret:PARTICLE"); let file = dir.join("secrets"); std::fs::write(&file, format!("PARTICLE={}\n", canary())).unwrap(); #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; std::fs::set_permissions(&file, std::fs::Permissions::from_mode(0o644)).unwrap(); let (code, text) = flowrsc( &[&doc, &expect], &[("FLOWRS_SECRETS", file.to_str().unwrap())], ); assert_ne!(code, 0, "{text}"); assert!(text.contains("chmod 600"), "{text}"); assert!(!text.contains(&canary()), "{text}"); std::fs::set_permissions(&file, std::fs::Permissions::from_mode(0o600)).unwrap(); } let (code, text) = flowrsc( &[&doc, &expect], &[("FLOWRS_SECRETS", file.to_str().unwrap())], ); assert_eq!(code, 0, "{text}"); assert!(!text.contains(&canary()), "{text}"); let _ = std::fs::remove_dir_all(&dir); }