Skip to content

fix(ftp): reject a status code that is not three digits (#2416) - #2419

Merged
JarryShaw merged 1 commit into
mainfrom
fix/2416-ftp-code-guard
Oct 28, 2026
Merged

JarryShaw merged 1 commit into
mainfrom
fix/2416-ftp-code-guard

Conversation

@JarryShaw

@JarryShaw JarryShaw commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner

What is the purpose of your pull request?

  • fix — corrects a defect

Description of your pull request and other information

Fixes #2416.

FTP.make's code branch ran int(code) with no guard, so 'abc' raised a bare ValueError and [220] a bare TypeError, while True wrote code 001, 3.7 wrote 003, 1000 wrote b'1000' and -5 wrote b'-05' — the %03d formatting accepted out-of-range values silently. The sibling cmmd (#2290) and args (#2410) parameters on the same method already guard their types.

A new FTP._make_code helper normalises and validates it: a bool is refused, an int (a ReturnCode member included) is taken as it is, a numeric str, bytes or bytearray is converted, and anything else — or a value outside 0–999 — raises FTP: invalid status code: must be an int in 0-999 or a numeric str, bytes or bytearray, not ....

Range: 0–999, the three digits the wire format holds and the [0-9]{3} of FTP_RESPONSE reads back — not the 1xx–5xx of RFC 959 §4.2 alone, since 6xx is RFC 2228 §5 and the read path accepts it. Narrowing further would refuse a reply read parses.

Numeric str and bytes stay valid per the annotation, which is widened to bytearray — int() already converted one.

Test: test_ftp_make_rejects_a_status_code_that_is_not_three_digits pins the exact message for True, False, 3.7, [220], memoryview(b'230'), a .value-carrying object, 'abc', b'abc', 1000, -5, '1000' and b'-5', plus the unchanged build for 230, ReturnCode.CODE_230, '230', b'230' and bytearray(b'230'), and the leading zeroes at 0 and 999.

Fail-without-fix: with the guard reverted the test fails 12/12 — four with the bare ValueError/TypeError, eight with ProtocolError not raised.

tests/protocols/ in full: 2035 passed / 51410 subtests / 0 failed. mypy reports nothing on ftp.py; pylint's findings on both touched files are unchanged in kind from main (the extracted helper keeps make off too-many-branches).

FTP.make's code branch ran int(code) with no guard, so 'abc' raised a bare
ValueError and [220] a bare TypeError, while True wrote code 001, 3.7 wrote
003, 1000 wrote b'1000' and -5 wrote b'-05' -- the %03d formatting accepted
out-of-range values silently. The sibling cmmd (#2290) and args (#2410)
parameters on the same method already guard their types.

Normalise and validate the code in a new FTP._make_code helper: a bool is
refused, an int (a ReturnCode member included) is taken as it is, a numeric
str, bytes or bytearray is converted, and anything else -- or a value outside
0-999 -- raises "FTP: invalid status code: must be an int in 0-999 or a
numeric str, bytes or bytearray, not ...". The range is the three digits the
wire format holds and the [0-9]{3} of FTP_RESPONSE reads back, not the
1xx-5xx of RFC 959 section 4.2 alone, since 6xx is RFC 2228 section 5 and
the read path accepts it. Widened the code annotation to bytearray, which
int() already converted, and extended the Args:/Raises: docstring entries.

Test: tests/protocols/application/test_ftp_unit.py gains
test_ftp_make_rejects_a_status_code_that_is_not_three_digits, pinning the
exact message for bool, float, list, memoryview, a .value-carrying object,
a non-numeric str and bytes, and 1000, -5, '1000' and b'-5', plus the
unchanged build for an int, a ReturnCode member and a numeric str, bytes and
bytearray, and the leading zeroes at 0 and 999. Confirmed fail-without-fix:
with the guard reverted the test fails 12/12 -- four with the bare
ValueError/TypeError and eight with "ProtocolError not raised".

tests/protocols/ in full: 2035 passed / 51410 subtests / 0 failed, after
regenerating examples/captures via examples/generators/make_samples.py. mypy
reports nothing on ftp.py and pylint's findings on both touched files are
unchanged in kind from main -- the extracted helper keeps make off
too-many-branches.
@JarryShaw JarryShaw added the bug Issues reporting a defect (set by the bug report template; a default, not an assessment) label Oct 28, 2026
@github-actions github-actions Bot added this to the PyPCAPKit v1.5 milestone Oct 28, 2026
@JarryShaw JarryShaw moved this to In review in PyPCAPKit Oct 28, 2026
@JarryShaw JarryShaw added the review: running A cross-review is in flight against the current head - no verdict yet label Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage: 91.73% (unit tier, Python 3.15, 9cb4d80b7, Unit Tests run success)

Package Statements Missed Branches Partial Cover
pcapkit (top level) 104 4 20 2 95.16%
pcapkit/const 19609 879 2620 866 92.13%
pcapkit/corekit 2596 74 818 37 96.51%
pcapkit/dumpkit 282 3 102 3 98.44%
pcapkit/foundation 3045 24 1062 26 98.78%
pcapkit/interface 138 4 52 3 96.32%
pcapkit/protocols 17592 193 4678 179 98.32%
pcapkit/toolkit 924 36 334 10 95.55%
pcapkit/utilities 502 7 144 5 98.14%
pcapkit/vendor 4449 1961 1020 167 52.64%

Per-file detail: the coverage-html artifact of this run.

@JarryShaw

Copy link
Copy Markdown
Owner Author

Review: GOOD TO GO at 9cb4d80b (Sonnet). bool checked before int in the guard, confirmed correctly orders. Diffed ~160 cases against main: all 54 ReturnCode members and valid inputs byte-identical, all invalid inputs now raise ProtocolError with the claimed message (were bare ValueError/TypeError/OverflowError or silent acceptance on main). Range 0-999 independently confirmed correct — round-tripped all 1000 codes through read→make (3000/3000), and an existing test (test_ftp_http_unassigned_code_1443_unit.py) already requires the full 000-999 range to rebuild byte-for-byte, so a narrower range would've broken it. Fail-without-fix confirmed (12/12). Test delta exactly matches (2035 vs 2034 passed). mypy/pylint clean. New test gives 100% line/branch coverage of the new helper. Non-blocking nit: the message says 'numeric str' for an out-of-range numeric string like '1000', which is slightly misleading but not wrong.

@JarryShaw JarryShaw added review: good-to-go Cross-review at the current head says ready; CI state is separate and removed review: running A cross-review is in flight against the current head - no verdict yet labels Oct 28, 2026
@JarryShaw

Copy link
Copy Markdown
Owner Author

We now accept bool values for integer args?

@JarryShaw

Copy link
Copy Markdown
Owner Author

No — bool is explicitly rejected. The new _make_code helper checks isinstance(code, bool) first and falls through to the rejection branch (bool is an int subclass, so without that explicit check it would've taken the int branch and silently written code 001/000). Confirmed in the cross-review by running code=True/code=False against the fix — both raise ProtocolError. The Raises: docstring's "(a bool included)" phrasing lists it among what's rejected, matching the existing cmmd/args convention. The type annotation widened to add bytearray only — bool was never in it and still isn't accepted at runtime.

@JarryShaw
JarryShaw merged commit 249bccc into main Oct 28, 2026
67 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in PyPCAPKit Oct 28, 2026
@JarryShaw
JarryShaw deleted the fix/2416-ftp-code-guard branch October 28, 2026 19:47
@JarryShaw JarryShaw removed the review: good-to-go Cross-review at the current head says ready; CI state is separate label Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Issues reporting a defect (set by the bug report template; a default, not an assessment)

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

fix(application): FTP.make's code argument accepts bool/float and rejects bad values with bare ValueError/TypeError

1 participant