Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion labs/15-pitot/adapter-verification.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
],
"agents": [
{"id": "claude", "label": "Claude"},
{"id": "cline", "label": "Cline"},
{"id": "cursor", "label": "Cursor"},
{"id": "codex", "label": "Codex"},
{"id": "copilot", "label": "GitHub Copilot CLI"},
{"id": "gemini", "label": "Gemini"},
{"id": "kimi", "label": "Kimi Code"},
{"id": "opencode", "label": "OpenCode"}
{"id": "opencode", "label": "OpenCode"},
{"id": "pi", "label": "Pi"},
{"id": "qwen", "label": "Qwen Code"}
]
}
29 changes: 29 additions & 0 deletions labs/15-pitot/integrations/cline/PreToolUse
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env bash
set -uo pipefail

PITOT_COMMAND="${PITOT_BIN:-pitot}"
PAYLOAD=$(cat)
if ! TOOL=$(printf '%s' "$PAYLOAD" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("preToolUse", {}).get("tool", ""))' 2>/dev/null); then
printf '{"cancel":true,"errorMessage":"Pitot received a malformed Cline hook payload"}\n'
exit 0
fi

if [ -z "$TOOL" ]; then
printf '{"cancel":true,"errorMessage":"Pitot received a malformed Cline hook payload"}\n'
exit 0
fi

# Cline has no matcher at this boundary. Non-shell tools are outside Pitot's
# controllable partition and must pass through unchanged.
if [ "$TOOL" != "execute_command" ]; then
printf '{"cancel":false}\n'
exit 0
fi

if printf '%s' "$PAYLOAD" | "$PITOT_COMMAND" hook cline >/dev/null; then
printf '{"cancel":false}\n'
exit 0
fi

printf '{"cancel":true,"errorMessage":"Pitot rejected the shell request"}\n'
exit 0
23 changes: 23 additions & 0 deletions labs/15-pitot/integrations/cline/PreToolUse.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
$payload = [Console]::In.ReadToEnd()
$pitot = if ($env:PITOT_BIN) { $env:PITOT_BIN } else { "pitot" }
try {
$event = $payload | ConvertFrom-Json -ErrorAction Stop
} catch {
@{ cancel = $true; errorMessage = "Pitot received a malformed Cline hook payload" } | ConvertTo-Json -Compress
exit 0
}
if (-not $event.preToolUse.tool) {
@{ cancel = $true; errorMessage = "Pitot received a malformed Cline hook payload" } | ConvertTo-Json -Compress
exit 0
}
if ($event.preToolUse.tool -ne "execute_command") {
@{ cancel = $false } | ConvertTo-Json -Compress
exit 0
}
$payload | & $pitot hook cline *> $null
if ($LASTEXITCODE -eq 0) {
@{ cancel = $false } | ConvertTo-Json -Compress
} else {
@{ cancel = $true; errorMessage = "Pitot rejected the shell request" } | ConvertTo-Json -Compress
}
exit 0
25 changes: 25 additions & 0 deletions labs/15-pitot/integrations/pi/pitot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { spawnSync } from "node:child_process";

// handleToolCall is exported so the shipped boundary can be tested without a
// live Pi session. Pi itself calls the default extension registration below.
export function handleToolCall(event, run = spawnSync) {
if (event.toolName !== "bash") return undefined;

const payload = JSON.stringify({
hook_event_name: "tool_call",
tool_name: "bash",
tool_input: { command: event.input?.command ?? "" },
});
const result = run(process.env.PITOT_BIN || "pitot", ["hook", "pi"], {
input: payload,
encoding: "utf8",
maxBuffer: 1024 * 1024,
});
if (result.status === 0) return undefined;
const reason = (result.stderr || "Pitot rejected the shell request").trim();
return { block: true, reason: reason.slice(0, 1024) };
}

