Skip to content

Protect public links, an integration suite, and the bugs it found - #95

Merged
ilyabrin merged 4 commits into
mainfrom
feat/public-settings
Oct 8, 2026
Merged

ilyabrin merged 4 commits into
mainfrom
feat/public-settings

Conversation

@ilyabrin

@ilyabrin ilyabrin commented Oct 8, 2026

Copy link
Copy Markdown
Owner

What

An integration suite against the real API, which found three bugs the
unit tests missed:

Bug Effect
Request bodies went out chunked, without Content-Length the API read PUT and PATCH bodies as empty; UpdateMetadata never stored anything
WaitForBatchOperation polled links to finished results batch copy and move of small files failed with "Operation not found"
BatchCopyMoveOptions.Overwrite was ignored batch copy and move could not replace a file

And public link protection: a password and an expiry.

Public link settings, as the API really behaves

Worked out on a paid personal account and checked as a stranger
without a token, plus in a browser:

Setting How Checked
Password PATCH after publishing the page asks for it; the public API answers 403
Expiry PATCH with a Unix time open before, 404 after
No downloads not possible: the web uses an internal call

Where the reference is wrong: settings sent with publishing are
ignored, available_until is a Unix time rather than a duration, and an
access needs "type". Address access is for Yandex 360 for business; on
a personal account it closes the link to everyone.

The integration suite

Behind the integration build tag, so go test ./... skips it. It works
in a new disk:/disk-it- folder that it deletes for good, and a
guard in the HTTP transport refuses any change outside it, any trash
item it did not create, and emptying the whole trash. Calls that read
the whole Disk log counts, never names. CONTRIBUTING explains how to
run it.

Checked

  • Integration suite on a real account: 13 of 13 steps pass.
  • Unit tests for each bug fail on the old code.
  • go vet, go test ./... (also as 386), golangci-lint v2.13.2.

Wrapping every body in io.LimitReader hid its length, so PUT and PATCH
bodies went out chunked, and the API read them as empty. UpdateMetadata
never stored anything. Bodies of known length now go as they are. The
test transport kept losing the length too, which is why no test
noticed.
BatchCopyMoveOptions.Overwrite was ignored. And WaitForBatchOperation
polled every returned link as an operation, but a copy that finishes
at once returns a link to the file, so waiting failed with "Operation
not found".
Built on what the live API does, which differs from its reference:
settings sent with publishing are ignored, so they follow in a PATCH;
the expiry is a Unix time, not a duration; and an access needs a type.
PublishResourceWithSettings unpublishes again if the settings fail,
so a link meant to be protected is never left open. Address access is
for Yandex 360 for business, and forbidding downloads is not available
through the public API; the docs say both.
Behind the integration build tag. It works in a new folder it deletes
for good at the end, and a guard in the HTTP transport refuses any
change outside it, any trash item it did not create, and emptying the
whole trash. It checks link protection as a stranger without a token
would. CONTRIBUTING explains how to run it.
@ilyabrin
ilyabrin merged commit 6ba1c0c into main Oct 8, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant