Persist NinjaOne context for scheduled scans
All checks were successful
OfficeCom Sentinel Client / validate-client (push) Successful in 24s
OfficeCom Sentinel Client / build-client-windows (push) Successful in 50s

This commit is contained in:
OfficeCom Codex
2026-07-31 01:59:40 +02:00
parent b97f8819f6
commit 94f5be8953
8 changed files with 208 additions and 10 deletions

View File

@@ -3,6 +3,7 @@ using System.Globalization;
using System.Net;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using OCSentinelCli.Configuration;
namespace OCSentinelCli;
@@ -68,7 +69,7 @@ internal sealed class AttackScanner
{
SchemaVersion = "2.0",
MachineName = Environment.MachineName,
NinjaOne = GetNinjaOneContext(),
NinjaOne = GetNinjaOneContext(options, errors),
GeneratedAtLocal = generatedAtLocal,
GeneratedAtUtc = generatedAtUtc,
ClientVersion = BuildMetadata.Version,
@@ -93,19 +94,38 @@ internal sealed class AttackScanner
};
}
private static NinjaOneContext GetNinjaOneContext()
private static NinjaOneContext GetNinjaOneContext(ScanOptions options, List<string> errors)
{
ClientConfiguration? clientConfiguration = null;
if (!string.IsNullOrWhiteSpace(options.ClientConfigPath))
{
try
{
clientConfiguration = ClientConfiguration.Load(options.ClientConfigPath);
}
catch (Exception exception)
{
errors.Add($"Could not load persisted NinjaOne context: {exception.Message}");
}
}
return new NinjaOneContext
{
OrganizationId = ReadEnvironmentVariable("NINJA_ORGANIZATION_ID"),
OrganizationName = ReadEnvironmentVariable("NINJA_ORGANIZATION_NAME"),
MachineId = ReadEnvironmentVariable("NINJA_AGENT_MACHINE_ID"),
NodeId = ReadEnvironmentVariable("NINJA_AGENT_NODE_ID"),
LocationId = ReadEnvironmentVariable("NINJA_LOCATION_ID"),
LocationName = ReadEnvironmentVariable("NINJA_LOCATION_NAME")
OrganizationId = ReadContextValue("NINJA_ORGANIZATION_ID", clientConfiguration?.NinjaOrganizationId),
OrganizationName = ReadContextValue("NINJA_ORGANIZATION_NAME", clientConfiguration?.NinjaOrganizationName),
MachineId = ReadContextValue("NINJA_AGENT_MACHINE_ID", clientConfiguration?.NinjaMachineId),
NodeId = ReadContextValue("NINJA_AGENT_NODE_ID", clientConfiguration?.NinjaNodeId),
LocationId = ReadContextValue("NINJA_LOCATION_ID", clientConfiguration?.NinjaLocationId),
LocationName = ReadContextValue("NINJA_LOCATION_NAME", clientConfiguration?.NinjaLocationName)
};
}
private static string ReadContextValue(string environmentName, string? persistedValue)
{
string currentValue = ReadEnvironmentVariable(environmentName);
return string.IsNullOrWhiteSpace(currentValue) ? persistedValue?.Trim() ?? string.Empty : currentValue;
}
private static string ReadEnvironmentVariable(string name)
{
return Environment.GetEnvironmentVariable(name)?.Trim() ?? string.Empty;

View File

@@ -23,7 +23,10 @@ internal static class ScanAndUploadCommand
if (string.Equals(args[i], "--client-config", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
{
clientConfigPath = args[++i];
string configPathValue = args[++i];
clientConfigPath = configPathValue;
scanArgs.Add("--client-config");
scanArgs.Add(configPathValue);
continue;
}

View File

@@ -14,6 +14,18 @@ internal sealed record ClientConfiguration
public string N8nWebhookUrl { get; init; } = string.Empty;
public string NinjaOrganizationId { get; init; } = string.Empty;
public string NinjaOrganizationName { get; init; } = string.Empty;
public string NinjaMachineId { get; init; } = string.Empty;
public string NinjaNodeId { get; init; } = string.Empty;
public string NinjaLocationId { get; init; } = string.Empty;
public string NinjaLocationName { get; init; } = string.Empty;
public string DeviceIdentifierMode { get; init; } = "machineName";
public int UploadTimeoutSeconds { get; init; } = 30;

View File

@@ -4,13 +4,14 @@ internal sealed record ScanOptions
{
public const string Usage = """
Usage:
OCSentinelCli [--output <path>] [--lookback-days <n>] [--top <n>] [--config <path>] [--vulnerability-csv <path>] [--json-only] [--ninja-output] [--fail-on-attacks] [--fail-on-threshold] [--help]
OCSentinelCli [--output <path>] [--lookback-days <n>] [--top <n>] [--config <path>] [--client-config <path>] [--vulnerability-csv <path>] [--json-only] [--ninja-output] [--fail-on-attacks] [--fail-on-threshold] [--help]
Options:
--output <path> Write the JSON report to the given file.
--lookback-days <n> Only include events newer than now minus n days. Default: 30
--top <n> Number of aggregated source IPs to show. Default: 10
--config <path> Load thresholds, path overrides, and exclusions from JSON.
--client-config <path> Load persisted NinjaOne identity and upload settings from JSON.
--vulnerability-csv <path>
Correlate local attack results with exported CVE data for this host.
--json-only Print only JSON to stdout.
@@ -36,6 +37,8 @@ Options:
public string? ConfigPath { get; init; }
public string? ClientConfigPath { get; init; }
public string? VulnerabilityCsvPath { get; init; }
public bool ShowHelp { get; init; }
@@ -72,6 +75,9 @@ Options:
case "--config":
options = options with { ConfigPath = ReadValue(args, ref i, arg) };
break;
case "--client-config":
options = options with { ClientConfigPath = ReadValue(args, ref i, arg) };
break;
case "--vulnerability-csv":
options = options with { VulnerabilityCsvPath = ReadValue(args, ref i, arg) };
break;