fix: guardrail webhook sends invalid JSON on large payloads; after_tool skipped for complete_task#527
Open
prince-shakyaa wants to merge 3 commits into
Conversation
…ped for complete_task - Replace raw byte-slice truncation in GuardrailHookService.invoke() with field-level truncation: cap model_output, tool_result, user_message strings before serialisation so the body always remains valid JSON. - Add X-Guardrail-Truncated: true and X-Guardrail-Full-Size: <N> headers when truncation occurs so the webhook receiver can detect it explicitly. - In _run_agent_loop(), invoke the after_tool guardrail hook for complete_task before the early return, ensuring every before_tool has a matching after_tool. Fixes: guardrail HMAC passes on broken JSON body; after_tool never fires for complete_task tool calls.
Signed-off-by: Prince Shakya <psprince2005@gmail.com>
…hook pairing - GWT-TRN-001: large payload truncated at field level, body remains valid JSON - GWT-TRN-002: normal-sized payload is sent unchanged, no truncation headers - GWT-TRN-003: X-Guardrail-Truncated / X-Guardrail-Full-Size headers set on truncation - GWT-TRN-004: HMAC signature is computed over the valid (post-truncation) body - GWT-ATL-001: after_tool guardrail hook fires for complete_task before early return
Author
|
Hii @saikishu @e2hln , Happy to make any changes based on your feedback. Thank You. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix: guardrail webhook sends invalid JSON on large payloads;
after_toolskipped forcomplete_taskSummary
Fixes #525
This PR fixes two bugs in the FinBot Labs guardrail webhook system:
Payload truncated mid-JSON before signing : large guardrail hook payloads were being sliced at a raw byte offset, producing invalid JSON that the receiver could not parse, while the HMAC signature continued to validate correctly. The receiver had no way to detect this corruption.
after_toolhook never fires forcomplete_task: the agent loop returned immediately aftercomplete_tasksucceeded, bypassing the correspondingafter_toolinvocation and leaving everycomplete_taskcall with an unmatchedbefore_toolevent.Problem 1 : Payload truncated mid-JSON
Root Cause
In
finbot/guardrails/service.py, theinvoke()method serializes the fullHookEnvelopeto JSON bytes and then slices them atLABS_GUARDRAIL_MAX_PAYLOAD_BYTES:A raw byte slice cuts the JSON in the middle of a field value. The result is syntactically invalid JSON (e.g.
..."model_output": "here is the ana), but because the HMAC is computed over the same truncated bytes, the signature check on the receiver side passes - the corruption is completely invisible to the receiver.Fix
Truncation is moved before serialization, at the field level:
model_dump_json(), we compute a tentative payload size.max_payload, we cap the long string fields (model_output,tool_result,user_message) inside the envelope so that re-serialization fits within the limit.X-Guardrail-Truncated: trueX-Guardrail-Full-Size: <original_byte_count>This allows the receiver to detect truncation explicitly rather than hitting a
JSONDecodeError.Files Changed
finbot/guardrails/service.pyX-Guardrail-TruncatedandX-Guardrail-Full-SizeheadersProblem 2 :
after_toolskipped forcomplete_taskRoot Cause
In
finbot/agents/base.py, lines 152–189, thebefore_toolguardrail hook fires for every tool call, includingcomplete_task. However, whencomplete_tasksucceeds, the agent loop returns immediately on line 174, before theafter_toolinvocation on line 183 is reached:Any guardrail that tracks paired
before_tool/after_toolevents (e.g. to measure tool execution time or audit a tool's output) will see everycomplete_taskappear open-ended.Fix
The
after_toolinvocation is moved to execute before the early return forcomplete_task:Files Changed
finbot/agents/base.pyafter_toolguardrail hook before the early return forcomplete_taskBehaviour After This PR
after_modelpayload exceeds limitX-Guardrail-Truncated: trueheader presentafter_toolpayload exceeds limitcomplete_tasktool callbefore_toolfires,after_toolnever firesbefore_toolandafter_toolfireTests
Five new unit tests are added in
tests/unit/labs/test_guardrail_truncation_and_hooks.py, following the existing patterns intest_guardrail_service.py:GWT-TRN-001GWT-TRN-002GWT-TRN-003X-Guardrail-Truncated: trueandX-Guardrail-Full-Size: <N>headers set when truncatedGWT-TRN-004GWT-ATL-001after_toolfires forcomplete_taskbefore the agent loop exits : no unpaired hookAll 5 tests pass locally.
Affected Files
finbot/guardrails/service.pyfinbot/agents/base.pyafter_toolhook forcomplete_taskbefore early returntests/unit/labs/test_guardrail_truncation_and_hooks.py