阅读文本 / Reading text
This paper uses synthetic samples to demonstrate the processing method. The code has been verified using examples in an isolated environment; this does not guarantee effectiveness for all real files or products. Before processing important files, please retain the original copies and verify them against the boundaries and acceptance criteria outlined in this document.
uploads a file named
../../report.pdf
, does not mean the server should save the file under that name. The filename originates from the user and constitutes input data; the disk path, on the other hand, falls within the system’s permission boundary. Mixing these two concepts can easily lead to issues such as path traversal, overwriting files with identical names, and inconsistencies between Windows and Linux behaviors.
display name and storage name are separated
retains its original name for interface display, but is assigned an internal identifier by the server. The storage suffix should also be derived from the server’s allowed list, rather than simply copying the extension provided by the user. Below, we only construct the path without creating any directories or files.
minimum path constructor
from pathlib import Path
import re
def storage_path(root, internal_id, extension):
if not re.fullmatch(r"[a-f0-9]{32}", internal_id):
raise ValueError("invalid internal id")
if extension not in {".pdf", ".docx", ".udoc"}:
raise ValueError("unsupported extension")
base = Path(root).resolve()
candidate = (base / (internal_id + extension)).resolve()
if candidate.parent != base:
raise ValueError("outside storage root")
return candidate
root = Path.cwd() / "synthetic-uploads"
identifier = "a" * 32
p = storage_path(root, identifier, ".pdf")
assert p.parent == root.resolve()
assert p.name == identifier + ".pdf"
for bad in ["../../report", "A" * 32, "a" * 31, "a" * 32 + "/x"]:
try:
storage_path(root, bad, ".pdf")
except ValueError:
pass
else:
raise AssertionError(bad)
This example assumes that the internal ID has already been generated by the server. It is not a secure file-writing implementation against arbitrary disk-based attacks: a race condition can still occur between the check and the actual open, and the parent directory permissions and symbolic-link policies must be controlled separately. A unique ID does not mean that the “prohibit-overwriting” policy can be ignored.
filename cleanup is not file identification
input phenomenon
should be processed at which layer
shortcomings
contains the path separator
is not used as a storage path
merely removing the slash is insufficient
extension is PDF
Content Detection and Parsing
extension can be forged
display name contains HTML
interface output escape
cannot be resolved by simply changing the disk name
identical display name
internal ID isolation
should not overwrite the previous upload
object storage also has the same boundary
R2 or other object stores do not fully capture the semantics of traditional directory traversal, but object keys still influence isolation, overwriting, and public URLs. Do not directly concatenate tenant-provided strings into shared prefixes; nor should the client infer authorization solely from key names.
Files should have an identity even before they enter the translation process.
accesses
OmniDoc (Product Entry)
During workflow execution, users can continue to see familiar file names, while internally independent task IDs and file IDs are used. This clear file identity makes it easier to match failed retries, manuscript downloads, and subsequent proofreading, and also reduces the risk of mistaking someone else’s file with the same name for the current result.
Reference:
Python pathlib
。
- 1 -
Updated 2026-10-08