Skip to content

Follow CPython's padding rules in a2b_base64 - #8570

Merged
youknowone merged 1 commit into
RustPython:mainfrom
luantaraschi:align-a2b-base64-padding
Aug 22, 2026
Merged

Follow CPython's padding rules in a2b_base64#8570
youknowone merged 1 commit into
RustPython:mainfrom
luantaraschi:align-a2b-base64-padding

Conversation

@luantaraschi

@luantaraschi luantaraschi commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Summary

binascii.a2b_base64 with strict_mode=True accepts padding that CPython rejects:

>>> binascii.a2b_base64(b'YWJj=', strict_mode=True)
b'abc'                 # CPython: binascii.Error: Excess padding not allowed
>>> binascii.a2b_base64(b'abcd====', strict_mode=True)
b'i\xb7\x1d'           # CPython: binascii.Error: Excess padding not allowed

The flag exists to reject malformed input, so accepting it silently defeats it.

The same loop also drops data in the default mode. It returns as soon as a pad completes a quad, so whatever follows is lost:

>>> binascii.a2b_base64(b'abc=a')
b'i\xb7'               # CPython: b'i\xb7\x1a'

Both come from the same place. CPython counts pads as it goes and decides at the end; this one decides at the first complete pad sequence and never looks at a pad while quad_pos is still 0. The loop now follows CPython: a pad that fits the current quad is consumed, any other pad is ignored outside strict mode, and strict mode names it leading, excess or discontinuous. The tail reports the two lengths no valid encoding can produce.

DecodeError::InvalidPadding was mapped to Incorrect padding, but nothing in the file ever built that variant, so it now carries Excess padding not allowed. The other messages are untouched.

Tests

Lib/test/test_binascii.py already had both cases, marked as expected failures. They run now:

  • test_base64_strict_mode, which covers all five padding errors across the four buffer types
  • test_base64_excess_data, whose marker had even recorded the symptom (AssertionError: b'i' != b'i\xb7')

What I ran:

  • cargo run --release -- -m test test_binascii: 93 tests, 17 skipped, SUCCESS
  • cargo run --release -- -m test test_base64: 41 tests, SUCCESS
  • cargo test --workspace --exclude rustpython_wasm --exclude rustpython-venvlauncher --exclude rustpython-capi: 41 test binaries, no failures
  • cargo fmt --check, and clippy with the flags CI uses: both clean

I also ran the inputs above plus the padding cases around them under CPython 3.14 and this build side by side. Every returned value agrees now. Two error strings still differ, both because of the error decoding base64: prefix that #7993 is already working through, so I left them alone.

AI assistance

Claude Code (claude-opus-5) helped with the comparison against CPython, the loop and this description. I read the final diff and ran the checks above on Linux.

Summary by CodeRabbit

  • Bug Fixes
    • Improved Base64 decoding behavior when padding is misplaced or excessive.
    • Strict decoding now provides distinct errors for leading, excess, discontinuous, and trailing data.
    • Updated final-length validation to correctly account for padding.

With strict_mode=True the decoder accepted padding CPython rejects:

    >>> binascii.a2b_base64(b'YWJj=', strict_mode=True)
    b'abc'

It also dropped data in the default mode, because it returned at the
first pad that completed a quad:

    >>> binascii.a2b_base64(b'abc=a')
    b'i\xb7'          # CPython: b'i\xb7\x1a'

The loop now counts pads and decides at the end, the way CPython does,
so a stray pad is ignored outside strict mode and named as leading,
excess or discontinuous inside it. DecodeError::InvalidPadding was dead
in this file and now carries the excess padding message.

test_base64_strict_mode and test_base64_excess_data were marked as
expected failures and pass now.

Assisted-by: Claude Code:claude-opus-5
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: b47292ae-d86a-4763-b5d7-8b06878960c4

📥 Commits

Reviewing files that changed from the base of the PR and between 3d2ee64 and 35fe452.

⛔ Files ignored due to path filters (1)
  • Lib/test/test_binascii.py is excluded by !Lib/**
📒 Files selected for processing (1)
  • crates/stdlib/src/binascii.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Base64 decoding now tracks padding per quartet. Non-strict mode ignores misplaced padding. Strict mode reports distinct leading, excess, discontinuous, and trailing data errors. Final-length validation accounts for padding, and the excess-padding message changed.

Changes

Base64 padding validation

Layer / File(s) Summary
Padding-aware decoding and errors
crates/stdlib/src/binascii.rs
The decoder tracks padding across quartets, applies mode-specific validation, rejects invalid data placement, validates incomplete final quartets, and reports excess padding with updated text.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 35fe4

The PR makes a localized base64 padding behavior correction with targeted tests and reported clean validation; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: shaharnaveh

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: aligning a2b_base64 padding behavior with CPython.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

📦 Library Dependencies

The following Lib/ modules were modified. Here are their dependencies:

[x] test: cpython/Lib/test/test_binascii.py

dependencies:

dependent tests: (95 tests)

  • binascii: test_base64 test_binascii test_codecs test_ctypes test_descr test_hashlib test_hmac test_plistlib test_struct test_zlib
    • base64: test_email test_gettext test_httpservers test_smtplib test_urllib2 test_urllib2_localnet test_xmlrpc test_zoneinfo
      • http.server: test_logging test_robotparser
      • logging.handlers: test_concurrent_futures test_pkgutil
      • secrets: test_secrets
      • smtplib: test_smtpnet
      • ssl: test_asyncio test_ftplib test_httplib test_imaplib test_poplib test_ssl test_urllib test_venv
      • urllib.request: test_http_cookiejar test_pathlib test_pydoc test_sax test_site test_urllib2net test_urllibnet
    • email: test_email test_mailbox test_zipfile
      • importlib.metadata: test_importlib
      • mailbox: test_genericalias
      • pydoc: test_enum
    • http.server:
      • wsgiref.simple_server: test_wsgiref
    • plistlib:
      • platform: test__locale test__osx_support test_asyncio test_baseexception test_builtin test_cmath test_ctypes test_fcntl test_math test_mimetypes test_os test_platform test_posix test_regrtest test_shutil test_socket test_strptime test_sysconfig test_time test_winreg
    • quopri: test_quopri
    • zipfile: test_pdb test_zipapp test_zipfile test_zipfile64 test_zipimport test_zipimport_support
      • shutil: test_argparse test_bz2 test_compileall test_ctypes test_embed test_filecmp test_glob test_importlib test_inspect test_largefile test_launcher test_modulefinder test_peg_generator test_py_compile test_reprlib test_string_literals test_subprocess test_support test_tarfile test_tempfile test_traceback test_unicode_file

Legend:

  • [+] path exists in CPython
  • [x] up-to-date, [ ] outdated

@youknowone youknowone left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@youknowone
youknowone merged commit 377c9db into RustPython:main Aug 22, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants