Repository navigation
Release 1.9.1 - #159
Merged
Merged
Release 1.9.1#159
Conversation
…n stream - Enforce MAX_LIMIT = 1000 in stream() to prevent silent page truncation. - Strip both 'limit' and 'Limit' keys in _normalize_stream_filters. - Add unit tests for max limit rejection, filter casing normalization, and empty collections.
…l lifecycle - Purge pre-existing 'open' webhooks prior to creation to prevent MJ18 collision errors. - Move misplaced DoesNotExistError and ValidationError imports to top of module.
- Add comparison of manual GET pagination vs lazy .stream() to README. - Add stream_all_contacts example in samples/contacts_sample.py. - Add stream pagination and offset resumption verification steps in smoke_readme_runner.py.
- Change ClusterFuzzLite batch schedule to monthly. - Add descriptive comment to security workflow schedule. - Bump commitizen, semgrep, typos, check-jsonschema, ruff, and mypy hook revisions.
- Enforce MAX_TAG_COUNT = 10000 in SecurityGuard to mitigate CWE-400 resource exhaustion. - Override handle_startendtag, handle_endtag, and handle_comment in _SpamGuardParser to count all tokens. - Synchronize is_safe and has_script threat flags upon detecting forbidden tags or event handlers. - Support parameterless _SpamGuardParser instantiation to maintain mock parser test compatibility.
- Replace 50% sub-5MB string multiplication with deterministic 1% >5MB boundary synthesis. - Cap base chaotic HTML generation to 4096 characters to maintain high execution throughput. - Eliminate slow-unit timeouts and excessive RSS memory peaks during Atheris runs.
skupriienko
marked this pull request as ready for review
October 7, 2026 20:21
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.
Summary
This PR finalizes the release of v1.9.1 (October 7, 2026), incorporating the auto-pagination fix from PR #158 alongside key reliability, security, and supply-chain enhancements:
Limit=1000) inEndpoint.stream(), normalizes query parameter filter keys (limitvsLimit), and addresses premature pagination termination caused by page-localTotalmetadata.MAX_TAG_COUNT) and explicitMAX_HTML_SIZE(5MB) enforcement in_SpamGuardParserto defend against HTML tag-bomb denial-of-service vectors (CWE-400).MJ18errors) via pre-test cleanup intest_live_eventcallbackurl_crud_lifecycle.Actions
Core SDK & Endpoint Pagination (
mailjet_rest/endpoint.py):Endpoint.stream()caused by page-localTotalmetadata (Total == len(Data)), continuing across full pages untillen(data) < chunk_size.MAX_LIMIT: int = 1000) inEndpoint.stream()with an explicitValueErrorguard to prevent silent page truncation caused by upstream clamping._normalize_stream_filters()to strip both"limit"and"Limit"keys from input filters, ensuring caller filters do not conflict withchunk_size.Endpoint.stream()docstrings to document the 1,000-record maximum chunk limit.Security & Guardrails (
mailjet_rest/utils/guardrails.py,SECURITY.md):SecurityGuard.MAX_TAG_COUNT = 10000and implemented_count_tag()across start, end, self-closing, and comment tag handlers in_SpamGuardParserto trigger immediateValidationErroron nested tag flood attacks.SecurityGuard.MAX_HTML_SIZE = 5 * 1024 * 1024(5MB) and safeguarded parser closing lifecycles.SECURITY.mdsupported versions table to designate versions< 1.9.1as end-of-life.Tests, Fuzzing & Reliability (
tests/):test_stream_with_page_local_totalverifying multi-page streaming across varied offsets (Offset: 0,Offset: 10) and collection sizes (size: 3,size: 4).test_stream_chunk_size_max_limit_rejectionto verifyValueErrortriggers whenchunk_size > 1000.test_stream_filter_strips_both_limit_casingsto ensure filter normalization cleanly strips both casing variants.test_stream_empty_collection_exits_immediatelyto verify a clean 1-request exit on empty collections.test_live_eventcallbackurl_crud_lifecyclefailure by adding idempotent pre-test cleanup for existing"open"webhooks, preventingMJ18duplicate resource constraint errors (400 Bad Request).DoesNotExistError,ValidationError) intests/integration/test_client.py.tests/fuzz/fuzz_spamguard.pyharness performance (bounded baseline buffer to 4,096 chars, deterministic >5MB memory exhaustion checks) and pruned duplicate entries fromtests/fuzz/fuzzer.dict.Documentation & Developer Experience (DX):
README.mdwith explicit side-by-side examples comparing manual single-page queries (.get(filters={"Limit": ..., "Offset": ...})) against lazy auto-pagination (.stream()).urllib.parse.parse_qs).stream_all_contacts()tosamples/contacts_sample.py..stream(), offset resumption, and multidict filters tosamples/smoke_readme_runner.py.CI/CD, Workflows & Dependencies:
.github/workflows/cflite_batch.ymlfrom every 6 hours to monthly (0 0 1 * *)..github/workflows/security.yml.commitizen(v4.19.1),semgrep(v1.179.0),typos(v1.50.3),check-jsonschema(0.38.2),slotscheck(requests>=2.33.0),ruff(v0.16.10), andmypy(v2.4.0).github/codeql-action/upload-sariffrom4.37.9to4.38.0(build(deps): bump github/codeql-action/upload-sarif from 4.37.9 to 4.38.0 #151) and to4.38.1(build(deps): bump github/codeql-action/upload-sarif from 4.38.0 to 4.38.1 #156).github/codeql-action/analyzefrom4.37.9to4.38.0(build(deps): bump github/codeql-action/analyze from 4.37.9 to 4.38.0 #152) and to4.38.1(build(deps): bump github/codeql-action/analyze from 4.38.0 to 4.38.1 #157).google/osv-scanner-action/osv-scanner-actionfrom2.5.1to2.6.0(build(deps): bump google/osv-scanner-action/osv-scanner-action from 2.5.1 to 2.6.0 #153).codecov/codecov-actionfrom7.0.0to7.1.1(build(deps): bump codecov/codecov-action from 7.0.0 to 7.1.1 #154).conda-incubator/setup-minicondafrom4.0.1to4.1.0(build(deps): bump conda-incubator/setup-miniconda from 4.0.1 to 4.1.0 #155).Release
Update files:
CHANGELOG.md(set release date to October 8, 2026, include merged PRs build(deps): bump github/codeql-action/upload-sarif from 4.37.9 to 4.38.0 #151–build(deps): bump github/codeql-action/analyze from 4.38.1 to 4.38.2 #161)1.9.1inmailjet_rest/_version.pyBuild & Publish:
python -m build)twine check dist/*)conda build conda.recipev1.9.1and publish release to PyPIVerification & Testing
To verify these changes locally, ensure your environment variables (
MJ_APIKEY_PUBLICandMJ_APIKEY_PRIVATE) are set, then execute:Validates core routing, pagination boundary math, SpamGuard DoS protections, and live CRUD lifecycles.
Executes mutation coverage across core handlers, parsers, HTML filters, and differential serializers.
Runs the unified documentation scripts demonstrating fluent builders, attachment chunking, and pagination streaming.
Notes