export default function pitotExtension(pi) {
pi.on("tool_call", async (event) => handleToolCall(event));
}
33 changes: 20 additions & 13 deletions labs/15-pitot/pitot-distribution/UPSTREAM.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"files": {
"CONTRIBUTING.md": "0613b71aa497f8ca7d7296bf34ade87bfc7237a664d2e812d9b77b3b6befb0ad",
"README.md": "99e2c764ec29dfcdb42ff09caa6583d0de9192dc34460a8a563d8ae5ea47dfa0",
"adapters/adapters.go": "b516fdd0fdd805a08cb1467f78ee367d5f280d0084a55169d09c6f3d0b795fc0",
"README.md": "098e759d255de328e4de2383738ea920b57d914d6e691c52329ffc7c881930bc",
"adapters/adapters.go": "57a7e3a50c464a4124e3f3231e1843b43f724dcd60316f2c52dd1c65043e9533",
"assets/pitot-boundary.png": "8a0ddb7d81831d94e14813f50ea4ca8670d77417f339ed2f91f0c653bf52f41d",
"assets/pitot-boundary.svg": "0c3871d70c84748573f231842091deb38a6def2862403ca34e8cc4493b9c9ebf",
"assets/pitot-hero.png": "a73532252b1e66c06273abbf5a4fe6261e98de3133b09e8d550edacfeeab92f8",
Expand All @@ -13,20 +13,23 @@
"bridge/bridge.go": "79ac2e025e16782f3c283b43cbea5b9ba4f837446583864df8f57d6346cae816",
"bridge/bridge_test.go": "23a19b7580d4b1e826224ec8322208ccca44ddd9a97b150efe15cafecc53e47f",
"cmd/generate-schema/main.go": "6e9d0030290d99e36967433f96e38385a122974f899ad9421aac1ef7e50d8fcb",
"cmd/pitot/main.go": "20542d225d3676edd7d4129db0b7009ef2c93a76b5dbd633ff2fcf1eef611775",
"cmd/pitot/main_test.go": "203ce391b9996e81a697f37c913157da1dff8db7ec5e2d7f80d4729c1841239c",
"cmd/pitot/main.go": "aa921d65706ea77b2c04aeddf6a482886ecf25f2c119eb0922edb4278aa53740",
"cmd/pitot/main_test.go": "377406a4c1479b4017505540164795dd224cba64c77a5cfa0297a9517194d940",
"conformance/conformance.go": "43b692114f45c8b52958e34b35aee1cee339d8321c90f92ab4f5b963e79935bb",
"conformance/conformance_test.go": "83ab0bcc15371265a954d177e4e97d81ad3ea734bbf736a29a54628ef64b52cd",
"conformance/fixtures/negative.jsonl": "383dd001910699886bb1074d9225c91d8a6201e9fb3267d1a1f6e1c2753b0ba6",
"conformance/fixtures/positive.jsonl": "d3af0f2529dac9b33fa4900f5938e36fd0b17383088dd4f0de7e6eca269441d1",
"doc.go": "9ef34dc37b30363272dbc870482154aed92187f1bdeb4d3683f871dde2c3e4b5",
"conformance/fixtures/negative.jsonl": "c8eff4f4155a5e86e7127a8d89aa06c14ecba74a7230b5bf7134ab1b7c4ca5f7",
"conformance/fixtures/positive.jsonl": "300f40a47e6b72324b76c57a351d3be7ff7a2592e73f02cfe7e66c0e1defdf6d",
"doc.go": "a8abdafac969b1bf4372c8bb023aa51125dc073f03218f4ab9913dfc5ffa877d",
"e2e/e2e_coverage_test.go": "28a6c27408338fdc51cf1241e1bf42a7f0ea17d3fb1305fbcd15901c21e21de8",
"e2e/e2e_hook_test.go": "6092f9a383657345587723cdfeebfcf8bbe87b6e441d595ad9d11ae06b95edb0",
"e2e/e2e_hook_test.go": "5e184dc8907b6e36daeab90bbbb1654fa5336312866412031805a13ba535d1f8",
"examples/doc.go": "58f3f9eb7d272d7b6eecdb05f43e1613d5e3ef92d15d97c5440bd4b6990c26f9",
"examples/local-approval/main.go": "51386af324cd7d3bb07fe3ace53503884b02714b96b83073342fde81ce3b83a5",
"examples/token-meter/main.go": "4b1b9c1a43c3cf48b09dba6f607776caced9d2b5b562373496b31ed184582dd1",
"go.mod": "7e71b29887a2370c920a3ecad84460130ecc5ad4acba7f62300b5d2568f0ee13",
"go.sum": "2f73a6c3c672f4022f4a618578fac165172095ef590db5d14caa4552675f1980",
"integrations/cline/PreToolUse": "05a449b402918876c5e945e837525b9613a4889fad932e91193b3f63d2778e22",
"integrations/cline/PreToolUse.ps1": "bc0cea5f2cf6e910a2751465090a1a3c37cf0eea8ab4b98b5e735738630548f0",
"integrations/pi/pitot.ts": "ed2d60d5ab6e33e115cfa058e4f96095100e93a061567d0af31249aa756bab3e",
"projection/projection.go": "4d3c823fd72a3ca5387dba3683838a1d7e455e9b18309acc839763a39b7bb35f",
"protocol/framing.go": "d4409314b72e09cfd472ad9a21d4c7a223b4341b26f58f6062a7c899e3f87482",
"protocol/framing_test.go": "9fcb13767fdcc7129d2c87bc2133a5800c69d6ac0166016dc266dca5fcfaa1c5",
Expand All @@ -38,19 +41,23 @@
"sdk/typescript/src/index.ts": "de43e6654eac51afd09a3a74c4c6992dcf2306d6c41b9be626ded1c6d6cc7833",
"sdk/typescript/src/pitot.ts": "9c243824cbb7edc54b1e125abfd828bf2ed77e7151a0bd5c5d4f63e81ca9b00c",
"sdk/typescript/tsconfig.json": "e4d7ecb203fcb7d93cd9b9fb235d7fd75d1b6fb2b28eff773f4aecfdc1924d5d",
"sensor/decode_fuzz_test.go": "6f3412865c16e3314980a369c36f1f26ba36a991c8b4a8a9d3b776ad9c0de2b0",
"sensor/sensor.go": "939e146eaa51906972f98cf4615747eb75811b0c54d467a938825750336abeae",
"sensor/sensor_test.go": "ea9a58d45ad56d29214a40fb2cfa935e769f85334afcb1856a20fb938d486245",
"sensor/decode_fuzz_test.go": "630064cb7b7e783c25f22f70a34bc5d3583180ba4b59bad360679f21bdf5cb37",
"sensor/sensor.go": "5c503d07ac33e7894d635f2d98bcd6d165d442d60c127ed6aeac80ec319086c5",
"sensor/sensor_test.go": "4ddbdde3e486c9e189a2ed4174ad413df107d43dc98f5242da3667a24c7a5da1",
"tests/e2e_claude_cli_test.sh": "70711580004b6f77be58f036f42669ae90e2883b62535ab70719f265168ce66e",
"tests/e2e_cline_cli_test.sh": "659794dd4f4e92d93264c58902ffc10db6cceb6659593819dad66018c187eb3b",
"tests/e2e_codex_cli_test.sh": "f5d49b83bb2f9f49a7e0005f4bd7a7bdd0f4130e68ba75a0cd4457d3e9beb175",
"tests/e2e_copilot_cli_test.sh": "2d9ad18993917ee7d2f7f84797a3e31bd31719b7f1834168b7a32978e05404ea",
"tests/e2e_cursor_cli_test.sh": "091a334a5008b88b94a9a2dafdeaf1388071bd436ef1a9b3376d0f0b28ed60a0",
"tests/e2e_gemini_cli_test.sh": "1559c6b50c5c043bb7a2a901e726986eec87d97dcdda026158d2f138365b8a4f",
"tests/e2e_kimi_cli_test.sh": "591b796ea88f1353ede0663f641044fe2e00a01528c26097b63a5d75ec350040",
"tests/e2e_opencode_cli_test.sh": "bfa4854fc6309fdbdb7a45fbb017ffadb7cb193604efc86ae0bcdfc55c4034f8",
"tests/e2e_unified_runner.sh": "7592f1ac6a5a1da0fb2ba4e0d1bbf8bdfa20c4d5b614069d77773dd8a1a58c20",
"tests/e2e_pi_cli_test.sh": "77793e34e2b2c6c5ca25772a9f34a37fb2e906b9e353f748a1aa9da943b71617",
"tests/e2e_qwen_cli_test.sh": "c08719ce9e8dab05cce2a91d52a479bc566599d38bd3f30e4c279cc6ff622040",
"tests/e2e_unified_runner.sh": "8c37e454a66145d69624bc10db1c3c6568f78ec15acb619b64bea48cb7c9a408",
"tests/mock_anthropic_server.js": "ecebea62f9e93791a79f1ae3dd3c67b8fa42490e9805b23b662b877edfdb0f0e",
"windtunnel/doc.go": "44e0bcde632da73e1f8b98beade3a34ca8e0d0ea79cdfb91d131de290b164fc4",
"windtunnel/windtunnel_test.go": "e18a1f0d8afbb32d45f16c6f081b1a6b2f1b82aa5262f70d8b51e6db3e8e87d7"
"windtunnel/windtunnel_test.go": "e987b3dacd6fdb05aa021bd15ff2c1e1f87b99f4a65aa28e9b74a93bb9b79c1a"
},
"schema_version": 1
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
### Supervise four more coding agents

