diff --git a/.generator/schemas/v2/openapi.yaml b/.generator/schemas/v2/openapi.yaml index 07cf64fab..2053789b9 100644 --- a/.generator/schemas/v2/openapi.yaml +++ b/.generator/schemas/v2/openapi.yaml @@ -17581,6 +17581,244 @@ components: required: - data type: object + CycloneDXAdvisory: + description: Advisory reference for a vulnerability. + properties: + url: + description: URL to the advisory. + example: https://example.com/advisory/CVE-2021-1234 + type: string + type: object + CycloneDXAffect: + description: Reference to a component affected by a vulnerability. + properties: + ref: + description: Reference to a component's bom-ref. + example: a3390fca-c315-41ae-ae05-af5e7859cdee + type: string + required: + - ref + type: object + CycloneDXAssetComponent: + description: The asset component represents the system or host being scanned. + properties: + bom-ref: + description: Optional reference to a component in the components list. + example: asset-ref-123 + type: string + name: + description: The name of the asset. + example: i-12345 + type: string + type: + description: The type of the asset component. + example: operating-system + type: string + required: + - name + type: object + CycloneDXBOM: + description: CycloneDX 1.5 Bill of Materials (BOM) for importing vulnerabilities. + properties: + bomFormat: + description: The format of the BOM. Must be "CycloneDX". + example: CycloneDX + type: string + components: + description: List of components (libraries, applications, or operating systems) + that are affected by vulnerabilities. + items: + $ref: '#/components/schemas/CycloneDXComponent' + type: array + metadata: + $ref: '#/components/schemas/CycloneDXMetadata' + specVersion: + description: The version of the CycloneDX specification. Must be "1.5". + example: '1.5' + type: string + version: + description: The version of the BOM. + example: 1 + format: int64 + type: integer + vulnerabilities: + description: List of vulnerabilities to be imported. + items: + $ref: '#/components/schemas/CycloneDXVulnerability' + type: array + required: + - bomFormat + - specVersion + - version + - metadata + - vulnerabilities + - components + type: object + CycloneDXComponent: + description: A component (library, application, or operating system) in the + BOM. + properties: + bom-ref: + description: Unique reference identifier for this component. + example: a3390fca-c315-41ae-ae05-af5e7859cdee + type: string + name: + description: The name of the component. + example: lodash + type: string + purl: + description: Package URL for the component. Required for library components. + example: pkg:npm/lodash@4.17.21 + type: string + type: + $ref: '#/components/schemas/CycloneDXComponentType' + version: + description: The version of the component. + example: 4.17.21 + type: string + required: + - bom-ref + - type + - name + - version + type: object + CycloneDXComponentType: + description: The type of the component. Supported types are library, application, + and operating-system. + enum: + - library + - application + - operating-system + example: library + type: string + x-enum-varnames: + - LIBRARY + - APPLICATION + - OPERATING_SYSTEM + CycloneDXMetadata: + description: Metadata for the CycloneDX BOM. + properties: + component: + $ref: '#/components/schemas/CycloneDXAssetComponent' + tools: + $ref: '#/components/schemas/CycloneDXTools' + required: + - component + - tools + type: object + CycloneDXRating: + description: Vulnerability rating information. + properties: + score: + description: The CVSS score. + example: 9.0 + format: double + type: number + severity: + description: The severity level. + example: high + type: string + vector: + description: The CVSS vector string. + example: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N + type: string + type: object + CycloneDXReference: + description: External reference for a vulnerability. + properties: + id: + description: Identifier of the reference. + example: GHSA-35m5-8cvj-8783 + type: string + source: + $ref: '#/components/schemas/CycloneDXReferenceSource' + type: object + CycloneDXReferenceSource: + description: Source information for a reference. + properties: + url: + description: URL to the source. + example: https://example.com + type: string + type: object + CycloneDXToolComponent: + description: Tool component that detected the vulnerabilities. + properties: + name: + description: The name of the tool. + example: my-scanner + type: string + type: + description: The type of the tool. + example: application + type: string + required: + - name + type: object + CycloneDXTools: + description: Tools used to generate the BOM. + properties: + components: + description: List of tool components. Only one tool is supported. + items: + $ref: '#/components/schemas/CycloneDXToolComponent' + maxItems: 1 + minItems: 1 + type: array + required: + - components + type: object + CycloneDXVulnerability: + description: A vulnerability in the CycloneDX BOM. + properties: + advisories: + description: List of advisory references. + items: + $ref: '#/components/schemas/CycloneDXAdvisory' + type: array + affects: + description: List of component references affected by this vulnerability. + items: + $ref: '#/components/schemas/CycloneDXAffect' + minItems: 1 + type: array + cwes: + description: List of CWE (Common Weakness Enumeration) identifiers. + example: + - 123 + - 345 + items: + type: integer + type: array + description: + description: Description of the vulnerability. + example: Sample vulnerability detected in the application. + type: string + detail: + description: Detailed information about the vulnerability. + example: Details about the vulnerability + type: string + id: + description: The unique identifier of the vulnerability (e.g., CVE ID). + example: CVE-2021-1234 + type: string + ratings: + description: List of vulnerability ratings. Must contain exactly one rating. + items: + $ref: '#/components/schemas/CycloneDXRating' + maxItems: 1 + minItems: 1 + type: array + references: + description: List of external references for the vulnerability. + items: + $ref: '#/components/schemas/CycloneDXReference' + type: array + required: + - id + - ratings + - affects + type: object DORACustomTags: description: A list of user-defined tags. The tags must follow the `key:value` pattern. Up to 100 may be added per event. @@ -53101,6 +53339,16 @@ components: meta: $ref: '#/components/schemas/SecurityFilterMeta' type: object + SecurityFindingType: + description: The type of security finding. + enum: + - vulnerability + - configuration_finding + example: vulnerability + type: string + x-enum-varnames: + - VULNERABILITY + - CONFIGURATION_FINDING SecurityFindingsAttributes: description: The JSON object containing all attributes of the security finding. properties: @@ -64143,6 +64391,18 @@ components: type: $ref: '#/components/schemas/ThreatHuntingJobDataType' type: object + ThreatIntelIndicatorType: + description: The type of threat indicator. + enum: + - ip_address + - domain + - sha256 + example: ip_address + type: string + x-enum-varnames: + - IP_ADDRESS + - DOMAIN + - SHA256 TimeAggregation: description: 'Time aggregation period (in seconds) is used to aggregate the results of the notification rule evaluation. @@ -93298,6 +93558,68 @@ paths: permissions: - security_monitoring_findings_read - appsec_vm_read + post: + description: 'Allows external integrations to send security findings to Datadog. + This endpoint accepts finding data in a custom format and returns an empty + response on success. + + + **Note**: This endpoint is in preview and is subject to change. + + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' + operationId: CreateSecurityFinding + parameters: + - description: The vendor providing the security finding. Must be lowercase. + example: vendor-name + in: header + name: vendor + required: true + schema: + type: string + - description: The type of security finding. + in: header + name: finding_type + required: true + schema: + $ref: '#/components/schemas/SecurityFindingType' + requestBody: + content: + application/json: + schema: + additionalProperties: {} + description: Security finding data in a custom format. + type: object + required: true + responses: + '200': + description: OK + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Bad Request + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Forbidden + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + security: + - apiKeyAuth: [] + appKeyAuth: [] + summary: Create security finding + tags: + - Security Monitoring + x-permission: + operator: OR + permissions: + - security_monitoring_findings_write + x-unstable: '**Note**: This endpoint is in preview and is subject to change. + + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' /api/v2/security/findings/cases: delete: description: 'Detach security findings from their case. @@ -94050,6 +94372,92 @@ paths: operator: OR permissions: - security_monitoring_notification_profiles_write + /api/v2/security/threat-intel-feed: + post: + description: 'Import threat intelligence feeds with support for IP addresses, + domains, and SHA256 hashes. This endpoint requires specific headers to identify + the vendor and indicator type. + + + **Note**: This endpoint is in preview and is subject to change. + + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' + operationId: ImportThreatIntel + parameters: + - description: The vendor providing the threat intelligence feed. + example: vendor-name + in: header + name: ti_vendor + required: true + schema: + type: string + - description: The type of threat indicator. Valid values are ip_address, domain, + or sha256. + in: header + name: ti_indicator + required: true + schema: + $ref: '#/components/schemas/ThreatIntelIndicatorType' + - description: Optional integration account identifier. + in: header + name: ti_integration_account + required: false + schema: + type: string + requestBody: + content: + application/json: + schema: + description: Threat intelligence feed data. + type: object + application/octet-stream: + schema: + description: Threat intelligence feed data in binary format. + format: binary + type: string + required: true + responses: + '200': + description: OK + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Bad Request + '401': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Unauthorized + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Internal Server Error + '503': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Service Unavailable + security: + - apiKeyAuth: [] + appKeyAuth: [] + summary: Import threat intelligence feed + tags: + - Security Monitoring + x-permission: + operator: OR + permissions: + - reference_tables_write + x-unstable: '**Note**: This endpoint is in preview and is subject to change. + + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' /api/v2/security/vulnerabilities: get: description: "Get a list of vulnerabilities.\n\n### Pagination\n\nPagination @@ -94463,6 +94871,58 @@ paths: x-unstable: '**Note**: This endpoint is a private preview. If you are interested in accessing this API, [fill out this form](https://forms.gle/kMYC1sDr6WDUBDsx9).' + post: + description: 'Import vulnerabilities in CycloneDX 1.5 format. This endpoint + validates the payload against the CycloneDX 1.5 schema and additional mandatory + field requirements. + + + **Note**: This endpoint is in preview and is subject to change. + + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' + operationId: ImportSecurityVulnerabilities + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/CycloneDXBOM' + required: true + responses: + '200': + description: OK + '400': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Bad Request + '403': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Forbidden + '429': + $ref: '#/components/responses/TooManyRequestsResponse' + '500': + content: + application/json: + schema: + $ref: '#/components/schemas/JSONAPIErrorResponse' + description: Internal Server Error + security: + - apiKeyAuth: [] + appKeyAuth: [] + summary: Import vulnerabilities + tags: + - Security Monitoring + x-permission: + operator: OR + permissions: + - security_monitoring_findings_write + x-unstable: '**Note**: This endpoint is in preview and is subject to change. + + If you have any feedback, contact [Datadog support](https://docs.datadoghq.com/help/).' /api/v2/security/vulnerabilities/notification_rules: get: description: Returns the list of notification rules for security vulnerabilities. diff --git a/examples/v2_security-monitoring_CreateSecurityFinding.rs b/examples/v2_security-monitoring_CreateSecurityFinding.rs new file mode 100644 index 000000000..6578401a1 --- /dev/null +++ b/examples/v2_security-monitoring_CreateSecurityFinding.rs @@ -0,0 +1,19 @@ +// Create security finding returns "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_security_monitoring::SecurityMonitoringAPI; +use datadog_api_client::datadogV2::model::SecurityFindingType; + +#[tokio::main] +async fn main() { + let mut configuration = datadog::Configuration::new(); + configuration.set_unstable_operation_enabled("v2.CreateSecurityFinding", true); + let api = SecurityMonitoringAPI::with_config(configuration); + let resp = api + .create_security_finding("vendor".to_string(), SecurityFindingType::VULNERABILITY) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_security-monitoring_ImportSecurityVulnerabilities.rs b/examples/v2_security-monitoring_ImportSecurityVulnerabilities.rs new file mode 100644 index 000000000..cddf34af3 --- /dev/null +++ b/examples/v2_security-monitoring_ImportSecurityVulnerabilities.rs @@ -0,0 +1,67 @@ +// Import vulnerabilities returns "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_security_monitoring::SecurityMonitoringAPI; +use datadog_api_client::datadogV2::model::CycloneDXAdvisory; +use datadog_api_client::datadogV2::model::CycloneDXAffect; +use datadog_api_client::datadogV2::model::CycloneDXAssetComponent; +use datadog_api_client::datadogV2::model::CycloneDXBOM; +use datadog_api_client::datadogV2::model::CycloneDXComponent; +use datadog_api_client::datadogV2::model::CycloneDXComponentType; +use datadog_api_client::datadogV2::model::CycloneDXMetadata; +use datadog_api_client::datadogV2::model::CycloneDXRating; +use datadog_api_client::datadogV2::model::CycloneDXReference; +use datadog_api_client::datadogV2::model::CycloneDXReferenceSource; +use datadog_api_client::datadogV2::model::CycloneDXToolComponent; +use datadog_api_client::datadogV2::model::CycloneDXTools; +use datadog_api_client::datadogV2::model::CycloneDXVulnerability; + +#[tokio::main] +async fn main() { + let body = + CycloneDXBOM::new( + "CycloneDX".to_string(), + vec![CycloneDXComponent::new( + "a3390fca-c315-41ae-ae05-af5e7859cdee".to_string(), + "lodash".to_string(), + CycloneDXComponentType::LIBRARY, + "4.17.21".to_string(), + ) + .purl("pkg:npm/lodash@4.17.21".to_string())], + CycloneDXMetadata::new( + CycloneDXAssetComponent::new("i-12345".to_string()) + .bom_ref("asset-ref-123".to_string()) + .type_("operating-system".to_string()), + CycloneDXTools::new(vec![CycloneDXToolComponent::new("my-scanner".to_string()) + .type_("application".to_string())]), + ), + "1.5".to_string(), + 1, + vec![CycloneDXVulnerability::new( + vec![CycloneDXAffect::new( + "a3390fca-c315-41ae-ae05-af5e7859cdee".to_string(), + )], + "CVE-2021-1234".to_string(), + vec![CycloneDXRating::new() + .score(9.0 as f64) + .severity("high".to_string()) + .vector("CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N".to_string())], + ) + .advisories(vec![CycloneDXAdvisory::new() + .url("https://example.com/advisory/CVE-2021-1234".to_string())]) + .cwes(vec![123, 345]) + .description("Sample vulnerability detected in the application.".to_string()) + .detail("Details about the vulnerability".to_string()) + .references(vec![CycloneDXReference::new() + .id("GHSA-35m5-8cvj-8783".to_string()) + .source(CycloneDXReferenceSource::new().url("https://example.com".to_string()))])], + ); + let mut configuration = datadog::Configuration::new(); + configuration.set_unstable_operation_enabled("v2.ImportSecurityVulnerabilities", true); + let api = SecurityMonitoringAPI::with_config(configuration); + let resp = api.import_security_vulnerabilities(body).await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/examples/v2_security-monitoring_ImportThreatIntel.rs b/examples/v2_security-monitoring_ImportThreatIntel.rs new file mode 100644 index 000000000..6698b79c3 --- /dev/null +++ b/examples/v2_security-monitoring_ImportThreatIntel.rs @@ -0,0 +1,24 @@ +// Import threat intelligence feed returns "OK" response +use datadog_api_client::datadog; +use datadog_api_client::datadogV2::api_security_monitoring::ImportThreatIntelOptionalParams; +use datadog_api_client::datadogV2::api_security_monitoring::SecurityMonitoringAPI; +use datadog_api_client::datadogV2::model::ThreatIntelIndicatorType; + +#[tokio::main] +async fn main() { + let mut configuration = datadog::Configuration::new(); + configuration.set_unstable_operation_enabled("v2.ImportThreatIntel", true); + let api = SecurityMonitoringAPI::with_config(configuration); + let resp = api + .import_threat_intel( + "ti_vendor".to_string(), + ThreatIntelIndicatorType::IP_ADDRESS, + ImportThreatIntelOptionalParams::default(), + ) + .await; + if let Ok(value) = resp { + println!("{:#?}", value); + } else { + println!("{:#?}", resp.unwrap_err()); + } +} diff --git a/src/datadog/configuration.rs b/src/datadog/configuration.rs index d00ab9a63..e2789ebe7 100644 --- a/src/datadog/configuration.rs +++ b/src/datadog/configuration.rs @@ -151,6 +151,7 @@ impl Default for Configuration { ("v2.activate_content_pack".to_owned(), false), ("v2.cancel_threat_hunting_job".to_owned(), false), ("v2.convert_job_result_to_signal".to_owned(), false), + ("v2.create_security_finding".to_owned(), false), ("v2.deactivate_content_pack".to_owned(), false), ("v2.delete_threat_hunting_job".to_owned(), false), ("v2.get_content_packs_states".to_owned(), false), @@ -163,6 +164,8 @@ impl Default for Configuration { false, ), ("v2.get_threat_hunting_job".to_owned(), false), + ("v2.import_security_vulnerabilities".to_owned(), false), + ("v2.import_threat_intel".to_owned(), false), ("v2.list_findings".to_owned(), false), ("v2.list_multiple_rulesets".to_owned(), false), ("v2.list_scanned_assets_metadata".to_owned(), false), diff --git a/src/datadogV2/api/api_security_monitoring.rs b/src/datadogV2/api/api_security_monitoring.rs index 51cf574ec..aa2867033 100644 --- a/src/datadogV2/api/api_security_monitoring.rs +++ b/src/datadogV2/api/api_security_monitoring.rs @@ -179,6 +179,22 @@ impl GetSuppressionVersionHistoryOptionalParams { } } +/// ImportThreatIntelOptionalParams is a struct for passing parameters to the method [`SecurityMonitoringAPI::import_threat_intel`] +#[non_exhaustive] +#[derive(Clone, Default, Debug)] +pub struct ImportThreatIntelOptionalParams { + /// Optional integration account identifier. + pub ti_integration_account: Option, +} + +impl ImportThreatIntelOptionalParams { + /// Optional integration account identifier. + pub fn ti_integration_account(mut self, value: String) -> Self { + self.ti_integration_account = Some(value); + self + } +} + /// ListAssetsSBOMsOptionalParams is a struct for passing parameters to the method [`SecurityMonitoringAPI::list_assets_sbo_ms`] #[non_exhaustive] #[derive(Clone, Default, Debug)] @@ -1258,6 +1274,15 @@ pub enum CreateSecurityFilterError { UnknownValue(serde_json::Value), } +/// CreateSecurityFindingError is a struct for typed errors of method [`SecurityMonitoringAPI::create_security_finding`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CreateSecurityFindingError { + JSONAPIErrorResponse(crate::datadogV2::model::JSONAPIErrorResponse), + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// CreateSecurityMonitoringCriticalAssetError is a struct for typed errors of method [`SecurityMonitoringAPI::create_security_monitoring_critical_asset`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -1589,6 +1614,24 @@ pub enum GetVulnerabilityNotificationRulesError { UnknownValue(serde_json::Value), } +/// ImportSecurityVulnerabilitiesError is a struct for typed errors of method [`SecurityMonitoringAPI::import_security_vulnerabilities`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ImportSecurityVulnerabilitiesError { + JSONAPIErrorResponse(crate::datadogV2::model::JSONAPIErrorResponse), + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + +/// ImportThreatIntelError is a struct for typed errors of method [`SecurityMonitoringAPI::import_threat_intel`] +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ImportThreatIntelError { + JSONAPIErrorResponse(crate::datadogV2::model::JSONAPIErrorResponse), + APIErrorResponse(crate::datadogV2::model::APIErrorResponse), + UnknownValue(serde_json::Value), +} + /// ListAssetsSBOMsError is a struct for typed errors of method [`SecurityMonitoringAPI::list_assets_sbo_ms`] #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] @@ -3626,6 +3669,175 @@ impl SecurityMonitoringAPI { } } + /// Allows external integrations to send security findings to Datadog. This endpoint accepts finding data in a custom format and returns an empty response on success. + /// + /// **Note**: This endpoint is in preview and is subject to change. + /// If you have any feedback, contact [Datadog support](). + pub async fn create_security_finding( + &self, + vendor: String, + finding_type: crate::datadogV2::model::SecurityFindingType, + body: std::collections::BTreeMap, + ) -> Result<(), datadog::Error> { + match self + .create_security_finding_with_http_info(vendor, finding_type, body) + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + /// Allows external integrations to send security findings to Datadog. This endpoint accepts finding data in a custom format and returns an empty response on success. + /// + /// **Note**: This endpoint is in preview and is subject to change. + /// If you have any feedback, contact [Datadog support](). + pub async fn create_security_finding_with_http_info( + &self, + vendor: String, + finding_type: crate::datadogV2::model::SecurityFindingType, + body: std::collections::BTreeMap, + ) -> Result, datadog::Error> { + let local_configuration = &self.config; + let operation_id = "v2.create_security_finding"; + if local_configuration.is_unstable_operation_enabled(operation_id) { + warn!("Using unstable operation {operation_id}"); + } else { + let local_error = datadog::UnstableOperationDisabledError { + msg: "Operation 'v2.create_security_finding' is not enabled".to_string(), + }; + return Err(datadog::Error::UnstableOperationDisabledError(local_error)); + } + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/security/findings", + local_configuration.get_operation_host(operation_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::POST, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + headers.insert("Accept", HeaderValue::from_static("*/*")); + + headers.insert( + "vendor", + vendor + .to_string() + .parse() + .expect("failed to parse vendor header"), + ); + headers.insert( + "finding_type", + finding_type + .to_string() + .parse() + .expect("failed to parse finding_type header"), + ); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + // build body parameters + let output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter); + if body.serialize(&mut ser).is_ok() { + if let Some(content_encoding) = headers.get("Content-Encoding") { + match content_encoding.to_str().unwrap_or_default() { + "gzip" => { + let mut enc = GzEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "deflate" => { + let mut enc = ZlibEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "zstd1" => { + let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap(); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + _ => { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + } else { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: None, + }) + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + /// Create a new critical asset. pub async fn create_security_monitoring_critical_asset( &self, @@ -8678,6 +8890,340 @@ impl SecurityMonitoringAPI { } } + /// Import vulnerabilities in CycloneDX 1.5 format. This endpoint validates the payload against the CycloneDX 1.5 schema and additional mandatory field requirements. + /// + /// **Note**: This endpoint is in preview and is subject to change. + /// If you have any feedback, contact [Datadog support](). + pub async fn import_security_vulnerabilities( + &self, + body: crate::datadogV2::model::CycloneDXBOM, + ) -> Result<(), datadog::Error> { + match self + .import_security_vulnerabilities_with_http_info(body) + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + /// Import vulnerabilities in CycloneDX 1.5 format. This endpoint validates the payload against the CycloneDX 1.5 schema and additional mandatory field requirements. + /// + /// **Note**: This endpoint is in preview and is subject to change. + /// If you have any feedback, contact [Datadog support](). + pub async fn import_security_vulnerabilities_with_http_info( + &self, + body: crate::datadogV2::model::CycloneDXBOM, + ) -> Result, datadog::Error> + { + let local_configuration = &self.config; + let operation_id = "v2.import_security_vulnerabilities"; + if local_configuration.is_unstable_operation_enabled(operation_id) { + warn!("Using unstable operation {operation_id}"); + } else { + let local_error = datadog::UnstableOperationDisabledError { + msg: "Operation 'v2.import_security_vulnerabilities' is not enabled".to_string(), + }; + return Err(datadog::Error::UnstableOperationDisabledError(local_error)); + } + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/security/vulnerabilities", + local_configuration.get_operation_host(operation_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::POST, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + headers.insert("Accept", HeaderValue::from_static("*/*")); + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + // build body parameters + let output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter); + if body.serialize(&mut ser).is_ok() { + if let Some(content_encoding) = headers.get("Content-Encoding") { + match content_encoding.to_str().unwrap_or_default() { + "gzip" => { + let mut enc = GzEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "deflate" => { + let mut enc = ZlibEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "zstd1" => { + let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap(); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + _ => { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + } else { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: None, + }) + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + + /// Import threat intelligence feeds with support for IP addresses, domains, and SHA256 hashes. This endpoint requires specific headers to identify the vendor and indicator type. + /// + /// **Note**: This endpoint is in preview and is subject to change. + /// If you have any feedback, contact [Datadog support](). + pub async fn import_threat_intel( + &self, + ti_vendor: String, + ti_indicator: crate::datadogV2::model::ThreatIntelIndicatorType, + body: std::collections::BTreeMap, + params: ImportThreatIntelOptionalParams, + ) -> Result<(), datadog::Error> { + match self + .import_threat_intel_with_http_info(ti_vendor, ti_indicator, body, params) + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + /// Import threat intelligence feeds with support for IP addresses, domains, and SHA256 hashes. This endpoint requires specific headers to identify the vendor and indicator type. + /// + /// **Note**: This endpoint is in preview and is subject to change. + /// If you have any feedback, contact [Datadog support](). + pub async fn import_threat_intel_with_http_info( + &self, + ti_vendor: String, + ti_indicator: crate::datadogV2::model::ThreatIntelIndicatorType, + body: std::collections::BTreeMap, + params: ImportThreatIntelOptionalParams, + ) -> Result, datadog::Error> { + let local_configuration = &self.config; + let operation_id = "v2.import_threat_intel"; + if local_configuration.is_unstable_operation_enabled(operation_id) { + warn!("Using unstable operation {operation_id}"); + } else { + let local_error = datadog::UnstableOperationDisabledError { + msg: "Operation 'v2.import_threat_intel' is not enabled".to_string(), + }; + return Err(datadog::Error::UnstableOperationDisabledError(local_error)); + } + + // unbox and build optional parameters + let ti_integration_account = params.ti_integration_account; + + let local_client = &self.client; + + let local_uri_str = format!( + "{}/api/v2/security/threat-intel-feed", + local_configuration.get_operation_host(operation_id) + ); + let mut local_req_builder = + local_client.request(reqwest::Method::POST, local_uri_str.as_str()); + + // build headers + let mut headers = HeaderMap::new(); + headers.insert("Content-Type", HeaderValue::from_static("application/json")); + headers.insert("Accept", HeaderValue::from_static("*/*")); + + headers.insert( + "ti_vendor", + ti_vendor + .to_string() + .parse() + .expect("failed to parse ti_vendor header"), + ); + headers.insert( + "ti_indicator", + ti_indicator + .to_string() + .parse() + .expect("failed to parse ti_indicator header"), + ); + if let Some(ref local) = ti_integration_account { + headers.insert( + "ti_integration_account", + local + .to_string() + .parse() + .expect("failed to parse ti_integration_account header"), + ); + } + + // build user agent + match HeaderValue::from_str(local_configuration.user_agent.as_str()) { + Ok(user_agent) => headers.insert(reqwest::header::USER_AGENT, user_agent), + Err(e) => { + log::warn!("Failed to parse user agent header: {e}, falling back to default"); + headers.insert( + reqwest::header::USER_AGENT, + HeaderValue::from_static(datadog::DEFAULT_USER_AGENT.as_str()), + ) + } + }; + + // build auth + if let Some(local_key) = local_configuration.auth_keys.get("apiKeyAuth") { + headers.insert( + "DD-API-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-API-KEY header"), + ); + }; + if let Some(local_key) = local_configuration.auth_keys.get("appKeyAuth") { + headers.insert( + "DD-APPLICATION-KEY", + HeaderValue::from_str(local_key.key.as_str()) + .expect("failed to parse DD-APPLICATION-KEY header"), + ); + }; + + // build body parameters + let output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter(output, datadog::DDFormatter); + if body.serialize(&mut ser).is_ok() { + if let Some(content_encoding) = headers.get("Content-Encoding") { + match content_encoding.to_str().unwrap_or_default() { + "gzip" => { + let mut enc = GzEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "deflate" => { + let mut enc = ZlibEncoder::new(Vec::new(), Compression::default()); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + "zstd1" => { + let mut enc = zstd::stream::Encoder::new(Vec::new(), 0).unwrap(); + let _ = enc.write_all(ser.into_inner().as_slice()); + match enc.finish() { + Ok(buf) => { + local_req_builder = local_req_builder.body(buf); + } + Err(e) => return Err(datadog::Error::Io(e)), + } + } + _ => { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + } else { + local_req_builder = local_req_builder.body(ser.into_inner()); + } + } + + local_req_builder = local_req_builder.headers(headers); + let local_req = local_req_builder.build()?; + log::debug!("request content: {:?}", local_req.body()); + let local_resp = local_client.execute(local_req).await?; + + let local_status = local_resp.status(); + let local_content = local_resp.text().await?; + log::debug!("response content: {}", local_content); + + if !local_status.is_client_error() && !local_status.is_server_error() { + Ok(datadog::ResponseContent { + status: local_status, + content: local_content, + entity: None, + }) + } else { + let local_entity: Option = + serde_json::from_str(&local_content).ok(); + let local_error = datadog::ResponseContent { + status: local_status, + content: local_content, + entity: local_entity, + }; + Err(datadog::Error::ResponseError(local_error)) + } + } + /// Get a list of assets SBOMs for an organization. /// /// ### Pagination diff --git a/src/datadogV2/model/mod.rs b/src/datadogV2/model/mod.rs index 5ab80a825..ec676f7a6 100644 --- a/src/datadogV2/model/mod.rs +++ b/src/datadogV2/model/mod.rs @@ -6410,6 +6410,8 @@ pub mod model_security_findings_page; pub use self::model_security_findings_page::SecurityFindingsPage; pub mod model_security_findings_status; pub use self::model_security_findings_status::SecurityFindingsStatus; +pub mod model_security_finding_type; +pub use self::model_security_finding_type::SecurityFindingType; pub mod model_detach_case_request; pub use self::model_detach_case_request::DetachCaseRequest; pub mod model_detach_case_request_data; @@ -6570,6 +6572,8 @@ pub mod model_patch_notification_rule_parameters_data; pub use self::model_patch_notification_rule_parameters_data::PatchNotificationRuleParametersData; pub mod model_patch_notification_rule_parameters_data_attributes; pub use self::model_patch_notification_rule_parameters_data_attributes::PatchNotificationRuleParametersDataAttributes; +pub mod model_threat_intel_indicator_type; +pub use self::model_threat_intel_indicator_type::ThreatIntelIndicatorType; pub mod model_vulnerability_type; pub use self::model_vulnerability_type::VulnerabilityType; pub mod model_vulnerability_severity; @@ -6618,6 +6622,32 @@ pub mod model_asset_entity_type; pub use self::model_asset_entity_type::AssetEntityType; pub mod model_vulnerabilities_type; pub use self::model_vulnerabilities_type::VulnerabilitiesType; +pub mod model_cyclone_dxbom; +pub use self::model_cyclone_dxbom::CycloneDXBOM; +pub mod model_cyclone_dx_component; +pub use self::model_cyclone_dx_component::CycloneDXComponent; +pub mod model_cyclone_dx_component_type; +pub use self::model_cyclone_dx_component_type::CycloneDXComponentType; +pub mod model_cyclone_dx_metadata; +pub use self::model_cyclone_dx_metadata::CycloneDXMetadata; +pub mod model_cyclone_dx_asset_component; +pub use self::model_cyclone_dx_asset_component::CycloneDXAssetComponent; +pub mod model_cyclone_dx_tools; +pub use self::model_cyclone_dx_tools::CycloneDXTools; +pub mod model_cyclone_dx_tool_component; +pub use self::model_cyclone_dx_tool_component::CycloneDXToolComponent; +pub mod model_cyclone_dx_vulnerability; +pub use self::model_cyclone_dx_vulnerability::CycloneDXVulnerability; +pub mod model_cyclone_dx_advisory; +pub use self::model_cyclone_dx_advisory::CycloneDXAdvisory; +pub mod model_cyclone_dx_affect; +pub use self::model_cyclone_dx_affect::CycloneDXAffect; +pub mod model_cyclone_dx_rating; +pub use self::model_cyclone_dx_rating::CycloneDXRating; +pub mod model_cyclone_dx_reference; +pub use self::model_cyclone_dx_reference::CycloneDXReference; +pub mod model_cyclone_dx_reference_source; +pub use self::model_cyclone_dx_reference_source::CycloneDXReferenceSource; pub mod model_list_vulnerable_assets_response; pub use self::model_list_vulnerable_assets_response::ListVulnerableAssetsResponse; pub mod model_asset; diff --git a/src/datadogV2/model/model_cyclone_dx_advisory.rs b/src/datadogV2/model/model_cyclone_dx_advisory.rs new file mode 100644 index 000000000..8771fc7c0 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_advisory.rs @@ -0,0 +1,105 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Advisory reference for a vulnerability. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXAdvisory { + /// URL to the advisory. + #[serde(rename = "url")] + pub url: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXAdvisory { + pub fn new() -> CycloneDXAdvisory { + CycloneDXAdvisory { + url: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn url(mut self, value: String) -> Self { + self.url = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for CycloneDXAdvisory { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for CycloneDXAdvisory { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXAdvisoryVisitor; + impl<'a> Visitor<'a> for CycloneDXAdvisoryVisitor { + type Value = CycloneDXAdvisory; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut url: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "url" => { + if v.is_null() { + continue; + } + url = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = CycloneDXAdvisory { + url, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXAdvisoryVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_affect.rs b/src/datadogV2/model/model_cyclone_dx_affect.rs new file mode 100644 index 000000000..445bb2736 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_affect.rs @@ -0,0 +1,92 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Reference to a component affected by a vulnerability. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXAffect { + /// Reference to a component's bom-ref. + #[serde(rename = "ref")] + pub ref_: String, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXAffect { + pub fn new(ref_: String) -> CycloneDXAffect { + CycloneDXAffect { + ref_, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXAffect { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXAffectVisitor; + impl<'a> Visitor<'a> for CycloneDXAffectVisitor { + type Value = CycloneDXAffect; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut ref_: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "ref" => { + ref_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let ref_ = ref_.ok_or_else(|| M::Error::missing_field("ref_"))?; + + let content = CycloneDXAffect { + ref_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXAffectVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_asset_component.rs b/src/datadogV2/model/model_cyclone_dx_asset_component.rs new file mode 100644 index 000000000..346eb5ae4 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_asset_component.rs @@ -0,0 +1,126 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// The asset component represents the system or host being scanned. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXAssetComponent { + /// Optional reference to a component in the components list. + #[serde(rename = "bom-ref")] + pub bom_ref: Option, + /// The name of the asset. + #[serde(rename = "name")] + pub name: String, + /// The type of the asset component. + #[serde(rename = "type")] + pub type_: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXAssetComponent { + pub fn new(name: String) -> CycloneDXAssetComponent { + CycloneDXAssetComponent { + bom_ref: None, + name, + type_: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn bom_ref(mut self, value: String) -> Self { + self.bom_ref = Some(value); + self + } + + pub fn type_(mut self, value: String) -> Self { + self.type_ = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXAssetComponent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXAssetComponentVisitor; + impl<'a> Visitor<'a> for CycloneDXAssetComponentVisitor { + type Value = CycloneDXAssetComponent; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut bom_ref: Option = None; + let mut name: Option = None; + let mut type_: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "bom-ref" => { + if v.is_null() { + continue; + } + bom_ref = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "name" => { + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "type" => { + if v.is_null() { + continue; + } + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let name = name.ok_or_else(|| M::Error::missing_field("name"))?; + + let content = CycloneDXAssetComponent { + bom_ref, + name, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXAssetComponentVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_component.rs b/src/datadogV2/model/model_cyclone_dx_component.rs new file mode 100644 index 000000000..088024fa8 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_component.rs @@ -0,0 +1,152 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// A component (library, application, or operating system) in the BOM. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXComponent { + /// Unique reference identifier for this component. + #[serde(rename = "bom-ref")] + pub bom_ref: String, + /// The name of the component. + #[serde(rename = "name")] + pub name: String, + /// Package URL for the component. Required for library components. + #[serde(rename = "purl")] + pub purl: Option, + /// The type of the component. Supported types are library, application, and operating-system. + #[serde(rename = "type")] + pub type_: crate::datadogV2::model::CycloneDXComponentType, + /// The version of the component. + #[serde(rename = "version")] + pub version: String, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXComponent { + pub fn new( + bom_ref: String, + name: String, + type_: crate::datadogV2::model::CycloneDXComponentType, + version: String, + ) -> CycloneDXComponent { + CycloneDXComponent { + bom_ref, + name, + purl: None, + type_, + version, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn purl(mut self, value: String) -> Self { + self.purl = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXComponent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXComponentVisitor; + impl<'a> Visitor<'a> for CycloneDXComponentVisitor { + type Value = CycloneDXComponent; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut bom_ref: Option = None; + let mut name: Option = None; + let mut purl: Option = None; + let mut type_: Option = None; + let mut version: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "bom-ref" => { + bom_ref = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "name" => { + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "purl" => { + if v.is_null() { + continue; + } + purl = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "type" => { + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + if let Some(ref _type_) = type_ { + match _type_ { + crate::datadogV2::model::CycloneDXComponentType::UnparsedObject(_type_) => { + _unparsed = true; + }, + _ => {} + } + } + } + "version" => { + version = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let bom_ref = bom_ref.ok_or_else(|| M::Error::missing_field("bom_ref"))?; + let name = name.ok_or_else(|| M::Error::missing_field("name"))?; + let type_ = type_.ok_or_else(|| M::Error::missing_field("type_"))?; + let version = version.ok_or_else(|| M::Error::missing_field("version"))?; + + let content = CycloneDXComponent { + bom_ref, + name, + purl, + type_, + version, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXComponentVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_component_type.rs b/src/datadogV2/model/model_cyclone_dx_component_type.rs new file mode 100644 index 000000000..5cd7fe0d5 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_component_type.rs @@ -0,0 +1,54 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum CycloneDXComponentType { + LIBRARY, + APPLICATION, + OPERATING_SYSTEM, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for CycloneDXComponentType { + fn to_string(&self) -> String { + match self { + Self::LIBRARY => String::from("library"), + Self::APPLICATION => String::from("application"), + Self::OPERATING_SYSTEM => String::from("operating-system"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for CycloneDXComponentType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::UnparsedObject(v) => v.serialize(serializer), + _ => serializer.serialize_str(self.to_string().as_str()), + } + } +} + +impl<'de> Deserialize<'de> for CycloneDXComponentType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "library" => Self::LIBRARY, + "application" => Self::APPLICATION, + "operating-system" => Self::OPERATING_SYSTEM, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_metadata.rs b/src/datadogV2/model/model_cyclone_dx_metadata.rs new file mode 100644 index 000000000..9becd4388 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_metadata.rs @@ -0,0 +1,105 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Metadata for the CycloneDX BOM. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXMetadata { + /// The asset component represents the system or host being scanned. + #[serde(rename = "component")] + pub component: crate::datadogV2::model::CycloneDXAssetComponent, + /// Tools used to generate the BOM. + #[serde(rename = "tools")] + pub tools: crate::datadogV2::model::CycloneDXTools, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXMetadata { + pub fn new( + component: crate::datadogV2::model::CycloneDXAssetComponent, + tools: crate::datadogV2::model::CycloneDXTools, + ) -> CycloneDXMetadata { + CycloneDXMetadata { + component, + tools, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXMetadata { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXMetadataVisitor; + impl<'a> Visitor<'a> for CycloneDXMetadataVisitor { + type Value = CycloneDXMetadata; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut component: Option = None; + let mut tools: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "component" => { + component = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "tools" => { + tools = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let component = component.ok_or_else(|| M::Error::missing_field("component"))?; + let tools = tools.ok_or_else(|| M::Error::missing_field("tools"))?; + + let content = CycloneDXMetadata { + component, + tools, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXMetadataVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_rating.rs b/src/datadogV2/model/model_cyclone_dx_rating.rs new file mode 100644 index 000000000..f165fdf0a --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_rating.rs @@ -0,0 +1,139 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Vulnerability rating information. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXRating { + /// The CVSS score. + #[serde(rename = "score")] + pub score: Option, + /// The severity level. + #[serde(rename = "severity")] + pub severity: Option, + /// The CVSS vector string. + #[serde(rename = "vector")] + pub vector: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXRating { + pub fn new() -> CycloneDXRating { + CycloneDXRating { + score: None, + severity: None, + vector: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn score(mut self, value: f64) -> Self { + self.score = Some(value); + self + } + + pub fn severity(mut self, value: String) -> Self { + self.severity = Some(value); + self + } + + pub fn vector(mut self, value: String) -> Self { + self.vector = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for CycloneDXRating { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for CycloneDXRating { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXRatingVisitor; + impl<'a> Visitor<'a> for CycloneDXRatingVisitor { + type Value = CycloneDXRating; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut score: Option = None; + let mut severity: Option = None; + let mut vector: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "score" => { + if v.is_null() { + continue; + } + score = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "severity" => { + if v.is_null() { + continue; + } + severity = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "vector" => { + if v.is_null() { + continue; + } + vector = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = CycloneDXRating { + score, + severity, + vector, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXRatingVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_reference.rs b/src/datadogV2/model/model_cyclone_dx_reference.rs new file mode 100644 index 000000000..530e4f216 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_reference.rs @@ -0,0 +1,122 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// External reference for a vulnerability. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXReference { + /// Identifier of the reference. + #[serde(rename = "id")] + pub id: Option, + /// Source information for a reference. + #[serde(rename = "source")] + pub source: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXReference { + pub fn new() -> CycloneDXReference { + CycloneDXReference { + id: None, + source: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn id(mut self, value: String) -> Self { + self.id = Some(value); + self + } + + pub fn source(mut self, value: crate::datadogV2::model::CycloneDXReferenceSource) -> Self { + self.source = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for CycloneDXReference { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for CycloneDXReference { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXReferenceVisitor; + impl<'a> Visitor<'a> for CycloneDXReferenceVisitor { + type Value = CycloneDXReference; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut id: Option = None; + let mut source: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "id" => { + if v.is_null() { + continue; + } + id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "source" => { + if v.is_null() { + continue; + } + source = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = CycloneDXReference { + id, + source, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXReferenceVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_reference_source.rs b/src/datadogV2/model/model_cyclone_dx_reference_source.rs new file mode 100644 index 000000000..41b0a3408 --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_reference_source.rs @@ -0,0 +1,105 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Source information for a reference. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXReferenceSource { + /// URL to the source. + #[serde(rename = "url")] + pub url: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXReferenceSource { + pub fn new() -> CycloneDXReferenceSource { + CycloneDXReferenceSource { + url: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn url(mut self, value: String) -> Self { + self.url = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl Default for CycloneDXReferenceSource { + fn default() -> Self { + Self::new() + } +} + +impl<'de> Deserialize<'de> for CycloneDXReferenceSource { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXReferenceSourceVisitor; + impl<'a> Visitor<'a> for CycloneDXReferenceSourceVisitor { + type Value = CycloneDXReferenceSource; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut url: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "url" => { + if v.is_null() { + continue; + } + url = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + + let content = CycloneDXReferenceSource { + url, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXReferenceSourceVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_tool_component.rs b/src/datadogV2/model/model_cyclone_dx_tool_component.rs new file mode 100644 index 000000000..a38aae8cb --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_tool_component.rs @@ -0,0 +1,109 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Tool component that detected the vulnerabilities. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXToolComponent { + /// The name of the tool. + #[serde(rename = "name")] + pub name: String, + /// The type of the tool. + #[serde(rename = "type")] + pub type_: Option, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXToolComponent { + pub fn new(name: String) -> CycloneDXToolComponent { + CycloneDXToolComponent { + name, + type_: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn type_(mut self, value: String) -> Self { + self.type_ = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXToolComponent { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXToolComponentVisitor; + impl<'a> Visitor<'a> for CycloneDXToolComponentVisitor { + type Value = CycloneDXToolComponent; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut name: Option = None; + let mut type_: Option = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "name" => { + name = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "type" => { + if v.is_null() { + continue; + } + type_ = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let name = name.ok_or_else(|| M::Error::missing_field("name"))?; + + let content = CycloneDXToolComponent { + name, + type_, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXToolComponentVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_tools.rs b/src/datadogV2/model/model_cyclone_dx_tools.rs new file mode 100644 index 000000000..2858998ea --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_tools.rs @@ -0,0 +1,93 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// Tools used to generate the BOM. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXTools { + /// List of tool components. Only one tool is supported. + #[serde(rename = "components")] + pub components: Vec, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXTools { + pub fn new(components: Vec) -> CycloneDXTools { + CycloneDXTools { + components, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXTools { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXToolsVisitor; + impl<'a> Visitor<'a> for CycloneDXToolsVisitor { + type Value = CycloneDXTools; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut components: Option> = + None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "components" => { + components = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let components = components.ok_or_else(|| M::Error::missing_field("components"))?; + + let content = CycloneDXTools { + components, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXToolsVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dx_vulnerability.rs b/src/datadogV2/model/model_cyclone_dx_vulnerability.rs new file mode 100644 index 000000000..d92cc4d0a --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dx_vulnerability.rs @@ -0,0 +1,202 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// A vulnerability in the CycloneDX BOM. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXVulnerability { + /// List of advisory references. + #[serde(rename = "advisories")] + pub advisories: Option>, + /// List of component references affected by this vulnerability. + #[serde(rename = "affects")] + pub affects: Vec, + /// List of CWE (Common Weakness Enumeration) identifiers. + #[serde(rename = "cwes")] + pub cwes: Option>, + /// Description of the vulnerability. + #[serde(rename = "description")] + pub description: Option, + /// Detailed information about the vulnerability. + #[serde(rename = "detail")] + pub detail: Option, + /// The unique identifier of the vulnerability (e.g., CVE ID). + #[serde(rename = "id")] + pub id: String, + /// List of vulnerability ratings. Must contain exactly one rating. + #[serde(rename = "ratings")] + pub ratings: Vec, + /// List of external references for the vulnerability. + #[serde(rename = "references")] + pub references: Option>, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXVulnerability { + pub fn new( + affects: Vec, + id: String, + ratings: Vec, + ) -> CycloneDXVulnerability { + CycloneDXVulnerability { + advisories: None, + affects, + cwes: None, + description: None, + detail: None, + id, + ratings, + references: None, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn advisories(mut self, value: Vec) -> Self { + self.advisories = Some(value); + self + } + + pub fn cwes(mut self, value: Vec) -> Self { + self.cwes = Some(value); + self + } + + pub fn description(mut self, value: String) -> Self { + self.description = Some(value); + self + } + + pub fn detail(mut self, value: String) -> Self { + self.detail = Some(value); + self + } + + pub fn references(mut self, value: Vec) -> Self { + self.references = Some(value); + self + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXVulnerability { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXVulnerabilityVisitor; + impl<'a> Visitor<'a> for CycloneDXVulnerabilityVisitor { + type Value = CycloneDXVulnerability; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut advisories: Option> = None; + let mut affects: Option> = None; + let mut cwes: Option> = None; + let mut description: Option = None; + let mut detail: Option = None; + let mut id: Option = None; + let mut ratings: Option> = None; + let mut references: Option> = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "advisories" => { + if v.is_null() { + continue; + } + advisories = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "affects" => { + affects = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "cwes" => { + if v.is_null() { + continue; + } + cwes = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "description" => { + if v.is_null() { + continue; + } + description = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "detail" => { + if v.is_null() { + continue; + } + detail = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "id" => { + id = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "ratings" => { + ratings = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "references" => { + if v.is_null() { + continue; + } + references = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let affects = affects.ok_or_else(|| M::Error::missing_field("affects"))?; + let id = id.ok_or_else(|| M::Error::missing_field("id"))?; + let ratings = ratings.ok_or_else(|| M::Error::missing_field("ratings"))?; + + let content = CycloneDXVulnerability { + advisories, + affects, + cwes, + description, + detail, + id, + ratings, + references, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXVulnerabilityVisitor) + } +} diff --git a/src/datadogV2/model/model_cyclone_dxbom.rs b/src/datadogV2/model/model_cyclone_dxbom.rs new file mode 100644 index 000000000..7c0f69d6a --- /dev/null +++ b/src/datadogV2/model/model_cyclone_dxbom.rs @@ -0,0 +1,155 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. +use serde::de::{Error, MapAccess, Visitor}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_with::skip_serializing_none; +use std::fmt::{self, Formatter}; + +/// CycloneDX 1.5 Bill of Materials (BOM) for importing vulnerabilities. +#[non_exhaustive] +#[skip_serializing_none] +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct CycloneDXBOM { + /// The format of the BOM. Must be "CycloneDX". + #[serde(rename = "bomFormat")] + pub bom_format: String, + /// List of components (libraries, applications, or operating systems) that are affected by vulnerabilities. + #[serde(rename = "components")] + pub components: Vec, + /// Metadata for the CycloneDX BOM. + #[serde(rename = "metadata")] + pub metadata: crate::datadogV2::model::CycloneDXMetadata, + /// The version of the CycloneDX specification. Must be "1.5". + #[serde(rename = "specVersion")] + pub spec_version: String, + /// The version of the BOM. + #[serde(rename = "version")] + pub version: i64, + /// List of vulnerabilities to be imported. + #[serde(rename = "vulnerabilities")] + pub vulnerabilities: Vec, + #[serde(flatten)] + pub additional_properties: std::collections::BTreeMap, + #[serde(skip)] + #[serde(default)] + pub(crate) _unparsed: bool, +} + +impl CycloneDXBOM { + pub fn new( + bom_format: String, + components: Vec, + metadata: crate::datadogV2::model::CycloneDXMetadata, + spec_version: String, + version: i64, + vulnerabilities: Vec, + ) -> CycloneDXBOM { + CycloneDXBOM { + bom_format, + components, + metadata, + spec_version, + version, + vulnerabilities, + additional_properties: std::collections::BTreeMap::new(), + _unparsed: false, + } + } + + pub fn additional_properties( + mut self, + value: std::collections::BTreeMap, + ) -> Self { + self.additional_properties = value; + self + } +} + +impl<'de> Deserialize<'de> for CycloneDXBOM { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct CycloneDXBOMVisitor; + impl<'a> Visitor<'a> for CycloneDXBOMVisitor { + type Value = CycloneDXBOM; + + fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_str("a mapping") + } + + fn visit_map(self, mut map: M) -> Result + where + M: MapAccess<'a>, + { + let mut bom_format: Option = None; + let mut components: Option> = None; + let mut metadata: Option = None; + let mut spec_version: Option = None; + let mut version: Option = None; + let mut vulnerabilities: Option< + Vec, + > = None; + let mut additional_properties: std::collections::BTreeMap< + String, + serde_json::Value, + > = std::collections::BTreeMap::new(); + let mut _unparsed = false; + + while let Some((k, v)) = map.next_entry::()? { + match k.as_str() { + "bomFormat" => { + bom_format = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "components" => { + components = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "metadata" => { + metadata = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "specVersion" => { + spec_version = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "version" => { + version = Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + "vulnerabilities" => { + vulnerabilities = + Some(serde_json::from_value(v).map_err(M::Error::custom)?); + } + &_ => { + if let Ok(value) = serde_json::from_value(v.clone()) { + additional_properties.insert(k, value); + } + } + } + } + let bom_format = bom_format.ok_or_else(|| M::Error::missing_field("bom_format"))?; + let components = components.ok_or_else(|| M::Error::missing_field("components"))?; + let metadata = metadata.ok_or_else(|| M::Error::missing_field("metadata"))?; + let spec_version = + spec_version.ok_or_else(|| M::Error::missing_field("spec_version"))?; + let version = version.ok_or_else(|| M::Error::missing_field("version"))?; + let vulnerabilities = + vulnerabilities.ok_or_else(|| M::Error::missing_field("vulnerabilities"))?; + + let content = CycloneDXBOM { + bom_format, + components, + metadata, + spec_version, + version, + vulnerabilities, + additional_properties, + _unparsed, + }; + + Ok(content) + } + } + + deserializer.deserialize_any(CycloneDXBOMVisitor) + } +} diff --git a/src/datadogV2/model/model_security_finding_type.rs b/src/datadogV2/model/model_security_finding_type.rs new file mode 100644 index 000000000..b589e9475 --- /dev/null +++ b/src/datadogV2/model/model_security_finding_type.rs @@ -0,0 +1,51 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum SecurityFindingType { + VULNERABILITY, + CONFIGURATION_FINDING, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for SecurityFindingType { + fn to_string(&self) -> String { + match self { + Self::VULNERABILITY => String::from("vulnerability"), + Self::CONFIGURATION_FINDING => String::from("configuration_finding"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for SecurityFindingType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::UnparsedObject(v) => v.serialize(serializer), + _ => serializer.serialize_str(self.to_string().as_str()), + } + } +} + +impl<'de> Deserialize<'de> for SecurityFindingType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "vulnerability" => Self::VULNERABILITY, + "configuration_finding" => Self::CONFIGURATION_FINDING, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/src/datadogV2/model/model_threat_intel_indicator_type.rs b/src/datadogV2/model/model_threat_intel_indicator_type.rs new file mode 100644 index 000000000..90bd5079e --- /dev/null +++ b/src/datadogV2/model/model_threat_intel_indicator_type.rs @@ -0,0 +1,54 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache-2.0 License. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2019-Present Datadog, Inc. + +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +#[non_exhaustive] +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ThreatIntelIndicatorType { + IP_ADDRESS, + DOMAIN, + SHA256, + UnparsedObject(crate::datadog::UnparsedObject), +} + +impl ToString for ThreatIntelIndicatorType { + fn to_string(&self) -> String { + match self { + Self::IP_ADDRESS => String::from("ip_address"), + Self::DOMAIN => String::from("domain"), + Self::SHA256 => String::from("sha256"), + Self::UnparsedObject(v) => v.value.to_string(), + } + } +} + +impl Serialize for ThreatIntelIndicatorType { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match self { + Self::UnparsedObject(v) => v.serialize(serializer), + _ => serializer.serialize_str(self.to_string().as_str()), + } + } +} + +impl<'de> Deserialize<'de> for ThreatIntelIndicatorType { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s: String = String::deserialize(deserializer)?; + Ok(match s.as_str() { + "ip_address" => Self::IP_ADDRESS, + "domain" => Self::DOMAIN, + "sha256" => Self::SHA256, + _ => Self::UnparsedObject(crate::datadog::UnparsedObject { + value: serde_json::Value::String(s.into()), + }), + }) + } +} diff --git a/tests/scenarios/features/v2/security_monitoring.feature b/tests/scenarios/features/v2/security_monitoring.feature index 5695a7ea4..3ad8d5c96 100644 --- a/tests/scenarios/features/v2/security_monitoring.feature +++ b/tests/scenarios/features/v2/security_monitoring.feature @@ -708,6 +708,24 @@ Feature: Security Monitoring When the request is sent Then the response status is 404 Not Found + @generated @skip @team:DataDog/asm-vm + Scenario: Create security finding returns "Bad Request" response + Given operation "CreateSecurityFinding" enabled + And new "CreateSecurityFinding" request + And request contains "vendor" parameter from "REPLACE.ME" + And request contains "finding_type" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/asm-vm + Scenario: Create security finding returns "OK" response + Given operation "CreateSecurityFinding" enabled + And new "CreateSecurityFinding" request + And request contains "vendor" parameter from "REPLACE.ME" + And request contains "finding_type" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 200 OK + @generated @skip @team:DataDog/k9-cloud-security-platform Scenario: Deactivate content pack returns "Accepted" response Given operation "DeactivateContentPack" enabled @@ -1397,6 +1415,40 @@ Feature: Security Monitoring When the request is sent Then the response status is 200 The list of notification rules. + @generated @skip @team:DataDog/asm-vm + Scenario: Import threat intelligence feed returns "Bad Request" response + Given operation "ImportThreatIntel" enabled + And new "ImportThreatIntel" request + And request contains "ti_vendor" parameter from "REPLACE.ME" + And request contains "ti_indicator" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/asm-vm + Scenario: Import threat intelligence feed returns "OK" response + Given operation "ImportThreatIntel" enabled + And new "ImportThreatIntel" request + And request contains "ti_vendor" parameter from "REPLACE.ME" + And request contains "ti_indicator" parameter from "REPLACE.ME" + When the request is sent + Then the response status is 200 OK + + @generated @skip @team:DataDog/asm-vm + Scenario: Import vulnerabilities returns "Bad Request" response + Given operation "ImportSecurityVulnerabilities" enabled + And new "ImportSecurityVulnerabilities" request + And body with value {"bomFormat": "CycloneDX", "components": [{"bom-ref": "a3390fca-c315-41ae-ae05-af5e7859cdee", "name": "lodash", "purl": "pkg:npm/lodash@4.17.21", "type": "library", "version": "4.17.21"}], "metadata": {"component": {"bom-ref": "asset-ref-123", "name": "i-12345", "type": "operating-system"}, "tools": {"components": [{"name": "my-scanner", "type": "application"}]}}, "specVersion": "1.5", "version": 1, "vulnerabilities": [{"advisories": [{"url": "https://example.com/advisory/CVE-2021-1234"}], "affects": [{"ref": "a3390fca-c315-41ae-ae05-af5e7859cdee"}], "cwes": [123, 345], "description": "Sample vulnerability detected in the application.", "detail": "Details about the vulnerability", "id": "CVE-2021-1234", "ratings": [{"score": 9.0, "severity": "high", "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N"}], "references": [{"id": "GHSA-35m5-8cvj-8783", "source": {"url": "https://example.com"}}]}]} + When the request is sent + Then the response status is 400 Bad Request + + @generated @skip @team:DataDog/asm-vm + Scenario: Import vulnerabilities returns "OK" response + Given operation "ImportSecurityVulnerabilities" enabled + And new "ImportSecurityVulnerabilities" request + And body with value {"bomFormat": "CycloneDX", "components": [{"bom-ref": "a3390fca-c315-41ae-ae05-af5e7859cdee", "name": "lodash", "purl": "pkg:npm/lodash@4.17.21", "type": "library", "version": "4.17.21"}], "metadata": {"component": {"bom-ref": "asset-ref-123", "name": "i-12345", "type": "operating-system"}, "tools": {"components": [{"name": "my-scanner", "type": "application"}]}}, "specVersion": "1.5", "version": 1, "vulnerabilities": [{"advisories": [{"url": "https://example.com/advisory/CVE-2021-1234"}], "affects": [{"ref": "a3390fca-c315-41ae-ae05-af5e7859cdee"}], "cwes": [123, 345], "description": "Sample vulnerability detected in the application.", "detail": "Details about the vulnerability", "id": "CVE-2021-1234", "ratings": [{"score": 9.0, "severity": "high", "vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N"}], "references": [{"id": "GHSA-35m5-8cvj-8783", "source": {"url": "https://example.com"}}]}]} + When the request is sent + Then the response status is 200 OK + @generated @skip @team:DataDog/k9-cloud-vm Scenario: List assets SBOMs returns "Bad request: The server cannot process the request due to invalid syntax in the request." response Given new "ListAssetsSBOMs" request diff --git a/tests/scenarios/features/v2/undo.json b/tests/scenarios/features/v2/undo.json index 75d0d7f71..110c6b94f 100644 --- a/tests/scenarios/features/v2/undo.json +++ b/tests/scenarios/features/v2/undo.json @@ -4304,6 +4304,12 @@ "type": "safe" } }, + "CreateSecurityFinding": { + "tag": "Security Monitoring", + "undo": { + "type": "unsafe" + } + }, "DetachCase": { "tag": "Security Monitoring", "undo": { @@ -4409,12 +4415,24 @@ "type": "idempotent" } }, + "ImportThreatIntel": { + "tag": "Security Monitoring", + "undo": { + "type": "unsafe" + } + }, "ListVulnerabilities": { "tag": "Security Monitoring", "undo": { "type": "safe" } }, + "ImportSecurityVulnerabilities": { + "tag": "Security Monitoring", + "undo": { + "type": "unsafe" + } + }, "GetVulnerabilityNotificationRules": { "tag": "Security Monitoring", "undo": { diff --git a/tests/scenarios/function_mappings.rs b/tests/scenarios/function_mappings.rs index 144f43821..36f4b1941 100644 --- a/tests/scenarios/function_mappings.rs +++ b/tests/scenarios/function_mappings.rs @@ -2557,6 +2557,10 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { "v2.ListSecurityFindingsWithPagination".into(), test_v2_list_security_findings_with_pagination, ); + world.function_mappings.insert( + "v2.CreateSecurityFinding".into(), + test_v2_create_security_finding, + ); world .function_mappings .insert("v2.DetachCase".into(), test_v2_detach_case); @@ -2610,10 +2614,17 @@ pub fn collect_function_calls(world: &mut DatadogWorld) { "v2.PatchSignalNotificationRule".into(), test_v2_patch_signal_notification_rule, ); + world + .function_mappings + .insert("v2.ImportThreatIntel".into(), test_v2_import_threat_intel); world.function_mappings.insert( "v2.ListVulnerabilities".into(), test_v2_list_vulnerabilities, ); + world.function_mappings.insert( + "v2.ImportSecurityVulnerabilities".into(), + test_v2_import_security_vulnerabilities, + ); world.function_mappings.insert( "v2.GetVulnerabilityNotificationRules".into(), test_v2_get_vulnerability_notification_rules, @@ -17965,6 +17976,35 @@ fn test_v2_list_security_findings_with_pagination( world.response.code = 200; } +fn test_v2_create_security_finding(world: &mut DatadogWorld, _parameters: &HashMap) { + let api = world + .api_instances + .v2_api_security_monitoring + .as_ref() + .expect("api instance not found"); + let vendor = serde_json::from_value(_parameters.get("vendor").unwrap().clone()).unwrap(); + let finding_type = + serde_json::from_value(_parameters.get("finding_type").unwrap().clone()).unwrap(); + let body = serde_json::from_value(_parameters.get("body").unwrap().clone()).unwrap(); + let response = + match block_on(api.create_security_finding_with_http_info(vendor, finding_type, body)) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + fn test_v2_detach_case(world: &mut DatadogWorld, _parameters: &HashMap) { let api = world .api_instances @@ -18444,6 +18484,44 @@ fn test_v2_patch_signal_notification_rule( world.response.code = response.status.as_u16(); } +fn test_v2_import_threat_intel(world: &mut DatadogWorld, _parameters: &HashMap) { + let api = world + .api_instances + .v2_api_security_monitoring + .as_ref() + .expect("api instance not found"); + let ti_vendor = serde_json::from_value(_parameters.get("ti_vendor").unwrap().clone()).unwrap(); + let ti_indicator = + serde_json::from_value(_parameters.get("ti_indicator").unwrap().clone()).unwrap(); + let body = serde_json::from_value(_parameters.get("body").unwrap().clone()).unwrap(); + let ti_integration_account = _parameters + .get("ti_integration_account") + .and_then(|param| Some(serde_json::from_value(param.clone()).unwrap())); + let mut params = datadogV2::api_security_monitoring::ImportThreatIntelOptionalParams::default(); + params.ti_integration_account = ti_integration_account; + let response = match block_on(api.import_threat_intel_with_http_info( + ti_vendor, + ti_indicator, + body, + params, + )) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + fn test_v2_list_vulnerabilities(world: &mut DatadogWorld, _parameters: &HashMap) { let api = world .api_instances @@ -18643,6 +18721,34 @@ fn test_v2_list_vulnerabilities(world: &mut DatadogWorld, _parameters: &HashMap< world.response.code = response.status.as_u16(); } +fn test_v2_import_security_vulnerabilities( + world: &mut DatadogWorld, + _parameters: &HashMap, +) { + let api = world + .api_instances + .v2_api_security_monitoring + .as_ref() + .expect("api instance not found"); + let body = serde_json::from_value(_parameters.get("body").unwrap().clone()).unwrap(); + let response = match block_on(api.import_security_vulnerabilities_with_http_info(body)) { + Ok(response) => response, + Err(error) => { + return match error { + Error::ResponseError(e) => { + world.response.code = e.status.as_u16(); + if let Some(entity) = e.entity { + world.response.object = serde_json::to_value(entity).unwrap(); + } + } + _ => panic!("error parsing response: {error}"), + }; + } + }; + world.response.object = serde_json::to_value(response.entity).unwrap(); + world.response.code = response.status.as_u16(); +} + fn test_v2_get_vulnerability_notification_rules( world: &mut DatadogWorld, _parameters: &HashMap,