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 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 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 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 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
124pub 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}