Skip to content

Commit f249753

Browse files
authored
feat: Add detailed assembly reporting to enable Vulnerability Management support. (#1685)
1 parent e8bdc34 commit f249753

21 files changed

+1179
-2
lines changed

src/Agent/NewRelic/Agent/Core/Configuration/DefaultConfiguration.cs

+24
Original file line numberDiff line numberDiff line change
@@ -2173,6 +2173,30 @@ public TimeSpan SqlTracesHarvestCycle
21732173
}
21742174
}
21752175

2176+
private TimeSpan? _updateLoadedModulesCycleOverride = null;
2177+
public TimeSpan UpdateLoadedModulesCycle
2178+
{
2179+
get
2180+
{
2181+
if (_updateLoadedModulesCycleOverride.HasValue)
2182+
{
2183+
return _updateLoadedModulesCycleOverride.Value;
2184+
}
2185+
2186+
if (_newRelicAppSettings.TryGetValue("OverrideUpdateLoadedModulesCycle", out var harvestCycle))
2187+
{
2188+
if (int.TryParse(harvestCycle, out var parsedHarvestCycle) && parsedHarvestCycle > 0)
2189+
{
2190+
Log.Info("Update loaded modules cycle overridden to " + parsedHarvestCycle + " seconds.");
2191+
_updateLoadedModulesCycleOverride = TimeSpan.FromSeconds(parsedHarvestCycle);
2192+
return _updateLoadedModulesCycleOverride.Value;
2193+
}
2194+
}
2195+
2196+
return DefaultHarvestCycle;
2197+
}
2198+
}
2199+
21762200
#endregion
21772201

21782202
#region Helpers

src/Agent/NewRelic/Agent/Core/Configuration/ReportedConfiguration.cs

+3
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,9 @@ public ReportedConfiguration(IConfiguration configuration)
638638
[JsonProperty("sql_traces.harvest_cycle")]
639639
public TimeSpan SqlTracesHarvestCycle => _configuration.SqlTracesHarvestCycle;
640640

641+
[JsonProperty("update_loaded_modules.cycle")]
642+
public TimeSpan UpdateLoadedModulesCycle => _configuration.UpdateLoadedModulesCycle;
643+
641644
public IReadOnlyDictionary<string, string> GetAppSettings()
642645
{
643646
return _configuration.GetAppSettings();

src/Agent/NewRelic/Agent/Core/DataTransport/DataTransportService.cs

+11
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public interface IDataTransportService
3535
DataTransportResponseStatus Send(IEnumerable<SqlTraceWireModel> sqlTraceWireModels);
3636
DataTransportResponseStatus Send(IEnumerable<CustomEventWireModel> customEvents);
3737
DataTransportResponseStatus Send(LogEventWireModelCollection loggingEvents);
38+
DataTransportResponseStatus Send(LoadedModuleWireModelCollection loadedModules);
3839
}
3940

4041
public class DataTransportService : ConfigurationBasedService, IDataTransportService
@@ -139,6 +140,16 @@ public DataTransportResponseStatus Send(IEnumerable<MetricWireModel> metrics)
139140
return status;
140141
}
141142

143+
public DataTransportResponseStatus Send(LoadedModuleWireModelCollection loadedModules)
144+
{
145+
if (loadedModules.LoadedModules.Count < 1)
146+
{
147+
return DataTransportResponseStatus.RequestSuccessful;
148+
}
149+
150+
return TrySendDataRequest("update_loaded_modules", loadedModules);
151+
}
152+
142153
#endregion Public API
143154

144155
#region Private helpers

src/Agent/NewRelic/Agent/Core/DependencyInjection/AgentServices.cs

+3
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@ public static void RegisterServices(IContainer container)
204204
}
205205
container.Register<NewRelicCore.DistributedTracing.ITracePriorityManager, NewRelicCore.DistributedTracing.TracePriorityManager>();
206206

207+
container.Register<UpdatedLoadedModulesService, UpdatedLoadedModulesService>();
208+
207209
container.Build();
208210
}
209211

