Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
- Evidence can be marked sensitive (query text, logins, hosts, programs, job error messages); rules mark it. Sensitive values and the server name are sent as [udeladt] unless the customer allows them. - Full-state submission contract (all active findings and rule outcomes per instance), idempotent by submission id - Local outbound queue in the repository (migration 0002): newest state per instance only, exponential backoff up to an hour, permanent rejections (bad license, bad request) are given up, sent items purged - Hub status (license state, last sent, last error) for the web UI - Sync runs only with a valid license (or in grace); monitoring never depends on it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
87 lines
3.1 KiB
C#
87 lines
3.1 KiB
C#
using SQLInsight.Agent.Hub;
|
|
using SQLInsight.Contracts;
|
|
using SQLInsight.Core.Findings;
|
|
using SQLInsight.Core.Instances;
|
|
using SQLInsight.Repository;
|
|
using SQLInsight.Rules.Engine;
|
|
|
|
namespace SQLInsight.Agent.Tests;
|
|
|
|
public class SubmissionBuilderTests
|
|
{
|
|
private static readonly DateTimeOffset Now = new(2026, 9, 29, 12, 0, 0, TimeSpan.Zero);
|
|
|
|
private static readonly InstanceInfo Info = new()
|
|
{
|
|
ServerName = "SQL01.corp.local",
|
|
Version = SqlServerVersion.Parse("15.0.4490.9"),
|
|
ProductLevel = "RTM",
|
|
ProductUpdateLevel = "CU32",
|
|
Edition = "Enterprise Edition",
|
|
EngineEdition = EngineEdition.Enterprise,
|
|
};
|
|
|
|
private static readonly StoredFinding Blocking = new()
|
|
{
|
|
FindingId = 1,
|
|
RuleId = "SQLI-BLK-001",
|
|
Subject = "session: 51",
|
|
Severity = Severity.High,
|
|
Title = "Session 51 blokerer",
|
|
Explanation = "Forklaring",
|
|
Recommendation = "Anbefaling",
|
|
FirstSeenAt = Now.UtcDateTime.AddMinutes(-5),
|
|
Evidence =
|
|
[
|
|
new Evidence("Blokerede sessioner", "3"),
|
|
new Evidence("Head blockers SQL", "UPDATE dbo.Salaries SET Amount = 100000 WHERE Name = 'Hansen'", Sensitive: true),
|
|
],
|
|
};
|
|
|
|
private static RuleResult[] Results => [new RuleResult { RuleId = "SQLI-BLK-001", RuleTitle = "t", Outcome = RuleOutcome.Findings }];
|
|
|
|
[Fact]
|
|
public void Sensitive_evidence_and_server_name_stay_on_prem_by_default()
|
|
{
|
|
var submission = SubmissionBuilder.Build("prod-sql01", Info, Results, [Blocking], new HubOptions(), Now);
|
|
|
|
var evidence = Assert.Single(submission.Findings).Evidence;
|
|
Assert.Equal("3", evidence[0].Value);
|
|
Assert.Equal(FindingsSubmission.Redacted, evidence[1].Value);
|
|
Assert.Null(submission.Instance.ServerName);
|
|
Assert.Equal("prod-sql01", submission.Instance.InstanceKey);
|
|
Assert.Equal("SQL Server 2019", submission.Instance.Release);
|
|
}
|
|
|
|
[Fact]
|
|
public void Customer_can_allow_sensitive_evidence_and_server_name()
|
|
{
|
|
var options = new HubOptions { SendSensitiveEvidence = true, SendServerName = true };
|
|
|
|
var submission = SubmissionBuilder.Build("prod-sql01", Info, Results, [Blocking], options, Now);
|
|
|
|
Assert.StartsWith("UPDATE dbo.Salaries", submission.Findings[0].Evidence[1].Value, StringComparison.Ordinal);
|
|
Assert.Equal("SQL01.corp.local", submission.Instance.ServerName);
|
|
}
|
|
|
|
[Fact]
|
|
public void Every_submission_has_a_new_id_and_carries_rule_outcomes()
|
|
{
|
|
var a = SubmissionBuilder.Build("x", Info, Results, [], new HubOptions(), Now);
|
|
var b = SubmissionBuilder.Build("x", Info, Results, [], new HubOptions(), Now);
|
|
|
|
Assert.NotEqual(a.SubmissionId, b.SubmissionId);
|
|
Assert.Equal("Findings", Assert.Single(a.Rules).Outcome);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(0, 1)]
|
|
[InlineData(1, 2)]
|
|
[InlineData(3, 8)]
|
|
[InlineData(10, 60)]
|
|
[InlineData(50, 60)]
|
|
public void Backoff_doubles_up_to_an_hour(int attempts, int minutes)
|
|
{
|
|
Assert.Equal(TimeSpan.FromMinutes(minutes), HubSyncService.Backoff(attempts));
|
|
}
|
|
}
|