sqlinsight-pro/tests/SQLInsight.IntegrationTests/CustomRuleIntegrationTests.cs
Claude 7f640e74e5
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
Fase 4: community and local rules written as SQL
Rules can now be data: a JSON file with one read-only SELECT where every
row is a finding, with {column} templates, server or per-database scope,
minimum version, sensitive columns and references. Community rules live
in rules/community (reviewed via pull request), ship with every build
and with the signed rule pack; local rules are read from the agent's
data folder every five minutes. Queries are checked for anything that
could write, run with the least-privilege login, and a missing
permission makes the rule not measurable rather than failing. The
rule engine picks up catalog changes without a restart. Authors get
'rules validate' and 'rules test --instance'. Four example community
rules (sa active, orphaned database owners, default trace off,
untrusted foreign keys).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 10:23:01 +00:00

58 lines
3.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using SQLInsight.Agent;
using SQLInsight.Agent.CustomRules;
using SQLInsight.Connectivity;
using SQLInsight.Core.CustomRules;
using SQLInsight.Rules.Lifecycle;
namespace SQLInsight.IntegrationTests;
/// <summary>Community and local rules run with the agent's least-privilege login against a real SQL Server.</summary>
[Collection(SqlServerTestGroup.Name)]
[Trait("Category", "Integration")]
public class CustomRuleIntegrationTests(SqlServerFixture sql) : IAsyncLifetime
{
private readonly string _database = "SQLInsightFk_" + Guid.NewGuid().ToString("N")[..8];
public Task InitializeAsync() => sql.ExecuteAsSaAsync($"""
CREATE DATABASE [{_database}];
EXEC('USE [{_database}];
CREATE TABLE dbo.Kunder (Id int PRIMARY KEY);
CREATE TABLE dbo.Ordrer (Id int PRIMARY KEY, KundeId int CONSTRAINT FK_Ordrer_Kunder REFERENCES dbo.Kunder (Id));
ALTER TABLE dbo.Ordrer NOCHECK CONSTRAINT FK_Ordrer_Kunder;
ALTER TABLE dbo.Ordrer CHECK CONSTRAINT FK_Ordrer_Kunder;');
""");
public Task DisposeAsync() => sql.ExecuteAsSaAsync($"ALTER DATABASE [{_database}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [{_database}];");
[Fact]
public async Task Community_and_local_rules_run_with_least_privilege()
{
var local = CustomRuleLoader.Parse("""
[
{ "id": "LOCAL-SEC-001", "title": "Databasemail", "severity": "Low", "query": "SELECT name FROM msdb.dbo.sysmail_account",
"findingTitle": "{name}", "explanation": "x", "recommendation": "y" },
{ "id": "LOCAL-SEC-002", "title": "Stavefejl", "severity": "Low", "query": "SELECT nosuchcolumn FROM sys.databases",
"findingTitle": "x", "explanation": "x", "recommendation": "y" }
]
""", "local.json", "lokal");
Assert.Empty(local.Errors);
var rules = RulePack.Embedded().CommunityRules.Concat(local.Rules).ToList();
var instance = new MonitoredInstance
{
Name = "itest", Server = sql.Server, Authentication = AuthenticationMode.SqlLogin, UserName = SqlServerFixture.AgentLogin,
Password = SqlServerFixture.AgentPassword, Provider = ConnectionProvider.SqlClient, TrustServerCertificate = true,
};
using var output = new StringWriter();
var exit = await RuleCommands.TestAsync(rules, instance, new SqlConnectionFactory(), output, CancellationToken.None);
var text = output.ToString();
Assert.Contains("COM-SEC-001: Findings", text, StringComparison.Ordinal);
Assert.Contains("Det indbyggede login 'sa' er aktivt", text, StringComparison.Ordinal);
Assert.Contains($"{_database}: fremmednøglen FK_Ordrer_Kunder på dbo.Ordrer er ikke betroet", text, StringComparison.Ordinal);
Assert.Contains("COM-CFG-001: Passed", text, StringComparison.Ordinal);
Assert.Contains("LOCAL-SEC-001: NotMeasurable – Agentens login mangler rettigheder", text, StringComparison.Ordinal);
Assert.Contains("LOCAL-SEC-002: Error", text, StringComparison.Ordinal);
Assert.Equal(2, exit);
}
}