rustsec/database/
query.rs1use crate::{
4 SourceId,
5 advisory::{Advisory, Severity},
6 collection::Collection,
7 package::{self, Package},
8};
9use platforms::target::{Arch, OS};
10use semver::Version;
11
12#[derive(Clone, Debug)]
14pub struct Query {
15 pub(super) collection: Option<Collection>,
17
18 pub(super) package_name: Option<package::Name>,
20
21 package_version: Option<Version>,
23
24 package_source: Option<SourceId>,
26
27 severity: Option<Severity>,
29
30 target_arch: Vec<Arch>,
32
33 target_os: Vec<OS>,
35
36 year: Option<u32>,
38
39 withdrawn: Option<bool>,
43
44 informational: Option<bool>,
46}
47
48impl Query {
49 pub fn new() -> Self {
59 Self {
60 collection: None,
61 package_name: None,
62 package_version: None,
63 package_source: None,
64 severity: None,
65 target_arch: Default::default(),
66 target_os: Default::default(),
67 year: None,
68 withdrawn: None,
69 informational: None,
70 }
71 }
72
73 pub fn crate_scope() -> Self {
79 Self::new()
80 .collection(Collection::Crates)
81 .withdrawn(false)
82 .informational(false)
83 }
84
85 pub fn collection(mut self, collection: Collection) -> Self {
87 self.collection = Some(collection);
88 self
89 }
90
91 #[allow(clippy::assigning_clones)]
93 pub fn package(mut self, package: &Package) -> Self {
94 self.package_name = Some(package.name.clone());
95 self.package_version = Some(package.version.clone());
96 self.package_source = package.source.clone();
97 self
98 }
99
100 pub fn package_name(mut self, name: package::Name) -> Self {
102 self.package_name = Some(name);
103 self
104 }
105
106 pub fn package_version(mut self, version: Version) -> Self {
108 self.package_version = Some(version);
109 self
110 }
111
112 pub fn package_source(mut self, source: SourceId) -> Self {
114 self.package_source = Some(source);
115 self
116 }
117
118 pub fn severity(mut self, severity: Severity) -> Self {
124 self.severity = Some(severity);
125 self
126 }
127
128 pub fn target_arch(mut self, arch: Vec<Arch>) -> Self {
130 self.target_arch = arch;
131 self
132 }
133
134 pub fn target_os(mut self, os: Vec<OS>) -> Self {
136 self.target_os = os;
137 self
138 }
139
140 pub fn year(mut self, year: u32) -> Self {
142 self.year = Some(year);
143 self
144 }
145
146 pub fn withdrawn(mut self, setting: bool) -> Self {
150 self.withdrawn = Some(setting);
151 self
152 }
153
154 pub fn informational(mut self, setting: bool) -> Self {
157 self.informational = Some(setting);
158 self
159 }
160
161 pub fn matches(&self, advisory: &Advisory) -> bool {
163 if let Some(collection) = self.collection {
164 if Some(collection) != advisory.metadata.collection {
165 return false;
166 }
167 }
168
169 if let Some(package_name) = &self.package_name {
170 if package_name != &advisory.metadata.package {
171 return false;
172 }
173 }
174
175 if let Some(package_version) = &self.package_version {
176 if !advisory.versions.is_vulnerable(package_version) {
177 return false;
178 }
179 }
180
181 if let Some(package_source) = &self.package_source {
182 let advisory_source = advisory
183 .metadata
184 .source
185 .as_ref()
186 .cloned()
187 .unwrap_or_default();
188
189 if advisory_source.kind() != package_source.kind()
191 || advisory_source.url() != package_source.url()
192 {
193 return false;
194 }
195 }
196
197 if let Some(severity_threshold) = self.severity {
198 if let Some(advisory_severity) = advisory.severity() {
199 if advisory_severity < severity_threshold {
200 return false;
201 }
202 }
203 }
204
205 if let Some(affected) = &advisory.affected {
206 if !affected.arch.is_empty()
207 && !self.target_arch.is_empty()
208 && !self
209 .target_arch
210 .iter()
211 .any(|target_arch| affected.arch.contains(target_arch))
212 {
213 return false;
214 }
215
216 if !affected.os.is_empty()
217 && !self.target_os.is_empty()
218 && !self
219 .target_os
220 .iter()
221 .any(|target_os| affected.os.contains(target_os))
222 {
223 return false;
224 }
225 }
226
227 if let Some(query_year) = self.year {
228 if let Some(advisory_year) = advisory.metadata.id.year() {
229 if query_year != advisory_year {
230 return false;
231 }
232 }
233 }
234
235 if let Some(withdrawn) = self.withdrawn {
236 if withdrawn != advisory.metadata.withdrawn.is_some() {
237 return false;
238 }
239 }
240
241 if let Some(informational) = self.informational {
242 if informational != advisory.metadata.informational.is_some() {
243 return false;
244 }
245 }
246
247 true
248 }
249}
250
251impl Default for Query {
252 fn default() -> Query {
253 Query::crate_scope()
254 }
255}