12 Commits

Author SHA1 Message Date
OfficeCom Codex
f91f45ad92 Prepare automated release validation
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 23s
OfficeCom Sentinel Client / build-client-windows (push) Failing after 17s
2026-07-25 10:25:07 +02:00
OfficeCom Codex
c27b53ea0d Use repository secret for Gitea releases
Some checks failed
OfficeCom Sentinel Client / build-client-windows (push) Has been cancelled
OfficeCom Sentinel Client / validate-client (push) Has been cancelled
2026-07-25 10:24:56 +02:00
OfficeCom Codex
f9d7647046 Detect Gitea release tags by reference name
Some checks failed
OfficeCom Sentinel Client / build-client-windows (push) Failing after 18s
OfficeCom Sentinel Client / validate-client (push) Successful in 23s
2026-07-25 10:22:43 +02:00
OfficeCom Codex
d37fba137e Run Gitea release pipeline from tag environment
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 23s
OfficeCom Sentinel Client / build-client-windows (push) Failing after 18s
2026-07-25 10:20:55 +02:00
OfficeCom Codex
e73d79b520 Publish stable manifest for v1.2.6
All checks were successful
OfficeCom Sentinel Client / validate-client (push) Successful in 22s
OfficeCom Sentinel Client / build-client-windows (push) Has been skipped
2026-07-25 10:16:52 +02:00
OfficeCom Codex
b0e3e7dc74 Preserve upload configuration during scan and upload
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 23s
OfficeCom Sentinel Client / build-client-windows (push) Failing after 19s
2026-07-25 10:15:23 +02:00
OfficeCom Codex
290033680b Publish release assets from tag workflow
All checks were successful
OfficeCom Sentinel Client / validate-client (push) Successful in 25s
OfficeCom Sentinel Client / build-client-windows (push) Has been skipped
2026-07-25 03:08:12 +02:00
OfficeCom Codex
3ea0baa147 Build Windows client packages on Linux runner
All checks were successful
OfficeCom Sentinel Client / validate-client (push) Successful in 24s
OfficeCom Sentinel Client / build-client-windows (push) Has been skipped
2026-07-25 03:06:31 +02:00
OfficeCom Codex
e01aa3dce3 Target Windows Gitea runner label
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 25s
OfficeCom Sentinel Client / build-client-windows (push) Has been cancelled
2026-07-25 02:43:12 +02:00
OfficeCom Codex
8bc1d78fc9 Publish stable manifest for v1.2.5
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 24s
OfficeCom Sentinel Client / build-client-windows (push) Has been cancelled
2026-07-25 02:40:40 +02:00
OfficeCom Codex
00778175fd Include NinjaOne identifiers in reports
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 25s
OfficeCom Sentinel Client / build-client-windows (push) Has been cancelled
2026-07-25 02:39:39 +02:00
OfficeCom Codex
34eff4e012 Publish stable manifest for v1.2.4
Some checks failed
OfficeCom Sentinel Client / validate-client (push) Successful in 24s
OfficeCom Sentinel Client / build-client-windows (push) Has been cancelled
2026-07-25 02:35:52 +02:00
6 changed files with 119 additions and 11 deletions

View File

@@ -33,8 +33,9 @@ jobs:
build-client-windows: build-client-windows:
needs: validate-client needs: validate-client
if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.build_windows == 'true') # .NET can publish a self-contained Windows x64 client from Linux.
runs-on: windows # This keeps releases independent of a Windows Gitea runner.
runs-on: ubuntu-22.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -50,10 +51,14 @@ jobs:
./build/build-client-package.ps1 ./build/build-client-package.ps1
- name: Build release manifest for tags - name: Build release manifest for tags
if: startsWith(github.ref, 'refs/tags/v')
shell: pwsh shell: pwsh
run: | run: |
$tag = "${{ github.ref_name }}" $ref = if ($env:GITHUB_REF) { $env:GITHUB_REF } else { $env:GITEA_REF }
$tag = if ($env:GITHUB_REF_NAME) { $env:GITHUB_REF_NAME } else { Split-Path -Leaf $ref }
if ($tag -notlike "v*") {
Write-Host "Not a version tag; skipping release manifest."
exit 0
}
$artifactUrl = "https://gitea.officecom.cloud/officecom/oc-sentinel/releases/download/$tag/OCSentinelClient-win-x64.zip" $artifactUrl = "https://gitea.officecom.cloud/officecom/oc-sentinel/releases/download/$tag/OCSentinelClient-win-x64.zip"
./build/build-release-manifest.ps1 -ArtifactUrl $artifactUrl ./build/build-release-manifest.ps1 -ArtifactUrl $artifactUrl
@@ -66,3 +71,41 @@ jobs:
artifacts/OCSentinelClient-win-x64.zip.sha256 artifacts/OCSentinelClient-win-x64.zip.sha256
artifacts/version.json artifacts/version.json
if-no-files-found: warn if-no-files-found: warn
- name: Publish Gitea release assets
shell: pwsh
env:
GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
$ref = if ($env:GITHUB_REF) { $env:GITHUB_REF } else { $env:GITEA_REF }
$tag = if ($env:GITHUB_REF_NAME) { $env:GITHUB_REF_NAME } else { Split-Path -Leaf $ref }
if ($tag -notlike "v*") {
Write-Host "Not a version tag; skipping Gitea release publication."
exit 0
}
$headers = @{ Authorization = "token $env:GITEA_TOKEN" }
$repository = if ($env:GITEA_REPOSITORY) { $env:GITEA_REPOSITORY } else { $env:GITHUB_REPOSITORY }
$serverUrl = if ($env:GITEA_SERVER_URL) { $env:GITEA_SERVER_URL } else { $env:GITHUB_SERVER_URL }
if ([string]::IsNullOrWhiteSpace($env:GITEA_TOKEN) -or [string]::IsNullOrWhiteSpace($repository) -or [string]::IsNullOrWhiteSpace($serverUrl)) {
throw "Gitea release environment is incomplete. Expected GITEA_TOKEN, repository, and server URL."
}
$baseUrl = "$serverUrl/api/v1/repos/$repository"
$releaseBody = @{
tag_name = $tag
target_commitish = "${{ github.sha }}"
name = "OfficeCom Sentinel $tag"
body = "Automated OfficeCom Sentinel client release."
} | ConvertTo-Json
try {
$release = Invoke-RestMethod -Method Get -Headers $headers -Uri "$baseUrl/releases/tags/$tag"
}
catch {
$release = Invoke-RestMethod -Method Post -Headers $headers -ContentType "application/json" -Body $releaseBody -Uri "$baseUrl/releases"
}
foreach ($file in @("artifacts/OCSentinelClient-win-x64.zip", "artifacts/OCSentinelClient-win-x64.zip.sha256", "artifacts/version.json")) {
$assetName = [System.IO.Path]::GetFileName($file)
Invoke-RestMethod -Method Post -Headers $headers -InFile $file -ContentType "application/octet-stream" -Uri "$baseUrl/releases/$($release.id)/assets?name=$assetName" | Out-Null
}

View File

@@ -1,8 +1,8 @@
{ {
"channel": "stable", "channel": "stable",
"version": "1.2.3", "version": "1.2.6",
"publishedAtUtc": "2026-07-16T22:58:05.1732183Z", "publishedAtUtc": "2026-07-25T00:00:00Z",
"artifactUrl": "https://gitea.officecom.cloud/officecom/oc-sentinel/releases/download/v1.2.3/OCSentinelClient-win-x64.zip", "artifactUrl": "https://gitea.officecom.cloud/officecom/oc-sentinel/releases/download/v1.2.6/OCSentinelClient-win-x64.zip",
"sha256": "50c7da8ff9cb96852493d4d89ad32bfd004a99fcf94b6acc70f8070ceaf737f2", "sha256": "d6256f3e376701bff18d9dfd8d5825498e85fb6d94c15227828a50678693d097",
"minUpdaterVersion": "1.0.0" "minUpdaterVersion": "1.0.0"
} }

View File

@@ -66,6 +66,7 @@ internal sealed class AttackScanner
{ {
SchemaVersion = "2.0", SchemaVersion = "2.0",
MachineName = Environment.MachineName, MachineName = Environment.MachineName,
NinjaOne = GetNinjaOneContext(),
GeneratedAtLocal = generatedAtLocal, GeneratedAtLocal = generatedAtLocal,
GeneratedAtUtc = generatedAtUtc, GeneratedAtUtc = generatedAtUtc,
ClientVersion = BuildMetadata.Version, ClientVersion = BuildMetadata.Version,
@@ -89,6 +90,24 @@ internal sealed class AttackScanner
}; };
} }
private static NinjaOneContext GetNinjaOneContext()
{
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")
};
}
private static string ReadEnvironmentVariable(string name)
{
return Environment.GetEnvironmentVariable(name)?.Trim() ?? string.Empty;
}
private static ScannerConfiguration LoadConfiguration(ScanOptions options) private static ScannerConfiguration LoadConfiguration(ScanOptions options)
{ {
if (string.IsNullOrWhiteSpace(options.ConfigPath)) if (string.IsNullOrWhiteSpace(options.ConfigPath))

View File

@@ -5,7 +5,10 @@ internal static class ScanAndUploadCommand
public static int Execute(string[] args) public static int Execute(string[] args)
{ {
string outputPath = @"C:\ProgramData\OCSentinel\reports\latest.json"; string outputPath = @"C:\ProgramData\OCSentinel\reports\latest.json";
string? clientConfigPath = null;
string? secretPath = null;
bool hasOutput = false; bool hasOutput = false;
var scanArgs = new List<string>();
for (int i = 0; i < args.Length; i++) for (int i = 0; i < args.Length; i++)
{ {
@@ -13,11 +16,26 @@ internal static class ScanAndUploadCommand
{ {
outputPath = args[i + 1]; outputPath = args[i + 1];
hasOutput = true; hasOutput = true;
break; scanArgs.Add(args[i]);
} scanArgs.Add(args[++i]);
continue;
}
if (string.Equals(args[i], "--client-config", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
{
clientConfigPath = args[++i];
continue;
}
if (string.Equals(args[i], "--secret-path", StringComparison.OrdinalIgnoreCase) && i + 1 < args.Length)
{
secretPath = args[++i];
continue;
}
scanArgs.Add(args[i]);
} }
List<string> scanArgs = [.. args];
if (!hasOutput) if (!hasOutput)
{ {
scanArgs.Add("--output"); scanArgs.Add("--output");
@@ -36,6 +54,16 @@ internal static class ScanAndUploadCommand
outputPath outputPath
}; };
if (!string.IsNullOrWhiteSpace(clientConfigPath))
{
uploadArgs.AddRange(["--client-config", clientConfigPath]);
}
if (!string.IsNullOrWhiteSpace(secretPath))
{
uploadArgs.AddRange(["--secret-path", secretPath]);
}
return UploadCommand.Execute([.. uploadArgs]); return UploadCommand.Execute([.. uploadArgs]);
} }
} }

View File

@@ -80,6 +80,9 @@ internal sealed record ScanResult
public string MachineName { get; init; } = string.Empty; public string MachineName { get; init; } = string.Empty;
// Populated only for runs launched by NinjaOne automation.
public NinjaOneContext NinjaOne { get; init; } = new();
public DateTimeOffset GeneratedAtLocal { get; init; } public DateTimeOffset GeneratedAtLocal { get; init; }
public DateTimeOffset GeneratedAtUtc { get; init; } public DateTimeOffset GeneratedAtUtc { get; init; }
@@ -111,6 +114,21 @@ internal sealed record ScanResult
public List<string> Errors { get; init; } = []; public List<string> Errors { get; init; } = [];
} }
internal sealed record NinjaOneContext
{
public string OrganizationId { get; init; } = string.Empty;
public string OrganizationName { get; init; } = string.Empty;
public string MachineId { get; init; } = string.Empty;
public string NodeId { get; init; } = string.Empty;
public string LocationId { get; init; } = string.Empty;
public string LocationName { get; init; } = string.Empty;
}
internal sealed record ScanRuntimeMetadata internal sealed record ScanRuntimeMetadata
{ {
public DateTimeOffset StartedAtUtc { get; init; } public DateTimeOffset StartedAtUtc { get; init; }

View File

@@ -9,7 +9,7 @@
<RootNamespace>OCSentinelCli</RootNamespace> <RootNamespace>OCSentinelCli</RootNamespace>
<Product>OfficeCom Sentinel</Product> <Product>OfficeCom Sentinel</Product>
<Company>OfficeCom</Company> <Company>OfficeCom</Company>
<Version>1.2.4</Version> <Version>1.2.9</Version>
<AssemblyVersion>1.2.3.0</AssemblyVersion> <AssemblyVersion>1.2.3.0</AssemblyVersion>
<FileVersion>1.2.3.0</FileVersion> <FileVersion>1.2.3.0</FileVersion>
<InformationalVersion>1.2.3</InformationalVersion> <InformationalVersion>1.2.3</InformationalVersion>