use std::fs::File;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use tempfile::NamedTempFile;
use zeroize::Zeroize;
pub struct SecureTempFile {
file: NamedTempFile,
path: PathBuf,
}
impl SecureTempFile {
pub fn new() -> io::Result<Self> {
let file = tempfile::Builder::new()
.prefix("seal-secret-")
.suffix(".yaml")
.rand_bytes(16)
.tempfile()?;
let path = file.path().to_path_buf();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(&path)?.permissions();
perms.set_mode(0o600); std::fs::set_permissions(&path, perms)?;
}
Ok(Self { file, path })
}
pub fn write_all(&mut self, data: &[u8]) -> io::Result<()> {
self.file.write_all(data)?;
self.file.flush()?;
Ok(())
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn delete(self) -> io::Result<()> {
Ok(())
}
}
impl Drop for SecureTempFile {
fn drop(&mut self) {
if let Ok(metadata) = std::fs::metadata(&self.path) {
let file_size = metadata.len() as usize;
if file_size > 0 && file_size < 10 * 1024 * 1024 {
let mut zeros = vec![0u8; file_size];
if let Ok(mut file) = File::create(&self.path) {
let _ = file.write_all(&zeros);
let _ = file.flush();
}
zeros.zeroize();
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_secure_temp_file_creation() -> io::Result<()> {
let temp_file = SecureTempFile::new()?;
assert!(temp_file.path().exists());
Ok(())
}
#[test]
fn test_secure_temp_file_write() -> io::Result<()> {
let mut temp_file = SecureTempFile::new()?;
temp_file.write_all(b"test data")?;
assert!(temp_file.path().exists());
Ok(())
}
#[test]
fn test_secure_temp_file_cleanup() -> io::Result<()> {
let path;
{
let temp_file = SecureTempFile::new()?;
path = temp_file.path().to_path_buf();
assert!(path.exists());
}
assert!(!path.exists());
Ok(())
}
}