wapm_cli/
validate.rs

1#![cfg_attr(
2    not(feature = "full"),
3    allow(dead_code, unused_imports, unused_variables)
4)]
5#[cfg(feature = "full")]
6use crate::database;
7use crate::dataflow::{interfaces::InterfaceFromServer, manifest_packages::ManifestResult};
8#[cfg(feature = "full")]
9use crate::interfaces;
10use std::{
11    fs,
12    io::Read,
13    path::{Path, PathBuf},
14};
15use thiserror::Error;
16use wasmer_wasm_interface::{validate, Interface};
17
18#[cfg(feature = "full")]
19pub fn validate_directory(pkg_path: PathBuf) -> anyhow::Result<()> {
20    // validate as dir
21    let manifest = match ManifestResult::find_in_directory(&pkg_path) {
22        ManifestResult::NoManifest => return Ok(()),
23        ManifestResult::ManifestError(e) => return Err(e.into()),
24        ManifestResult::Manifest(manifest) => manifest,
25    };
26    if let Some(modules) = manifest.module {
27        for module in modules.into_iter() {
28            let source_path = if module.source.is_relative() {
29                manifest.base_directory_path.join(&module.source)
30            } else {
31                module.source.clone()
32            };
33            let source_path_string = source_path.to_string_lossy().to_string();
34            let mut wasm_file =
35                fs::File::open(&source_path).map_err(|_| ValidationError::MissingFile {
36                    file: source_path_string.clone(),
37                })?;
38            let mut wasm_buffer = Vec::new();
39            wasm_file.read_to_end(&mut wasm_buffer).map_err(|err| {
40                ValidationError::MiscCannotRead {
41                    file: source_path_string.clone(),
42                    error: format!("{}", err),
43                }
44            })?;
45
46            if let Some(bindings) = &module.bindings {
47                validate_bindings(bindings, &manifest.base_directory_path)?;
48            }
49
50            // hack, short circuit if no interface for now
51            if module.interfaces.is_none() {
52                return validate_wasm_and_report_errors_old(&wasm_buffer[..], source_path_string);
53            }
54
55            let mut conn = database::open_db()?;
56            let mut interface: Interface = Default::default();
57            for (interface_name, interface_version) in
58                module.interfaces.unwrap_or_default().into_iter()
59            {
60                if !interfaces::interface_exists(&mut conn, &interface_name, &interface_version)? {
61                    // download interface and store it if we don't have it locally
62                    let interface_data_from_server = InterfaceFromServer::get(
63                        interface_name.clone(),
64                        interface_version.clone(),
65                    )?;
66                    interfaces::import_interface(
67                        &mut conn,
68                        &interface_name,
69                        &interface_version,
70                        &interface_data_from_server.content,
71                    )?;
72                }
73                let sub_interface = interfaces::load_interface_from_db(
74                    &mut conn,
75                    &interface_name,
76                    &interface_version,
77                )?;
78                interface = interface
79                    .merge(sub_interface)
80                    .map_err(|e| anyhow!("Failed to merge interface {}: {}", &interface_name, e))?;
81            }
82            validate::validate_wasm_and_report_errors(&wasm_buffer, &interface).map_err(|e| {
83                ValidationError::InvalidWasm {
84                    file: source_path_string,
85                    error: format!("{:?}", e),
86                }
87            })?;
88        }
89    }
90    debug!("package at path {:#?} validated", &pkg_path);
91
92    Ok(())
93}
94
95fn validate_bindings(
96    bindings: &wapm_toml::Bindings,
97    base_directory_path: &Path,
98) -> Result<(), ValidationError> {
99    // Note: checking for referenced files will make sure they all exist.
100    let _ = bindings.referenced_files(base_directory_path)?;
101
102    Ok(())
103}
104
105#[cfg(not(feature = "full"))]
106pub fn validate_directory(pkg_path: PathBuf) -> anyhow::Result<()> {
107    Ok(())
108}
109
110#[derive(Debug, Error)]
111pub enum ValidationError {
112    #[error("WASM file \"{file}\" detected as invalid because {error}")]
113    InvalidWasm { file: String, error: String },
114    #[error("Could not find file {file}")]
115    MissingFile { file: String },
116    #[error("Failed to read file {file}; {error}")]
117    MiscCannotRead { file: String, error: String },
118    #[error("Failed to unpack archive \"{file}\"! {error}")]
119    CannotUnpackArchive { file: String, error: String },
120    #[error(transparent)]
121    Imports(#[from] wapm_toml::ImportsError),
122}
123
124// legacy function, validates wasm.  TODO: clean up
125pub fn validate_wasm_and_report_errors_old(wasm: &[u8], file_name: String) -> anyhow::Result<()> {
126    use wasmparser::WasmDecoder;
127    let mut parser = wasmparser::ValidatingParser::new(wasm, None);
128    loop {
129        let state = parser.read();
130        match state {
131            wasmparser::ParserState::EndWasm => return Ok(()),
132            wasmparser::ParserState::Error(e) => {
133                return Err(ValidationError::InvalidWasm {
134                    file: file_name,
135                    error: format!("{}", e),
136                }
137                .into());
138            }
139            _ => {}
140        }
141    }
142}