1#![allow(clippy::module_name_repetitions)]
22#![allow(clippy::unused_self)]
23
24use std::str::FromStr;
25
26use askama::Template;
27use chrono::Datelike;
28
29use tracing::info;
30
31use crate::service::source::SourceService;
32use skootrs_model::{
33 security_insights::insights10::{
34 SecurityInsightsVersion100YamlSchema,
35 SecurityInsightsVersion100YamlSchemaContributionPolicy,
36 SecurityInsightsVersion100YamlSchemaDependencies,
37 SecurityInsightsVersion100YamlSchemaDependenciesSbomItem,
38 SecurityInsightsVersion100YamlSchemaDependenciesSbomItemSbomCreation,
39 SecurityInsightsVersion100YamlSchemaHeader,
40 SecurityInsightsVersion100YamlSchemaHeaderSchemaVersion,
41 SecurityInsightsVersion100YamlSchemaProjectLifecycle,
42 SecurityInsightsVersion100YamlSchemaProjectLifecycleStatus,
43 SecurityInsightsVersion100YamlSchemaVulnerabilityReporting,
44 },
45 skootrs::{
46 facet::{
47 APIBundleFacet, APIBundleFacetParams, APIContent, CommonFacetCreateParams,
48 FacetCreateParams, FacetSetCreateParams, InitializedFacet, SourceBundleFacet,
49 SourceBundleFacetCreateParams, SourceFile, SourceFileContent, SupportedFacetType,
50 },
51 label::Label,
52 InitializedEcosystem, InitializedGithubRepo, InitializedRepo, SkootError,
53 },
54};
55
56use super::source::LocalSourceService;
57
58#[derive(Debug)]
60pub struct LocalFacetService {}
61
62pub trait RootFacetService {
66 fn initialize(
67 &self,
68 params: FacetCreateParams,
69 ) -> impl std::future::Future<Output = Result<InitializedFacet, SkootError>> + Send;
70 fn initialize_all(
71 &self,
72 params: FacetSetCreateParams,
73 ) -> impl std::future::Future<Output = Result<Vec<InitializedFacet>, SkootError>> + Send;
74}
75
76pub trait SourceBundleFacetService {
82 fn initialize(
88 &self,
89 params: SourceBundleFacetCreateParams,
90 ) -> Result<SourceBundleFacet, SkootError>;
91}
92
93impl SourceBundleFacetService for LocalFacetService {
94 fn initialize(
100 &self,
101 params: SourceBundleFacetCreateParams,
102 ) -> Result<SourceBundleFacet, SkootError> {
103 let source_service = LocalSourceService {};
104 let default_source_bundle_content_handler = DefaultSourceBundleContentHandler {};
105 let language_specific_source_bundle_content_handler = match params.common.ecosystem {
107 InitializedEcosystem::Go(_) => GoGithubSourceBundleContentHandler {},
108 InitializedEcosystem::Maven(_) => todo!(),
109 };
110
111 let source_bundle_content = match params.facet_type {
112 SupportedFacetType::Readme
113 | SupportedFacetType::License
114 | SupportedFacetType::SecurityPolicy
115 | SupportedFacetType::Scorecard
116 | SupportedFacetType::SecurityInsights => {
117 default_source_bundle_content_handler.generate_content(¶ms)?
118 }
119 SupportedFacetType::Gitignore
120 | SupportedFacetType::SLSABuild
121 | SupportedFacetType::DependencyUpdateTool => {
122 language_specific_source_bundle_content_handler.generate_content(¶ms)?
123 }
124 SupportedFacetType::SBOMGenerator => todo!(),
125 SupportedFacetType::StaticCodeAnalysis => todo!(),
126 SupportedFacetType::BranchProtection => todo!(),
127 SupportedFacetType::CodeReview => todo!(),
128 SupportedFacetType::Fuzzing => {
129 language_specific_source_bundle_content_handler.generate_content(¶ms)?
130 }
131 SupportedFacetType::PublishPackages => todo!(),
132 SupportedFacetType::PinnedDependencies => todo!(),
133 SupportedFacetType::SAST => {
134 default_source_bundle_content_handler.generate_content(¶ms)?
135 }
136 SupportedFacetType::VulnerabilityScanner => todo!(),
137 SupportedFacetType::GUACForwardingConfig => todo!(),
138 SupportedFacetType::Allstar => todo!(),
139 SupportedFacetType::DefaultSourceCode => {
140 language_specific_source_bundle_content_handler.generate_content(¶ms)?
141 }
142 SupportedFacetType::VulnerabilityReporting => {
143 unimplemented!("VulnerabilityReporting is not implemented for source bundles")
144 }
145 SupportedFacetType::Other => todo!(),
146 };
147
148 for source_file_content in &source_bundle_content.source_files_content {
149 info!(
150 "Starting to write file {} to {}",
151 source_file_content.name, source_file_content.path
152 );
153 source_service.write_file(
154 params.common.source.clone(),
155 source_file_content.path.clone(),
156 source_file_content.name.clone(),
157 source_file_content.content.clone(),
158 )?;
159 }
160
161 let source_files: Vec<SourceFile> = source_bundle_content
162 .source_files_content
163 .iter()
164 .map(|source_file_content| {
165 Ok::<SourceFile, SkootError>(SourceFile {
166 name: source_file_content.name.clone(),
167 path: source_file_content.path.clone(),
168 hash: source_service.hash_file(
169 ¶ms.common.source,
170 source_file_content.path.clone(),
171 source_file_content.name.clone(),
172 )?,
173 })
174 })
175 .collect::<Result<Vec<_>, _>>()?;
176
177 let source_bundle_facet = SourceBundleFacet {
178 source_files: Some(source_files),
179 facet_type: params.facet_type,
180 source_files_content: None,
181 labels: params.labels,
182 };
183
184 Ok(source_bundle_facet)
185 }
186}
187
188pub trait APIBundleFacetService {
193 fn initialize(
194 &self,
195 params: APIBundleFacetParams,
196 ) -> impl std::future::Future<Output = Result<APIBundleFacet, SkootError>> + Send;
197}
198
199impl APIBundleFacetService for LocalFacetService {
200 async fn initialize(&self, params: APIBundleFacetParams) -> Result<APIBundleFacet, SkootError> {
201 match params.facet_type {
203 SupportedFacetType::CodeReview
204 | SupportedFacetType::BranchProtection
205 | SupportedFacetType::VulnerabilityReporting => {
206 let github_api_bundle_handler = GithubAPIBundleHandler {};
207 let api_bundle_facet = github_api_bundle_handler.generate(¶ms).await?;
208 Ok(api_bundle_facet)
209 }
210 _ => todo!("Not implemented yet"),
211 }
212 }
213}
214
215pub struct SourceBundleContent {
217 pub source_files_content: Vec<SourceFileContent>,
218 pub facet_type: SupportedFacetType,
219}
220
221impl RootFacetService for LocalFacetService {
222 async fn initialize(&self, params: FacetCreateParams) -> Result<InitializedFacet, SkootError> {
223 match params {
224 FacetCreateParams::SourceBundle(params) => {
225 let source_bundle_facet = SourceBundleFacetService::initialize(self, params)?;
226 Ok(InitializedFacet::SourceBundle(source_bundle_facet))
227 }
228 FacetCreateParams::APIBundle(params) => {
229 let api_bundle_facet = APIBundleFacetService::initialize(self, params).await?;
230 Ok(InitializedFacet::APIBundle(api_bundle_facet))
231 }
232 }
233 }
234
235 async fn initialize_all(
236 &self,
237 params: FacetSetCreateParams,
238 ) -> Result<Vec<InitializedFacet>, SkootError> {
239 let futures = params
240 .facets_params
241 .iter()
242 .map(move |params| RootFacetService::initialize(self, params.clone()));
243
244 let results = futures::future::try_join_all(futures).await?;
245 Ok(results)
246 }
247}
248
249trait APIBundleHandler {
253 async fn generate(&self, params: &APIBundleFacetParams) -> Result<APIBundleFacet, SkootError>;
254}
255
256struct GithubAPIBundleHandler {}
259
260impl APIBundleHandler for GithubAPIBundleHandler {
261 async fn generate(&self, params: &APIBundleFacetParams) -> Result<APIBundleFacet, SkootError> {
262 let InitializedRepo::Github(repo) = ¶ms.common.repo;
263 match params.facet_type {
264 SupportedFacetType::BranchProtection => self.generate_branch_protection(repo).await,
265 SupportedFacetType::VulnerabilityReporting => {
266 self.generate_vulnerability_reporting(repo).await
267 }
268 _ => todo!("Not implemented yet"),
269 }
270 }
271}
272
273impl GithubAPIBundleHandler {
274 async fn generate_branch_protection(
275 &self,
276 repo: &InitializedGithubRepo,
277 ) -> Result<APIBundleFacet, SkootError> {
278 let enforce_branch_protection_endpoint = format!(
279 "/repos/{owner}/{repo}/branches/{branch}/protection",
280 owner = repo.organization.get_name(),
281 repo = repo.name,
282 branch = "main",
283 );
284 info!(
285 "Enabling branch protection for {}",
286 enforce_branch_protection_endpoint
287 );
288 let enforce_branch_protection_body = serde_json::json!({
290 "enforce_admins": true,
291 "required_pull_request_reviews": null,
292 "required_status_checks": null,
293 "restrictions": null,
294 "required_linear_history": true,
295 "allow_force_pushes": false,
296 "allow_deletions": null,
297 });
298
299 let o: octocrab::Octocrab = octocrab::Octocrab::builder()
301 .personal_token(
302 std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env var must be populated"),
303 )
304 .build()?;
305 octocrab::initialise(o);
306 let response: serde_json::Value = octocrab::instance()
307 .put(
308 &enforce_branch_protection_endpoint,
309 Some(&enforce_branch_protection_body),
310 )
311 .await?;
312
313 let apis = vec![APIContent {
314 name: "Enforce Branch Protection".to_string(),
315 url: enforce_branch_protection_endpoint,
316 response: serde_json::to_string_pretty(&response)?,
317 }];
318
319 Ok(APIBundleFacet {
320 facet_type: SupportedFacetType::BranchProtection,
321 apis,
322 labels: vec![],
323 })
324 }
325
326 async fn generate_vulnerability_reporting(
327 &self,
328 repo: &InitializedGithubRepo,
329 ) -> Result<APIBundleFacet, SkootError> {
330 let vulnerability_reporting_endpoint = format!(
331 "/repos/{owner}/{repo}/private-vulnerability-reporting",
332 owner = repo.organization.get_name(),
333 repo = repo.name,
334 );
335 info!(
336 "Enabling vulnerability reporting for {}",
337 &vulnerability_reporting_endpoint
338 );
339 octocrab::instance()
342 ._put(&vulnerability_reporting_endpoint, None::<&()>)
343 .await?;
344 let apis = vec![APIContent {
345 name: "Enabling vulnerability reporting".to_string(),
346 url: vulnerability_reporting_endpoint.clone(),
347 response: "Success".to_string(),
348 }];
349 info!(
350 "Vulnerability reporting enabled for {}",
351 &vulnerability_reporting_endpoint
352 );
353
354 Ok(APIBundleFacet {
355 facet_type: SupportedFacetType::VulnerabilityReporting,
356 apis,
357 labels: vec![],
358 })
359 }
360}
361
362trait SourceBundleContentGenerator {
365 fn generate_content(
366 &self,
367 params: &SourceBundleFacetCreateParams,
368 ) -> Result<SourceBundleContent, SkootError>;
369}
370
371struct DefaultSourceBundleContentHandler {}
374
375impl SourceBundleContentGenerator for DefaultSourceBundleContentHandler {
376 fn generate_content(
377 &self,
378 params: &SourceBundleFacetCreateParams,
379 ) -> Result<SourceBundleContent, SkootError> {
380 match params.facet_type {
381 SupportedFacetType::Readme => self.generate_readme_content(params),
382 SupportedFacetType::License => self.generate_license_content(params),
383 SupportedFacetType::SecurityPolicy => self.generate_security_policy_content(params),
384 SupportedFacetType::Scorecard => self.generate_scorecard_content(params),
385 SupportedFacetType::SecurityInsights => self.generate_security_insights_content(params),
386 SupportedFacetType::SAST => self.generate_sast_content(params),
387 _ => todo!("Not implemented yet"),
388 }
389 }
390}
391impl DefaultSourceBundleContentHandler {
392 fn generate_readme_content(
393 &self,
394 params: &SourceBundleFacetCreateParams,
395 ) -> Result<SourceBundleContent, SkootError> {
396 #[derive(Template)]
397 #[template(path = "README.md", escape = "none")]
398 struct ReadmeTemplateParams {
399 project_name: String,
400 }
401
402 let readme_template_params = ReadmeTemplateParams {
403 project_name: params.common.project_name.clone(),
404 };
405
406 let content = readme_template_params.render()?;
407
408 Ok(SourceBundleContent {
409 source_files_content: vec![SourceFileContent {
410 name: "README.md".to_string(),
411 path: "./".to_string(),
412 content,
413 }],
414 facet_type: SupportedFacetType::Readme,
415 })
416 }
417 fn generate_license_content(
419 &self,
420 params: &SourceBundleFacetCreateParams,
421 ) -> Result<SourceBundleContent, SkootError> {
422 #[derive(Template)]
423 #[template(path = "LICENSE", escape = "none")]
424 struct LicenseTemplateParams {
425 project_name: String,
426 date: i32,
427 }
428
429 let license_template_params = LicenseTemplateParams {
430 project_name: params.common.project_name.clone(),
431 date: chrono::Utc::now().year(),
432 };
433
434 let content = license_template_params.render()?;
435
436 Ok(SourceBundleContent {
437 source_files_content: vec![SourceFileContent {
438 name: "LICENSE".to_string(),
439 path: "./".to_string(),
440 content,
441 }],
442 facet_type: SupportedFacetType::License,
443 })
444 }
445 fn generate_security_policy_content(
447 &self,
448 _params: &SourceBundleFacetCreateParams,
449 ) -> Result<SourceBundleContent, SkootError> {
450 #[derive(Template)]
452 #[template(path = "SECURITY.prerelease.md", escape = "none")]
453 struct SecurityPolicyTemplateParams {}
454
455 let security_policy_template_params = SecurityPolicyTemplateParams {};
456 let content = security_policy_template_params.render()?;
457
458 Ok(SourceBundleContent {
459 source_files_content: vec![SourceFileContent {
460 name: "SECURITY.md".to_string(),
461 path: "./".to_string(),
462 content,
463 }],
464 facet_type: SupportedFacetType::SecurityPolicy,
465 })
466 }
467
468 fn generate_scorecard_content(
469 &self,
470 _params: &SourceBundleFacetCreateParams,
471 ) -> Result<SourceBundleContent, SkootError> {
472 #[derive(Template)]
474 #[template(path = "scorecard.yml", escape = "none")]
475 struct ScorecardTemplateParams {}
476
477 let scorecard_template_params = ScorecardTemplateParams {};
478 let content = scorecard_template_params.render()?;
479
480 Ok(SourceBundleContent {
481 source_files_content: vec![SourceFileContent {
482 name: "scorecard.yml".to_string(),
483 path: "./.github/workflows".to_string(),
484 content,
485 }],
486 facet_type: SupportedFacetType::Scorecard,
487 })
488 }
489
490 #[allow(clippy::too_many_lines)]
491 fn generate_security_insights_content(
492 &self,
493 params: &SourceBundleFacetCreateParams,
494 ) -> Result<SourceBundleContent, SkootError> {
495 let insights = SecurityInsightsVersion100YamlSchema {
496 contribution_policy: SecurityInsightsVersion100YamlSchemaContributionPolicy {
497 accepts_automated_pull_requests: true,
498 accepts_pull_requests: true,
499 automated_tools_list: None,
500 code_of_conduct: None,
501 contributing_policy: None,
502 },
503 dependencies: Some(SecurityInsightsVersion100YamlSchemaDependencies{
504 dependencies_lifecycle: None,
505 dependencies_lists: vec![
506 format!("{}/blob/main/go.mod", ¶ms.common.repo.full_url())
507 ],
508 env_dependencies_policy: None,
509 sbom: Some(vec![
510 SecurityInsightsVersion100YamlSchemaDependenciesSbomItem {
511 sbom_creation: Some(
512 SecurityInsightsVersion100YamlSchemaDependenciesSbomItemSbomCreation::from_str("Created by goreleaser")?),
513 sbom_file: Some(format!("{}/releases/latest/download/main-linux-amd64.spdx.sbom.json", ¶ms.common.repo.full_url())),
514 sbom_format: Some("SPDX".to_string()),
515 sbom_url: Some("https://spdx.github.io/spdx-spec/v2.3/".to_string()),
516 },
517 SecurityInsightsVersion100YamlSchemaDependenciesSbomItem {
518 sbom_creation: Some(
519 SecurityInsightsVersion100YamlSchemaDependenciesSbomItemSbomCreation::from_str("Created by goreleaser")?),
520 sbom_file: Some(format!("{}/releases/latest/download/main-linux-arm.spdx.sbom.json", ¶ms.common.repo.full_url())),
521 sbom_format: Some("SPDX".to_string()),
522 sbom_url: Some("https://spdx.github.io/spdx-spec/v2.3/".to_string()),
523 },
524 SecurityInsightsVersion100YamlSchemaDependenciesSbomItem {
525 sbom_creation: Some(
526 SecurityInsightsVersion100YamlSchemaDependenciesSbomItemSbomCreation::from_str("Created by goreleaser")?),
527 sbom_file: Some(format!("{}/releases/latest/download/main-linux-arm64.spdx.sbom.json", ¶ms.common.repo.full_url())),
528 sbom_format: Some("SPDX".to_string()),
529 sbom_url: Some("https://spdx.github.io/spdx-spec/v2.3/".to_string()),
530 },
531 SecurityInsightsVersion100YamlSchemaDependenciesSbomItem {
532 sbom_creation: Some(
533 SecurityInsightsVersion100YamlSchemaDependenciesSbomItemSbomCreation::from_str("Created by goreleaser")?),
534 sbom_file: Some(format!("{}/releases/latest/download/main-windows-amd64.exe.spdx.sbom.json", ¶ms.common.repo.full_url())),
535 sbom_format: Some("SPDX".to_string()),
536 sbom_url: Some("https://spdx.github.io/spdx-spec/v2.3/".to_string()),
537 },
538 SecurityInsightsVersion100YamlSchemaDependenciesSbomItem {
539 sbom_creation: Some(
540 SecurityInsightsVersion100YamlSchemaDependenciesSbomItemSbomCreation::from_str("Created by goreleaser")?),
541 sbom_file: Some(format!("{}/releases/latest/download/main.spdx.sbom.json", ¶ms.common.repo.full_url())),
542 sbom_format: Some("SPDX".to_string()),
543 sbom_url: Some("https://spdx.github.io/spdx-spec/v2.3/".to_string()),
544 },
545 ]),
546 third_party_packages: Some(true),
547 }),
548 distribution_points: Vec::new(),
549 documentation: None,
550 header: SecurityInsightsVersion100YamlSchemaHeader {
551 changelog: None,
552 commit_hash: None,
553 expiration_date: chrono::Utc::now() + chrono::Duration::days(365),
554 last_reviewed: Some(chrono::Utc::now()),
555 last_updated: Some(chrono::Utc::now()),
556 license: Some(format!(
557 "{}/blob/main/LICENSE",
558 ¶ms.common.repo.full_url()
559 )),
560 project_release: None,
561 project_url: params.common.repo.full_url(),
562 schema_version: SecurityInsightsVersion100YamlSchemaHeaderSchemaVersion::_100,
563 },
564 project_lifecycle: SecurityInsightsVersion100YamlSchemaProjectLifecycle {
565 bug_fixes_only: false,
566 core_maintainers: None,
567 release_cycle: None,
568 release_process: None,
569 roadmap: None,
570 status: SecurityInsightsVersion100YamlSchemaProjectLifecycleStatus::Active,
571 },
572 security_artifacts: None,
575 security_assessments: None,
576 security_contacts: Vec::new(),
577 security_testing: Vec::new(),
578 vulnerability_reporting: SecurityInsightsVersion100YamlSchemaVulnerabilityReporting {
579 accepts_vulnerability_reports: true,
580 bug_bounty_available: None,
581 bug_bounty_url: None,
582 comment: None,
583 email_contact: None,
584 in_scope: None,
585 out_scope: None,
586 pgp_key: None,
587 security_policy: Some(format!("{}/blob/main/SECURITY.md", ¶ms.common.repo.full_url())),
588 },
589 };
590
591 let content = serde_yaml::to_string(&insights)?;
592
593 Ok(SourceBundleContent {
594 source_files_content: vec![SourceFileContent {
595 name: "SECURITY-INSIGHTS.yml".to_string(),
596 path: "./".to_string(),
597 content,
598 }],
599 facet_type: SupportedFacetType::SecurityInsights,
600 })
601 }
602
603 fn generate_sast_content(
604 &self,
605 _params: &SourceBundleFacetCreateParams,
606 ) -> Result<SourceBundleContent, SkootError> {
607 #[derive(Template)]
608 #[template(path = "codeql.yml", escape = "none")]
609 struct SASTTemplateParams {}
610
611 let sast_template_params = SASTTemplateParams {};
612 let content = sast_template_params.render()?;
613
614 Ok(SourceBundleContent {
615 source_files_content: vec![SourceFileContent {
616 name: "codeql.yml".to_string(),
617 path: "./.github/workflows".to_string(),
618 content,
619 }],
620 facet_type: SupportedFacetType::SAST,
621 })
622 }
623}
624
625struct GoGithubSourceBundleContentHandler {}
628
629impl SourceBundleContentGenerator for GoGithubSourceBundleContentHandler {
630 fn generate_content(
631 &self,
632 params: &SourceBundleFacetCreateParams,
633 ) -> Result<SourceBundleContent, SkootError> {
634 match params.facet_type {
635 SupportedFacetType::Gitignore => self.generate_gitignore_content(params),
636 SupportedFacetType::SLSABuild => self.generate_slsa_build_content(params),
642 SupportedFacetType::DependencyUpdateTool => {
643 self.generate_dependency_update_tool_content(params)
644 }
645 SupportedFacetType::Fuzzing => self.generate_fuzzing_content(params),
646 SupportedFacetType::DefaultSourceCode => {
647 self.generate_default_source_code_content(params)
648 }
649 _ => todo!("Not implemented yet"),
650 }
651 }
652}
653impl GoGithubSourceBundleContentHandler {
654 fn generate_gitignore_content(
655 &self,
656 _params: &SourceBundleFacetCreateParams,
657 ) -> Result<SourceBundleContent, SkootError> {
658 #[derive(Template)]
659 #[template(path = "go.gitignore", escape = "none")]
660 struct GitignoreTemplateParams {}
661
662 let gitignore_template_params = GitignoreTemplateParams {};
663 let content = gitignore_template_params.render()?;
664
665 Ok(SourceBundleContent {
666 source_files_content: vec![SourceFileContent {
667 name: ".gitignore".to_string(),
668 path: "./".to_string(),
669 content,
670 }],
671 facet_type: SupportedFacetType::Gitignore,
672 })
673 }
674 fn generate_slsa_build_content(
678 &self,
679 params: &SourceBundleFacetCreateParams,
680 ) -> Result<SourceBundleContent, SkootError> {
681 #[derive(Template)]
683 #[template(path = "go.releases.yml", escape = "none")]
684 struct ReleaseTemplateParams {}
685
686 #[derive(Template)]
687 #[template(path = "Dockerfile.goreleaser", escape = "none")]
688 struct DockerfileTemplateParams {
689 project_name: String,
690 }
691
692 #[derive(Template)]
693 #[template(path = "goreleaser.yml", escape = "none")]
694 struct GoReleaserTemplateParams {
695 project_name: String,
696 module_name: String,
697 }
698
699 #[allow(clippy::match_wildcard_for_single_variants)]
700 let module = match ¶ms.common.ecosystem {
701 InitializedEcosystem::Go(go) => go.module(),
702 _ => unreachable!("Ecosystem should be Go"),
703 };
704
705 let slsa_build_template_params = ReleaseTemplateParams {};
706 let dockerfile_template_params = DockerfileTemplateParams {
707 project_name: params.common.project_name.clone(),
708 };
709 let goreleaser_template_params = GoReleaserTemplateParams {
710 project_name: params.common.project_name.clone(),
711 module_name: module,
712 };
713
714 Ok(SourceBundleContent {
715 source_files_content: vec![
716 SourceFileContent {
717 name: "releases.yml".to_string(),
718 path: ".github/workflows/".to_string(),
719 content: slsa_build_template_params.render()?,
720 },
721 SourceFileContent {
722 name: "Dockerfile.goreleaser".to_string(),
723 path: "./".to_string(),
724 content: dockerfile_template_params.render()?,
725 },
726 SourceFileContent {
727 name: ".goreleaser.yml".to_string(),
728 path: "./".to_string(),
729 content: goreleaser_template_params.render()?,
730 },
731 ],
732 facet_type: SupportedFacetType::SLSABuild,
733 })
734 }
735
736 fn generate_dependency_update_tool_content(
737 &self,
738 _params: &SourceBundleFacetCreateParams,
739 ) -> Result<SourceBundleContent, SkootError> {
740 #[derive(Template)]
741 #[template(path = "dependabot.yml", escape = "none")]
742 struct DependabotTemplateParams {
743 ecosystem: String,
744 }
745
746 let dependabot_template_params = DependabotTemplateParams {
747 ecosystem: "gomod".to_string(),
748 };
749 let content = dependabot_template_params.render()?;
750
751 Ok(SourceBundleContent {
752 source_files_content: vec![SourceFileContent {
753 name: "dependabot.yml".to_string(),
754 path: ".github/".to_string(),
755 content,
756 }],
757 facet_type: SupportedFacetType::DependencyUpdateTool,
758 })
759 }
760
761 fn generate_fuzzing_content(
762 &self,
763 params: &SourceBundleFacetCreateParams,
764 ) -> Result<SourceBundleContent, SkootError> {
765 #[derive(Template)]
766 #[template(path = "cifuzz.yml", escape = "none")]
767 struct FuzzingTemplateParams {
768 project_name: String,
769 language: String,
770 }
771
772 let fuzzing_template_params = FuzzingTemplateParams {
773 project_name: params.common.project_name.clone(),
774 language: "go".to_string(),
775 };
776 let content = fuzzing_template_params.render()?;
777
778 Ok(SourceBundleContent {
779 source_files_content: vec![SourceFileContent {
780 name: "cifuzz.yml".to_string(),
781 path: ".github/workflows/".to_string(),
782 content,
783 }],
784 facet_type: SupportedFacetType::Fuzzing,
785 })
786 }
787
788 fn generate_default_source_code_content(
789 &self,
790 _params: &SourceBundleFacetCreateParams,
791 ) -> Result<SourceBundleContent, SkootError> {
792 #[derive(Template)]
793 #[template(path = "main.go.tmpl", escape = "none")]
794 struct DefaultSourceCodeTemplateParams {}
795
796 let default_source_code_template_params = DefaultSourceCodeTemplateParams {};
797 let content = default_source_code_template_params.render()?;
798
799 Ok(SourceBundleContent {
800 source_files_content: vec![SourceFileContent {
801 name: "main.go".to_string(),
802 path: "./".to_string(),
803 content,
804 }],
805 facet_type: SupportedFacetType::DefaultSourceCode,
806 })
807 }
808}
809
810pub struct FacetSetParamsGenerator {}
813
814impl FacetSetParamsGenerator {
815 pub fn generate_default(
822 &self,
823 common_params: &CommonFacetCreateParams,
824 ) -> Result<FacetSetCreateParams, SkootError> {
825 let source_bundle_params =
826 self.generate_default_source_bundle_facet_params(common_params)?;
827 let api_bundle_params = self.generate_default_api_bundle(common_params)?;
828 let total_params = FacetSetCreateParams {
829 facets_params: [
830 source_bundle_params.facets_params,
831 api_bundle_params.facets_params,
832 ]
833 .concat(),
834 };
835
836 Ok(total_params)
837 }
838
839 pub fn generate_default_api_bundle(
845 &self,
846 common_params: &CommonFacetCreateParams,
847 ) -> Result<FacetSetCreateParams, SkootError> {
848 use SupportedFacetType::{BranchProtection, VulnerabilityReporting};
849 let supported_facets = [
850 BranchProtection,
852 VulnerabilityReporting,
853 ];
854 let facets_params = supported_facets
855 .iter()
856 .map(|facet_type| {
857 FacetCreateParams::APIBundle(APIBundleFacetParams {
858 common: common_params.clone(),
859 facet_type: facet_type.clone(),
860 })
861 })
862 .collect::<Vec<FacetCreateParams>>();
863
864 Ok(FacetSetCreateParams { facets_params })
865 }
866
867 pub fn generate_default_source_bundle_facet_params(
874 &self,
875 common_params: &CommonFacetCreateParams,
876 ) -> Result<FacetSetCreateParams, SkootError> {
877 use SupportedFacetType::{
878 DefaultSourceCode, DependencyUpdateTool, Gitignore, License, Readme, SLSABuild,
879 Scorecard, SecurityInsights, SecurityPolicy, SAST,
880 };
881 let supported_facets = [
882 FacetTypeLabels {
883 supported_facet_type: Readme,
884 labels: vec![],
885 },
886 FacetTypeLabels {
887 supported_facet_type: License,
888 labels: vec![],
889 },
890 FacetTypeLabels {
891 supported_facet_type: Gitignore,
892 labels: vec![],
893 },
894 FacetTypeLabels {
895 supported_facet_type: SecurityPolicy,
896 labels: vec![],
897 },
898 FacetTypeLabels {
899 supported_facet_type: SecurityInsights,
900 labels: vec![],
901 },
902 FacetTypeLabels {
903 supported_facet_type: SLSABuild,
904 labels: vec![Label::SLSABuildLevel3, Label::S2C2FAUD1],
905 },
906 FacetTypeLabels {
909 supported_facet_type: DependencyUpdateTool,
910 labels: vec![Label::S2C2FUPD2],
911 },
912 FacetTypeLabels {
916 supported_facet_type: Scorecard,
917 labels: vec![],
918 },
919 FacetTypeLabels {
922 supported_facet_type: SAST,
923 labels: vec![Label::S2C2FSCA1],
924 },
925 FacetTypeLabels {
932 supported_facet_type: DefaultSourceCode,
933 labels: vec![],
934 },
935 ];
936 let facets_params = supported_facets
937 .iter()
938 .map(|facet_type_labels| {
939 FacetCreateParams::SourceBundle(SourceBundleFacetCreateParams {
940 common: common_params.clone(),
941 facet_type: facet_type_labels.supported_facet_type.clone(),
942 labels: facet_type_labels.labels.clone(),
943 })
944 })
945 .collect::<Vec<FacetCreateParams>>();
946
947 Ok(FacetSetCreateParams { facets_params })
948 }
949}
950
951struct FacetTypeLabels {
952 supported_facet_type: SupportedFacetType,
953 labels: Vec<Label>,
954}