Skip to content

Commit

Permalink
WIP: added chain auditing functionality in the extension
Browse files Browse the repository at this point in the history
  • Loading branch information
lzoghbi committed Jul 25, 2024
1 parent 57ad7b9 commit 19c5832
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 40 deletions.
5 changes: 5 additions & 0 deletions editor-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
"command": "cargo-scan.create_chain",
"title": "Create Chain",
"category": "cargo-scan"
},
{
"command": "cargo-scan.audit_chain",
"title": "Audit Chain",
"category": "cargo-scan"
}
],
"views": {
Expand Down
21 changes: 20 additions & 1 deletion editor-plugin/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,26 @@ export function registerCommands(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.commands.registerCommand('cargo-scan.create_chain', async () => {
client.sendRequest('cargo-scan.create_chain');
context.globalState.update('annotateEffects', false);
context.globalState.update('annotateEffects', false);
locationsProvider.clear();
annotations.clear();
})
);

context.subscriptions.push(
vscode.commands.registerCommand('cargo-scan.audit_chain', async () => {
const response = await client.sendRequest<AuditResponse>('cargo-scan.audit_chain');
context.globalState.update('annotateEffects', true);
let effectsMap = new Map<EffectResponseData, string>();

response.effects.forEach(x => {
const location = convertLocation(x[0].location);
effectsMap.set({ ...x[0], location }, x[1]);
});

locationsProvider.clear();
locationsProvider.setLocations([...effectsMap.keys()]);
annotations.setPreviousAnnotations(locationsProvider.getGroupedEffects(), effectsMap);
})
);
}
84 changes: 83 additions & 1 deletion lang_server/src/notification.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
use std::path::{Path, PathBuf};

use anyhow::{Context, Error};
use cargo_scan::{
audit_chain::AuditChain,
audit_file::AuditFile,
ident::CanonicalPath,
scanner::{scan_crate, ScanResults},
};
use lsp_types::notification::Notification;
use serde::{Deserialize, Serialize};

use crate::request::EffectsResponse;
use crate::{
location::convert_annotation, request::EffectsResponse, util::find_effect_instance,
};

#[derive(Debug, Deserialize, Serialize)]
pub struct AuditNotificationParams {
Expand All @@ -15,3 +26,74 @@ impl Notification for AuditNotification {
type Params = AuditNotificationParams;
const METHOD: &'static str = "cargo-scan.set_annotation";
}

impl AuditNotification {
pub fn annotate_effects_in_single_audit(
params: AuditNotificationParams,
af: &mut AuditFile,
scan_res: &ScanResults,
audit_file_path: PathBuf,
) -> Result<(), Error> {
let annotation = params.safety_annotation;
let effect = params.effect;

if let Some(tree) = find_effect_instance(af, effect)? {
let new_ann = convert_annotation(annotation);
tree.set_annotation(new_ann);
af.recalc_pub_caller_checked(&scan_res.pub_fns);
af.version += 1;

af.save_to_file(audit_file_path)?;
}

Ok(())
}

pub fn annotate_effects_in_chain_audit(
params: AuditNotificationParams,
chain_manifest: &Path,
scan_res: &ScanResults,
root_crate_path: &PathBuf,
) -> Result<(), Error> {
let annotation = params.safety_annotation;
let effect = params.effect;

let crate_name =
CanonicalPath::new_owned(effect.get_caller()).crate_name().to_string();

if let Some(mut chain) =
AuditChain::read_audit_chain(chain_manifest.to_path_buf())?
{
let crate_id = chain
.resolve_crate_id(&crate_name)
.context(format!("Couldn't resolve crate_name for {}", &crate_name))?;

if let Some(prev_af) = chain.read_audit_file(&crate_id)? {
let mut new_af = prev_af.clone();
if let Some(tree) = find_effect_instance(&mut new_af, effect.clone())? {
let new_ann = convert_annotation(annotation);
tree.set_annotation(new_ann);

if new_af.base_dir != *root_crate_path {
let scan_res =
scan_crate(&new_af.base_dir, &new_af.scanned_effects, true)?;
new_af.recalc_pub_caller_checked(&scan_res.pub_fns);
} else {
new_af.recalc_pub_caller_checked(&scan_res.pub_fns);
};

chain.save_audit_file(&crate_id, &new_af)?;

// update parent crates based off updated effects
let removed_fns = AuditFile::pub_diff(&prev_af, &new_af);
chain
.remove_cross_crate_effects(removed_fns, &chain.root_crate()?)?;

chain.save_to_file()?;
}
}
}

Ok(())
}
}
76 changes: 56 additions & 20 deletions lang_server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ use cargo_scan::{
auditing::chain::{Command, CommandRunner, OuterArgs},
effect::{self},
ident::CanonicalPath,
scanner,
scanner::{self},
util::load_cargo_toml,
};
use home::home_dir;
use log::{debug, info};
use lsp_server::{Connection, Message};
use lsp_types::{
Expand All @@ -18,13 +20,16 @@ use lsp_types::{
use serde::{Deserialize, Serialize};

use crate::{
location::{convert_annotation, to_src_loc},
location::to_src_loc,
notification::{AuditNotification, AuditNotificationParams},
request::{
audit_req, scan_req, AuditCommandResponse, CallerCheckedResponse,
EffectsResponse, ScanCommandResponse,
},
util::{add_callers_to_tree, find_effect_instance, get_new_audit_locs},
util::{
add_callers_to_tree, find_effect_instance, get_all_chain_effects,
get_new_audit_locs,
},
};

#[derive(Serialize, Deserialize, Debug)]
Expand Down Expand Up @@ -95,14 +100,15 @@ fn runner(
.ok_or_else(|| anyhow!("Couldn't get root path from workspace folders"))?;

let root_crate_path = std::path::PathBuf::from_str(root_uri.path())?;
debug!("Crate path received in cargo-scan LSP server: {}", root_crate_path.display());
info!("Crate path received in cargo-scan LSP server: {}", root_crate_path.display());

let scan_res =
scanner::scan_crate(&root_crate_path, effect::DEFAULT_EFFECT_TYPES, false)?;

info!("Starting main server loop\n");
let mut audit_file: Option<AuditFile> = None;
let mut audit_file_path = PathBuf::new();
let mut chain_manifest = PathBuf::new();
for msg in &conn.receiver {
match msg {
Message::Request(req) => {
Expand Down Expand Up @@ -163,16 +169,19 @@ fn runner(
}
"cargo-scan.create_chain" => {
let outer_args = OuterArgs::default();
let root_crate_id = load_cargo_toml(&root_crate_path)?;

if let Some(mut dir) = home_dir() {
dir.push(".cargo_audits");
dir.push("chain_policies");
dir.push(format!("{}.manifest", root_crate_id));

chain_manifest = dir;
};

let create_args = Create {
crate_path: root_crate_path
.to_str()
.unwrap_or("./")
.to_string(),
manifest_path: root_crate_path
.join("policy.manifest")
.to_str()
.unwrap_or("./policy.manifest")
.to_string(),
crate_path: root_crate_path.to_string_lossy().to_string(),
manifest_path: chain_manifest.to_string_lossy().to_string(),
force_overwrite: true,
..Default::default()
};
Expand All @@ -182,6 +191,27 @@ fn runner(
outer_args,
)?;
}
"cargo-scan.audit_chain" => {
let root_crate_id = load_cargo_toml(&root_crate_path)?;
chain_manifest = home_dir()
.ok_or_else(|| anyhow!("Could not find homw directory"))?;
chain_manifest.push(".cargo_audits");
chain_manifest.push("chain_policies");
chain_manifest.push(format!("{}.manifest", root_crate_id));

debug!(
"Auditing chain with manifest path: {}",
chain_manifest.display()
);
let effects = get_all_chain_effects(&chain_manifest)?;
let res = AuditCommandResponse::new(&effects)?.to_json_value()?;

conn.sender.send(Message::Response(lsp_server::Response {
id: req.id,
result: Some(res),
error: None,
}))?;
}
_ => {}
};
}
Expand All @@ -190,15 +220,21 @@ fn runner(
if notif.method == AuditNotification::METHOD {
let params: AuditNotificationParams =
serde_json::from_value(notif.params)?;
let annotation = params.safety_annotation;
let effect = params.effect;

if let Some(af) = audit_file.as_mut() {
if let Some(tree) = find_effect_instance(af, effect)? {
let new_ann = convert_annotation(annotation);
tree.set_annotation(new_ann);
af.save_to_file(audit_file_path.clone())?;
}
AuditNotification::annotate_effects_in_single_audit(
params,
af,
&scan_res,
audit_file_path.clone(),
)?;
} else if chain_manifest.is_file() {
AuditNotification::annotate_effects_in_chain_audit(
params,
&chain_manifest,
&scan_res,
&root_crate_path,
)?;
}
}
}
Expand Down
26 changes: 25 additions & 1 deletion lang_server/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use anyhow::Error;
use std::{collections::HashMap, path::Path};

use anyhow::{anyhow, Error};
use cargo_scan::{
audit_chain::AuditChain,
audit_file::{AuditFile, EffectInfo, EffectTree, SafetyAnnotation},
effect::EffectInstance,
ident::CanonicalPath,
scanner::ScanResults,
};
Expand Down Expand Up @@ -53,3 +57,23 @@ pub fn add_callers_to_tree(
*tree = EffectTree::Branch(curr_effect, new_audit_locs);
}
}

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())
})?;

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)
}
34 changes: 17 additions & 17 deletions src/audit_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ use std::str::FromStr;
use toml;

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

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AuditChain {
#[serde(skip)]
manifest_path: PathBuf,
Expand Down Expand Up @@ -127,7 +127,9 @@ impl AuditChain {
pub fn resolve_all_crates(&self, search_name: &str) -> Vec<CrateId> {
let mut res = Vec::new();
for (crate_id, _) in self.crate_policies.iter() {
if crate_id.crate_name == search_name {
let mut crate_name = crate_id.crate_name.clone();
replace_hyphens(&mut crate_name);
if crate_name == search_name || crate_id.crate_name == search_name {
res.push(crate_id.clone());
}
}
Expand Down Expand Up @@ -338,25 +340,23 @@ impl Create {

impl Default for Create {
fn default() -> Self {
let audit_path = home::home_dir()
.map(|mut dir| {
dir.push(".cargo_audits");
dir
})
.unwrap_or_else(|| PathBuf::from(".audit_files"))
.to_string_lossy()
.to_string();

Self {
crate_path: ".".to_string(),
manifest_path: "./policy.manifest".to_string(),
audit_path: ".audit_files".to_string(),
audit_path,
force_overwrite: false,
download_root_crate: None,
download_version: None,
effect_types: [
EffectType::SinkCall,
EffectType::FFICall,
EffectType::UnsafeCall,
EffectType::RawPointer,
EffectType::UnionField,
EffectType::StaticMut,
EffectType::StaticExt,
EffectType::FnPtrCreation,
EffectType::ClosureCreation,
]
.to_vec(),
effect_types: DEFAULT_EFFECT_TYPES.to_vec(),
}
}
}
Expand Down

0 comments on commit 19c5832

Please sign in to comment.