Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added __pycache__/verify_nzb.cpython-312.pyc
Binary file not shown.
Binary file added tests/__pycache__/test_verify_nzb.cpython-312.pyc
Binary file not shown.
40 changes: 28 additions & 12 deletions verify_nzb.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,19 +115,35 @@ def _parse_yenc_attrs(line: bytes) -> dict[str, str]:
return attrs


_YENC_DECODE_TABLE = bytes((i - 42) % 256 for i in range(256))

def _decode_yenc_lines(lines: Iterable[bytes]) -> bytes:
decoded = bytearray()
for line in lines:
index = 0
while index < len(line):
byte = line[index]
if byte == 61:
index += 1
if index >= len(line):
raise ValueError("dangling yEnc escape")
byte = (line[index] - 64) % 256
decoded.append((byte - 42) % 256)
index += 1
# Joining all lines up-front to leverage C-level split and translate
data = b"".join(lines)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Detect dangling escape characters at line boundaries

_decode_yenc_lines now concatenates all input lines before decoding, so an escape byte (=) at the end of one line is treated as escaping the first byte of the next line instead of raising ValueError("dangling yEnc escape") as before. In yEnc, line breaks are significant for this condition (a line must not end with a bare escape), so malformed bodies that should be rejected can now be accepted if the following line starts with decodable data, weakening validation correctness.

Useful? React with πŸ‘Β / πŸ‘Ž.

if b"=" not in data:
return data.translate(_YENC_DECODE_TABLE)

parts = data.split(b"=")
decoded = bytearray(parts[0].translate(_YENC_DECODE_TABLE))

i = 1
length = len(parts)
while i < length:
part = parts[i]
if not part:
if i == length - 1:
raise ValueError("dangling yEnc escape")
# If part is empty, it means we encountered b"=="
decoded.append(211) # (61 - 106) % 256 = 211
i += 1
part = parts[i]
if part:
decoded.extend(part.translate(_YENC_DECODE_TABLE))
else:
decoded.append((part[0] - 106) % 256) # -64 - 42 = -106
if len(part) > 1:
decoded.extend(part[1:].translate(_YENC_DECODE_TABLE))
i += 1
return bytes(decoded)


Expand Down