1use crate::error::{Error, Result};
2
3use super::commands::{
4 CCAttestationCommands, DatasetCommands, EvaluationCommands, ManifestCommands, ModelCommands,
5 PipelineCommands, SoftwareCommands,
6};
7use crate::cc_attestation;
8use crate::manifest;
9use crate::manifest::config::ManifestCreationConfig;
10use crate::manifest::dataset::list_dataset_manifests;
11use crate::slsa;
12use crate::storage::database::DatabaseStorage;
13use crate::storage::filesystem::FilesystemStorage;
14use crate::storage::rekor::RekorStorage;
15
16use crate::StorageBackend;
17
18pub fn handle_dataset_command(cmd: DatasetCommands) -> Result<()> {
19 let _storage = RekorStorage::new()?;
20 match cmd {
21 DatasetCommands::Create {
22 paths,
23 ingredient_names,
24 name,
25 author_org,
26 author_name,
27 description,
28 linked_manifests,
29 storage_type,
30 storage_url,
31 print,
32 encoding,
33 key,
34 hash_alg,
35 with_tdx,
36 } => {
37 let storage: Option<&'static dyn StorageBackend> = match storage_type.as_str() {
38 "database" => {
39 let db_storage = Box::new(DatabaseStorage::new(*storage_url.clone())?);
40 Some(Box::leak(db_storage))
41 }
42 "rekor" => {
43 let rekor_storage = Box::new(RekorStorage::new_with_url(*storage_url.clone())?);
44 Some(Box::leak(rekor_storage))
45 }
46 "local-fs" => {
47 let fs_storage = Box::new(FilesystemStorage::new(storage_url.as_str())?);
48 Some(Box::leak(fs_storage))
49 }
50 _ => None,
51 };
52
53 let config = ManifestCreationConfig {
54 paths,
55 ingredient_names,
56 name,
57 author_org,
58 author_name,
59 description,
60 linked_manifests,
61 storage,
62 print,
63 output_encoding: encoding,
64 key_path: key,
65 hash_alg: hash_alg.to_cose_algorithm(),
66 with_cc: with_tdx,
67 software_type: None,
68 version: None,
69 custom_fields: None,
70 };
71
72 manifest::create_dataset_manifest(config)
73 }
74 DatasetCommands::List {
75 storage_type,
76 storage_url,
77 } => {
78 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
79 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
80 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
81 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
82 _ => return Err(Error::Validation("Invalid storage type".to_string())),
83 };
84
85 list_dataset_manifests(storage.as_ref())
86 }
87 DatasetCommands::Verify {
88 id,
89 storage_type,
90 storage_url,
91 } => {
92 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
93 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
94 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
95 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
96 _ => return Err(Error::Validation("Invalid storage type".to_string())),
97 };
98
99 manifest::verify_dataset_manifest(&id, storage.as_ref())
100 }
101 }
102}
103
104pub fn handle_model_command(cmd: ModelCommands) -> Result<()> {
105 let _storage = RekorStorage::new()?;
106 match cmd {
107 ModelCommands::Create {
108 paths,
109 ingredient_names,
110 name,
111 author_org,
112 author_name,
113 description,
114 linked_manifests,
115 storage_type,
116 storage_url,
117 print,
118 encoding,
119 format,
120 key,
121 hash_alg,
122 with_tdx,
123 } => {
124 let storage: Option<&'static dyn StorageBackend> = match storage_type.as_str() {
125 "database" => {
126 let db_storage = Box::new(DatabaseStorage::new(*storage_url.clone())?);
127 Some(Box::leak(db_storage))
128 }
129 "rekor" => {
130 let rekor_storage = Box::new(RekorStorage::new_with_url(*storage_url.clone())?);
131 Some(Box::leak(rekor_storage))
132 }
133 "local-fs" => {
134 let fs_storage = Box::new(FilesystemStorage::new(storage_url.as_str())?);
135 Some(Box::leak(fs_storage))
136 }
137 _ => None,
138 };
139
140 let config = ManifestCreationConfig {
141 paths,
142 ingredient_names,
143 name,
144 author_org,
145 author_name,
146 description,
147 linked_manifests,
148 storage,
149 print,
150 output_encoding: encoding,
151 key_path: key,
152 hash_alg: hash_alg.to_cose_algorithm(),
153 with_cc: with_tdx,
154 software_type: None,
155 version: None,
156 custom_fields: None,
157 };
158
159 match format.as_str() {
160 "standalone" => manifest::create_model_manifest(config),
161 "oms" => manifest::common::create_oms_manifest(config),
162 _ => {
163 return Err(Error::InitializationError(
164 "Unsupported output format".to_string(),
165 ));
166 }
167 }
168 }
169 ModelCommands::List {
170 storage_type,
171 storage_url,
172 } => {
173 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
174 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
175 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
176 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
177 _ => return Err(Error::Validation("Invalid storage type".to_string())),
178 };
179
180 manifest::list_model_manifest(storage.as_ref())
181 }
182 ModelCommands::Verify {
183 id,
184 storage_type,
185 storage_url,
186 } => {
187 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
188 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
189 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
190 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
191 _ => return Err(Error::Validation("Invalid storage type".to_string())),
192 };
193
194 manifest::verify_model_manifest(&id, storage.as_ref())
195 }
196 ModelCommands::LinkDataset {
197 model_id,
198 dataset_id,
199 storage_type,
200 storage_url,
201 } => {
202 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
203 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
204 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
205 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
206 _ => return Err(Error::Validation("Invalid storage type".to_string())),
207 };
208
209 let updated_manifest =
210 manifest::linking::link_dataset_to_model(&model_id, &dataset_id, storage.as_ref())?;
211
212 println!("Successfully linked dataset {dataset_id} to model {model_id}");
213 println!("Updated manifest ID: {}", updated_manifest.instance_id);
214
215 Ok(())
216 }
217 }
218}
219
220pub fn handle_manifest_command(cmd: ManifestCommands) -> Result<()> {
221 match cmd {
222 ManifestCommands::Link {
223 source,
224 target,
225 storage_type,
226 storage_url,
227 } => {
228 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
229 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
230 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
231 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
232 _ => return Err(Error::Validation("Invalid storage type".to_string())),
233 };
234
235 manifest::link_manifests(&source, &target, &*storage)
236 }
237 ManifestCommands::Show {
238 id,
239 storage_type,
240 storage_url,
241 } => {
242 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
243 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
244 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
245 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
246 _ => return Err(Error::Validation("Invalid storage type".to_string())),
247 };
248
249 manifest::show_manifest(&id, &*storage)
250 }
251 ManifestCommands::Validate {
252 id,
253 storage_type,
254 storage_url,
255 } => {
256 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
257 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
258 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
259 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
260 _ => return Err(Error::Validation("Invalid storage type".to_string())),
261 };
262
263 manifest::validate_linked_manifests(&id, &*storage)
264 }
265 ManifestCommands::VerifyLink {
266 source,
267 target,
268 storage_type,
269 storage_url,
270 } => {
271 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
272 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
273 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
274 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
275 _ => return Err(Error::Validation("Invalid storage type".to_string())),
276 };
277
278 let result = manifest::verify_manifest_link(&source, &target, &*storage)?;
279 if result {
280 println!("Link verification successful");
281 Ok(())
282 } else {
283 Err(Error::Validation("Link verification failed".to_string()))
284 }
285 }
286 ManifestCommands::Export {
287 id,
288 storage_type,
289 storage_url,
290 encoding,
291 output,
292 max_depth,
293 } => {
294 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
295 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
296 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
297 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
298 _ => return Err(Error::Validation("Invalid storage type".to_string())),
299 };
300
301 manifest::export_provenance(
302 &id,
303 &*storage,
304 encoding.as_str(),
305 output.as_deref(),
306 max_depth,
307 )
308 }
309 }
310}
311
312pub fn handle_evaluation_command(cmd: EvaluationCommands) -> Result<()> {
313 match cmd {
314 EvaluationCommands::Create {
315 path,
316 name,
317 model_id,
318 dataset_id,
319 metrics,
320 author_org,
321 author_name,
322 description,
323 storage_type,
324 storage_url,
325 print,
326 encoding,
327 key,
328 hash_alg,
329 } => {
330 let storage: Option<&'static dyn StorageBackend> = match storage_type.as_str() {
331 "database" => {
332 let db_storage = Box::new(DatabaseStorage::new(*storage_url.clone())?);
333 Some(Box::leak(db_storage))
334 }
335 "rekor" => {
336 let rekor_storage = Box::new(RekorStorage::new_with_url(*storage_url.clone())?);
337 Some(Box::leak(rekor_storage))
338 }
339 "local-fs" => {
340 let fs_storage = Box::new(FilesystemStorage::new(storage_url.as_str())?);
341 Some(Box::leak(fs_storage))
342 }
343 _ => None,
344 };
345
346 let config = ManifestCreationConfig {
347 paths: vec![path],
348 ingredient_names: vec!["Evaluation Results".to_string()],
349 name,
350 author_org,
351 author_name,
352 description,
353 linked_manifests: None, storage,
355 print,
356 output_encoding: encoding,
357 key_path: key,
358 hash_alg: hash_alg.to_cose_algorithm(),
359 with_cc: false,
360 software_type: None,
361 version: None,
362 custom_fields: None, };
364
365 manifest::evaluation::create_manifest(config, model_id, dataset_id, metrics)
366 }
367 EvaluationCommands::List {
368 storage_type,
369 storage_url,
370 } => {
371 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
372 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
373 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
374 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
375 _ => return Err(Error::Validation("Invalid storage type".to_string())),
376 };
377
378 manifest::evaluation::list_evaluation_manifests(storage.as_ref())
379 }
380 EvaluationCommands::Verify {
381 id,
382 storage_type,
383 storage_url,
384 } => {
385 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
386 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
387 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
388 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
389 _ => return Err(Error::Validation("Invalid storage type".to_string())),
390 };
391
392 manifest::evaluation::verify_evaluation_manifest(&id, storage.as_ref())
393 }
394 }
395}
396
397pub fn handle_cc_attestation_command(cmd: CCAttestationCommands) -> Result<()> {
398 match cmd {
399 CCAttestationCommands::Show => {
400 let _r = cc_attestation::get_report(true).unwrap();
401 Ok(())
402 }
403
404 CCAttestationCommands::GetLaunchMeasurement => {
405 let m = cc_attestation::get_launch_measurement().unwrap();
406 println!("Launch measurement raw bytes: 0x{}", hex::encode(m));
407 Ok(())
408 }
409
410 CCAttestationCommands::VerifyLaunch { host_platform } => {
411 let result = cc_attestation::verify_launch_endorsement(&host_platform).unwrap();
412 if result {
413 println!(
414 "Passed: launch endorsement verification for {host_platform} host platform"
415 );
416 } else {
417 println!(
418 "Failed: launch endorsement verification for {host_platform} host platform"
419 );
420 }
421 Ok(())
422 }
423 }
424}
425
426pub fn handle_software_command(cmd: SoftwareCommands) -> Result<()> {
427 match cmd {
428 SoftwareCommands::Create {
429 paths,
430 ingredient_names,
431 name,
432 software_type,
433 version,
434 author_org,
435 author_name,
436 description,
437 linked_manifests,
438 storage_type,
439 storage_url,
440 print,
441 encoding,
442 key,
443 hash_alg,
444 with_tdx,
445 } => {
446 let storage: Option<&'static dyn StorageBackend> = match storage_type.as_str() {
447 "database" => {
448 let db_storage = Box::new(DatabaseStorage::new(*storage_url.clone())?);
449 Some(Box::leak(db_storage))
450 }
451 "rekor" => {
452 let rekor_storage = Box::new(RekorStorage::new_with_url(*storage_url.clone())?);
453 Some(Box::leak(rekor_storage))
454 }
455 "local-fs" => {
456 let fs_storage = Box::new(FilesystemStorage::new(storage_url.as_str())?);
457 Some(Box::leak(fs_storage))
458 }
459 _ => None,
460 };
461
462 let config = ManifestCreationConfig {
463 paths,
464 ingredient_names,
465 name,
466 author_org,
467 author_name,
468 description,
469 linked_manifests,
470 storage,
471 print,
472 output_encoding: encoding,
473 key_path: key,
474 hash_alg: hash_alg.to_cose_algorithm(),
475 with_cc: with_tdx,
476 software_type: Some(software_type.clone()),
477 version: version.clone(),
478 custom_fields: None,
479 };
480
481 manifest::software::create_manifest(config, software_type, version)
482 }
483 SoftwareCommands::List {
484 storage_type,
485 storage_url,
486 } => {
487 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
488 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
489 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
490 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
491 _ => return Err(Error::Validation("Invalid storage type".to_string())),
492 };
493
494 manifest::software::list_software_manifests(storage.as_ref())
495 }
496 SoftwareCommands::Verify {
497 id,
498 storage_type,
499 storage_url,
500 } => {
501 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
502 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
503 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
504 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
505 _ => return Err(Error::Validation("Invalid storage type".to_string())),
506 };
507
508 manifest::software::verify_software_manifest(&id, storage.as_ref())
509 }
510 SoftwareCommands::LinkModel {
511 software_id,
512 model_id,
513 storage_type,
514 storage_url,
515 } => {
516 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
517 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
518 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
519 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
520 _ => return Err(Error::Validation("Invalid storage type".to_string())),
521 };
522
523 manifest::link_manifests(&model_id, &software_id, storage.as_ref())
525 }
526 SoftwareCommands::LinkDataset {
527 software_id,
528 dataset_id,
529 storage_type,
530 storage_url,
531 } => {
532 let storage: Box<dyn StorageBackend> = match storage_type.as_str() {
533 "database" => Box::new(DatabaseStorage::new(*storage_url.clone())?),
534 "rekor" => Box::new(RekorStorage::new_with_url(*storage_url.clone())?),
535 "local-fs" => Box::new(FilesystemStorage::new(storage_url.as_str())?),
536 _ => return Err(Error::Validation("Invalid storage type".to_string())),
537 };
538
539 manifest::link_manifests(&dataset_id, &software_id, storage.as_ref())
541 }
542 }
543}
544
545pub fn handle_pipeline_command(cmd: PipelineCommands) -> Result<()> {
546 match cmd {
547 PipelineCommands::GenerateProvenance {
548 inputs,
549 pipeline,
550 products,
551 key,
552 hash_alg,
553 encoding,
554 print,
555 storage_type,
556 storage_url,
557 with_tdx,
558 } => {
559 let storage: Option<&'static dyn StorageBackend> = match storage_type.as_str() {
560 "local-fs" => {
561 let fs_storage = Box::new(FilesystemStorage::new(storage_url.as_str())?);
562 Some(Box::leak(fs_storage))
563 }
564 _ => None,
565 };
566
567 slsa::cli::generate_build_provenance(
568 inputs,
569 pipeline,
570 products,
571 key,
572 hash_alg.to_cose_algorithm(),
573 encoding,
574 print,
575 storage,
576 with_tdx,
577 )
578 }
579 }
580}