Skip to content

fix(apps): harden signed URL downloads - #2241

Open
liangshuo-1 wants to merge 1 commit into
mainfrom
fix/apps-file-download
Open

fix(apps): harden signed URL downloads#2241
liangshuo-1 wants to merge 1 commit into
mainfrom
fix/apps-file-download

Conversation

@liangshuo-1

@liangshuo-1 liangshuo-1 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • route apps +file-download signed URLs through the shared validated download pipeline
  • use 32 MiB ranged reads for immutable Apps objects while automatically applying strong ETag/If-Range validation when available
  • preserve network/timeout error types through atomic FileIO saves and retry transient transport/server failures

Why

The previous direct http.Client.Do path issued one unvalidated full-body request. It could not resume a failed body, had no progress-based idle timeout, accepted unexpected response encoding, and could report a network body failure as an output validation error.

Apps storage paths identify immutable uploaded objects, and the current object store supports byte ranges and strong ETags. Reusing internal/download keeps HTTP policy, representation consistency, retry, exact-length validation, and streaming output in their existing owners without adding Apps-specific transport logic.

Impact

Successful CLI output and flags are unchanged. Files up to the Apps 100 MiB limit use at most four 32 MiB range requests; servers that ignore Range continue through the returned full response.

Validation

  • go test ./shortcuts/apps ./internal/download ./internal/vfs/localfileio ./shortcuts/common ./tests/cli_e2e/apps -count=1
  • go test -race ./shortcuts/apps ./internal/download ./internal/vfs/localfileio -count=1
  • repeated Apps download tests (20 normal runs, 5 race runs) and download package tests (10 runs)
  • go vet ./shortcuts/apps ./internal/download
  • full CLI build and git diff --check
  • real Apps storage uploads/downloads at 1, 40, 70, and 99 MiB: HTTP/2 range responses, strong ETag/If-Range, exact request counts (1/2/3/4), and matching SHA-256 checksums

Summary by CodeRabbit

  • Bug Fixes
    • Improved file download reliability by supporting transient download failures and retries.
    • Preserved typed network errors for clearer failure handling.
    • Prevented incomplete files from being left behind when downloads fail.
    • Improved handling of large downloads through ranged streaming.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The app file download path now uses the internal streaming API with 32 MiB ranged requests. Tests cover request headers, transient failure retries, typed network errors, and removal of partial output.

Changes

App file download streaming

Layer / File(s) Summary
Streaming download path
shortcuts/apps/apps_file_download.go
Signed-URL downloads use an immutable URL source with 32 MiB parts. Stream metadata is passed to FileIO().Save, and save errors use common.WrapSaveErrorTyped.
Download behavior validation
shortcuts/apps/apps_file_download_test.go
Tests verify ranged requests, identity encoding, single-request success, retry after HTTP 503, typed errors for truncated bodies, and no partial output file.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AppsFileDownload
  participant openAppsFileDownload
  participant ImmutableURLSource
  participant SignedURLServer
  participant FileIO
  AppsFileDownload->>openAppsFileDownload: Open signed-URL stream
  openAppsFileDownload->>ImmutableURLSource: Configure 32 MiB parts
  ImmutableURLSource->>SignedURLServer: Send ranged GET
  SignedURLServer-->>ImmutableURLSource: Return response metadata and body
  ImmutableURLSource-->>FileIO: Provide stream and metadata
  FileIO-->>AppsFileDownload: Return save result
Loading

Possibly related PRs

  • larksuite/cli#2223: Introduces the internal streaming API and immutable URL transport used by this download path.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: hardening signed URL downloads for Apps.
Description check ✅ Passed The description explains the motivation, implementation, impact, and validation; it is complete despite not using every template heading.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/apps-file-download

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the size/M Single-domain feat or fix with limited business impact label Aug 7, 2026
@liangshuo-1
liangshuo-1 marked this pull request as ready for review August 7, 2026 10:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@shortcuts/apps/apps_file_download_test.go`:
- Around line 146-174: Strengthen
TestAppsFileDownload_PreservesNetworkBodyFailure by asserting the expected
network problem.Subtype and verifying the preserved underlying cause with
errors.Is against io.ErrUnexpectedEOF. Keep using errs.ProblemOf for typed
metadata, but do not access Param because this is not a validation error.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 98bf4dd1-e990-4504-b296-e7b3154dfa0c

📥 Commits

Reviewing files that changed from the base of the PR and between a6f3e63 and bfceb03.

📒 Files selected for processing (2)
  • shortcuts/apps/apps_file_download.go
  • shortcuts/apps/apps_file_download_test.go

Comment on lines +146 to +174
func TestAppsFileDownload_PreservesNetworkBodyFailure(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Length", "10")
w.WriteHeader(http.StatusOK)
io.WriteString(w, "short")
}))
defer srv.Close()

dir := t.TempDir()
oldWD, _ := os.Getwd()
if err := os.Chdir(dir); err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = os.Chdir(oldWD) })

factory, stdout, reg := newAppsExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "POST", URL: fileSignURLForDownload,
Body: map[string]interface{}{"code": 0, "data": map[string]interface{}{"signed_url": srv.URL}},
})
err := runAppsShortcut(t, AppsFileDownload,
[]string{"+file-download", "--app-id", "app_x", "--path", "/short.bin", "--output", "out.bin", "--as", "user"}, factory, stdout)
problem, ok := errs.ProblemOf(err)
if !ok || problem.Category != errs.CategoryNetwork {
t.Fatalf("error = %T %v, want typed network error", err, err)
}
if _, statErr := os.Stat(filepath.Join(dir, "out.bin")); !os.IsNotExist(statErr) {
t.Fatalf("partial output exists after body failure: %v", statErr)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert the complete typed error contract and the preserved cause.

This test asserts only errs.CategoryNetwork. It will pass if the save pipeline changes the error subtype or drops the truncated-body cause. Assert the expected problem.Subtype and the underlying read error with errors.Is, such as io.ErrUnexpectedEOF.

Do not read Param from errs.ProblemOf. Problem has no Param, and this is not a validation error. As per coding guidelines, error-path tests must assert typed metadata and cause preservation. Based on learnings, errs.ProblemOf does not expose Param.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@shortcuts/apps/apps_file_download_test.go` around lines 146 - 174, Strengthen
TestAppsFileDownload_PreservesNetworkBodyFailure by asserting the expected
network problem.Subtype and verifying the preserved underlying cause with
errors.Is against io.ErrUnexpectedEOF. Keep using errs.ProblemOf for typed
metadata, but do not access Param because this is not a validation error.

Sources: Coding guidelines, Learnings

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@bfceb03a5a278755b876453e79e60ae0e3d420b3

🧩 Skill update

npx skills add larksuite/cli#fix/apps-file-download -y -g

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 76.38%. Comparing base (0d6f2c6) to head (bfceb03).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/apps/apps_file_download.go 90.90% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2241      +/-   ##
==========================================
- Coverage   76.46%   76.38%   -0.08%     
==========================================
  Files        1000     1009       +9     
  Lines      107351   110981    +3630     
==========================================
+ Hits        82083    84774    +2691     
- Misses      19042    19736     +694     
- Partials     6226     6471     +245     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant