Écrire un hook sous forme de module WebAssembly
Ce guide montre comment écrire un hook qui s'exécute comme module WebAssembly à l'intérieur de mm, dans un bac à sable, au lieu d'un script : rien à installer à côté de mm, aucun processus lancé à chaque événement, et un hook qui ne voit aucun fichier, aucune variable d'environnement et aucun réseau, sauf ce que son entrée lui ouvre. Il suppose que vous savez comment fonctionnent les hooks et que TinyGo est installé.
Étapes
-
Écrivez le hook comme un programme Go ordinaire. Il lit sur stdin le même objet JSON qu'un hook script, et écrit sur stdout la même réponse — ou rien, pour laisser le tour continuer. Celui-ci refuse
git push --force:// hooks-src/no_force_push/main.go package main import ( "encoding/json" "os" "regexp" ) var forcePush = regexp.MustCompile(`\bgit\s+push\b.*(--force|-f\b)`) func main() { var in struct { Tool struct { Input struct { Command string `json:"command"` } `json:"input"` } `json:"tool"` } if err := json.NewDecoder(os.Stdin).Decode(&in); err != nil { os.Exit(2) } if forcePush.MatchString(in.Tool.Input.Command) { json.NewEncoder(os.Stdout).Encode(map[string]string{ "action": "deny", "reason": "force-push is not allowed in this project", }) } } -
Compilez-le en module WASI :
tinygo build -o hooks/no_force_push.wasm -target=wasip1 -no-debug ./hooks-src/no_force_push -
Déclarez-le avec
wasm:à la place decommand:;matcher,timeoutetonErrorfonctionnent comme pour un script :hooks: preToolCall: - matcher: ^bash$ wasm: ./hooks/no_force_push.wasm onError: failLe chemin du module est relatif au fichier d'agent.
-
Lancez l'agent avec
displayHooks: truedans le fichier, et demandez quelque chose qui lancebash. Après la réponse, chaque exécution du hook est listée sous le nom de son module :🪝 1. preToolCall bash · no_force_push.wasm → deny
Variantes
- Un hook qui lit ou écrit des fichiers. Ouvrez les dossiers dont il a besoin, relatifs au répertoire de travail :
read: [rules](lecture seule),write: [logs](lecture-écriture). Il les ouvre sous le même nom (logs/hooks-audit.jsonl) ; rien d'autre n'existe pour lui. Un dossier doit exister quand le hook s'exécute. - Un module pour plusieurs événements. Lisez
eventdans l'entrée, ou la variableMM_HOOK_EVENT— la seule que voit le module. - Un hook qui a besoin de tout le répertoire de travail (pour en lister les fichiers, par exemple) ou du réseau reste un script : un hook wasm peut ouvrir des dossiers à l'intérieur du répertoire de travail, pas le répertoire lui-même, et n'a pas de réseau.
- Les chemins dans le module. Écrivez-les propres :
logs/a.jsonl, pas./logs/a.jsonl— la couche WASI de TinyGo refuse./et... - La première exécution d'un processus compile le module (quelques centaines de millisecondes) ; les suivantes prennent bien moins d'une milliseconde.
Voir aussi
- Un exemple complet — un filtre de secrets, une garde pour
bashet un journal d'audit, les hooks dedemos/hooksréécrits en modules :demos/wasm-hooks/ - Chaque clé, ce que voit le module, les échecs : référence des hooks
- Pourquoi, et ce qui a été mesuré : Hooks — explication
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
|