From af6dea78eb07bccd93f478038d91aba7f41a790c Mon Sep 17 00:00:00 2001 From: NachoEchevarria Date: Fri, 11 Oct 2024 10:09:55 +0200 Subject: [PATCH] Revert "Merge branch 'master' into nacho/SendUserAgentHeaderAsStringToTheWAF" This reverts commit 4091dea0cc8416e23b2c23a38c9c7e445600eb00, reversing changes made to 7ebd3aa688a909666248f3769534f86c775f61bd. --- .../StackSamplerLoopManager.cpp | 13 ++-- .../StackSamplerLoopManager.h | 4 +- tracer/missing-nullability-files.csv | 1 + .../Datadog.Trace/AppSec/Rcm/AsmDdProduct.cs | 11 +++- .../AppSec/Rcm/ConfigurationStatus.cs | 58 ++++++++--------- .../AppSec/Rcm/Models/AsmDd/RuleSet.cs | 37 +++-------- tracer/src/Datadog.Trace/AppSec/Security.cs | 11 +++- tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs | 6 +- .../Waf/Initialization/WafConfigurator.cs | 7 --- .../Waf/ReturnTypes.Managed/InitResult.cs | 6 +- tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs | 39 ++++++------ .../AutoInstrumentation/Wcf/WcfCommon.cs | 6 +- .../Rcm/AspNetCore5AsmData.cs | 62 +++++++++---------- 13 files changed, 124 insertions(+), 137 deletions(-) diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.cpp b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.cpp index e1d12cee5d9f..2c0173c1a39d 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.cpp +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.cpp @@ -91,8 +91,8 @@ const char* StackSamplerLoopManager::GetName() bool StackSamplerLoopManager::StartImpl() { - InitializeSampler(); - RunWatcherAndSampler(); + RunStackSampling(); + RunWatcher(); return true; } @@ -106,7 +106,7 @@ bool StackSamplerLoopManager::StopImpl() return true; } -void StackSamplerLoopManager::InitializeSampler() +void StackSamplerLoopManager::RunStackSampling() { _pStackSamplerLoop = std::make_unique( _pCorProfilerInfo, @@ -119,9 +119,11 @@ void StackSamplerLoopManager::InitializeSampler() _pWallTimeCollector, _pCpuTimeCollector, _metricsRegistry); + + _pStackSamplerLoop->Start(); } -void StackSamplerLoopManager::RunWatcherAndSampler() +void StackSamplerLoopManager::RunWatcher() { _pWatcherThread = std::make_unique([this] { @@ -145,9 +147,6 @@ void StackSamplerLoopManager::WatcherLoop() Log::Info("StackSamplerLoopManager::WatcherLoop started."); _pThreadsCpuManager->Map(OpSysTools::GetThreadId(), WatcherThreadName); - // Start the sampler loop only when the watcher is ready - _pStackSamplerLoop->Start(); - while (false == _isWatcherShutdownRequested) { try diff --git a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.h b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.h index f32890edb90c..6c9f7b5d1cea 100644 --- a/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.h +++ b/profiler/src/ProfilerEngine/Datadog.Profiler.Native/StackSamplerLoopManager.h @@ -105,9 +105,9 @@ class StackSamplerLoopManager inline bool GetUpdateIsThreadSafeForStackSampleCollection(ManagedThreadInfo* pThreadInfo, bool* pIsStatusChanged); static inline bool ShouldCollectThread(std::uint64_t threadAggPeriodDeadlockCount, std::uint64_t globalAggPeriodDeadlockCount) ; - void InitializeSampler(); + void RunStackSampling(); - void RunWatcherAndSampler(); + void RunWatcher(); void ShutdownWatcher(); void WatcherLoop(); diff --git a/tracer/missing-nullability-files.csv b/tracer/missing-nullability-files.csv index f65cc13d46b1..ad2464eeee8e 100644 --- a/tracer/missing-nullability-files.csv +++ b/tracer/missing-nullability-files.csv @@ -270,6 +270,7 @@ src/Datadog.Trace/Agent/Transports/MimeTypes.cs src/Datadog.Trace/Agent/Transports/SocketHandlerRequestFactory.cs src/Datadog.Trace/AppSec/Concurrency/ReaderWriterLock.Core.cs src/Datadog.Trace/AppSec/Concurrency/ReaderWriterLock.Framework.cs +src/Datadog.Trace/AppSec/Waf/IWaf.cs src/Datadog.Trace/AppSec/Waf/WafConstants.cs src/Datadog.Trace/AppSec/Waf/WafReturnCode.cs src/Datadog.Trace/Ci/Agent/ApmAgentWriter.cs diff --git a/tracer/src/Datadog.Trace/AppSec/Rcm/AsmDdProduct.cs b/tracer/src/Datadog.Trace/AppSec/Rcm/AsmDdProduct.cs index 08e2bc4f826f..2f89e8f7154a 100644 --- a/tracer/src/Datadog.Trace/AppSec/Rcm/AsmDdProduct.cs +++ b/tracer/src/Datadog.Trace/AppSec/Rcm/AsmDdProduct.cs @@ -40,9 +40,18 @@ public void ProcessUpdates(ConfigurationStatus configurationStatus, List removedConfigsForThisProduct) { + var oneRemoved = false; foreach (var removedConfig in removedConfigsForThisProduct) { - configurationStatus.RulesByFile.Remove(removedConfig.Path); + oneRemoved |= configurationStatus.RulesByFile.Remove(removedConfig.Path); + } + + if (configurationStatus.RulesByFile.Count == 0) + { + configurationStatus.IncomingUpdateState.FallbackToEmbeddedRuleset(); + } + else if (oneRemoved) + { configurationStatus.IncomingUpdateState.WafKeysToApply.Add(ConfigurationStatus.WafRulesKey); } } diff --git a/tracer/src/Datadog.Trace/AppSec/Rcm/ConfigurationStatus.cs b/tracer/src/Datadog.Trace/AppSec/Rcm/ConfigurationStatus.cs index 0ef41a46f94a..2e4ce01d72f2 100644 --- a/tracer/src/Datadog.Trace/AppSec/Rcm/ConfigurationStatus.cs +++ b/tracer/src/Datadog.Trace/AppSec/Rcm/ConfigurationStatus.cs @@ -13,6 +13,7 @@ using Datadog.Trace.AppSec.Rcm.Models.AsmDd; using Datadog.Trace.AppSec.Rcm.Models.AsmFeatures; using Datadog.Trace.AppSec.Waf.Initialization; +using Datadog.Trace.ExtensionMethods; using Datadog.Trace.Logging; using Datadog.Trace.RemoteConfigurationManagement; using Datadog.Trace.Vendors.Newtonsoft.Json.Linq; @@ -47,6 +48,8 @@ internal record ConfigurationStatus public ConfigurationStatus(string? embeddedRulesPath) => _embeddedRulesPath = embeddedRulesPath; + internal RuleSet? FallbackEmbeddedRuleSet { get; set; } + internal bool? EnableAsm { get; set; } = null; internal string? AutoUserInstrumMode { get; set; } = null; @@ -101,73 +104,67 @@ internal static List MergeRuleData(IEnumerable res) return finalRuleData; } - internal object? BuildDictionaryForWafAccordingToIncomingUpdate(string? embeddedRulesetPath) + internal Dictionary BuildDictionaryForWafAccordingToIncomingUpdate() { - var configuration = new Dictionary(); + var dictionary = new Dictionary(); if (IncomingUpdateState.WafKeysToApply.Contains(WafExclusionsKey)) { var exclusions = ExclusionsByFile.SelectMany(x => x.Value).ToList(); - configuration.Add(WafExclusionsKey, new JArray(exclusions)); + dictionary.Add(WafExclusionsKey, new JArray(exclusions)); } if (IncomingUpdateState.WafKeysToApply.Contains(WafRulesOverridesKey)) { var overrides = RulesOverridesByFile.SelectMany(x => x.Value).ToList(); - configuration.Add(WafRulesOverridesKey, overrides.Select(r => r.ToKeyValuePair()).ToArray()); + dictionary.Add(WafRulesOverridesKey, overrides.Select(r => r.ToKeyValuePair()).ToArray()); } if (IncomingUpdateState.WafKeysToApply.Contains(WafRulesDataKey)) { var rulesData = MergeRuleData(RulesDataByFile.SelectMany(x => x.Value)); - configuration.Add(WafRulesDataKey, rulesData.Select(r => r.ToKeyValuePair()).ToArray()); + dictionary.Add(WafRulesDataKey, rulesData.Select(r => r.ToKeyValuePair()).ToArray()); } if (IncomingUpdateState.WafKeysToApply.Contains(WafExclusionsDataKey)) { var rulesData = MergeRuleData(ExclusionsDataByFile.SelectMany(x => x.Value)); - configuration.Add(WafExclusionsDataKey, rulesData.Select(r => r.ToKeyValuePair()).ToArray()); + dictionary.Add(WafExclusionsDataKey, rulesData.Select(r => r.ToKeyValuePair()).ToArray()); } if (IncomingUpdateState.WafKeysToApply.Contains(WafActionsKey)) { var actions = ActionsByFile.SelectMany(x => x.Value).ToList(); - configuration.Add(WafActionsKey, actions.Select(r => r.ToKeyValuePair()).ToArray()); + dictionary.Add(WafActionsKey, actions.Select(r => r.ToKeyValuePair()).ToArray()); } if (IncomingUpdateState.WafKeysToApply.Contains(WafCustomRulesKey)) { var customRules = CustomRulesByFile.SelectMany(x => x.Value).ToList(); var mergedCustomRules = new JArray(customRules); - configuration.Add(WafCustomRulesKey, mergedCustomRules); + dictionary.Add(WafCustomRulesKey, mergedCustomRules); } - // if there's incoming rules or empty rules, or if asm is to be activated, we also want the rules key in waf arguments - if (IncomingUpdateState.WafKeysToApply.Contains(WafRulesKey) || (IncomingUpdateState.SecurityStateChange && (EnableAsm ?? false))) + if (IncomingUpdateState.FallbackToEmbeddedRulesetAtNextUpdate) { - var rulesetFromRcm = RulesByFile.Values.FirstOrDefault(); - // should deserialize from LocalRuleFile - if (rulesetFromRcm is null) + if (FallbackEmbeddedRuleSet == null) { - var deserializedFromLocalRules = WafConfigurator.DeserializeEmbeddedOrStaticRules(embeddedRulesetPath); - if (deserializedFromLocalRules is not null) + var result = WafConfigurator.DeserializeEmbeddedOrStaticRules(_embeddedRulesPath); + if (result != null) { - if (configuration.Count == 0) - { - return deserializedFromLocalRules; - } - - var ruleSet = RuleSet.From(deserializedFromLocalRules); - ruleSet.AddToDictionaryAtRoot(configuration); + FallbackEmbeddedRuleSet = RuleSet.From(result); } } - else - { - rulesetFromRcm?.AddToDictionaryAtRoot(configuration); - } + + FallbackEmbeddedRuleSet?.AddToDictionaryAtRoot(dictionary); + } + else if (IncomingUpdateState.WafKeysToApply.Contains(WafRulesKey)) + { + var rulesetFromRcm = RulesByFile.Values.FirstOrDefault(); + rulesetFromRcm?.AddToDictionaryAtRoot(dictionary); } - return configuration.Count > 0 ? configuration : null; + return dictionary; } /// @@ -250,7 +247,7 @@ public bool StoreLastConfigState(Dictionary> c } } - // only deserialize and apply asm_features as it will decide if asm gets toggled on and if we deserialize all the others + // only treat asm_features as it will decide if asm gets toggled on and if we deserialize all the others // (the enable of auto user instrumentation as added to asm_features) _asmFeatureProduct.ProcessUpdates(this, asmFeaturesToUpdate); _asmFeatureProduct.ProcessRemovals(this, asmFeaturesToRemove); @@ -285,14 +282,19 @@ internal record IncomingUpdateStatus { internal HashSet WafKeysToApply { get; } = new(); + internal bool FallbackToEmbeddedRulesetAtNextUpdate { get; private set; } + internal bool SecurityStateChange { get; set; } public void Reset() { + FallbackToEmbeddedRulesetAtNextUpdate = false; WafKeysToApply.Clear(); SecurityStateChange = false; } + public void FallbackToEmbeddedRuleset() => FallbackToEmbeddedRulesetAtNextUpdate = true; + public void SignalSecurityStateChange() => SecurityStateChange = true; } } diff --git a/tracer/src/Datadog.Trace/AppSec/Rcm/Models/AsmDd/RuleSet.cs b/tracer/src/Datadog.Trace/AppSec/Rcm/Models/AsmDd/RuleSet.cs index 890ceacbb31c..8c9480e8bd55 100644 --- a/tracer/src/Datadog.Trace/AppSec/Rcm/Models/AsmDd/RuleSet.cs +++ b/tracer/src/Datadog.Trace/AppSec/Rcm/Models/AsmDd/RuleSet.cs @@ -1,4 +1,4 @@ -// +// // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // @@ -22,22 +22,13 @@ internal class RuleSet [JsonProperty("processors")] internal JToken? Processors { get; set; } - [JsonProperty("actions")] - internal JToken? Actions { get; set; } - [JsonProperty("scanners")] internal JToken? Scanners { get; set; } - [JsonProperty("exclusions")] - internal JToken? Exclusions { get; set; } - - [JsonProperty("custom_rules")] - internal JToken? CustomRules { get; set; } + public JToken? All { get; set; } public static RuleSet From(JToken result) { - // can rules from rc contains exclusions and custom rules? - var ruleset = new RuleSet { Version = result["version"]?.ToString(), @@ -45,9 +36,7 @@ public static RuleSet From(JToken result) Rules = result["rules"], Processors = result["processors"], Scanners = result["scanners"], - Actions = result["actions"], - Exclusions = result["exclusions"], - CustomRules = result["custom_rules"] + All = result }; return ruleset; } @@ -60,37 +49,27 @@ public void AddToDictionaryAtRoot(Dictionary dictionary) { if (Rules != null) { - dictionary["rules"] = Rules; + dictionary.Add("rules", Rules); } if (Metadata != null) { - dictionary["metadata"] = Metadata; + dictionary.Add("metadata", Metadata); } if (Version != null) { - dictionary["version"] = Version; + dictionary.Add("version", Version); } if (Processors != null) { - dictionary["processors"] = Processors; + dictionary.Add("processors", Processors); } if (Scanners != null) { - dictionary["scanners"] = Scanners; - } - - if (Exclusions is not null) - { - dictionary["exclusions"] = Exclusions; - } - - if (CustomRules is not null) - { - dictionary["custom_rules"] = CustomRules; + dictionary.Add("scanners", Scanners); } } } diff --git a/tracer/src/Datadog.Trace/AppSec/Security.cs b/tracer/src/Datadog.Trace/AppSec/Security.cs index b96b7585e1ed..cb9f0420e0a1 100644 --- a/tracer/src/Datadog.Trace/AppSec/Security.cs +++ b/tracer/src/Datadog.Trace/AppSec/Security.cs @@ -208,6 +208,7 @@ private ApplyDetails[] UpdateFromRcm(Dictionary 0) { var newSubscription = new Subscription(UpdateFromRcm, newKeys); diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs index c5cf36ee2e82..6e88a9479ba4 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/IWaf.cs @@ -2,7 +2,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License. // This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc. // -#nullable enable + using System; using Datadog.Trace.AppSec.Rcm; using Datadog.Trace.AppSec.Waf.NativeBindings; @@ -14,11 +14,11 @@ internal interface IWaf : IDisposable { public string Version { get; } - public IContext? CreateContext(); + public IContext CreateContext(); internal unsafe WafReturnCode Run(IntPtr contextHandle, DdwafObjectStruct* rawPersistentData, DdwafObjectStruct* rawEphemeralData, ref DdwafResultStruct retNative, ulong timeoutMicroSeconds); - UpdateResult UpdateWafFromConfigurationStatus(ConfigurationStatus configurationStatus, string? staticRulesFilePath = null); + UpdateResult UpdateWafFromConfigurationStatus(ConfigurationStatus configurationStatus); public string[] GetKnownAddresses(); diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs index d277e8b43ead..72a393024aee 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Initialization/WafConfigurator.cs @@ -82,13 +82,6 @@ private static void LogRuleDetailsIfDebugEnabled(JToken root) return File.OpenRead(rulesFile); } - /// - /// Deserialize rules for the waf as Jtoken - /// If null is passed, will deserialize embedded rule file in the app - /// If a path is given but file isn't found, it won't fallback on the embedded rule file - /// - /// if null, will fallback on embedded rules file - /// the rules, might be null if file not found internal static JToken? DeserializeEmbeddedOrStaticRules(string? rulesFilePath) { JToken root; diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/InitResult.cs b/tracer/src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/InitResult.cs index 7cdb4e25026b..e74bcb239e66 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/InitResult.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/ReturnTypes.Managed/InitResult.cs @@ -10,6 +10,7 @@ using Datadog.Trace.AppSec.WafEncoding; using Datadog.Trace.Logging; using Datadog.Trace.Vendors.Newtonsoft.Json; +using Datadog.Trace.Vendors.Newtonsoft.Json.Linq; namespace Datadog.Trace.AppSec.Waf.ReturnTypes.Managed { @@ -17,10 +18,11 @@ internal class InitResult { private static readonly IDatadogLogger Log = DatadogLogging.GetLoggerFor(typeof(InitResult)); - private InitResult(ushort failedToLoadRules, ushort loadedRules, string ruleFileVersion, IReadOnlyDictionary errors, bool unusableRuleFile = false, IntPtr? wafHandle = null, WafLibraryInvoker? wafLibraryInvoker = null, IEncoder? encoder = null, bool shouldEnableWaf = true, bool incompatibleWaf = false) + private InitResult(ushort failedToLoadRules, ushort loadedRules, string ruleFileVersion, IReadOnlyDictionary errors, JToken? embeddedRules = null, bool unusableRuleFile = false, IntPtr? wafHandle = null, WafLibraryInvoker? wafLibraryInvoker = null, IEncoder? encoder = null, bool shouldEnableWaf = true, bool incompatibleWaf = false) { HasErrors = errors.Count > 0; Errors = errors; + EmbeddedRules = embeddedRules; FailedToLoadRules = failedToLoadRules; LoadedRules = loadedRules; RuleFileVersion = ruleFileVersion; @@ -53,6 +55,8 @@ private InitResult(ushort failedToLoadRules, ushort loadedRules, string ruleFile internal IReadOnlyDictionary Errors { get; } + public JToken? EmbeddedRules { get; set; } + internal string ErrorMessage { get; } internal bool HasErrors { get; } diff --git a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs index 0bb02c80e839..1f0be2ffb0f6 100644 --- a/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs +++ b/tracer/src/Datadog.Trace/AppSec/Waf/Waf.cs @@ -6,14 +6,11 @@ #nullable enable using System; -using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Datadog.Trace.AppSec.Rcm; using Datadog.Trace.AppSec.Rcm.Models.AsmData; -using Datadog.Trace.AppSec.Rcm.Models.AsmDd; using Datadog.Trace.AppSec.Waf.Initialization; using Datadog.Trace.AppSec.Waf.NativeBindings; using Datadog.Trace.AppSec.Waf.ReturnTypes.Managed; @@ -23,7 +20,6 @@ using Datadog.Trace.Telemetry; using Datadog.Trace.Vendors.Newtonsoft.Json; using Datadog.Trace.Vendors.Serilog.Events; -using static Datadog.Trace.AppSec.Rcm.ConfigurationStatus; namespace Datadog.Trace.AppSec.Waf { @@ -58,7 +54,7 @@ internal Waf(IntPtr wafHandle, WafLibraryInvoker wafLibraryInvoker, IEncoder enc /// the regex that will be used to obfuscate possible sensitive data in values that are highlighted WAF as potentially malicious, /// empty string means use default embedded in the WAF /// can be null, means use rules embedded in the manifest - /// can be null. RemoteConfig rules json. Takes precedence over rulesFile + /// can be null. RemoteConfig rules json. Takes precedence over rulesFile /// use legacy encoder /// if debug level logs should be enabled for the WAF /// the waf wrapper around waf native @@ -67,7 +63,7 @@ internal static InitResult Create( string obfuscationParameterKeyRegex, string obfuscationParameterValueRegex, string? embeddedRulesetPath = null, - ConfigurationStatus? remoteConfigStatus = null, + ConfigurationStatus? configurationStatus = null, bool useUnsafeEncoder = false, bool wafDebugEnabled = false) { @@ -75,17 +71,19 @@ internal static InitResult Create( // set the log level and setup the logger wafLibraryInvoker.SetupLogging(wafDebugEnabled); + object? configurationToEncode = null; - if (remoteConfigStatus is not null) - { - configurationToEncode = remoteConfigStatus.BuildDictionaryForWafAccordingToIncomingUpdate(embeddedRulesetPath); - } - else + if (configurationStatus is not null) { - var deserializedFromLocalRules = WafConfigurator.DeserializeEmbeddedOrStaticRules(embeddedRulesetPath); - configurationToEncode = deserializedFromLocalRules; + var configFromRcm = configurationStatus.BuildDictionaryForWafAccordingToIncomingUpdate(); + if (configFromRcm.Count > 0) + { + configurationToEncode = configFromRcm; + } } + configurationToEncode ??= WafConfigurator.DeserializeEmbeddedOrStaticRules(embeddedRulesetPath)!; + if (configurationToEncode is null) { return InitResult.FromUnusableRuleFile(); @@ -104,7 +102,8 @@ internal static InitResult Create( try { - var initResult = wafConfigurator.Configure(ref rulesObj, encoder, configWafStruct, ref diagnostics, remoteConfigStatus == null ? embeddedRulesetPath : "RemoteConfig"); + var initResult = wafConfigurator.Configure(ref rulesObj, encoder, configWafStruct, ref diagnostics, configurationStatus == null ? embeddedRulesetPath : "RemoteConfig"); + initResult.EmbeddedRules = initResult.EmbeddedRules; return initResult; } finally @@ -170,16 +169,16 @@ private unsafe UpdateResult UpdateWafAndDispose(IEncodeResult updateData) return res; } - public UpdateResult UpdateWafFromConfigurationStatus(ConfigurationStatus configurationStatus, string? rulesPath = null) + public UpdateResult UpdateWafFromConfigurationStatus(ConfigurationStatus configurationStatus) { - var dic = configurationStatus.BuildDictionaryForWafAccordingToIncomingUpdate(rulesPath); - if (dic is null) + var dic = configurationStatus.BuildDictionaryForWafAccordingToIncomingUpdate(); + if (dic.IsEmpty()) { Log.Warning("A waf update came from remote configuration but final merged dictionary for waf is empty, no update will be performed."); return UpdateResult.FromNothingToUpdate(); } - return Update(dic!); + return Update(dic); } /// @@ -216,7 +215,7 @@ public UpdateResult UpdateWafFromConfigurationStatus(ConfigurationStatus configu return Context.GetContext(contextHandle, this, _wafLibraryInvoker, _encoder); } - private UpdateResult Update(object arguments) + private UpdateResult Update(IDictionary arguments) { UpdateResult updated; try @@ -230,7 +229,7 @@ private UpdateResult Update(object arguments) updated = UpdateWafAndDispose(encodedArgs); // only if rules are provided will the waf give metrics - if (arguments is Dictionary dic && dic.ContainsKey("rules")) + if (arguments.ContainsKey("rules")) { TelemetryFactory.Metrics.RecordCountWafUpdates(); } diff --git a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs index 90d643ec7ede..882e38749ce4 100644 --- a/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs +++ b/tracer/src/Datadog.Trace/ClrProfiler/AutoInstrumentation/Wcf/WcfCommon.cs @@ -63,10 +63,8 @@ internal class WcfCommon WebHeadersCollection? headers = null; IDictionary? requestProperties = requestMessage.Properties; - if (requestProperties is not null - && requestProperties.TryGetValue("httpRequest", out var httpRequestProperty) - && httpRequestProperty?.GetType().FullName != null - && httpRequestProperty.GetType().FullName!.Equals(HttpRequestMessagePropertyTypeName, StringComparison.OrdinalIgnoreCase)) + if (requestProperties?.TryGetValue("httpRequest", out var httpRequestProperty) ?? false + && httpRequestProperty.GetType().FullName.Equals(HttpRequestMessagePropertyTypeName, StringComparison.OrdinalIgnoreCase)) { var httpRequestPropertyProxy = httpRequestProperty.DuckCast(); var webHeaderCollection = httpRequestPropertyProxy.Headers; diff --git a/tracer/test/Datadog.Trace.Security.IntegrationTests/Rcm/AspNetCore5AsmData.cs b/tracer/test/Datadog.Trace.Security.IntegrationTests/Rcm/AspNetCore5AsmData.cs index cb1c78ead0d8..27885d7f2193 100644 --- a/tracer/test/Datadog.Trace.Security.IntegrationTests/Rcm/AspNetCore5AsmData.cs +++ b/tracer/test/Datadog.Trace.Security.IntegrationTests/Rcm/AspNetCore5AsmData.cs @@ -12,6 +12,8 @@ using System.Collections.Immutable; using System.Linq; using System.Threading.Tasks; +using Datadog.Trace.AppSec; +using Datadog.Trace.AppSec.Rcm; using Datadog.Trace.AppSec.Rcm.Models.AsmData; using Datadog.Trace.AppSec.Rcm.Models.AsmFeatures; using Datadog.Trace.Configuration; @@ -73,17 +75,18 @@ public async Task RunTest(string test, string url) var sanitisedUrl = VerifyHelper.SanitisePathsForVerify(url); // we want to see the ip here var scrubbers = VerifyHelper.SpanScrubbers.Where(s => s.RegexPattern.ToString() != @"http.client_ip: (.)*(?=,)"); - var settings = VerifyHelper.GetSpanVerifierSettings(scrubbers: scrubbers, parameters: [test, sanitisedUrl]); + var settings = VerifyHelper.GetSpanVerifierSettings(scrubbers: scrubbers, parameters: new object[] { test, sanitisedUrl }); var spanBeforeAsmData = await SendRequestsAsync(agent, url); await agent.SetupRcmAndWait( Output, - [ - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 5545453532, Value = MainIp }] }] }, + new[] + { + ((object)new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 5545453532, Value = MainIp } } } } }, RcmProducts.AsmData, nameof(AspNetCore5AsmDataBlockingRequestIp)), - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 1545453532, Value = MainIp }] }] }, + (new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 1545453532, Value = MainIp } } } } }, RcmProducts.AsmData, nameof(AspNetCore5AsmDataBlockingRequestIp) + "2"), - ]); + }); var spanAfterAsmData = await SendRequestsAsync(agent, url); var spans = new List(); @@ -110,11 +113,11 @@ public async Task RunTest(string test, string url) var sanitisedUrl = VerifyHelper.SanitisePathsForVerify(url); // we want to see the ip here var scrubbers = VerifyHelper.SpanScrubbers.Where(s => s.RegexPattern.ToString() != @"http.client_ip: (.)*(?=,)"); - var settings = VerifyHelper.GetSpanVerifierSettings(scrubbers: scrubbers, parameters: [test, sanitisedUrl]); + var settings = VerifyHelper.GetSpanVerifierSettings(scrubbers: scrubbers, parameters: new object[] { test, sanitisedUrl }); var spanBeforeAsmData = await SendRequestsAsync(agent, url); var asmFeaturesFileId = nameof(AspNetCore5AsmDataSecurityEnabledBlockingRequestIpOneClick); - var request = await agent.SetupRcmAndWait(Output, [(new AsmFeatures { Asm = new AsmFeature { Enabled = true } }, RcmProducts.AsmFeatures, asmFeaturesFileId)]); + var request = await agent.SetupRcmAndWait(Output, new[] { ((object)new AsmFeatures { Asm = new AsmFeature { Enabled = true } }, RcmProducts.AsmFeatures, asmFeaturesFileId) }); request.Should().NotBeNull(); request.CachedTargetFiles.Should().HaveCount(1); var spanAfterAsmActivated = await SendRequestsAsync(agent, url); @@ -124,12 +127,13 @@ public async Task RunTest(string test, string url) request = await agent.SetupRcmAndWait( Output, - [ - (new AsmFeatures { Asm = new AsmFeature { Enabled = true } }, RcmProducts.AsmFeatures, asmFeaturesFileId), - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 5545453532, Value = MainIp }, new Data { Expiration = null, Value = "123.1.1.1" }] }] }, RcmProducts.AsmData, fileId), - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 1545453532, Value = MainIp }] }] }, RcmProducts.AsmData, + new[] + { + ((object)new AsmFeatures { Asm = new AsmFeature { Enabled = true } }, RcmProducts.AsmFeatures, asmFeaturesFileId), + (new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 5545453532, Value = MainIp }, new Data { Expiration = null, Value = "123.1.1.1" } } } } }, RcmProducts.AsmData, fileId), + ((object)new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 1545453532, Value = MainIp } } } } }, RcmProducts.AsmData, fileId2) - ]); + }); request.Should().NotBeNull(); request.CachedTargetFiles.Should().HaveCount(3); request.CachedTargetFiles.Any(c => c.Path.Contains(fileId)).Should().BeTrue(); @@ -139,34 +143,28 @@ public async Task RunTest(string test, string url) request = await agent.SetupRcmAndWait( Output, - [ - (new AsmFeatures { Asm = new AsmFeature { Enabled = false } }, RcmProducts.AsmFeatures, + new[] + { + ((object)new AsmFeatures { Asm = new AsmFeature { Enabled = false } }, RcmProducts.AsmFeatures, asmFeaturesFileId), - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 5545453532, Value = MainIp }, new Data { Expiration = null, Value = "123.1.1.1" }] }] }, + (new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 5545453532, Value = MainIp }, new Data { Expiration = null, Value = "123.1.1.1" } } } } }, RcmProducts.AsmData, fileId), - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 1545453532, Value = MainIp }] }] }, + ((object)new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 1545453532, Value = MainIp } } } } }, RcmProducts.AsmData, fileId2) - ]); + }); request.Should().NotBeNull(); request.CachedTargetFiles.Should().HaveCount(3); var spanAfterAsmDeactivated = await SendRequestsAsync(agent, url); - // we have to send first asm features = true, because asm_data won't be taken into account as rcm subscriptions to asm_data have been removed when turning off the waf. and then, later on, send, separately the asm data. That's the trade off of not subscribing to asm_data and asm when appsec is turned off request = await agent.SetupRcmAndWait( Output, - [ - (new AsmFeatures { Asm = new AsmFeature { Enabled = true } }, RcmProducts.AsmFeatures, asmFeaturesFileId)]); - request.Should().NotBeNull(); - request.CachedTargetFiles.Should().HaveCount(1); - - request = await agent.SetupRcmAndWait( - Output, - [ - (new AsmFeatures { Asm = new AsmFeature { Enabled = true } }, RcmProducts.AsmFeatures, asmFeaturesFileId), - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 5545453532, Value = MainIp }, new Data { Expiration = null, Value = "123.1.1.1" }] }] }, RcmProducts.AsmData, fileId), - (new Payload { RulesData = [new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = [new Data { Expiration = 1545453532, Value = MainIp }] }] }, + new[] + { + ((object)new AsmFeatures { Asm = new AsmFeature { Enabled = true } }, RcmProducts.AsmFeatures, asmFeaturesFileId), (new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 5545453532, Value = MainIp }, new Data { Expiration = null, Value = "123.1.1.1" } } } } }, + RcmProducts.AsmData, fileId), + ((object)new Payload { RulesData = new[] { new RuleData { Id = "blocked_ips", Type = "ip_with_expiration", Data = new[] { new Data { Expiration = 1545453532, Value = MainIp } } } } }, RcmProducts.AsmData, fileId2) - ]); + }); request.Should().NotBeNull(); request.CachedTargetFiles.Should().HaveCount(3); var spanAfterAsmDataReactivated = await SendRequestsAsync(agent, url); @@ -198,7 +196,7 @@ public async Task RunTest(string test, string url) await TryStartApp(); var agent = Fixture.Agent; var sanitisedUrl = VerifyHelper.SanitisePathsForVerify(url); - var settings = VerifyHelper.GetSpanVerifierSettings(parameters: [test, sanitisedUrl]); + var settings = VerifyHelper.GetSpanVerifierSettings(parameters: new object[] { test, sanitisedUrl }); var spanBeforeAsmData = await SendRequestsAsync(agent, url); // make sure this is unique if it s going to be run parallel @@ -206,7 +204,7 @@ public async Task RunTest(string test, string url) await agent.SetupRcmAndWait( Output, - [(new Payload { RulesData = [new RuleData { Id = "blocked_users", Type = "data_with_expiration", Data = [new Data { Expiration = 5545453532, Value = "user3" }] }] }, RcmProducts.AsmData, fileId)]); + new[] { ((object)new Payload { RulesData = new[] { new RuleData { Id = "blocked_users", Type = "data_with_expiration", Data = new[] { new Data { Expiration = 5545453532, Value = "user3" } } } } }, RcmProducts.AsmData, acknowledgedId: fileId) }); var spanAfterAsmData = await SendRequestsAsync(agent, url); var spans = new List(); spans.AddRange(spanBeforeAsmData);