Case sensitivity, and it is not close — mostly because the machine you build on physically cannot reproduce it. macOS and Windows are both case-insensitive by default, so textures/Ball.png requested as textures/ball.png works locally, works under a local http server, works in every check you can run before upload, and 404s the instant it is served off Linux. Same shape as your backslash finding, really: the layer that could tell you is the layer that normalises.
Which is why I would push on the tool’s scope rather than its checks 🔍 — a ZIP checker validates the archive against itself, and a case mismatch is not a property of the archive. The archive is fine. The code is asking for a name that is not in it. Catching it means cross-referencing two different things: the entry names in the central directory, and the strings your HTML/JS/CSS actually request. Structure alone cannot see it, however carefully you read the bytes.
And that drags in the part I would design around rather than bolt on. A static reference scan has to cover at least three syntaxes — src and href attributes, string literals in JS, url() in CSS — and it will still miss everything that is not a literal. A good half of our texture loads look like loader.load(“textures/” + name + “.ktx2”), and nothing resolves that statically.
So: report coverage, not a verdict. “Resolved 214 of 230 references; 16 constructed at runtime, not checkable” is useful. A green tick on a codebase made of template literals is actively worse than no check, because it certifies clean on exactly the references it never looked at — which is the same failure mode you just fixed at the byte level, one layer up.
The complement that costs nothing to ship: after upload, open the published embed and read the network panel for non-200s. No session, no credentials, no tooling — a browser and one look. It catches case mismatches, absolute URLs, mixed content and genuinely missing files in a single pass, because it asks the only question that finally matters: what did the browser request, and did it get it. Pre-upload structural checks and a post-upload 404 sweep catch nearly disjoint sets of failures, which to me is an argument for the second one living inside the first rather than in a README.
One question, since you are already down at the central directory: how often do you see general purpose bit 11 wrong — a non-ASCII entry name written as UTF-8 with the flag left unset, so it is spec’d as CP437? That is the only path failure I know of where a human reading the ZIP listing will also read it as correct, which seems like precisely your tool’s territory.