Repository navigation
OpenConceptLab/ocl_issues#2864 | export download saves under the API's name by default - #13
Conversation
…s name by default -o is now optional. Without it, or when it names a directory, the file is saved under the name the API gives the export: Content-Disposition first, then the signed URL's response-content-disposition, then the URL's last path segment. Only the base name is used. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…laceholder-free f-string (ruff) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
paynejd
left a comment
There was a problem hiding this comment.
Codex adversarial review, pass 1 (codex-cli 0.160.1, commit f69e0e6)
-
[P1] Automatically selected paths can overwrite files outside the destination. export.py:135
Ifdownloads/<API_NAME>already symlinks to../important.txt, the subsequentopen(..., "wb")follows it and truncates that target. Basename sanitization does not ensure containment. Ordinary existing files are silently overwritten too. Automatically named downloads should use atomic no-clobber creation, with an explicit overwrite policy. -
[P2]
filenameincorrectly overridesfilename*. export.py:91
Reproduced withattachment; filename="cafe.zip"; filename*=UTF-8''caf%C3%A9.zip: the result iscafe.zip, regardless of parameter order. RFC 6266 says to prefer the extended filename, so this loses the API’s intended non-ASCII name. Both header and signed-query extraction are affected. Explicitly prioritize validfilename*. RFC 6266 §4.3 -
[P2] A malformed fallback can abort an otherwise valid download. export.py:102
All candidates are parsed eagerly. A valid header naminggood.zipstill fails if the query disposition containsfilename*=UTF-8''a.zip; filename*0*=UTF-8''b.zip: Python’s email parser raises an uncaughtTypeError. A malformed primary header likewise prevents trying a valid query name. Evaluate candidates sequentially and handle parsing failures individually. -
[P2] Filename validation accepts unusable names and Windows special targets. export.py:108
filename*=UTF-8''evil%00.zipproduces an embedded NUL, passes validation, and causesValueErrorwhen opened—even with a usable query fallback. Windows path handling also leavesNUL.zipandvictim.txt:payload.zipintact; these identify a device or alternate stream rather than an ordinary export file. Reject unusable candidates and continue the fallback chain, or require-o FILE. Windows naming rules -
[P3] Clarify that
-o DIRrequires an existing directory. README.md:194
A nonexistent-o downloadscreates a file nameddownloads;-o downloads/fails with an unhandled filesystem error. The README should state the existing-directory requirement. The naming example otherwise matches the companion API implementation; expansion is included only when present.
The new tests omit these failure cases, filename-parameter precedence, collisions/symlinks, and the actual 302 handoff. Read-only probes verified the normal redirect/header/query paths, standalone UTF-8, quoting, and separator stripping. Filesystem-writing tests were not run. No files were modified.
Overall verdict: Request changes. The ordinary API example works, but filename precedence and safe output handling need correction.
…rict names, no clobbering - filename* wins over filename (RFC 6266); each name source is tried in turn, and a malformed one falls through to the next. - A name must be made of the characters the API uses (letters, digits, . _ - @) and not be a Windows device name, which rules out paths, NUL bytes and alternate streams. - A name the API chose is created exclusively, so it never replaces an existing file or follows a symlink; -o FILE still overwrites. - -o DIR/ that doesn't exist is an error; README says DIR must exist. - Tests for each, plus the API's 302 handoff through OCLAPIClient. Addresses Codex pass 1 on ocl-cli#13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Replies to Codex pass 1. All five are fixed in 65f4ef5:
Also added |
…parsing, names may start with _ - @ - Content-Disposition parameters are parsed in order, so a quoted value containing ';' or 'filename=' can't be mistaken for the filename. - Storage names may start with _, - or @, as OCL owner names can. - README: an export the API can't name keeps its storage name. Addresses Codex pass 2 on ocl-cli#13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
paynejd
left a comment
There was a problem hiding this comment.
Codex adversarial review, pass 2 (codex-cli 0.160.1, commit 65f4ef5)
-
[P2] Valid legacy storage filenames are rejected — export.py:86. OCL allows owner names beginning with
_,-, or@, but this pattern requires an initial alphanumeric character. For a timestamp-less legacy export at/orgs/_PIH/_PIH_PIH_v1.8.24.zip, the API deliberately supplies no disposition name. The CLI rejects the safe storage basename and demands-o FILE. Allow these initial characters and add a fallback regression test. -
[P3] Filename regexes match inside quoted metadata — export.py:95. The valid header
attachment; note="x; filename=wrong.zip; y"; filename="right.zip"produceswrong.zip. Both regexes search without respecting quoted parameter boundaries. Parse parameters before selectingfilename*orfilename, and test embedded semicolons. The current API’s simple header avoids this case, but the parser mishandles valid RFC 6266 syntax. -
[P3] README overstates the naming guarantee — README.md:210. “Every export” is inaccurate: the accompanying API change deliberately leaves timestamp-less legacy exports unnamed. Document the storage-basename fallback and the requirement for
-o FILEwhen no usable name remains.
The earlier precedence, overwrite protection, and missing-directory fixes are present; nine mocked CLI checks passed. Ten pytest tests passed, including the redirect integration test. Eleven filesystem-writing tests were excluded to preserve read-only operation. Ruff and diff checks passed; no files changed.
Overall verdict: changes requested for the legacy-filename compatibility defect.
|
Replies to Codex pass 2. All three are fixed in ee04c51:
|
paynejd
left a comment
There was a problem hiding this comment.
Codex adversarial review, pass 3 (codex-cli 0.160.1, commit ee04c51)
- [P2] Handle API filenames exceeding filesystem limits — src/ocl_cli/commands/export.py:180. A valid 220-character repository ID produces a 261-byte download name.
_usable_filename()accepts it, but saving on a filesystem with a 255-byte limit raises uncaughtENAMETOOLONG. Both omitted-oand-o DIRfail without the documented guidance. Catch this failure and ask for-o FILE, preserving the rule against inventing names; add a regression test.
Validation: the 302 integration test and 34 in-memory checks passed. Reproduced the length failure using read-only filesystem lookup; no files modified. The full filesystem-writing suite was not run.
Overall verdict: one remaining fix needed.
… the filesystem asks for -o FILE Addresses Codex pass 3 on ocl-cli#13. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Reply to Codex pass 3: name too long for the filesystem (fixed in 22bd9cc). When a name the API chose fails with |
Part of OpenConceptLab/ocl_issues#2864 (ocl-cli piece). The API side is OpenConceptLab/oclapi2#929.
What
ocl repo export downloadno longer needs-o. By default it saves the file under the name the API gives the export, for exampleorgs_PIH_sources_PIH_1.8.24_2026-09-30_123456.zip.-o: saved in the current directory under the API's name.-o DIR(an existing directory): saved there under the API's name.-o FILE: saved asFILE, as before.The name comes from the
Content-Dispositionheader first, then from the signed URL'sresponse-content-disposition, then from the URL's last path segment. Only the base name is used, so a name can't put the file outside the target directory. If none of those gives a name, the command asks for-o.Until oclapi2#929 is deployed, the default name is the end of the storage key (
PIH_PIH_v1.8.24.2026-09-30_123456.zip), which is what TBv2 andcurl -Osave today.The ticket said
export downloadalready readContent-Disposition. It didn't, so this PR adds that.Also
sysimport and an f-string with no placeholders inexport.py. Ruff flagged both, and both were already onmain.Tests
tests/test_export_commands.pycovers the header, the signed URL, the storage-name fallback,-o DIR,-o FILE, a../name, and an unnamed response.pytest: 16 passed.ruff checkis clean on the changed files.🤖 Generated with Claude Code