1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
use dev_prefix::*;
use types::*;


/// validate that all files that could be affected by
/// the project are within the repo and the `settings.artifact_paths`
/// partof: #SPC-security
pub fn validate(repo: &Path, project: &Project) -> Result<()> {
    let mut files: HashSet<&PathBuf> = HashSet::new();
    files.extend(project.artifacts.values().map(|a| &a.def));
    files.extend(project.files.iter());
    files.extend(project.repo_map.keys());

    // PARENT_DEF is never written to, so ignore
    files.remove(&*PARENT_DEF);

    for f in files {
        if !f.is_absolute() {
            let msg = format!("{} is not an absolute path", f.display());
            return Err(ErrorKind::Internal(msg).into());
        }
        // only allow files that are in the cwd repo
        if !f.starts_with(repo) {
            let msg = format!(
                "{} is not a subdir of cwd repo {}",
                f.display(),
                repo.display()
            );
            return Err(ErrorKind::Security(msg).into());
        }
        // only allow files that are in the artifact_paths
        if !project
            .settings
            .artifact_paths
            .iter()
            .any(|p| f.starts_with(p))
        {
            let msg = format!(
                "{} is not a subdir of any artifact_paths {:?}",
                f.display(),
                project.settings.artifact_paths
            );
            return Err(ErrorKind::Security(msg).into());
        }
        // files that do not already exist are invalid
        if !f.exists() {
            let msg = format!(
                "{} does not already exist and cannot be created here",
                f.display()
            );
            return Err(ErrorKind::Security(msg).into());
        }
    }
    Ok(())
}

pub fn validate_settings(repo: &Path, settings: &Settings) -> Result<()> {
    println!(
        "repo={:?}, artifact_paths={:?}",
        repo,
        settings.artifact_paths
    );
    if settings.artifact_paths.iter().any(|p| !p.starts_with(repo)) {
        //TODO improve message
        let msg = "artifact_paths invalid".to_string();
        Err(ErrorKind::Security(msg).into())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use test_data;
    use utils;
    use user;

    // TODO: not sure if this is even needed anymore... will check again
    // later
    //#[test]
    ///// make sure that artifacts which are loaded "out of bounds"
    ///// don't make it past the security checker
    ///// partof: #TST-security-gen
    //fn test_bounds_checker() {
    //    let design = test_data::TINVALID_BOUNDS.join("repo").join("design");
    //    let repo = utils::find_repo(&design).unwrap();
    //    match user::load_repo(&repo) {
    //        Err(e) => {
    //            match *e.kind() {
    //                ErrorKind::Security(_) => { [> expected <] }
    //                _ => panic!("unexpected error: {:?}", e.display()),
    //            }
    //        }
    //        Ok(_) => panic!("fmt accidentally suceeded -- may need to reset with git"),
    //    }
    //    //let project = user::load_repo(&repo).unwrap();
    //    //let req_bounds = NameRc::from_str("REQ-bounds").unwrap();
    //    //assert!(project.artifacts.contains_key(&req_bounds));
    //    //assert_eq!(project.artifacts[&req_bounds].path,
    //    //           test_data::TINVALID_BOUNDS.join("out_bounds.toml"));
    //    //assert!(validate(&repo, &project).is_err());
    //}

    #[test]
    fn test_security() {
        let design = test_data::TINVALID_BOUNDS.join("repo").join("design");
        let repo = utils::find_repo(&design).unwrap();
        match user::load_repo(&repo) {
            Err(e) => {
                match *e.kind() {
                    ErrorKind::Security(_) => { /* expected */ }
                    _ => panic!("unexpected error: {:?}", e.display()),
                }
            }
            Ok(_) => panic!("fmt accidentally suceeded -- may need to reset with git"),
        }
    }
}