circomspect_program_structure/utils/
sarif_conversion.rs1use codespan_reporting::files::Files;
2use log::{debug, trace};
3use serde_sarif::sarif;
4use std::collections::HashSet;
5use std::fmt;
6use std::ops::Range;
7use std::path::PathBuf;
8use thiserror::Error;
9
10use crate::report::{Report, ReportCollection, ReportLabel};
11use crate::file_definition::{FileID, FileLibrary};
12
13const SARIF_VERSION: &str = "2.1.0";
15const DRIVER_NAME: &str = "Circomspect";
16const ORGANIZATION: &str = "Trail of Bits";
17const DOWNLOAD_URI: &str = "https://github.com/trailofbits/circomspect";
18
19pub trait ToSarif {
21 type Sarif;
22 type Error;
23
24 fn to_sarif(&self, files: &FileLibrary) -> Result<Self::Sarif, Self::Error>;
26}
27
28impl ToSarif for ReportCollection {
29 type Sarif = sarif::Sarif;
30 type Error = SarifError;
31
32 fn to_sarif(&self, files: &FileLibrary) -> Result<Self::Sarif, Self::Error> {
33 debug!("converting report collection to Sarif-format");
34 trace!("building reporting descriptors");
36 let rules = self
37 .iter()
38 .map(|report| (report.name(), report.id()))
39 .collect::<HashSet<_>>()
40 .iter()
41 .map(|(name, id)| {
42 sarif::ReportingDescriptorBuilder::default().name(name).id(id).build()
43 })
44 .collect::<Result<Vec<_>, _>>()
45 .map_err(SarifError::from)?;
46 trace!("building tool");
50 let driver = sarif::ToolComponentBuilder::default()
51 .name(DRIVER_NAME)
52 .organization(ORGANIZATION)
53 .download_uri(DOWNLOAD_URI)
54 .rules(rules)
55 .build()?;
56 let tool = sarif::ToolBuilder::default().driver(driver).build()?;
57 trace!("building run");
59 let results =
60 self.iter().map(|report| report.to_sarif(files)).collect::<SarifResult<Vec<_>>>()?;
61 let run = sarif::RunBuilder::default().tool(tool).results(results).build()?;
62 trace!("building main Sarif object");
64 let sarif = sarif::SarifBuilder::default().runs(vec![run]).version(SARIF_VERSION).build();
65 sarif.map_err(SarifError::from)
66 }
67}
68
69impl ToSarif for Report {
70 type Sarif = sarif::Result;
71 type Error = SarifError;
72
73 fn to_sarif(&self, files: &FileLibrary) -> SarifResult<sarif::Result> {
74 let level = self.category().to_level();
75 let rule_id = self.id();
76 trace!("building message");
78 let message = sarif::MessageBuilder::default().text(self.message()).build()?;
79 trace!("building locations");
81 let locations = self
82 .primary()
83 .iter()
84 .map(|label| label.to_sarif(files))
85 .collect::<SarifResult<Vec<_>>>()?;
86 let related_locations = self
87 .secondary()
88 .iter()
89 .map(|label| label.to_sarif(files))
90 .collect::<SarifResult<Vec<_>>>()?;
91 let rule = sarif::ReportingDescriptorReferenceBuilder::default()
93 .id(&rule_id)
94 .build()
95 .map_err(SarifError::from)?;
96 trace!("building result");
98 sarif::ResultBuilder::default()
99 .level(level)
100 .message(message)
101 .rule_id(rule_id)
102 .rule(rule)
103 .locations(locations)
104 .related_locations(related_locations)
105 .build()
106 .map_err(SarifError::from)
107 }
108}
109
110impl ToSarif for ReportLabel {
111 type Sarif = sarif::Location;
112 type Error = SarifError;
113
114 fn to_sarif(&self, files: &FileLibrary) -> SarifResult<sarif::Location> {
115 trace!("building artifact location");
117 let file_uri = self.file_id.to_uri(files)?;
118 let artifact_location = sarif::ArtifactLocationBuilder::default().uri(file_uri).build()?;
119 trace!("building region");
121 assert!(self.range.start <= self.range.end);
122 let start = files
123 .to_storage()
124 .location(self.file_id, self.range.start)
125 .map_err(|_| SarifError::UnknownLocation(self.file_id, self.range.clone()))?;
126 let end = files
127 .to_storage()
128 .location(self.file_id, self.range.end)
129 .map_err(|_| SarifError::UnknownLocation(self.file_id, self.range.clone()))?;
130 let region = sarif::RegionBuilder::default()
131 .start_line(start.line_number as i64)
132 .start_column(start.column_number as i64)
133 .end_line(end.line_number as i64)
134 .end_column(end.column_number as i64)
135 .build()?;
136 trace!("building physical location");
138 let physical_location = sarif::PhysicalLocationBuilder::default()
139 .artifact_location(artifact_location)
140 .region(region)
141 .build()?;
142 trace!("building message");
144 let message = sarif::MessageBuilder::default().text(self.message.clone()).build()?;
145 trace!("building location");
147 sarif::LocationBuilder::default()
148 .physical_location(physical_location)
149 .id(0)
150 .message(message)
151 .build()
152 .map_err(SarifError::from)
153 }
154}
155
156trait ToUri {
157 type Error;
158 fn to_uri(&self, files: &FileLibrary) -> Result<String, Self::Error>;
159}
160
161impl ToUri for FileID {
162 type Error = SarifError;
163
164 fn to_uri(&self, files: &FileLibrary) -> Result<String, SarifError> {
165 let path: PathBuf = files
166 .to_storage()
167 .get(*self)
168 .map_err(|_| SarifError::UnknownFile(*self))?
169 .name()
170 .replace('"', "")
171 .into();
172 Ok(format!("file://{}", path.to_str().unwrap()))
174 }
175}
176
177#[derive(Error, Debug)]
178pub enum SarifError {
179 InvalidReportingDescriptorReference(#[from] sarif::ReportingDescriptorReferenceBuilderError),
180 InvalidReportingDescriptor(#[from] sarif::ReportingDescriptorBuilderError),
181 InvalidPhysicalLocationError(#[from] sarif::PhysicalLocationBuilderError),
182 InvalidArtifactLocation(#[from] sarif::ArtifactLocationBuilderError),
183 InvalidToolComponent(#[from] sarif::ToolComponentBuilderError),
184 InvalidLocation(#[from] sarif::LocationBuilderError),
185 InvalidMessage(#[from] sarif::MessageBuilderError),
186 InvalidRegion(#[from] sarif::RegionBuilderError),
187 InvalidResult(#[from] sarif::ResultBuilderError),
188 InvalidRun(#[from] sarif::RunBuilderError),
189 InvalidSarif(#[from] sarif::SarifBuilderError),
190 InvalidTool(#[from] sarif::ToolBuilderError),
191 InvalidFix(#[from] sarif::FixBuilderError),
192 UnknownLocation(FileID, Range<usize>),
193 UnknownFile(FileID),
194}
195
196type SarifResult<T> = Result<T, SarifError>;
197
198impl fmt::Display for SarifError {
199 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200 write!(f, "failed to convert analysis results to Sarif format")
201 }
202}