#643: Entscheidungen wecken den Agenten nur noch bei Ablehnung oder gescheiterter Ausfuehrung, gesammelt in einer Aufgabe je Agent (5 min Ruhefenster), frisch per @new:agent statt im fortgefuehrten Chatverlauf. Eine einmalige Aufgabe, deren Lauf wegen Budget oder Herunterfahren gar nicht stattfand, bleibt faellig (Wiedervorlage nach 30 min) statt mit gesetztem Marker liegenzubleiben. #641: Rechte stehen in der Werkzeugkonfiguration des Agenten (erlaubt/verboten). Erlaubtes laeuft, Nicht-Erlaubtes wird einzeln vorgelegt statt verweigert, Verbotenes gar nicht erst. "Gleichartiges kuenftig erlauben" erteilt eine widerrufbare Dauerfreigabe je Agent/Werkzeug/Operation; nach drei gleichartigen Freigaben schlaegt die Oberflaeche sie vor. Aenderungen an Agenten bleiben ausnahmslos freigabepflichtig. Database: Operation ist das SQL-Schluesselwort; die eigene Zugriffspruefung greift nur noch ohne Freigabestelle (AgentToolContext.FreigabeGeprueft). WebUI: Rechte-Tabelle je Werkzeug, Dauerfreigaben mit Widerruf. Version 0.1.11. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,8 +30,9 @@ public sealed class DatabaseTool : IAgentTool
|
||||
Aufruf, keine SQL-Kommentare. Nur die freigegebenen Tabellen sind erreichbar.
|
||||
Tabellenstruktur: "DESCRIBE <tabelle>" oder "SHOW COLUMNS FROM <tabelle>" - pruefe die
|
||||
Spaltennamen, bevor du schreibst. Schreibende Statements liefern die Zahl betroffener Zeilen.
|
||||
Je nach Einstellung werden schreibende oder strukturaendernde Statements zur Freigabe
|
||||
vorgelegt statt sofort ausgefuehrt; liegt derselbe Vorschlag schon vor, lege ihn nicht erneut vor.
|
||||
Welche Operationen (SELECT, INSERT, UPDATE, ALTER ...) du ohne Rueckfrage ausfuehren darfst,
|
||||
legt deine Werkzeugkonfiguration fest. Andere werden einzeln zur Freigabe vorgelegt statt
|
||||
sofort ausgefuehrt; liegt derselbe Vorschlag schon vor, lege ihn nicht erneut vor.
|
||||
|
||||
MongoDB: action query (filter), insert (document) in einer freigegebenen collection.
|
||||
""";
|
||||
@@ -67,9 +68,12 @@ public sealed class DatabaseTool : IAgentTool
|
||||
""").RootElement.Clone();
|
||||
|
||||
/// <summary>
|
||||
/// #579: Was das Werkzeug ohnehin ablehnen wuerde, kommt nicht in die Freigabeliste. Im
|
||||
/// Testbetrieb legte ein Agent mit ReadOnly seine Test-INSERTs zur Freigabe vor - nach der
|
||||
/// Freigabe scheiterten sie an genau dieser Pruefung.
|
||||
/// #579: Was das Werkzeug ohnehin ablehnen wuerde, kommt nicht in die Freigabeliste -
|
||||
/// unlesbares SQL, nicht freigegebene Tabellen und Collections.
|
||||
///
|
||||
/// <para>#641: Die Operation selbst (SELECT, INSERT, ALTER ...) prueft hier niemand mehr.
|
||||
/// Was nicht erlaubt ist, entscheidet die Freigabestelle - und legt es einzeln vor, statt
|
||||
/// es zu verweigern.</para>
|
||||
/// </summary>
|
||||
public string? Preflight(JsonElement input, AgentToolContext context)
|
||||
{
|
||||
@@ -77,16 +81,11 @@ public sealed class DatabaseTool : IAgentTool
|
||||
if (string.IsNullOrWhiteSpace(type))
|
||||
return null; // Konfigurationsfehler meldet die Ausfuehrung selbst
|
||||
|
||||
var accessLevel = GetAccessLevel(context);
|
||||
|
||||
if (type == "mongodb")
|
||||
{
|
||||
var action = GetString(input, "action");
|
||||
var collection = GetString(input, "collection");
|
||||
if (!string.IsNullOrWhiteSpace(collection) && !IsCollectionAllowed(collection, context))
|
||||
return $"Sicherheitsfehler: Zugriff auf Collection '{collection}' ist nicht erlaubt.";
|
||||
if (action is "insert" or "upsert" && accessLevel == DatabaseAccessLevel.ReadOnly)
|
||||
return "Sicherheitsfehler: Schreibzugriff deaktiviert.";
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -95,34 +94,51 @@ public sealed class DatabaseTool : IAgentTool
|
||||
return "'sql' ist erforderlich.";
|
||||
|
||||
var inspection = SqlGuard.Inspect(sql, GetAllowedTables(context));
|
||||
return Verweigerung(inspection, accessLevel);
|
||||
return inspection.IsValid ? null : $"Sicherheitsfehler: {inspection.Error}";
|
||||
}
|
||||
|
||||
/// <summary>Die Operationen, die sich je Agent erlauben lassen (#641).</summary>
|
||||
public IReadOnlyList<string> Operationen { get; } =
|
||||
["select", "insert", "update", "delete", "replace", "create", "alter", "drop", "truncate"];
|
||||
|
||||
/// <summary>
|
||||
/// #609: Der Freigabeschluessel folgt dem Statement, nicht dem action-Argument. Vorher lief
|
||||
/// ein INSERT, das als action "insert" zur Freigabe lag, eine Sekunde spaeter als action
|
||||
/// "query" ohne Freigabe durch.
|
||||
/// #609/#641: Der Freigabeschluessel folgt dem Statement, nicht dem action-Argument - ein
|
||||
/// INSERT ist "insert", auch wenn das Modell action "query" schickt.
|
||||
/// </summary>
|
||||
public string? StagingAktion(JsonElement input)
|
||||
{
|
||||
var sql = GetString(input, "sql");
|
||||
if (string.IsNullOrWhiteSpace(sql))
|
||||
return null; // MongoDB: dort gilt das action-Argument
|
||||
if (!string.IsNullOrWhiteSpace(sql))
|
||||
return SqlGuard.Operationsschluessel(sql);
|
||||
|
||||
return SqlGuard.Inspect(sql, ["*"]).Operation switch
|
||||
return GetString(input, "action") switch
|
||||
{
|
||||
SqlOperation.Read => "query",
|
||||
SqlOperation.Write => "write",
|
||||
SqlOperation.Schema => "schema",
|
||||
"query" => "select",
|
||||
"insert" => "insert",
|
||||
"upsert" => "update",
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
|
||||
private static string? Verweigerung(SqlInspection inspection, DatabaseAccessLevel accessLevel)
|
||||
/// <summary>
|
||||
/// Ohne <c>erlaubt</c> in der Konfiguration: Lesen laeuft, alles andere wird vorgelegt.
|
||||
/// </summary>
|
||||
public Core.Staging.StagingDecision? Vorgabe(string operation, IReadOnlyDictionary<string, object?> config)
|
||||
=> operation == "select" ? Core.Staging.StagingDecision.Auto : Core.Staging.StagingDecision.Approve;
|
||||
|
||||
/// <summary>
|
||||
/// Die eigene Rechtepruefung des Werkzeugs. Laeuft nur, wenn keine Freigabestelle den
|
||||
/// Aufruf geprueft hat (<see cref="AgentToolContext.FreigabeGeprueft"/>) - dann gilt die
|
||||
/// Zugriffsstufe als harte Grenze.
|
||||
/// </summary>
|
||||
private static string? Verweigerung(SqlInspection inspection, DatabaseAccessLevel accessLevel, bool freigabeGeprueft)
|
||||
{
|
||||
if (!inspection.IsValid)
|
||||
return $"Sicherheitsfehler: {inspection.Error}";
|
||||
|
||||
if (freigabeGeprueft)
|
||||
return null;
|
||||
|
||||
return inspection.Operation switch
|
||||
{
|
||||
SqlOperation.Schema when accessLevel != DatabaseAccessLevel.Admin =>
|
||||
@@ -178,7 +194,7 @@ public sealed class DatabaseTool : IAgentTool
|
||||
// werden dabei behandelt, bevor Operation und Tabellen bestimmt werden.
|
||||
var inspection = SqlGuard.Inspect(sql, GetAllowedTables(context));
|
||||
|
||||
if (Verweigerung(inspection, accessLevel) is { } verweigert)
|
||||
if (Verweigerung(inspection, accessLevel, context.FreigabeGeprueft) is { } verweigert)
|
||||
return ToolResult.Fail(verweigert);
|
||||
|
||||
using DbConnection conn = type switch
|
||||
@@ -245,7 +261,7 @@ public sealed class DatabaseTool : IAgentTool
|
||||
}
|
||||
else if (action == "insert")
|
||||
{
|
||||
if (accessLevel == DatabaseAccessLevel.ReadOnly) return ToolResult.Fail("Sicherheitsfehler: Schreibzugriff deaktiviert.");
|
||||
if (accessLevel == DatabaseAccessLevel.ReadOnly && !context.FreigabeGeprueft) return ToolResult.Fail("Sicherheitsfehler: Schreibzugriff deaktiviert.");
|
||||
var docJson = input.TryGetProperty("document", out var d) ? d.GetString() : throw new ArgumentException("'document' erforderlich.");
|
||||
var doc = BsonDocument.Parse(docJson);
|
||||
await collection.InsertOneAsync(doc, cancellationToken: ct);
|
||||
@@ -253,7 +269,7 @@ public sealed class DatabaseTool : IAgentTool
|
||||
}
|
||||
else // upsert
|
||||
{
|
||||
if (accessLevel == DatabaseAccessLevel.ReadOnly) return ToolResult.Fail("Sicherheitsfehler: Schreibzugriff deaktiviert.");
|
||||
if (accessLevel == DatabaseAccessLevel.ReadOnly && !context.FreigabeGeprueft) return ToolResult.Fail("Sicherheitsfehler: Schreibzugriff deaktiviert.");
|
||||
return ToolResult.Fail("Upsert für MongoDB noch nicht voll implementiert.");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user