[0.34.0] - 2026-07-16
Security
- Unauthenticated reflected SSRF in the REST API (CWE-918) — the
POST /humanizehandler forwarded the client-controlledoss_api_url
straight into the OSS provider'surllib.request.urlopen(), letting an
unauthenticated caller make the server fetch arbitrary internal URLs
(loopback services,169.254.169.254cloud metadata, private ranges) and
reflect the response body back in thetextfield. Reported responsibly by
Natnael Wodsnoen. Fixed with defence in depth:- Remote AI backends are now disabled by default in the REST API.
Requests usingbackend != "local"or supplyingoss_api_url/
openai_api_key/ollama_urlare rejected with HTTP403unless the
operator setsTEXTHUMANIZE_API_ALLOW_REMOTE_BACKENDS=1. - New SSRF guard
texthumanize.validate_outbound_url()(and
UnsafeURLError) — resolves the host and rejects loopback, private,
link-local (cloud-metadata), reserved, multicast and unspecified targets,
non-http(s) schemes, and embedded credentials. Applied to any user-supplied
URL at the API boundary (HTTP400on rejection).
- Remote AI backends are now disabled by default in the REST API.
- REST API binds to
127.0.0.1by default (was0.0.0.0). Exposing the
unauthenticated API on all interfaces now requires an explicit
--host 0.0.0.0and prints a warning. SeeSECURITY.md→ "REST API
hardening". - Bounded outbound responses — all AI-backend HTTP calls now go through a
singlesafe_urlopen()choke point that re-validates the URL and caps the
response body (10 MB), so a hostile or oversized upstream reply can't be
reflected wholesale or exhaust memory. - Configurable CORS —
TEXTHUMANIZE_API_CORS_ORIGINoverrides the default
*so browser access to the API can be locked to a specific origin.
Added
- Security scanning in CI —
bandit(SAST) andpip-audit(dependency
advisories) run on every push/PR; new CodeQL workflow (Python + JS/TS,
security-extended) and Dependabot config (pip, npm, composer, actions). - Coverage gate — a dedicated CI job runs the suite under coverage with a
regression floor (--cov-fail-under). scripts/bump_version.py— writes every version literal that
check_version_sync.pyvalidates (manifests, ports, version-assertion tests,
CHANGELOG heading, README) in one command, removing manual release drift.
Changed
- REST API uses
ThreadingHTTPServerso concurrent clients and the SSE
endpoint no longer block each other. Documented as dev/self-hosted; front it
with a reverse proxy for production. - ReDoS timing tests scale their wall-clock budget under instrumentation
(coverage/debugger) so the coverage gate is reliable while still catching
catastrophic (exponential) backtracking.