← Community
bugopen

`assert_local_tree_matches_head` in verify_module_tree.py is dead code — signer can silently include stray files with no safety net

Muhammad Akif JanjuaMuhammad Akif Janjua#41d ago · 6 views
affected: station-v1.5.8

Reproduction steps:

  1. Open tools/verify_module_tree.py (a standalone script pattern used by

module publishers; the same shape likely exists in other RailCall-adjacent
tooling built from the same template).

  1. Note signed_tree(root) walks the raw filesystem via os.walk, filtered

only by .moduleignore patterns — it does not consult .gitignore or any
git-tracked-files list.

  1. Note assert_local_tree_matches_head(root) is defined immediately below

it: it computes signed_tree(root), compares the resulting file set
against committed_module_paths(root, ...) (the actual git-committed
tree), and calls fail(...) if they differ — clearly the intended safety
check for exactly the scenario in step 4.

  1. Search the file (and main() specifically) for any call site of

assert_local_tree_matches_head: there is none. It is dead code.

  1. Confirmed directly: create any extra file or directory inside the

module's project root that isn't excluded by .moduleignore (in our case,
an uncommitted local dev-build folder placed inside the repo). Run the
local signer — it happily includes the stray files in the signed tree and
produces a "valid" signature over a tree that no longer matches what's
actually committed to git. main()'s ordinary verify output still prints a
clean PASS.

Expected: A local module signer/verifier should refuse (or at minimum
loudly warn) when the tree it's about to sign or has just verified doesn't
match the actual committed source — that's the entire point of a function
named assert_local_tree_matches_head sitting right next to the code it
should be guarding.

Actual: The check exists, is well-named, and does exactly the right thing
when called — but is never invoked anywhere in the file. main()'s verify
path checks internal self-consistency only (does this signature match this
exact file set?), not correctness (does this file set match what's supposed
to be signed?). A signer can walk a working directory containing files that
were never committed, sign over all of them, and get a clean "PASS" with no
indication anything is wrong.

Root cause: The function was written (with a clear, specific purpose baked
into its name and logic) but the wiring to call it from main() — or from
the signing script before it writes module.sig — was never added. This
reads as an incomplete implementation rather than a deliberate design
choice: there's no reason to write a function that compares local tree to
committed tree and then never use the result.

Suggested fix: Call assert_local_tree_matches_head(root) from main()'s
verify path (and/or from the signing step, before a new signature is
written) so a mismatch between the working directory and the committed git
tree fails loudly instead of silently producing a technically-self-
consistent but practically-wrong signature.

Impact: Found via a real near-miss, not a hypothetical: a local dev-build
folder (holding an unlicensed test copy of a now-paid module) was briefly
created inside the actual project repo. Re-signing at that point pulled in
49 files instead of the correct 23 — the stray folder's contents got
silently folded into the signed manifest, and verify_module_tree.py reported
a clean pass throughout. The only reason it didn't reach a commit or a
publish was that the file count was checked manually against a known-good
baseline (23 files) out of habit, not because any tooling caught it. Any
publisher relying on this script's "PASS" as their correctness signal —
which is exactly what its own output invites — would have no warning that
their signed tree includes untracked, possibly sensitive or incorrect, local
files.

0 replies

Sign in to reply.