
Home lab tools cover a pretty wide range. On one end you’ve got low-level firmware flashers talking directly to Samsung eMMC partitions over USB; on the other end you’ve got web dashboards showing DNS query stats in a browser. This batch of contributions touches both ends — and a few things in between: documentation that was missing, a security policy that needed to exist, a firmware safety check that didn’t, and a regex flag that was silently crashing Pi-hole’s dashboard in every WebKit browser.
Heimdall
Heimdall is an open-source cross-platform tool for flashing firmware to Samsung Android devices — the open alternative to Samsung’s own Odin utility. It communicates with Samsung’s proprietary Odin protocol over USB and writes directly to eMMC partitions defined in a Partition Information Table (PIT) file. Three PRs this round, all different in nature.
PR #553 — Document the Linux udev Rules That Were Already There
The issue: Linux users building Heimdall from source were hitting a Permission denied libusb error when running as a non-root user (issue #530). The standard workaround is a udev rules file that grants non-root USB access — and Heimdall actually ships one already, at Linux/60-heimdall.rules. It just wasn’t documented anywhere, so users either ran as root or went searching forums for an answer.
The fix: Added a new Appendix C to Linux/README with step-by-step instructions: copy the rules file to /etc/udev/rules.d/, reload udev with udevadm control --reload-rules && udevadm trigger, replug the device, and optionally add the user to the plugdev group. Also added a callout to Appendix B warning that sudo make install does not install the udev rules automatically. Updated README.md to point Linux users to Appendix C when they see the permission error.
No code changed — the fix was already in the repo. It just needed to be visible.
PR #554 — A SECURITY.md That’s Actually Accurate
The issue: Issue #549 requested a SECURITY.md for the project. An existing open PR had already attempted this, but it used the unmodified GitHub template — placeholder version numbers like “5.1.x” and “4.0.x” that don’t correspond to any real Heimdall release. Shipping boilerplate with incorrect version history is arguably worse than shipping nothing.
The fix: Wrote a SECURITY.md from scratch with accurate content. The supported versions table reflects Heimdall’s actual release history: only 1.4.x is actively maintained; all prior versions are unsupported. The reporting section gives researchers a clear path: email Benjamin Dobell privately with the subject [Heimdall] Security Vulnerability, include reproduction steps and impact, expect acknowledgement within a few days and a targeted fix in the next release. Credit in release notes by default, anonymity available on request.
Security policies only work if they’re accurate. A template with wrong version numbers signals that nobody’s actually read it — which isn’t a great look for a tool that writes directly to device firmware.
PR #555 — Warn or Abort When a Flash File Exceeds Partition Capacity
The issue: Heimdall previously allowed flashing a file larger than its target partition with no warning whatsoever (issue #494). Depending on the device, this either silently overwrites adjacent partitions or produces an opaque bootloader error with no indication of what went wrong. Either outcome is bad — one is a brick waiting to happen, the other leaves the user completely in the dark.
The root cause: setupPartitionFlashInfo in FlashAction.cpp mapped each file to its PIT entry but never validated file size against the partition’s block count.
The complication: Samsung devices use either 512-byte or 4096-byte eMMC blocks depending on device age, and the PIT’s blockSizeOrOffset field is ambiguous — it can mean either a byte offset or a block count depending on the Loke protocol version. That means you can’t always know definitively whether a file fits. A file that overflows a 512-byte-block partition might fit fine on a 4096-byte-block partition.
The fix: A two-tier bracket approach — hard abort for definite overflows, warning-and-continue for the ambiguous middle range:
uint64_t minCapacity = (uint64_t)blockCount * 512;uint64_t maxCapacity = (uint64_t)blockCount * 4096;if (fileSize > maxCapacity){ Interface::PrintError("File for partition \"%s\" is too large..."); return (false); // Hard abort — definitely overflows on any block size}else if (fileSize > minCapacity){ Interface::Print("WARNING: File for partition \"%s\" may exceed capacity..."); // Continue — may be fine on 4096-byte block devices}
The logic breaks down into four clean cases:
| Condition | Action |
|---|---|
| fileSize > blockCount × 4096 | Abort — definitely too large for any Samsung block size |
| blockCount × 512 < fileSize ≤ blockCount × 4096 | Warn and continue — may fit on 4096-byte devices |
| fileSize ≤ blockCount × 512 | Silent — definitely fits on any block size |
| blockCount == 0 | Skip — dynamically-sized partition, no validation possible |
What I learned: When you can’t know the exact answer — in this case the true block size — you can often still establish bounds. The lower bound (512-byte blocks) gives you the minimum safe capacity; the upper bound (4096-byte blocks) gives you the maximum possible capacity. Anything outside that range has a definitive answer; the middle is where you warn instead of guess. This bracket pattern for ambiguous hardware parameters turns out to be cleaner than trying to detect the actual block size at runtime.
Pi-hole Web
PR #3778 — Replace ES2024 Regex v Flag with u for WebKit Compatibility
The project: Pi-hole’s web interface is the admin dashboard for Pi-hole, the popular network-wide ad blocker. It shows real-time DNS query stats, charts, blocklist management, and client activity — the thing you check when something on your network isn’t loading and you’re not sure if Pi-hole is the reason.
The issue: On Safari and other WebKit-based browsers (DuckDuckGo on macOS, anything on iOS), the dashboard was completely broken — all stats showed ---, all charts spun indefinitely, nothing loaded (issue #3757). The cause: multiple JavaScript files used the ES2024 Unicode Sets regex v flag, which WebKit hadn’t implemented yet. When any affected file loaded, WebKit threw:
SyntaxError: Invalid regular expression: invalid flags
That error terminates the entire script, not just the regex that caused it — so the dashboard failed silently across the board from the user’s perspective.
The fix: Swap every v flag for u across 13 files. The v flag’s exclusive capabilities — Unicode set notation like [A--B] or [A&&B] — weren’t being used in any of these patterns, so u is functionally identical in every case. Behavior is unchanged in Chrome and Firefox; WebKit browsers now load the dashboard correctly.
// utils.js (8 replacements like this throughout the file)-/[^+/\w]/v+/[^+/\w]/u// gravity.js-/\r?\n/v+/\r?\n/u
A linter rule was also added to xo.config.js to enforce u over v going forward, so the same regression can’t sneak back in:
// xo.config.js'prefer-u-over-v-for-regex': 'error'
What I learned: Bleeding-edge JS features can be surprisingly easy to miss in code review — a flag character on a regex is a small thing, and the resulting error in unsupported browsers is cryptic enough that users just see a broken dashboard, not a helpful message. The linter rule is the more important part of this PR: the fix itself is mechanical, but without the lint enforcement, the same pattern would probably drift back in within a few months. Always close the door behind you.
Wrap Up
Four PRs, four different problems: documentation that existed but wasn’t written down, a security policy that needed to be accurate, a firmware safety check that could prevent a bricked device, and a regex flag that was crashing a dashboard in every WebKit browser. Home lab contributions don’t have a single shape — sometimes it’s a C++ validation check with a decision table, sometimes it’s swapping one letter across 13 files. All of it is worth doing.