@@ -231,6 +233,7 @@ public static void StartServices(IContainer container)
231233
container.Resolve<ThreadStatsSampler>().Start();
232234
container.Resolve<ConfigurationTracker>();
233235
container.Resolve<LiveInstrumentationServerConfigurationListener>();
236+
container.Resolve<UpdatedLoadedModulesService>();
234237
}
235238
}
236239
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright 2020 New Relic, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using System;
5+
using NewRelic.Agent.Core.WireModels;
6+
using Newtonsoft.Json;
7+
8+
namespace NewRelic.Agent.Core.JsonConverters
9+
{
10+
public class LoadedModuleWireModelCollectionJsonConverter : JsonConverter<LoadedModuleWireModelCollection>
11+
{
12+
// The payload is labeled "Jars" since the collector method was originally meant for and used by Java.
13+
private const string JarsName = "Jars";
14+
15+
public override LoadedModuleWireModelCollection ReadJson(JsonReader reader, Type objectType, LoadedModuleWireModelCollection existingValue, bool hasExistingValue, JsonSerializer serializer)
16+
{
17+
throw new NotImplementedException();
18+
}
19+
20+
public override void WriteJson(JsonWriter jsonWriter, LoadedModuleWireModelCollection value, JsonSerializer serializer)
21+
{
22+
WriteJsonImpl(jsonWriter, value);
23+
}
24+
25+
private static void WriteJsonImpl(JsonWriter jsonWriter, LoadedModuleWireModelCollection value)
26+
{
27+
jsonWriter.WriteValue(JarsName);
28+
29+
jsonWriter.WriteStartArray();
30+
31+
foreach (var loadedModule in value.LoadedModules)
32+
{
33+
// MODULE
34+
jsonWriter.WriteStartArray();
35+
36+
jsonWriter.WriteValue(loadedModule.AssemblyName);
37+
jsonWriter.WriteValue(loadedModule.Version ?? " ");
38+
39+
// DATA DICTIONARY
40+
jsonWriter.WriteStartObject();
41+
foreach (var item in loadedModule.Data)
42+
{
43+
jsonWriter.WritePropertyName(item.Key);
44+
jsonWriter.WriteValue(item.Value.ToString());
45+
}
46+
47+
jsonWriter.WriteEndObject();
48+
49+
jsonWriter.WriteEndArray();
50+
}
51+
52+
jsonWriter.WriteEndArray();
53+
}
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2020 New Relic, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using NewRelic.Agent.Configuration;
8+
using NewRelic.Agent.Core.DataTransport;
9+
using NewRelic.Agent.Core.Time;
10+
using NewRelic.Agent.Core.WireModels;
11+
12+
namespace NewRelic.Agent.Core.Utilities
13+
{
14+
public class UpdatedLoadedModulesService : DisposableService
15+
{
16+
private readonly IList<string> _loadedModulesSeen = new List<string>();
17+
private readonly IScheduler _scheduler;
18+
private readonly IDataTransportService _dataTransportService;
19+
private readonly IConfigurationService _configurationService;
20+
private IConfiguration _configuration => _configurationService?.Configuration;
21+
22+
public UpdatedLoadedModulesService(IScheduler scheduler, IDataTransportService dataTransportService, IConfigurationService configurationService)
23+
{
24+
_configurationService = configurationService;
25+
_dataTransportService = dataTransportService;
26+
_scheduler = scheduler;
27+
_scheduler.ExecuteEvery(GetLoadedModules, _configuration.UpdateLoadedModulesCycle);
28+
}
29+
30+
private void GetLoadedModules()
31+
{
32+
var assemblies = AppDomain.CurrentDomain.GetAssemblies()
33+
.Where(assembly => assembly != null)
34+
.Where(assembly => !_loadedModulesSeen.Contains(assembly.GetName().Name))
35+
#if NETFRAMEWORK
36+
.Where(assembly => !(assembly is System.Reflection.Emit.AssemblyBuilder))
37+
#endif
38+
.ToList();
39+
40+
if (assemblies.Count < 1)
41+
{
42+
return;
43+
}
44+
45+
var loadedModulesCollection = LoadedModuleWireModelCollection.Build(assemblies);
46+
47+
SendUpdatedLoadedModules(loadedModulesCollection);
48+
}
49+
50+
private void SendUpdatedLoadedModules(LoadedModuleWireModelCollection loadedModulesCollection)
51+
{
52+
var responseStatus = _dataTransportService.Send(loadedModulesCollection);
53+
if (responseStatus != DataTransportResponseStatus.RequestSuccessful)
54+
{
55+
// Try again next time
56+
return;
57+
}
58+
59+
foreach (var module in loadedModulesCollection.LoadedModules)
60+
{
61+
_loadedModulesSeen.Add(module.Data["namespace"].ToString());
62+
}
63+
}
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2020 New Relic, Inc. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
using System.Collections.Generic;
5+
6+
namespace NewRelic.Agent.Core.WireModels
7+
{
8+
public class LoadedModuleWireModel
9+
{
10+
public string AssemblyName { get; }
11+
12+
public string Version { get; }
13+
14+
public Dictionary<string, object> Data { get; }
15+
16+
public LoadedModuleWireModel(string assemblyName, string version)
17+
{
18+
AssemblyName = assemblyName;
19+
Version = version;
20+
Data = new Dictionary<string, object>();
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)