Remove legacy AttackTracer repo content
Some checks failed
OfficeCom Sentinel Client / build-client (push) Has been cancelled
Some checks failed
OfficeCom Sentinel Client / build-client (push) Has been cancelled
This commit is contained in:
41
src/OCSentinelCli/Commands/ScanAndUploadCommand.cs
Normal file
41
src/OCSentinelCli/Commands/ScanAndUploadCommand.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
namespace OCSentinelCli.Commands;
|
||||
|
||||
internal static class ScanAndUploadCommand
|
||||
{
|
||||
public static int Execute(string[] args)
|
||||
{
|
||||
string outputPath = @"C:\ProgramData\OCSentinel\reports\latest.json";
|
||||
bool hasOutput = false;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
if (string.Equals(args[i], "--output", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
|
||||
{
|
||||
outputPath = args[i + 1];
|
||||
hasOutput = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
List<string> scanArgs = [.. args];
|
||||
if (!hasOutput)
|
||||
{
|
||||
scanArgs.Add("--output");
|
||||
scanArgs.Add(outputPath);
|
||||
}
|
||||
|
||||
int scanExitCode = ScanCommand.Execute([.. scanArgs]);
|
||||
if (scanExitCode != 0)
|
||||
{
|
||||
return scanExitCode;
|
||||
}
|
||||
|
||||
var uploadArgs = new List<string>
|
||||
{
|
||||
"--report",
|
||||
outputPath
|
||||
};
|
||||
|
||||
return UploadCommand.Execute([.. uploadArgs]);
|
||||
}
|
||||
}
|
||||
138
src/OCSentinelCli/Commands/ScanCommand.cs
Normal file
138
src/OCSentinelCli/Commands/ScanCommand.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System.Text.Json;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace OCSentinelCli.Commands;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
internal static class ScanCommand
|
||||
{
|
||||
public static int Execute(string[] args)
|
||||
{
|
||||
ScanOptions options;
|
||||
|
||||
try
|
||||
{
|
||||
options = ScanOptions.Parse(args);
|
||||
}
|
||||
catch (ArgumentException ex)
|
||||
{
|
||||
Console.Error.WriteLine(ex.Message);
|
||||
Console.Error.WriteLine();
|
||||
Console.Error.WriteLine(ScanOptions.Usage);
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (options.ShowHelp)
|
||||
{
|
||||
Console.WriteLine(ScanOptions.Usage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var scanner = new AttackScanner();
|
||||
ScanResult result = scanner.Run(options);
|
||||
string json = JsonSerializer.Serialize(result, JsonOptions.Default);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.OutputPath))
|
||||
{
|
||||
string outputPath = Path.GetFullPath(options.OutputPath);
|
||||
string? outputDirectory = Path.GetDirectoryName(outputPath);
|
||||
if (!string.IsNullOrWhiteSpace(outputDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(outputDirectory);
|
||||
}
|
||||
|
||||
File.WriteAllText(outputPath, json);
|
||||
}
|
||||
|
||||
WriteConsoleOutput(result, options, json);
|
||||
|
||||
if (options.FailOnThreshold && !string.Equals(result.AlertState, "ok", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return options.FailOnAttacks && result.TotalEvents > 0 ? 1 : 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.Error.WriteLine($"Scanner failed: {ex}");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteConsoleOutput(ScanResult result, ScanOptions options, string json)
|
||||
{
|
||||
if (!options.JsonOnly)
|
||||
{
|
||||
Console.WriteLine($"OfficeCom Sentinel summary for {result.MachineName}");
|
||||
Console.WriteLine($"Generated: {result.GeneratedAtLocal:yyyy-MM-dd HH:mm:ss}");
|
||||
Console.WriteLine($"Events: {result.TotalEvents}");
|
||||
Console.WriteLine($"Unique IPs: {result.UniqueIpCount}");
|
||||
Console.WriteLine($"Status: {result.AlertState}");
|
||||
Console.WriteLine($"Reason: {result.AlertReason}");
|
||||
Console.WriteLine($"Base status: {result.BaseAlertState}");
|
||||
Console.WriteLine($"Scan errors: {result.Errors.Count}");
|
||||
Console.WriteLine();
|
||||
|
||||
if (result.TopSources.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No attacks found in the selected lookback window.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Top source IPs:");
|
||||
for (int i = 0; i < result.TopSources.Count; i++)
|
||||
{
|
||||
AggregatedAttack aggregate = result.TopSources[i];
|
||||
Console.WriteLine($"{i + 1}. {aggregate.SourceIp} | {aggregate.Count} hits | {aggregate.FirstSeenLocal:yyyy-MM-dd HH:mm:ss} -> {aggregate.LastSeenLocal:yyyy-MM-dd HH:mm:ss} | {aggregate.RateLabel}");
|
||||
Console.WriteLine($" Targets: {string.Join(", ", aggregate.Targets)} | Users: {string.Join(", ", aggregate.Usernames)}");
|
||||
}
|
||||
}
|
||||
|
||||
if (result.VulnerabilityCorrelation.TotalCount > 0)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Vulnerability correlation:");
|
||||
Console.WriteLine($"CVE findings for this host: {result.VulnerabilityCorrelation.TotalCount}");
|
||||
Console.WriteLine($"Critical/High findings: {result.VulnerabilityCorrelation.CriticalCount}");
|
||||
Console.WriteLine($"CVSS >= 8 findings: {result.VulnerabilityCorrelation.HighCvssCount}");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(options.OutputPath))
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"JSON report written to {Path.GetFullPath(options.OutputPath)}");
|
||||
}
|
||||
|
||||
if (result.Errors.Count > 0)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Warnings:");
|
||||
foreach (string error in result.Errors)
|
||||
{
|
||||
Console.WriteLine($"- {error}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(json);
|
||||
}
|
||||
|
||||
if (options.NinjaOutput)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine($"ATTACKTRACER_STATUS={result.AlertState}");
|
||||
Console.WriteLine($"ATTACKTRACER_REASON={result.AlertReason}");
|
||||
Console.WriteLine($"ATTACKTRACER_BASE_STATUS={result.BaseAlertState}");
|
||||
Console.WriteLine($"ATTACKTRACER_EVENTS={result.TotalEvents}");
|
||||
Console.WriteLine($"ATTACKTRACER_UNIQUE_IPS={result.UniqueIpCount}");
|
||||
Console.WriteLine($"ATTACKTRACER_ERRORS={result.Errors.Count}");
|
||||
Console.WriteLine($"ATTACKTRACER_CVE_TOTAL={result.VulnerabilityCorrelation.TotalCount}");
|
||||
Console.WriteLine($"ATTACKTRACER_CVE_CRITICAL={result.VulnerabilityCorrelation.CriticalCount}");
|
||||
Console.WriteLine($"ATTACKTRACER_CVE_HIGH_CVSS={result.VulnerabilityCorrelation.HighCvssCount}");
|
||||
}
|
||||
}
|
||||
}
|
||||
100
src/OCSentinelCli/Commands/UploadCommand.cs
Normal file
100
src/OCSentinelCli/Commands/UploadCommand.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
using OCSentinelCli.Configuration;
|
||||
using System.Text.Json;
|
||||
using OCSentinelCli.Security;
|
||||
using OCSentinelCli.Transport;
|
||||
|
||||
namespace OCSentinelCli.Commands;
|
||||
|
||||
internal static class UploadCommand
|
||||
{
|
||||
public static int Execute(string[] args)
|
||||
{
|
||||
string? reportPath = null;
|
||||
string? configPath = null;
|
||||
string? secretPath = null;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
switch (args[i])
|
||||
{
|
||||
case "--report":
|
||||
reportPath = ReadValue(args, ref i, "--report");
|
||||
break;
|
||||
case "--client-config":
|
||||
configPath = ReadValue(args, ref i, "--client-config");
|
||||
break;
|
||||
case "--secret-path":
|
||||
secretPath = ReadValue(args, ref i, "--secret-path");
|
||||
break;
|
||||
case "--help":
|
||||
case "-h":
|
||||
Console.WriteLine("Usage: OCSentinelCli upload --report <path> [--client-config <path>] [--secret-path <path>]");
|
||||
return 0;
|
||||
default:
|
||||
throw new ArgumentException($"Unknown argument: {args[i]}");
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(reportPath))
|
||||
{
|
||||
throw new ArgumentException("Missing required argument: --report");
|
||||
}
|
||||
|
||||
string resolvedConfigPath = Path.GetFullPath(configPath ?? @"C:\ProgramData\OCSentinel\config\ocsentinel-client.json");
|
||||
string resolvedSecretPath = Path.GetFullPath(secretPath ?? @"C:\ProgramData\OCSentinel\secrets\ocsentinel-upload-secret.dat");
|
||||
ClientConfiguration config = ClientConfiguration.Load(resolvedConfigPath);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(config.N8nWebhookUrl))
|
||||
{
|
||||
throw new InvalidOperationException("Client configuration does not define n8nWebhookUrl.");
|
||||
}
|
||||
|
||||
if (!File.Exists(resolvedSecretPath))
|
||||
{
|
||||
throw new FileNotFoundException($"Protected upload secret file not found: {resolvedSecretPath}");
|
||||
}
|
||||
|
||||
string reportFullPath = Path.GetFullPath(reportPath);
|
||||
string json = File.ReadAllText(reportFullPath);
|
||||
ScanResult? parsedReport = JsonSerializer.Deserialize<ScanResult>(json, JsonOptions.Default);
|
||||
if (parsedReport is not null)
|
||||
{
|
||||
parsedReport = parsedReport with
|
||||
{
|
||||
Runtime = parsedReport.Runtime with
|
||||
{
|
||||
UploadAttempted = true
|
||||
}
|
||||
};
|
||||
|
||||
json = JsonSerializer.Serialize(parsedReport, JsonOptions.Default);
|
||||
File.WriteAllText(reportFullPath, json);
|
||||
}
|
||||
|
||||
string secret = ProtectedSecretStore.LoadSecret(resolvedSecretPath);
|
||||
var client = new N8nUploadClient();
|
||||
var result = client.UploadJson(config.N8nWebhookUrl, Environment.MachineName, BuildMetadata.Version, json, secret, config.UploadTimeoutSeconds);
|
||||
|
||||
if (!result.Success)
|
||||
{
|
||||
Console.Error.WriteLine($"Upload failed ({result.StatusCode}): {result.Message}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Console.WriteLine($"Upload succeeded ({result.StatusCode})");
|
||||
Console.WriteLine($"Nonce: {result.Nonce}");
|
||||
Console.WriteLine($"Payload SHA256: {result.PayloadSha256}");
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static string ReadValue(string[] args, ref int index, string argName)
|
||||
{
|
||||
if (index + 1 >= args.Length)
|
||||
{
|
||||
throw new ArgumentException($"Missing value for {argName}");
|
||||
}
|
||||
|
||||
index++;
|
||||
return args[index];
|
||||
}
|
||||
}
|
||||
10
src/OCSentinelCli/Commands/VersionCommand.cs
Normal file
10
src/OCSentinelCli/Commands/VersionCommand.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace OCSentinelCli.Commands;
|
||||
|
||||
internal static class VersionCommand
|
||||
{
|
||||
public static int Execute()
|
||||
{
|
||||
Console.WriteLine(BuildMetadata.Version);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user