Pitot now ships verified hook boundaries for GitHub Copilot CLI, Qwen Code, Pi,
and Cline. The single adapter supervisor requires each integration across the
registry, public distribution, E2E report, and all three supported platforms.
70 changes: 70 additions & 0 deletions labs/15-pitot/pitot/adapters/adapters.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ const (
Gemini Host = "gemini"
Opencode Host = "opencode"
Kimi Host = "kimi"
Copilot Host = "copilot"
Qwen Host = "qwen"
Pi Host = "pi"
Cline Host = "cline"
)

// AdapterVersion is the semantic version stamped onto normalized events so
Expand All @@ -39,6 +43,7 @@ const AdapterVersion = "0.1.0"
// into normalized action types and command strings.
type ParserConfig struct {
CanonicalEvent []byte
EventNameFor func(raw RawHookEvent) string
CommandFor func(raw RawHookEvent) (string, bool)
ActionKinds map[string]string // hook event name -> normalized action kind ("shell", "mcp")
}
Expand Down Expand Up @@ -68,6 +73,33 @@ type HostConfig struct {
var (
registryMu sync.RWMutex
registry = map[Host]HostConfig{
Copilot: preToolUseHost(),
Qwen: preToolUseHost(),
Pi: {
MainEventName: "tool_call",
Parser: ParserConfig{
CanonicalEvent: []byte(`{"hook_event_name":"tool_call","tool_name":"bash","tool_input":{"command":"git status --short"}}`),
CommandFor: toolInputCommand,
ActionKinds: map[string]string{"tool_call": "shell"},
},
Partition: ControlPartition{Controllable: []string{"tool_call"}},
},
Cline: {
MainEventName: "PreToolUse",
Parser: ParserConfig{
CanonicalEvent: []byte(`{"hookName":"PreToolUse","preToolUse":{"tool":"execute_command","parameters":{"command":"git status --short"}}}`),
EventNameFor: func(raw RawHookEvent) string { return raw.HookName },
CommandFor: func(raw RawHookEvent) (string, bool) {
if raw.PreToolUse.Tool != "execute_command" {
return "", false
}
value, present := raw.PreToolUse.Parameters["command"].(string)
return value, present && value != ""
},
ActionKinds: map[string]string{"PreToolUse": "shell"},
},
Partition: ControlPartition{Controllable: []string{"PreToolUse"}},
},
Cursor: {
MainEventName: "beforeShellExecution",
Parser: ParserConfig{
Expand Down Expand Up @@ -188,6 +220,28 @@ var (
}
)

func toolInputCommand(raw RawHookEvent) (string, bool) {
value, present := raw.ToolInput["command"].(string)
return value, present && value != ""
}

func preToolUseHost() HostConfig {
return HostConfig{
MainEventName: "PreToolUse",
Parser: ParserConfig{
CanonicalEvent: []byte(`{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`),
CommandFor: func(raw RawHookEvent) (string, bool) {
if raw.ToolName != "Bash" {
return "", false
}
return toolInputCommand(raw)
},
ActionKinds: map[string]string{"PreToolUse": "shell"},
},
Partition: ControlPartition{Controllable: []string{"PreToolUse"}},
}
}

// RegisterHost registers a custom host with its configuration, enforcing
// that it complies with the required supervisory control laws.
func RegisterHost(h Host, config HostConfig) error {
Expand Down Expand Up @@ -306,6 +360,22 @@ type RawHookEvent struct {
// ToolName / ToolInput are populated by PreToolUse-style hosts and Gemini.
ToolName string `json:"tool_name"`
ToolInput map[string]any `json:"tool_input"`
// HookName / PreToolUse preserve Cline's native nested hook shape.
HookName string `json:"hookName"`
PreToolUse struct {
Tool string `json:"tool"`
Parameters map[string]any `json:"parameters"`
} `json:"preToolUse"`
}

// EventNameFor extracts the host-specific event discriminator.
func (h Host) EventNameFor(raw RawHookEvent) string {
registryMu.RLock()
defer registryMu.RUnlock()
if config, exists := registry[h]; exists && config.Parser.EventNameFor != nil {
return config.Parser.EventNameFor(raw)
}
return raw.HookEventName
}

// CommandFor extracts the shell command a raw hook event describes, per host
Expand Down
2 changes: 1 addition & 1 deletion labs/15-pitot/pitot/cmd/pitot/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func run(args []string, stdout, stderr io.Writer) error {
// from stdin, normalizes it, and exits with 0 (allow) or 2 (block/deny).
func runHook(args []string, stdout, stderr io.Writer) error {
if len(args) == 0 {
return fmt.Errorf("pitot: hook requires a host identifier (cursor, claude, codex, gemini, kimi, opencode)")
return fmt.Errorf("pitot: hook requires a host identifier (claude, cline, codex, copilot, cursor, gemini, kimi, opencode, pi, qwen)")
}
host := adapters.Host(args[0])
if !adapters.IsSupported(host) {
Expand Down
2 changes: 1 addition & 1 deletion labs/15-pitot/pitot/cmd/pitot/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TestDoctorReportsBoundary(t *testing.T) {
t.Fatalf("doctor: %v", err)
}
out := stdout.String()
for _, want := range []string{"local boundary", "cursor", "claude", "codex", "gemini", "kimi", "opencode", "decoder=PASS", "unauthenticated local socket: none"} {
for _, want := range []string{"local boundary", "claude", "cline", "codex", "copilot", "cursor", "gemini", "kimi", "opencode", "pi", "qwen", "decoder=PASS", "unauthenticated local socket: none"} {
if !strings.Contains(out, want) {
t.Errorf("doctor output missing %q\n%s", want, out)
}
Expand Down
3 changes: 3 additions & 0 deletions labs/15-pitot/pitot/conformance/fixtures/negative.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
{"name":"claude-null-tool-input","host":"claude","mode":"omit","input":{"hook_event_name":"PreToolUse","tool_name":"Bash"},"reason":"empty-command"}
{"name":"mismatched-event-name","host":"claude","mode":"omit","input":{"hook_event_name":"PostToolUse","tool_name":"Bash","tool_input":{"command":"ls"}},"reason":"malformed-event"}
{"name":"unsupported-host","host":"aider","mode":"omit","input":{"hook_event_name":"PreToolUse"},"reason":"unsupported-host"}
{"name":"cline-mismatched-event","host":"cline","mode":"omit","input":{"hookName":"PostToolUse","preToolUse":{"tool":"execute_command","parameters":{"command":"ls"}}},"reason":"malformed-event"}
{"name":"cline-non-shell-tool","host":"cline","mode":"omit","input":{"hookName":"PreToolUse","preToolUse":{"tool":"read_file","parameters":{"command":"ls"}}},"reason":"empty-command"}
{"name":"qwen-non-shell-tool","host":"qwen","mode":"omit","input":{"hook_event_name":"PreToolUse","tool_name":"ReadFile","tool_input":{"command":"ls"}},"reason":"empty-command"}
4 changes: 4 additions & 0 deletions labs/15-pitot/pitot/conformance/fixtures/positive.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@
{"name":"claude-pre-tool","host":"claude","mode":"sha256","input":{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}},"expect_kind":"shell"}
{"name":"codex-pre-tool","host":"codex","mode":"full","input":{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls"}},"expect_kind":"shell"}
{"name":"claude-missing-event-name-tolerated","host":"claude","mode":"omit","input":{"tool_name":"Bash","tool_input":{"command":"pwd"}},"expect_kind":"shell"}
{"name":"copilot-pre-tool","host":"copilot","mode":"sha256","input":{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}},"expect_kind":"shell"}
{"name":"qwen-pre-tool","host":"qwen","mode":"sha256","input":{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}},"expect_kind":"shell"}
{"name":"pi-tool-call","host":"pi","mode":"sha256","input":{"hook_event_name":"tool_call","tool_name":"bash","tool_input":{"command":"git status --short"}},"expect_kind":"shell"}
{"name":"cline-pre-tool","host":"cline","mode":"sha256","input":{"hookName":"PreToolUse","preToolUse":{"tool":"execute_command","parameters":{"command":"git status --short"}}},"expect_kind":"shell"}
2 changes: 1 addition & 1 deletion labs/15-pitot/pitot/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
//
// schema/ public event and response types + versioned constants
// protocol/ newline-delimited JSON framing and state-machine helpers
// adapters/ Claude Code, Cursor, Codex, Gemini CLI, Kimi Code, and OpenCode host boundaries
// adapters/ supervised coding-agent host boundaries and payload normalization
// sensor/ normalization and observation pipeline (decoder)
// bridge/ controller routing and single-response transport
// projection/ full, sha256, and omit content policies
Expand Down
16 changes: 5 additions & 11 deletions labs/15-pitot/pitot/e2e/e2e_hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,17 @@ func TestE2ESensorsConformityAcrossAllAdapters(t *testing.T) {
h.Write([]byte(cmdToVerify))
expectedHash := fmt.Sprintf("%x", h.Sum(nil))

rawHostPayloads := map[adapters.Host]string{
adapters.Claude: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
adapters.Cursor: `{"hook_event_name":"beforeShellExecution","command":"git status --short"}`,
adapters.Codex: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
adapters.Gemini: `{"hook_event_name":"BeforeTool","tool_name":"run_shell_command","tool_input":{"command":"git status --short"}}`,
adapters.Kimi: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
adapters.Opencode: `{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status --short"}}`,
}

hosts := adapters.Supported()
for _, host := range hosts {
payload := rawHostPayloads[host]
payload, err := adapters.CanonicalHookEvent(host)
if err != nil {
t.Fatalf("canonical payload for %s: %v", host, err)
}

for _, mode := range []projection.Mode{projection.Full, projection.SHA256} {
testName := fmt.Sprintf("%s/%s", host, mode)
t.Run(testName, func(t *testing.T) {
event, err := sensor.Decode(host, []byte(payload), mode)
event, err := sensor.Decode(host, payload, mode)
if err != nil {
t.Fatalf("sensor decoding failed: %v", err)
}
Expand Down
1 change: 1 addition & 0 deletions labs/15-pitot/pitot/sensor/decode_fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func FuzzDecode(f *testing.F) {
[]byte(`{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls"}}`),
[]byte(`{"hook_event_name":"PreToolUse","tool_input":null}`),
[]byte(`{"hook_event_name":123}`),
[]byte(`{"hookName":"PreToolUse","preToolUse":{"tool":"execute_command","parameters":{"command":"ls"}}}`),
[]byte(`[]`),
}
for _, seed := range seeds {
Expand Down
Loading
Loading