feat(proxy): PreToolUse-Hook blockiert Bash-curl wenn Skill existiert
Variante A endlich umgesetzt: echter Hard-Block vor Bash-Ausfuehrung.
Anders als 14 seed_rules + Bypass-Lehre, die ARIA ignorieren kann,
ist das ein technisch erzwungener Reject auf claude-CLI-Ebene.
Komponenten:
1. aria-brain main.py: neuer Endpoint POST /skills/can-bash-host
Bekommt {command}, parst https-URLs raus, prueft gegen aktive Skills
(stem-match: 'spotify' im Hostname 'api.spotify.com'). Returnt
{block, host, skill, safe_tool} wenn ein Skill den Host abdeckt.
2. proxy-patches/pre-tool-bash-block.js: Node-Script das vom claude-CLI
als PreToolUse-Hook fuer das Bash-Tool aufgerufen wird. Liest Tool-
Use-Payload via stdin, ruft Brain-Endpoint mit kurzem Timeout (3s),
bei block=true → exit 2 mit Stderr-Message. claude-CLI gibt Stderr
als tool_use_error an das LLM zurueck — echter Fehler, nicht
ignorierbar.
Fail-open bei Brain-Down / Timeout / JSON-Fehler: kein Lockout.
3. proxy-patches/managed-settings.json: claude-CLI Hook-Config mit
PreToolUse-Matcher 'Bash' der das Node-Script ausfuehrt.
/etc/claude-code/managed-settings.json hat Vorrang vor User-Settings
und betrifft NICHT Stefans Host-~/.claude/settings.json.
4. docker-compose.yml: proxy-Command erweitert um
`mkdir -p /etc/claude-code && cp managed-settings.json dorthin`
damit beim Container-Start die Hook-Config aktiv ist.
Beobachtung die das motiviert: 14 seed_rules + Bypass-Lehre +
Auto-Scaffold + Safe-Names. ARIA hat trotzdem letzten Test mit 2
verschachtelten Bash-curls bedient statt run_spotify zu rufen
(content_len=73, tool_calls=0). Prompt-Engineering ausgereizt.
ARIA bekommt jetzt:
🚨 BASH GEGEN api.spotify.com BLOCKIERT.
Es existiert bereits ein Skill 'spotify' fuer diesen Host. ...
Konkret: nutze JETZT `run_spotify` mit den passenden Parametern
(method/path/body) statt curl.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "Bash",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node /proxy-patches/pre-tool-bash-block.js"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* ARIA claude-CLI PreToolUse-Hook: blockiert Bash-Calls gegen externe APIs
|
||||
* fuer die bereits ein matching Skill im Brain existiert.
|
||||
*
|
||||
* Wird von claude-CLI PRO Tool-Use vor der Ausfuehrung mit dem Tool-Use-
|
||||
* JSON via stdin aufgerufen. Wenn wir exit 2 mit Stderr returnen, lehnt
|
||||
* claude-CLI den Tool-Call ab und gibt die Stderr als tool_use_error
|
||||
* an das LLM zurueck — ARIA bekommt also eine echte Fehlermeldung und
|
||||
* MUSS umdenken (nicht nur Prompt-Anweisung die sie ignorieren kann).
|
||||
*
|
||||
* Fail-open: bei jeder Art von Fehler (Brain nicht erreichbar, kaputtes
|
||||
* JSON etc.) exit 0 — wir blockieren Stefan's eigentliche Arbeit nicht
|
||||
* nur weil der Block-Mechanismus selber haengt.
|
||||
*/
|
||||
|
||||
const http = require("http");
|
||||
|
||||
const BRAIN_URL = process.env.BRAIN_INTERNAL_URL || "http://aria-brain:8080";
|
||||
const BRAIN_TIMEOUT_MS = 3000;
|
||||
|
||||
function fail_open(reason) {
|
||||
if (process.env.HOOK_DEBUG) console.error(`hook-skip: ${reason}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
function block(message) {
|
||||
// exit 2 = block in claude-CLI PreToolUse hook contract
|
||||
process.stderr.write(message);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
let stdinBuf = "";
|
||||
process.stdin.on("data", chunk => stdinBuf += chunk);
|
||||
process.stdin.on("end", () => {
|
||||
let payload;
|
||||
try {
|
||||
payload = JSON.parse(stdinBuf || "{}");
|
||||
} catch (_) {
|
||||
return fail_open("stdin not json");
|
||||
}
|
||||
// claude-CLI Hook-Format kann je nach Version variieren —
|
||||
// wir akzeptieren tool_name oder hook_event_name in Kombination
|
||||
const toolName = payload.tool_name || payload.tool || "";
|
||||
if (toolName !== "Bash") return fail_open("tool != Bash");
|
||||
const command = (payload.tool_input && payload.tool_input.command) ||
|
||||
payload.command || "";
|
||||
if (!command) return fail_open("no command");
|
||||
// Schnellfilter: nur wenn ueberhaupt eine URL drin ist
|
||||
if (!/https?:\/\//i.test(command)) return fail_open("no url");
|
||||
|
||||
// Brain fragen ob ein matching Skill existiert
|
||||
const body = JSON.stringify({ command });
|
||||
const req = http.request(
|
||||
BRAIN_URL + "/skills/can-bash-host",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Content-Length": Buffer.byteLength(body),
|
||||
},
|
||||
timeout: BRAIN_TIMEOUT_MS,
|
||||
},
|
||||
(res) => {
|
||||
let chunks = "";
|
||||
res.on("data", d => chunks += d);
|
||||
res.on("end", () => {
|
||||
let r;
|
||||
try { r = JSON.parse(chunks); } catch (_) { return fail_open("brain bad json"); }
|
||||
if (!r || !r.block) return fail_open("brain says ok");
|
||||
const skill = r.skill || "?";
|
||||
const host = r.host || "?";
|
||||
const safeTool = r.safe_tool || `run_${skill}`;
|
||||
const msg =
|
||||
`🚨 BASH GEGEN ${host} BLOCKIERT.\n\n` +
|
||||
`Es existiert bereits ein Skill '${skill}' fuer diesen Host. ` +
|
||||
`Stefan hat das System so eingerichtet dass Skills via ` +
|
||||
`\`${safeTool}\` direkt aufgerufen werden — das ist 5-10x ` +
|
||||
`schneller als der Bash-Curl-Wrapper.\n\n` +
|
||||
`Konkret: nutze JETZT \`${safeTool}\` mit den passenden ` +
|
||||
`Parametern (method/path/body) statt curl. Wenn der Skill ` +
|
||||
`nicht das liefert was Du brauchst: skill_update mit Fix, ` +
|
||||
`nicht zurueck zu Bash.`;
|
||||
block(msg);
|
||||
});
|
||||
}
|
||||
);
|
||||
req.on("error", () => fail_open("brain network error"));
|
||||
req.on("timeout", () => { req.destroy(); fail_open("brain timeout"); });
|
||||
req.write(body);
|
||||
req.end();
|
||||
});
|
||||
|
||||
// Falls stdin nie ein 'end' triggert — Timeout damit wir nicht haengen
|
||||
setTimeout(() => fail_open("stdin timeout"), 4000);
|
||||
Reference in New Issue
Block a user