Skip to content

String field binary format accepts non-base64 values (b64decode called without validate=True) #1804

Description

@glaziermag

Overview

A string field with format: binary accepts values that are not base64, so validate() reports them as valid. Whether a non-base64 value is accepted depends only on how many base64-alphabet characters it happens to contain, which I don't think is the intended behavior for a Table Schema implementation.

docs/fields/string.md links the Table Schema standard for the binary format, which describes it as "A base64 encoded string representing binary data" (https://specs.frictionlessdata.io/table-schema/#string). The existing parametrized test in frictionless/fields/__spec__/test_string.py:24-27 also encodes the intent that non-base64 input is rejected:

("binary", "dGVzdA==", "dGVzdA=="),
("binary", "", None),
("binary", "string", None),   # non-base64 -> None (error)
("binary", 0, None),

The "string" case passes, but only by accident: it has 6 base64-alphabet characters, and 6 is not a multiple of 4, so it trips a padding error. A junk value whose alphabet-character count is a multiple of 4 is accepted.

Reproduction

pip install frictionless==5.19.0
from frictionless import Schema, Resource, fields

f = fields.StringField(name="blob", format="binary")

# read_cell returns (value, notes); notes is None when the cell is accepted.
print(f.read_cell("!!!!"))     # ('!!!!', None)      -- accepted, not base64
print(f.read_cell("@@@@"))     # ('@@@@', None)      -- accepted, not base64
print(f.read_cell("()()"))     # ('()()', None)      -- accepted, not base64
print(f.read_cell("string"))   # (None, {'type': 'type is "string/binary"'})  -- rejected

# Whole-resource validation of non-base64 data passes:
schema = Schema(fields=[fields.StringField(name="blob", format="binary")])
report = Resource(data=[["blob"], ["!!!!"], ["@@@@"], ["()()"]], schema=schema).validate()
print(report.valid)            # True

Observed: !!!!, @@@@, ()() are accepted and report.valid is True.
Expected (per the linked spec and the test_string.py cases above): non-base64 values are rejected with a type error, the same way "string" is.

Cause

The binary value reader calls base64.b64decode without validate=True (frictionless/fields/string.py:69):

def value_reader(cell: Any):
    if not isinstance(cell, str):
        return None
    try:
        base64.b64decode(cell)
    except Exception:
        return None
    return cell

base64.b64decode defaults to validate=False, which silently discards every character outside the base64 alphabet before decoding. So acceptance depends only on whether the count of alphabet characters that survive is a multiple of 4, not on whether the input is actually base64:

import base64
base64.b64decode("!!!!")                 # b''  (all four chars discarded, no error)
base64.b64decode("!!!!", validate=True)  # raises binascii.Error: Only base64 data is allowed

Possible fix and a tradeoff

Passing validate=True makes the reader reject non-base64 input, which matches the existing test cases. One thing to weigh before doing that: validate=True also rejects otherwise-valid base64 that contains embedded whitespace/newlines (MIME-style, RFC 2045), e.g. "dGVz\ndA==", which is accepted today. There is no test covering that case, so the choice between strict RFC 4648 and lenient MIME decoding is a call for the maintainers. I'm happy to open a PR in whichever direction you prefer, with the corresponding tests.

Environment

  • frictionless 5.19.0 (also present on main at frictionless/fields/string.py:69)
  • Python 3.14, reproduces independent of platform (pure base64 behavior)

Disclosure: I found and verified this with AI assistance; the reproduction above was run and confirmed against 5.19.0 and current main.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions