#631: Derselbe Vorschlag (gleicher Agent, gleiches Werkzeug, gleiche Argumente unabhaengig von Schluesselreihenfolge) wird nicht erneut angelegt - der Agent erfaehrt die bestehende Nummer. Offene Vorschlaege verfallen nach 24 h (Status Expired). #609: Werkzeuge koennen den Freigabeschluessel aus dem Inhalt bestimmen (IAgentTool.StagingAktion). Database: SELECT/SHOW/DESCRIBE -> query, INSERT/UPDATE/DELETE -> write, DDL -> schema; write und schema sind freigabepflichtig. Ausgefuehrt wird nach Statement-Art, schreibende Statements melden die Zeilenzahl. #579: Database-Vorpruefung (Zugriffsebene, Tabellenfreigabe, SqlGuard) - was das Werkzeug ablehnen wuerde, kommt nicht mehr in die Freigabeliste. #610: SqlGuard ueberspringt IF [NOT] EXISTS und erkennt DESCRIBE/DESC <tabelle>. #632: WebFetch fetch mit filter liefert aus JSON/JSONP nur passende Eintraege; beim Blaettern durch grosses JSON empfiehlt der Hinweis den filter. Version 0.1.10. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
68 lines
3.0 KiB
C#
68 lines
3.0 KiB
C#
using System.Text.Json;
|
|
using ClawdDotNet.Core.Tools;
|
|
using ClawdDotNet.Tools.Database;
|
|
using ClawdDotNet.Tools.Tests.FileRW;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Shouldly;
|
|
|
|
namespace ClawdDotNet.Tools.Tests.Database;
|
|
|
|
/// <summary>
|
|
/// Vorpruefung (#579) und Freigabeschluessel (#609) des Database-Werkzeugs - beides ohne
|
|
/// Datenbankverbindung pruefbar, weil es nur Konfiguration und Statement ansieht.
|
|
/// </summary>
|
|
public sealed class DatabaseVorpruefungTests
|
|
{
|
|
private static readonly DatabaseTool Tool = new();
|
|
|
|
private static AgentToolContext Kontext(string accessLevel, params string[] tabellen) => new(
|
|
"agent", "inst",
|
|
new Dictionary<string, object?>
|
|
{
|
|
["type"] = "mysql",
|
|
["connectionString"] = "Server=nirgends",
|
|
["accessLevel"] = accessLevel,
|
|
["allowedTables"] = JsonSerializer.SerializeToElement(tabellen)
|
|
},
|
|
new NullStateStore(), NullLogger.Instance, CancellationToken.None);
|
|
|
|
private static JsonElement Sql(string sql, string action = "query")
|
|
=> JsonSerializer.SerializeToElement(new { action, sql });
|
|
|
|
[Fact]
|
|
public void Ein_Agent_mit_Leserechten_bekommt_sein_INSERT_sofort_abgelehnt_statt_zur_Freigabe()
|
|
=> Tool.Preflight(Sql("INSERT INTO messwerte (ort) VALUES ('Berlin')", "insert"), Kontext("ReadOnly", "messwerte"))
|
|
.ShouldNotBeNull().ShouldContain("ReadOnly");
|
|
|
|
[Fact]
|
|
public void Strukturaenderung_ohne_Admin_wird_vorab_abgelehnt()
|
|
=> Tool.Preflight(Sql("ALTER TABLE messwerte ADD COLUMN x INT"), Kontext("ReadWrite", "messwerte"))
|
|
.ShouldNotBeNull().ShouldContain("DDL");
|
|
|
|
[Fact]
|
|
public void Fremde_Tabelle_wird_vorab_abgelehnt()
|
|
=> Tool.Preflight(Sql("SELECT * FROM protokoll"), Kontext("ReadOnly", "messwerte"))
|
|
.ShouldNotBeNull().ShouldContain("protokoll");
|
|
|
|
[Theory]
|
|
[InlineData("SELECT * FROM messwerte", "ReadOnly")]
|
|
[InlineData("INSERT INTO messwerte (ort) VALUES ('Berlin')", "ReadWrite")]
|
|
[InlineData("CREATE TABLE IF NOT EXISTS messwerte (id INT)", "Admin")]
|
|
[InlineData("DESCRIBE messwerte", "ReadOnly")]
|
|
public void Erlaubtes_passiert_die_Vorpruefung(string sql, string level)
|
|
=> Tool.Preflight(Sql(sql), Kontext(level, "messwerte")).ShouldBeNull();
|
|
|
|
[Theory]
|
|
[InlineData("SELECT * FROM messwerte", "query", "query")]
|
|
[InlineData("INSERT INTO messwerte (ort) VALUES ('Berlin')", "query", "write")]
|
|
[InlineData("UPDATE messwerte SET ort = 'X'", "insert", "write")]
|
|
[InlineData("CREATE TABLE messwerte (id INT)", "query", "schema")]
|
|
[InlineData("DROP TABLE IF EXISTS messwerte", "query", "schema")]
|
|
public void Der_Freigabeschluessel_folgt_dem_Statement_nicht_dem_action_Argument(string sql, string action, string erwartet)
|
|
=> Tool.StagingAktion(Sql(sql, action)).ShouldBe(erwartet);
|
|
|
|
[Fact]
|
|
public void Ohne_sql_gilt_das_action_Argument_wie_bei_MongoDB()
|
|
=> Tool.StagingAktion(JsonSerializer.SerializeToElement(new { action = "insert", collection = "c" })).ShouldBeNull();
|
|
}
|