blob: 0d2778d23b6eee45900f8ce6de6b005a441730e9 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
mod allowlist;
use {
allowlist::{AllowlistFilter, UnversionedAllowlist, V0Allowlist, V1Allowlist},
anyhow::{bail, Context, Result},
ffx_scrutiny_verify_args::routes::{default_capability_types, Command},
scrutiny_config::Config,
scrutiny_frontend::{command_builder::CommandBuilder, launcher},
scrutiny_plugins::verify::CapabilityRouteResults,
scrutiny_utils::path::join_and_canonicalize,
serde_json,
std::{collections::HashSet, fs, io::Read, path::PathBuf},
};
struct Query {
capability_types: Vec<String>,
response_level: String,
build_path: PathBuf,
update_package_path: PathBuf,
blobfs_paths: Vec<PathBuf>,
allowlist_paths: Vec<PathBuf>,
component_tree_config_path: Option<PathBuf>,
}
impl From<&Command> for Query {
fn from(cmd: &Command) -> Self {
// argh(default = "vec![...]") does not work due to failed trait bound:
// FromStr on Vec<_>. Apply default when vec is empty.
let capability_types = if cmd.capability_type.len() > 0 {
cmd.capability_type.clone()
} else {
default_capability_types()
}
.into_iter()
.map(|capability_type| capability_type.into())
.collect();
let update_package_path = join_and_canonicalize(&cmd.build_path, &cmd.update);
let blobfs_paths = cmd
.blobfs
.iter()
.map(|blobfs| join_and_canonicalize(&cmd.build_path, blobfs))
.collect();
let allowlist_paths = cmd
.allowlist
.iter()
.map(|allowlist| join_and_canonicalize(&cmd.build_path, allowlist))
.collect();
let component_tree_config_path =
cmd.component_tree_config.as_ref().map(|component_tree_config| {
join_and_canonicalize(&cmd.build_path, &component_tree_config)
});
Query {
capability_types,
response_level: cmd.response_level.clone().into(),
build_path: cmd.build_path.clone(),
update_package_path,
blobfs_paths,
allowlist_paths,
component_tree_config_path,
}
}
}
fn load_allowlist(allowlist_paths: &Vec<PathBuf>) -> Result<Box<dyn AllowlistFilter>> {
let builders = vec![UnversionedAllowlist::new(), V0Allowlist::new(), V1Allowlist::new()];
let mut err = None;
for mut builder in builders.into_iter() {
for path in allowlist_paths.iter() {
let reader: Box<dyn Read> =
Box::new(fs::File::open(path).context("Failed to open allowlist fragment")?);
if let Err(load_err) = builder.load(reader) {
err = Some(load_err);
break;
}
}
if err.is_none() {
return Ok(builder.build());
}
}
Err(err.unwrap())
}
pub async fn verify(cmd: &Command) -> Result<HashSet<PathBuf>> {
let query: Query = cmd.into();
let mut config = Config::run_command_with_plugins(
CommandBuilder::new("verify.capability_routes")
.param("capability_types", query.capability_types.join(" "))
.param("response_level", query.response_level)
.build(),
vec!["DevmgrConfigPlugin", "StaticPkgsPlugin", "CorePlugin", "VerifyPlugin"],
);
config.runtime.model.build_path = query.build_path;
config.runtime.model.update_package_path = query.update_package_path;
config.runtime.model.blobfs_paths = query.blobfs_paths;
config.runtime.model.component_tree_config_path = query.component_tree_config_path;
config.runtime.logging.silent_mode = true;
let results = launcher::launch_from_config(config).context("Failed to launch scrutiny")?;
let route_analysis: CapabilityRouteResults = serde_json5::from_str(&results)
.context(format!("Failed to deserialize verify routes results: {}", results))?;
let allowlist_filter = load_allowlist(&query.allowlist_paths)
.context("Failed to parse all allowlist fragments from supported format")?;
let filtered_analysis = allowlist_filter.filter_analysis(route_analysis.results);
for entry in filtered_analysis.iter() {
if !entry.results.errors.is_empty() {
bail!(
"
Static Capability Flow Analysis Error:
The route verifier failed to verify all capability routes in this build.
See https://fuchsia.dev/fuchsia-src/development/components/troubleshooting#static-analyzer
If the broken route is required for a transition it can be temporarily added
to the allowlist located at: {:?}
Verification Errors:
{}",
query.allowlist_paths,
serde_json::to_string_pretty(&filtered_analysis).unwrap()
);
}
}
Ok(route_analysis.deps)
}