Security review fixes: one fence, honest API calls, a bounded and throttled port, CI that runs #22
No reviewers
Labels
No labels
bug
documentation
enhancement
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo-mcp!22
Loading…
Reference in a new issue
No description provided.
Delete branch "sec2-fixes"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What and why, per issue
mcp>=2.2.0, fixed release of CVE-2026-59951); pyjwt 2.15.1 viauv lock --upgrade; pip-audit over the exported lock reports nothing. Streamable HTTP runsstateless_http=True, request bodies capped at 1 MiB. The app is built inserving.pyand run with uvicorn so the throttle can sit outside it.fence.pyfor answers AND errors: per-fence random code in the opening and closing lines (only the matching closer closes), NFKC/format-character/_/-/guillemet look-alikes replaced before the text goes in,whatrestricted so it cannot write a line. Short third-party fields (title,name,location,role,subject,company_name) stay readable when short and single-line and are flattened and fenced otherwise. INSTRUCTIONS updated.http://address to another host is warned about on stderr (not refused).errorsof a 422 (capped at 5), the wait of a 429 (body orRetry-After, repeated only as a number),detailof a 404. Found on the way:PostuloErrorwas a plainException, which the mcp SDK treats as a crash (model sees only "Error executing tool"), so no refusal ever reached the agent. It is now aToolError(resources get aResourceError).get_listingtool andpostulo://listings/{id}resource overGET /listings/{pk}, advert fenced; prompt and README name it. Snapshot retaken from core42be52048; fixtures built from the response schemas (listing, nodescriptiononListingOut); the fake validates fixtures against the success schema and request bodies against the request schema (unknown or missing members fail).--allowed-host nameallowsnameandname:*;name:443/name:80also allow the bare name; origins follow;--host 0.0.0.0/::is refused without an--allowed-host. README example corrected.planrefuses a non-printable-ASCII secret.Retry-After) to an address after 10 wrong secrets in 5 minutes (checked end to end against a real uvicorn).node:22-bookwormwithpersist-credentials: false,uv sync --locked, matrix 3.12/3.13/3.14, uv installer pinned by version and sha256 as in the core (#790), newauditjob (pip-audit --strictover the exported lock).Tests
73 pass (was 34) on 3.12, 3.13, 3.14 locally;
ruff checkandruff format --checkclean. Without the fix the new serving tests fail (13 failed with the oldserving.py). Hostile-text tests cover the look-alike closers, forged codes, fenced errors (HTML body, JSON body, connection error), titles and locations with line breaks.Left open
Nothing among the bug/security issues. #16 (enhancement), #11, #6, #4, #2, #1 (documentation/enhancement) are not part of this pull request.
Notes for review: the branch has two commits (CI; everything else) because the tests share one file. The throttle keys on the TCP peer address, so behind a reverse proxy it bars the proxy; the README says to limit at the proxy as well. Secrets of 16 to 21 characters that worked before are now refused at start-up (the issue asks for 128 bits).
Closes #7, #8, #9, #10, #12, #13, #14, #15, #17, #18, #19, #20, #21
🤖 Generated with Claude Code