Skip to content

Commit

Permalink
show only the dependency effects that are propagated to the top-level…
Browse files Browse the repository at this point in the history
… package in extension tree view
  • Loading branch information
lzoghbi committed Sep 17, 2024
1 parent a7a7d6f commit 5dfc633
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 16 deletions.
2 changes: 1 addition & 1 deletion lang_server/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub struct ScanCommandResponse {
#[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
pub struct AuditCommandResponse {
#[serde_as(as = "Vec<(_, _)>")]
effects: HashMap<EffectsResponse, Vec<(EffectsResponse,String)>>,
effects: HashMap<EffectsResponse, Vec<(EffectsResponse, String)>>,
}

impl AuditCommandResponse {
Expand Down
14 changes: 2 additions & 12 deletions lang_server/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{collections::HashMap, path::Path};

use anyhow::{anyhow, Error};
use cargo_scan::{
audit_chain::AuditChain,
audit_chain::{collect_propagated_sinks, AuditChain},
audit_file::{AuditFile, EffectInfo, EffectTree, SafetyAnnotation},
effect::EffectInstance,
ident::CanonicalPath,
Expand Down Expand Up @@ -58,7 +58,6 @@ pub fn add_callers_to_tree(
pub fn get_all_chain_effects(
chain_manifest: &Path,
) -> Result<HashMap<EffectInstance, Vec<(EffectInfo, String)>>, Error> {
let mut effects = HashMap::new();
let mut chain = AuditChain::read_audit_chain(chain_manifest.to_path_buf())?
.ok_or_else(|| {
anyhow!("Couldn't find audit chain manifest at {}", chain_manifest.display())
Expand All @@ -68,14 +67,5 @@ pub fn get_all_chain_effects(
// and update chain before loading all existing effects
let removed_sinks = chain.collect_all_safe_sinks()?;
chain.remove_cross_crate_effects(removed_sinks, &chain.root_crate()?)?;

for crate_id in chain.to_owned().all_crates() {
if let Some(af) = chain.read_audit_file(crate_id)? {
for (effect_instance, audit_tree) in &af.audit_trees {
effects.insert(effect_instance.clone(), audit_tree.get_all_annotations());
}
}
}

Ok(effects)
collect_propagated_sinks(&mut chain)
}
78 changes: 75 additions & 3 deletions src/audit_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use cargo_toml::Manifest;
use clap::Args as ClapArgs;
use log::info;
use petgraph::graph::{DiGraph, NodeIndex};
use petgraph::visit::DfsPostOrder;
use petgraph::visit::{Dfs, DfsPostOrder};
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use std::fs::{create_dir_all, remove_file, File};
Expand All @@ -18,8 +18,8 @@ use std::path::{Path, PathBuf};
use std::str::FromStr;
use toml;

use crate::audit_file::{AuditFile, AuditVersion, DefaultAuditType};
use crate::effect::{EffectType, DEFAULT_EFFECT_TYPES};
use crate::audit_file::{AuditFile, AuditVersion, DefaultAuditType, EffectInfo};
use crate::effect::{EffectInstance, EffectType, DEFAULT_EFFECT_TYPES};
use crate::ident::{replace_hyphens, CanonicalPath, IdentPath};
use crate::util::{load_cargo_toml, CrateId};

Expand Down Expand Up @@ -567,6 +567,78 @@ pub fn create_new_audit_chain(
Ok(chain)
}

/// Collect all the sink calls that are propagated
/// from the dependencies to the top-level package.
pub fn collect_propagated_sinks(
chain: &mut AuditChain,
) -> Result<HashMap<EffectInstance, Vec<(EffectInfo, String)>>> {
let mut current_path: Vec<NodeIndex> = Vec::new();
let mut effects = HashMap::new();

let root_name = chain.root_crate()?;
let lockfile = chain.load_lockfile()?;

let (graph, package_map, root_node) =
make_dependency_graph(&lockfile.packages, &root_name.to_string());
let mut traverse = Dfs::new(&graph, root_node);
while let Some(node) = traverse.next(&graph) {
let package = package_map.get(&node).unwrap();
let id = CrateId::new(package.name.to_string(), package.version.clone());
let af = chain
.read_audit_file(&id)?
.context("Couldn't read audit file while collecting dependency sinks")?;

if node == root_node {
for (effect_instance, audit_tree) in &af.audit_trees {
effects.insert(effect_instance.clone(), audit_tree.get_all_annotations());
}
continue;
}

current_path.push(node);
check_sink_calls(af, &mut effects)?;

// If we have already visited a current package's
// dependency, revisit it now to check if any of
// its public caller-checked functions are called
// in the current package.
for neighbor in graph.neighbors(node) {
if current_path.contains(&neighbor) {
let package = package_map.get(&neighbor).unwrap();
let id = CrateId::new(package.name.to_string(), package.version.clone());
let af = chain.read_audit_file(&id)?.context(
"Couldn't read audit file while collecting dependency sinks",
)?;
check_sink_calls(af, &mut effects)?;
}
}
}

Ok(effects)
}

fn check_sink_calls(
af: AuditFile,
effects: &mut HashMap<EffectInstance, Vec<(EffectInfo, String)>>,
) -> Result<()> {
for (pub_cc_fn, base_effs) in af.pub_caller_checked {
if effects.keys().any(|i| {
*i.callee() == pub_cc_fn && i.caller().crate_name() != pub_cc_fn.crate_name()
}) {
for inst in base_effs {
let tree = af
.audit_trees
.get(&inst)
.ok_or_else(|| anyhow!("couldn't find tree for effect instance"))?;

effects.insert(inst.clone(), tree.get_all_annotations());
}
}
}

Ok(())
}

// Mirror of the above that returns HashSet of sinks
pub fn create_dependency_sinks(
_args: Create,
Expand Down

0 comments on commit 5dfc633

Please sign in to comment.