fix(static): finish gitignore's rule for hide patterns - #310
Conversation
A leading separator anchored the pattern and then compared it against a mount-relative path that never carries one; a trailing separator was compared literally against paths that are never bare directories; ** met FNM_PATHNAME and stopped one level down. Each accepted the pattern without complaint and served the file. Underneath the third: the Windows fnmatch shim cast its flags away, so turning FNM_PATHNAME off crossed directories on POSIX and did nothing on Windows. The shim reads the flag. hide() refuses a pattern longer than the matcher reads, rather than accepting one that can only match nothing.
CoverageTotal lines: 83.16% → 83.18% (+0.03 pp)
|
… shim
A pattern opening with a double star and a separator names every
directory, and the mount root is one of them - but there the separator
has nothing to match, so hide("**/secret.txt") served the root copy
while covering every nested one. That is the disclosure direction.
Crossing separators widened what the Windows shim backtracks over, from
one path segment to the whole path. Collapsing a run of stars keeps the
form this rule now encourages from paying for each star twice over.
The contract says which measure the length limit uses and that case
follows the platform filesystem, neither of which the reader could tell
from the signature.
Review follow-upA Fable Critic worked the matcher by hand against adversarial inputs. Two findings, both fixed here. A pattern opening with a double star did not reach the mount root. Crossing separators widened what the Windows shim backtracks over. It is recursive, and each The Critic also proved the buffer bounds hold for every reachable input, including a 512-byte pattern in the Two things it raised that stay as they are, named rather than fixed:
Suite after the rework: 497 tests, 260 executed, 0 failed. One warning, |
Closes #309.
hide('/index.php'),hide('cache/')andhide('logs/**')are the three shapesan operator carries over from gitignore after #270 taught the other two. The
first two covered no path at all; the third stopped one level down. Each was
accepted without complaint, and each ended with the file on the wire.
it against a mount-relative path that never carries a leading
/. It isstripped now, and the pattern pins the mount root —
/index.phphides theroot's own file and leaves
sub/index.phpalone.bare directories. It names a directory now: that directory wherever it sits,
and everything under it at any depth.
**metFNM_PATHNAME, which stops each*at the separator. A patterncontaining
**turns that flag off for its whole length.A bare pattern still reads the file name, so a directory named like the pattern
keeps serving what it holds —
cache/is how to say otherwise. That half is adeliberate decision with a unit row behind it and is untouched.
The platform split underneath
src/static/http_static_path.ccarries anfnmatchshim for Windows whoseflagsparameter was(void)-cast away, so*stopped at a separator therewhatever the caller asked. Turning
FNM_PATHNAMEoff therefore crosseddirectories on POSIX and did nothing on Windows: the
**half of this changepassed its POSIX build and failed on mine until the shim was taught to read the
flag. Worth knowing for anything else that reaches for those flags.
hide()refuses an overlong patternThe matcher reads at most
HTTP_STATIC_HIDE_GLOB_MAX(512) bytes. A longerpattern could only ever match nothing, which is the answer an operator reads as
"hidden", so
hide()throwsHttpServerInvalidArgumentExceptionwhere thepattern is written rather than leaving it to be silently inert.
Evidence
static/024-static-hide-gitignore-formsdrives a live mount at/with allthree patterns. Against
dbe66f0it prints— three files the operator meant to hide, served — and here it hands all three
to the handler while
/sub/index.phpand/app.svgstill come off disk. TheStaticHideunit table gains a group per shape, including the anchoreddirectory pattern (
var/cache/coversvar/cache/x.txt, notapp/var/cache/x.txt) and the negative rows (cache/covers neithervar/cached/x.txtnorcache.txt).Whole local suite, openssl on PATH so the TLS group executes: 497 tests, 260
executed, 0 failed, 0 warned.
Backward compatibility
The rule only widens what is hidden — a pattern that covered nothing now covers
what it names. Nothing previously hidden becomes visible. The one behaviour
change an existing configuration could notice: a mount that wrote
logs/**meaning "one level" now hides the subtree, which is what the pattern says.