From 03de0ed29601002617c5c9233d9ad20fba6e6b00 Mon Sep 17 00:00:00 2001 From: Florian Scherb <1345770+yardlogs@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:46:31 +0200 Subject: [PATCH 1/2] Fix json output when a node with attributes has the same key several times --- src/json_output.rs | 58 +++++++-- ...nt_json_with_multiple_nodes_same_name.snap | 5 + ...ith_multiple_nodes_same_name_separate.snap | 120 +++++++++--------- 3 files changed, 112 insertions(+), 71 deletions(-) diff --git a/src/json_output.rs b/src/json_output.rs index ad1d1b9..02d0476 100644 --- a/src/json_output.rs +++ b/src/json_output.rs @@ -214,28 +214,62 @@ impl JsonOutput { .to_string(), } })?; + // We do a linear probe in case XML contains duplicate keys + if let Some(old_attribute) = value.insert(format!("{}_attributes", name), Value::Null) { + if let Some(old_value) = value.insert(name.to_string(), Value::Null) { + let mut free_slot = 1; + // If it is a concrete value, we look for another slot. + while value.get(&format!("{}_{}", name, free_slot)).is_some() || value.get(&format!("{}_{}_attributes", name, free_slot)).is_some() { + // Value is an empty object - we can override it's value. + free_slot += 1 + } + if let Some(old_value_object) = old_value.as_object() { + if !old_value_object.is_empty(){ + value.insert(format!("{}_{}", name, free_slot), old_value); + } + }; + if let Some(old_attribute_object) = old_attribute.as_object() { + if !old_attribute_object.is_empty() { + value.insert(format!("{}_{}_attributes", name, free_slot), old_attribute); + }; + }; + }; + }; value.insert(format!("{name}_attributes"), Value::Object(attributes)); // If the element's main value is empty, we want to remove it because we // do not want the value to represent an empty object. - if value[name] == Value::Object(Map::new()) { + if value[name].is_null() || value[name] == Value::Object(Map::new()) { value.remove(name); } } else { - let value = self - .get_or_create_current_path() - .as_object_mut() - .ok_or_else(|| { - SerializationError::JsonStructureError { - message: - "This is a bug - expected current value to exist, and to be an object type. - Check that the value is not `Value::null`" - .to_string(), - } - })?; + let container = self.get_current_parent().as_object_mut().ok_or_else(|| { + SerializationError::JsonStructureError { + message: + "This is a bug - expected parent container to exist, and to be an object type.\ + Check that the referencing parent is not `Value::null`" + .to_string(), + } + })?; + // We do a linear probe in case XML contains duplicate keys + if let Some(old_value) = container.insert(name.to_string(), Value::Null) { + if let Some(map) = old_value.as_object() { + if !map.is_empty() { + let mut free_slot = 1; + // If it is a concrete value, we look for another slot. + while container.get(&format!("{}_{}", name, free_slot)).is_some() { + // Value is an empty object - we can override it's value. + free_slot += 1 + } + container.insert(format!("{}_{}", name, free_slot), old_value); + } + } + }; + let mut value = Map::new(); value.insert("#attributes".to_owned(), Value::Object(attributes)); + container.insert(name.to_string(), Value::Object(value)); } } else { // If the object does not have attributes, replace it with a null placeholder, diff --git a/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name.snap b/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name.snap index f466abd..446df5c 100644 --- a/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name.snap +++ b/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name.snap @@ -50,6 +50,11 @@ expression: "&value" "name": "NoProxy" } }, + "Action_1": { + "#attributes": { + "name": "NoProxy" + } + }, "HTTPRequestHeadersInfo": { "Header": "Connection: Keep-Alive", "Header_1": "GET /pki/crl/products/microsoftrootcert.crl HTTP/1.1", diff --git a/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name_separate.snap b/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name_separate.snap index a059ab4..fd0f374 100644 --- a/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name_separate.snap +++ b/tests/snapshots/test_record_samples__event_json_with_multiple_nodes_same_name_separate.snap @@ -1,42 +1,71 @@ --- source: tests/test_record_samples.rs -assertion_line: 258 expression: "&value" --- { + "Event_attributes": { + "xmlns": "http://schemas.microsoft.com/win/2004/08/events/event" + }, "Event": { "System": { - "Channel": "Microsoft-Windows-CAPI2/Operational", - "Computer": "WIN-M5327EF98B9", - "Correlation": null, + "Provider_attributes": { + "Name": "Microsoft-Windows-CAPI2", + "Guid": "{5bbca4a8-b209-48dc-a8c7-b23d3e5216fb}" + }, "EventID": 53, + "Version": 0, + "Level": 4, + "Task": 53, + "Opcode": 2, + "Keywords": "0x4000000000000036", + "TimeCreated_attributes": { + "SystemTime": "2017-05-19T02:02:36.203125Z" + }, "EventRecordID": 28, + "Correlation": null, "Execution_attributes": { "ProcessID": 1396, "ThreadID": 2132 }, - "Keywords": "0x4000000000000036", - "Level": 4, - "Opcode": 2, - "Provider_attributes": { - "Guid": "{5bbca4a8-b209-48dc-a8c7-b23d3e5216fb}", - "Name": "Microsoft-Windows-CAPI2" - }, + "Channel": "Microsoft-Windows-CAPI2/Operational", + "Computer": "WIN-M5327EF98B9", "Security_attributes": { "UserID": "S-1-5-21-1223297778-3299746493-1462173606-500" - }, - "Task": 53, - "TimeCreated_attributes": { - "SystemTime": "2017-05-19T02:02:36.203125Z" - }, - "Version": 0 + } }, "UserData": { "CryptRetrieveObjectByUrlWire": { + "URL_attributes": { + "scheme": "http" + }, + "URL": "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl", + "Object_attributes": { + "type": "CONTEXT_OID_CRL", + "constant": "2" + }, + "Timeout": "PT9.984S", + "Flags_attributes": { + "value": "202005", + "CRYPT_RETRIEVE_MULTIPLE_OBJECTS": "true", + "CRYPT_WIRE_ONLY_RETRIEVAL": "true", + "CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL": "true", + "CRYPT_PROXY_CACHE_RETRIEVAL": "true" + }, + "AuxInfo_attributes": { + "maxUrlRetrievalByteCount": "104857600", + "fProxyCacheRetrieval": "true" + }, "AdditionalInfo": { + "NetworkConnectivityStatus_attributes": { + "value": "1", + "_SENSAPI_NETWORK_ALIVE_LAN": "true" + }, "Action_attributes": { "name": "NoProxy" }, + "Action_1_attributes": { + "name": "NoProxy" + }, "HTTPRequestHeadersInfo": { "Header": "Connection: Keep-Alive", "Header_1": "GET /pki/crl/products/microsoftrootcert.crl HTTP/1.1", @@ -49,8 +78,6 @@ expression: "&value" "HTTPResponseHeadersInfo": { "Header": "x-ms-blob-type: BlockBlob", "Header_1": "HTTP/1.1 200 OK", - "Header_10": "x-ms-version: 2009-09-19", - "Header_11": "x-ms-lease-status: unlocked", "Header_2": "Connection: keep-alive", "Header_3": "Date: Thu, 18 May 2017 11:37:58 GMT", "Header_4": "Content-Length: 813", @@ -58,62 +85,37 @@ expression: "&value" "Header_6": "Last-Modified: Tue, 02 May 2017 22:24:24 GMT", "Header_7": "ETag: 0x8D491A9FD112A27", "Header_8": "Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0", - "Header_9": "x-ms-request-id: 477c132d-0001-0045-443b-c49ae1000000" - }, - "NetworkConnectivityStatus_attributes": { - "_SENSAPI_NETWORK_ALIVE_LAN": "true", - "value": "1" + "Header_9": "x-ms-request-id: 477c132d-0001-0045-443b-c49ae1000000", + "Header_10": "x-ms-version: 2009-09-19", + "Header_11": "x-ms-lease-status: unlocked" } }, - "AuxInfo_attributes": { - "fProxyCacheRetrieval": "true", - "maxUrlRetrievalByteCount": "104857600" + "CacheInfo_attributes": { + "lastSyncTime": "2017-05-19T02:02:36.203Z" }, "CacheInfo": { "URLCacheResponseInfo_attributes": { - "lastModifiedTime": "2017-05-02T22:24:24Z", - "responseType": "CRYPTNET_URL_CACHE_RESPONSE_HTTP" + "responseType": "CRYPTNET_URL_CACHE_RESPONSE_HTTP", + "lastModifiedTime": "2017-05-02T22:24:24Z" } }, - "CacheInfo_attributes": { - "lastSyncTime": "2017-05-19T02:02:36.203Z" - }, - "CorrelationAuxInfo_attributes": { - "SeqNumber": "7", - "TaskId": "{74E4CD40-C966-49F5-B50A-032DFFEE57CA}" + "RetrievedObjects": { + "CertificateRevocationList_attributes": { + "fileRef": "0986764ED95D3C77F3F1AD8340EDD2F36C3BF8E7.crl", + "issuerName": "Microsoft Root Certificate Authority" + } }, "EventAuxInfo_attributes": { "ProcessName": "Setup.exe" }, - "Flags_attributes": { - "CRYPT_LDAP_SCOPE_BASE_ONLY_RETRIEVAL": "true", - "CRYPT_PROXY_CACHE_RETRIEVAL": "true", - "CRYPT_RETRIEVE_MULTIPLE_OBJECTS": "true", - "CRYPT_WIRE_ONLY_RETRIEVAL": "true", - "value": "202005" - }, - "Object_attributes": { - "constant": "2", - "type": "CONTEXT_OID_CRL" + "CorrelationAuxInfo_attributes": { + "TaskId": "{74E4CD40-C966-49F5-B50A-032DFFEE57CA}", + "SeqNumber": "7" }, "Result_attributes": { "value": "0" - }, - "RetrievedObjects": { - "CertificateRevocationList_attributes": { - "fileRef": "0986764ED95D3C77F3F1AD8340EDD2F36C3BF8E7.crl", - "issuerName": "Microsoft Root Certificate Authority" - } - }, - "Timeout": "PT9.984S", - "URL": "http://crl.microsoft.com/pki/crl/products/microsoftrootcert.crl", - "URL_attributes": { - "scheme": "http" } } } - }, - "Event_attributes": { - "xmlns": "http://schemas.microsoft.com/win/2004/08/events/event" } } From f094b9e5201f58087938d0fb41b8b6d6209041bd Mon Sep 17 00:00:00 2001 From: fukusuket <41001169+fukusuket@users.noreply.github.com> Date: Mon, 4 Nov 2024 11:07:59 +0900 Subject: [PATCH 2/2] fix: apply cargo fmt --- src/json_output.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/json_output.rs b/src/json_output.rs index 02d0476..77033ca 100644 --- a/src/json_output.rs +++ b/src/json_output.rs @@ -215,22 +215,31 @@ impl JsonOutput { } })?; // We do a linear probe in case XML contains duplicate keys - if let Some(old_attribute) = value.insert(format!("{}_attributes", name), Value::Null) { + if let Some(old_attribute) = + value.insert(format!("{}_attributes", name), Value::Null) + { if let Some(old_value) = value.insert(name.to_string(), Value::Null) { let mut free_slot = 1; // If it is a concrete value, we look for another slot. - while value.get(&format!("{}_{}", name, free_slot)).is_some() || value.get(&format!("{}_{}_attributes", name, free_slot)).is_some() { + while value.get(&format!("{}_{}", name, free_slot)).is_some() + || value + .get(&format!("{}_{}_attributes", name, free_slot)) + .is_some() + { // Value is an empty object - we can override it's value. free_slot += 1 } if let Some(old_value_object) = old_value.as_object() { - if !old_value_object.is_empty(){ + if !old_value_object.is_empty() { value.insert(format!("{}_{}", name, free_slot), old_value); } }; if let Some(old_attribute_object) = old_attribute.as_object() { if !old_attribute_object.is_empty() { - value.insert(format!("{}_{}_attributes", name, free_slot), old_attribute); + value.insert( + format!("{}_{}_attributes", name, free_slot), + old_attribute, + ); }; }; };