Skip to content

rpctest: filter tar members on extraction; document two API contracts - #409

Open
xanimo wants to merge 1 commit into
dogecoinfoundation:0.1.5-devfrom
xanimo:0.1.5-dev-api-contracts
Open

rpctest: filter tar members on extraction; document two API contracts#409
xanimo wants to merge 1 commit into
dogecoinfoundation:0.1.5-devfrom
xanimo:0.1.5-dev-api-contracts

Conversation

@xanimo

@xanimo xanimo commented Aug 6, 2026

Copy link
Copy Markdown
Member

rpctest/fetch.py extracts a downloaded archive unfiltered

tar.extractall(os.getcwd())

No member filtering, so a crafted archive writes outside the destination via ../ members, absolute paths, or symlinks pointing out of the tree.

Demonstrated, not assumed. An archive whose only member is ../escaped.txt:

UNFILTERED -> escaped file written outside dest? True
  contents: pwned

With filter='data', the same archive:

filter=data REFUSED: OutsideDestinationError
escaped file created outside dest? False

The checksum check above the extraction narrows who can supply such an archive, but it doesn't make extraction safe — those are independent concerns, and the fix costs nothing.

Note the .zip branch was never exposed: Python's zipfile.extractall already sanitises member paths. Only tar needed this.

Uses filter='data' where available. Where it isn't — PEP 706 landed in 3.12 and was backported to security releases of 3.8+ — it checks members by hand rather than silently extracting unfiltered; both the member path and any link target must resolve inside the destination.

Two contracts that were only discoverable by reading the implementation

dogecoin_generate_mnemonic's entropy_out needs MAX_ENTROPY_STRING_SIZE bytes — the type is really HEX_ENTROPY, but the signature says char*, so the requirement is invisible at the call site.

Worth being precise about severity here, because static analysis flags the strcpy as an unbounded write and it isn't one: entropy_size is validated to 128–256 bits before use, which caps the output at 64 hex characters plus a terminator. It's a documentation gap, not an overflow. I'd rather say that plainly than let it get filed as a buffer overflow.

dogecoin_cheap_random_bytes is exported under a name that invites misuse. It's seeded from the wall clock and is not cryptographic. In-tree use is appropriate — P2P nonces only — and the comment now says so, and points at dogecoin_random_bytes for anything else.


No functional change to the library; the only behaviour change is in a test helper script.

This will show red on cppcheck until #403 lands — that job fails on 0.1.5-dev itself, not on this branch.

rpctest/fetch.py downloaded a dogecoind release and ran

    tar.extractall(os.getcwd())

with no member filtering, so a crafted archive can write outside the
destination through '../' members, absolute paths, or symlinks pointing
out of the tree. Demonstrated rather than assumed: an archive whose only
member is '../escaped.txt' places that file one level above the
destination, contents intact. With filter='data' the same archive raises
OutsideDestinationError and nothing is written.

The checksum check above the extraction narrows who can supply such an
archive, but it does not make extraction safe on its own, and the two are
independent concerns. Note the .zip branch was never exposed: Python's
zipfile.extractall already sanitises member paths.

Uses filter='data' where available, and where it is not -- PEP 706 landed
in 3.12 and was backported to security releases of 3.8+ -- checks members
by hand rather than silently extracting unfiltered. Both the member path
and any link target must resolve inside the destination.

Also documents two contracts that were only discoverable by reading the
implementation:

dogecoin_generate_mnemonic's entropy_out takes a hex string and needs
MAX_ENTROPY_STRING_SIZE bytes; the type is really HEX_ENTROPY, but the
signature says char*, so the requirement is invisible at the call site.
The write itself is bounded -- entropy_size is validated to 128..256 bits
before use, capping output at 64 hex characters plus a terminator -- so
this is a documentation gap rather than an overflow, which is worth
stating plainly since static analysis flags the strcpy.

dogecoin_cheap_random_bytes is exported under a name that invites misuse.
It is seeded from the wall clock and is not cryptographic. In-tree it is
used only for P2P nonces, which is appropriate; the comment says so and
points at dogecoin_random_bytes for anything else.
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.

1 participant