Repository navigation
Refuse a symlink where the parsed output goes, and actually write it - #504
Merged
chrismattmann merged 1 commit intoNov 9, 2026
Conversation
Two things in parseAndSave, both in the same three lines.
The write sat inside the `else` for the outDir case. So parseAndSave with no
outDir computed a path, wrote nothing, and returned the name of a file that
did not exist -- in a function called parseAndSave whose docstring says it
writes the extracted metadata. It now writes in both branches. This is a
behaviour change: callers who passed no outDir previously got no files, and
will now get <input>_meta.json next to each input, which is what the name and
the docstring promise.
The destination is opened without following a symlink at the final component.
The output name is derived from the input name, so it is predictable, and a
link planted at it was written through as whoever runs tika. That is the same
shape as the log and cache files, and it applies to both branches -- the
no-outDir case writes next to the input, which is just as predictable.
Unlike the cache, this is not treated as ours:
- ownership is not checked. The directory is the caller's to choose and may
legitimately be group-writable or shared, and refusing that would break
ordinary use. The symlink refusal is the part that matters here.
- the mode stays 0o666 so the umask decides, exactly as the plain open() it
replaces did. These are outputs somebody asked for, not cache files, and
forcing 0600 would quietly change who can read them.
_openPrivate takes checkOwner, encoding and perm for that distinction, and a
text 'w' mode so the utf-8 encoding the old call specified is preserved.
tests/test_output_paths.py, 10 cases. Suite 68 -> 78. The symlink case was
confirmed to fail against the plain open() rather than assumed:
FAILED test_a_symlink_at_the_output_name_is_not_written_through
Also pinned: that both callers of the opener differ the way they are supposed
to -- a cache file lands 0600 and ownership-checked, an output file does not.
chrismattmann
merged commit Nov 9, 2026
85ce51b
into
verify-the-server-jar-before-running-it
12 checks passed
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two things in
parseAndSave, both in the same three lines.It did not write anything without an outDir
The write sat inside the
else:So
parseAndSave(option, paths)computed a path, wrote nothing, and returned the name of a file that did not exist — in a function calledparseAndSave, whose docstring says it "write[s] extracted metadata and/or text in JSON format to matching filename".This is a behaviour change. Callers who passed no
outDirpreviously got no files and will now get<input>_meta.jsonnext to each input, which is what the name and the docstring promise. Say the word if you'd rather I split this out or leave the old behaviour.A symlink at the output name was written through
The destination name is derived from the input name, so it is predictable, and a link planted at it got written through as whoever runs tika. Same shape as the log and cache files in #502, and it applies to both branches — the no-
outDircase writes next to the input, which is just as predictable.Unlike the cache, this path is deliberately not treated as ours:
0o666, so the umask decides, exactly as the plainopen()did. These are outputs somebody asked for, not cache files — forcing0600would quietly change who can read them._openPrivategrowscheckOwner,encodingandpermfor that distinction, plus a text'w'mode so the utf-8 encoding the old call specified is preserved.Tests
tests/test_output_paths.py, 10 cases; suite 68 → 78, all passing. The symlink case was confirmed to fail against the plainopen()rather than assumed:Also pinned: that the opener's two callers differ the way they're meant to — a cache file lands
0600and ownership-checked, an output file does not; that the umask still decides output permissions; that an existing output is overwritten rather than appended; and that utf-8 survives.