Skip to content

Commit 38a5c90

Browse files
authored
Merge pull request #376 from cmu-sei/v8
continued refinement of chat api
2 parents 73682fe + 81758d8 commit 38a5c90

File tree

6 files changed

+70
-35
lines changed

6 files changed

+70
-35
lines changed

README.md

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ GHOSTS simulates what anyone might do at a computer, creating documents, browsin
44

55
GHOSTS has many use cases in cyber training and exercises, most notably for bringing non-player characters (NPCs) to life, but GHOSTS can be used for many other purposes where realistic activity on a computer is needed as well.
66

7+
There is a [short demonstration video available on YouTube](https://www.youtube.com/watch?v=EkwK-cqwjjA) (3:03).
8+
79
---
810

911
**Version 8 is here (with breaking changes!).** It has absorbed the other modules of the GHOSTS framework, [ANIMATOR (now archived)](https://github.com/cmu-sei/GHOSTS-ANIMATOR) and [SPECTRE (now archived)](https://github.com/cmu-sei/GHOSTS-SPECTRE). This was done in order to greatly simplify installation, configuration, and the administration of a GHOSTS instance, but also to bring further capability to the core agents by more tightly combining information segregated into separate databases and systems until now.
@@ -18,8 +20,6 @@ Sorry, but there is no upgrade path from previous versions — install a fresh i
1820

1921
---
2022

21-
There is a [short demonstration video available on YouTube](https://www.youtube.com/watch?v=EkwK-cqwjjA) (3:03).
22-
2323
## Key Links
2424

2525
- [Quick Start: Installation from distribution binaries](https://cmu-sei.github.io/GHOSTS/quickstart/)
@@ -46,6 +46,18 @@ The API server provides a way for clients to interact with the GHOSTS system and
4646
- Get/manage information from clients regarding their previous or current activities, etc.
4747
- Orchestrate new activities for particular clients to perform
4848

49+
### [Ghosts Lite](src/Ghosts.Client.Lite/)
50+
51+
A resource light version of the Windows GHOSTS client that can be run on minimal hardware.
52+
53+
### [Pandora Content Server](src/ghosts.pandora/)
54+
55+
A server that provides content to GHOSTS clients (or otherwise). Pandora determines what you most likely requested, creates that content, and serves it back in the response. Pandora also has the ability to serve predetermined static content for training and exercise purposes (and red-teaming).
56+
57+
### [Pandora Socializer Server](src/ghosts.pandora.socializer/)
58+
59+
The social media (x.com) server that enables Ghosts clients to post and interact with social media content.
60+
4961
## License
5062

5163
[DISTRIBUTION STATEMENT A] This material has been approved for public release and unlimited distribution.

src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/Chat/ChatClient.cs

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
using System.Text;
1111
using System.Text.Json;
1212
using System.Threading.Tasks;
13-
using Ghosts.Api;
1413
using ghosts.api.Hubs;
14+
using Ghosts.Api.Infrastructure;
1515
using ghosts.api.Infrastructure.Animations.AnimationDefinitions.Chat.Mattermost;
1616
using ghosts.api.Infrastructure.ContentServices;
1717
using Ghosts.Api.Infrastructure.Data;
@@ -28,6 +28,7 @@ public class ChatClient
2828
{
2929
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
3030
private readonly ChatJobConfiguration _configuration;
31+
private readonly ApplicationSettings.AnimatorSettingsDetail.AnimationsSettings.ChatSettings _chatSettings;
3132
private readonly string _baseUrl;
3233
private readonly HttpClient _client;
3334
private string _token;
@@ -37,9 +38,10 @@ public class ChatClient
3738
private readonly ApplicationDbContext _context;
3839
private IHubContext<ActivityHub> _activityHubContext;
3940

40-
public ChatClient(ChatJobConfiguration config, IFormatterService formatterService, IHubContext<ActivityHub> activityHubContext, ApplicationDbContext context)
41+
public ChatClient(ApplicationSettings.AnimatorSettingsDetail.AnimationsSettings.ChatSettings chatSettings, ChatJobConfiguration config, IFormatterService formatterService, IHubContext<ActivityHub> activityHubContext, ApplicationDbContext context)
4142
{
4243
_configuration = config;
44+
_chatSettings = chatSettings;
4345
this._baseUrl = _configuration.Chat.BaseUrl;
4446
this._client = new HttpClient();
4547
this._formatterService = formatterService;
@@ -88,8 +90,8 @@ private async Task<User> Login(string username, string password)
8890
}
8991
catch (Exception e)
9092
{
91-
_log.Error(e);
92-
return null;
93+
_log.Error($"Cannot login {username}:{password} with error {e.Message}|{e.StackTrace}");
94+
throw;
9395
}
9496
}
9597

@@ -130,7 +132,7 @@ private async Task<IEnumerable<Channel>> GetMyChannels(User user)
130132
}
131133
catch (Exception e)
132134
{
133-
_log.Error($"No channels found {e}");
135+
_log.Trace($"No channels found {e}");
134136
return new List<Channel>();
135137
}
136138
}
@@ -503,32 +505,49 @@ private async Task StepEx(Random random, Guid NpcId, string username, string pas
503505
if (lastPost != null)
504506
postId = lastPost.PostId;
505507
var posts = await this.GetPostsByChannel(channel.Id, postId);
506-
foreach (var post in posts.Posts.Where(x => x.Value.Type == ""))
508+
try
507509
{
508-
var user = await this.GetUserById(post.Value.UserId);
509-
if (user == null)
510+
if (posts?.Posts != null)
510511
{
511-
const string email = "[email protected]";
512-
user = new User
513-
{ FirstName = "some", LastName = "one", Email = email, Username = email.CreateUsernameFromEmail() };
512+
foreach (var post in posts.Posts.Where(x => x.Value?.Type == ""))
513+
{
514+
var user = await this.GetUserById(post.Value.UserId);
515+
if (user == null)
516+
{
517+
const string email = "[email protected]";
518+
user = new User
519+
{
520+
FirstName = "some",
521+
LastName = "one",
522+
Email = email,
523+
Username = email.CreateUsernameFromEmail()
524+
};
525+
}
526+
527+
channelHistory.Add(new ChannelHistory
528+
{
529+
ChannelId = channel.Id,
530+
ChannelName = channel.Name,
531+
UserId = post.Value.Id,
532+
PostId = post.Value.Id,
533+
UserName = user.Username,
534+
Created = post.Value.CreateAt.ToDateTime(),
535+
Message = post.Value.Message
536+
});
537+
}
514538
}
515-
516-
channelHistory.Add(new ChannelHistory
517-
{
518-
ChannelId = channel.Id, ChannelName = channel.Name, UserId = post.Value.Id,
519-
PostId = post.Value.Id,
520-
UserName = user.Username,
521-
Created = post.Value.CreateAt.ToDateTime(),
522-
Message = post.Value.Message
523-
});
539+
}
540+
catch (Exception ex)
541+
{
542+
_log.Trace($"An error occurred: {ex.Message}");
524543
}
525544
}
526545
}
527546

528547
var subPrompts = this._configuration.Prompts.GetRandom(random);
529548
_log.Trace($"{username} looking at posts...");
530549

531-
if (random.Next(0, 100) > 50)
550+
if (random.Next(0, 99) < _chatSettings.PercentWillPost)
532551
{
533552
_log.Info($"{username} exiting.");
534553
return;
@@ -550,11 +569,13 @@ private async Task StepEx(Random random, Guid NpcId, string username, string pas
550569

551570
var history = channelHistory.Where(x => x.ChannelId == randomChannelToPostTo && x.UserName != me.Username).MaxBy(x => x.Created);
552571
//var historyString = history is { Message.Length: >= 100 } ? history.Message[..100] : history?.Message;
553-
var historyString = history.Message;
572+
var historyString = string.Empty;
573+
if (history != null)
574+
historyString = history.Message;
554575

555576
var prompt = $"Write my update to the chat system that {subPrompts}";
556577
var respondingTo = string.Empty;
557-
if (random.Next(0, 99) < Program.ApplicationSettings.AnimatorSettings.Animations.Chat.PercentReplyVsNew && !string.IsNullOrEmpty(historyString) && history.UserId != me.Id)
578+
if (random.Next(0, 99) < _chatSettings.PercentReplyVsNew && !string.IsNullOrEmpty(historyString) && history.UserId != me.Id)
558579
{
559580
prompt =
560581
$"How do I respond to this? {historyString}";

src/Ghosts.Api/Infrastructure/Animations/AnimationDefinitions/ChatJob.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ namespace ghosts.api.Infrastructure.Animations.AnimationDefinitions;
2020
public class ChatJob
2121
{
2222
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
23-
private readonly ApplicationSettings _configuration;
23+
private readonly ApplicationSettings.AnimatorSettingsDetail.AnimationsSettings.ChatSettings _configuration;
2424
private readonly ApplicationDbContext _context;
2525
private readonly Random _random;
2626
private readonly ChatClient _chatClient;
2727
private readonly int _currentStep;
2828
private CancellationToken _cancellationToken;
2929
private IFormatterService _formatterService;
3030

31-
public ChatJob(ApplicationSettings configuration, IServiceScopeFactory scopeFactory, Random random,
31+
public ChatJob(ApplicationSettings.AnimatorSettingsDetail.AnimationsSettings.ChatSettings configuration, IServiceScopeFactory scopeFactory, Random random,
3232
IHubContext<ActivityHub> activityHubContext, CancellationToken cancellationToken)
3333
{
3434
//todo: post results to activityHubContext for "top" reporting
@@ -45,20 +45,20 @@ public ChatJob(ApplicationSettings configuration, IServiceScopeFactory scopeFact
4545
new JsonSerializerOptions { PropertyNameCaseInsensitive = true }) ?? throw new InvalidOperationException();
4646

4747
this._formatterService =
48-
new ContentCreationService(_configuration.AnimatorSettings.Animations.Chat.ContentEngine).FormatterService;
48+
new ContentCreationService(_configuration.ContentEngine).FormatterService;
4949

50-
this._chatClient = new ChatClient(chatConfiguration, this._formatterService, activityHubContext, this._context);
50+
this._chatClient = new ChatClient(_configuration, chatConfiguration, this._formatterService, activityHubContext, this._context);
5151

5252
while (!_cancellationToken.IsCancellationRequested)
5353
{
54-
if (this._currentStep > _configuration.AnimatorSettings.Animations.Chat.MaximumSteps)
54+
if (this._currentStep > _configuration.MaximumSteps)
5555
{
5656
_log.Trace($"Maximum steps met: {this._currentStep - 1}. Chat Job is exiting...");
5757
return;
5858
}
5959

6060
this.Step(random, chatConfiguration);
61-
Thread.Sleep(this._configuration.AnimatorSettings.Animations.Chat.TurnLength);
61+
Thread.Sleep(this._configuration.TurnLength);
6262

6363
this._currentStep++;
6464
}

src/Ghosts.Api/Infrastructure/Animations/AnimationsManager.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -319,13 +319,13 @@ private void Run(AnimationConfiguration animationConfiguration)
319319
_chatJobThread = new Thread(() =>
320320
{
321321
Thread.CurrentThread.IsBackground = true;
322-
_ = new ChatJob(settings, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
322+
_ = new ChatJob(chatSettings, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
323323
});
324324
_chatJobThread.Start();
325325
}
326326
else
327327
{
328-
_ = new ChatJob(settings, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
328+
_ = new ChatJob(chatSettings, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
329329
}
330330

331331
break;
@@ -435,13 +435,13 @@ private void Run()
435435
_chatJobThread = new Thread(() =>
436436
{
437437
Thread.CurrentThread.IsBackground = true;
438-
_ = new ChatJob(this._configuration, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
438+
_ = new ChatJob(this._configuration.AnimatorSettings.Animations.Chat, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
439439
});
440440
_chatJobThread.Start();
441441
}
442442
else
443443
{
444-
_ = new ChatJob(this._configuration, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
444+
_ = new ChatJob(this._configuration.AnimatorSettings.Animations.Chat, _scopeFactory, this._random, this._activityHubContext, this._chatJobJobCancellationTokenSource.Token);
445445
}
446446
}
447447
else

src/Ghosts.Api/Infrastructure/ApplicationSettings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public class ChatSettings
8282
public int MaximumSteps { get; set; }
8383
public bool IsSendingTimelinesToGhostsApi { get; set; }
8484
public int PercentReplyVsNew { get; set; }
85+
public int PercentWillPost { get; set; }
8586
public string PostUrl { get; set; }
8687
public ContentEngineSettings ContentEngine { get; set; }
8788
}

src/Ghosts.Api/appsettings.json

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)