acativa 0.2.1

Multi-Agent Collaboration Strategy Compiler — generic runtime, state machine and durable strategy store
Documentation
use anyhow::{Result, anyhow, ensure};
use std::fs;
use std::io;
use std::path::Path;

use super::validation;

pub fn ensure_private_dir(path: &Path) -> Result<()> {
    validation::validate_private_path_ancestors(path)?;
    fs::create_dir_all(path)?;
    harden_private_path(path)?;
    validation::validate_private_path_ancestors(path)
}

pub fn harden_private_tree(path: &Path) -> Result<()> {
    let metadata = match fs::symlink_metadata(path) {
        Ok(metadata) => metadata,
        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()),
        Err(error) => return Err(error.into()),
    };
    ensure!(
        !metadata.file_type().is_symlink(),
        "private tree contains a symbolic link"
    );
    if metadata.file_type().is_dir() {
        harden_private_path(path)?;
        for entry in fs::read_dir(path)? {
            let entry = entry?;
            harden_private_tree(&entry.path())?;
        }
        let stable = fs::symlink_metadata(path)?;
        ensure!(
            stable.file_type().is_dir() && !stable.file_type().is_symlink(),
            "private tree directory changed during hardening"
        );
        return validation::ensure_same_file(&metadata, &stable);
    }
    ensure!(
        metadata.file_type().is_file(),
        "private tree contains an unsupported node"
    );
    harden_private_path(path)?;
    let stable = fs::symlink_metadata(path)?;
    ensure!(
        stable.file_type().is_file() && !stable.file_type().is_symlink(),
        "private tree file changed during hardening"
    );
    validation::ensure_same_file(&metadata, &stable)
}

pub fn harden_private_path(path: &Path) -> Result<()> {
    validation::validate_private_path_ancestors(path)?;
    let before = match fs::symlink_metadata(path) {
        Ok(metadata) => metadata,
        Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()),
        Err(_) => return Err(anyhow!("private path metadata is unavailable")),
    };
    ensure!(
        !before.file_type().is_symlink()
            && (before.file_type().is_dir() || before.file_type().is_file()),
        "private path is not a stable file or directory"
    );
    #[cfg(unix)]
    super::unix_hardening::harden_path(path, before.file_type().is_dir())
        .map_err(|_| anyhow!("private path permissions could not be applied"))?;
    #[cfg(windows)]
    super::windows_acl::apply_owner_only(path)?;

    let after =
        fs::symlink_metadata(path).map_err(|_| anyhow!("private path changed during hardening"))?;
    ensure!(
        !after.file_type().is_symlink()
            && before.file_type().is_dir() == after.file_type().is_dir()
            && before.file_type().is_file() == after.file_type().is_file(),
        "private path changed during hardening"
    );
    validation::ensure_same_file(&before, &after)?;
    validation::validate_private_path_ancestors(path)
}