[BUG]: A download that answers 403, 429 or 500 is saved as the archive, and the install ends in "Checksum error"

Aperta Adatta ai principianti
#746 1 commento 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
2/5
Tempo stimato
1-3 ore
Idoneità per principianti
72/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
go, lua
Ambito
cli

Direzione di ricerca

Il bug si trova in Download in internal/sdk/sdk.go, dove prima di scrivere il file viene controllato solo resp.StatusCode == http.StatusNotFound. Riproducilo con il plugin repro a quattro file e il server Go della issue, esegui vfox install repro@403 e controlla l'output 'Checksum error'. È completato quando qualsiasi stato diverso da 2xx fa fallire il download con un errore che indica lo stato e l'URL, e il messaggio per il 404 resta invariato. La questione dello User-Agent è una decisione separata e più ampia, e va esclusa da questa correzione.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug

Version
vfox 1.0.13 (release binary) and main at cdf0747 (2026-11-02, RuntimeVersion is still 1.0.13). The two builds print the same lines in everything below.

OS
Linux amd64, in a golang:1.27.1 container. The code involved depends on neither the OS nor the shell.

Describe the bug
Download in internal/sdk/sdk.go fails on one status only, 404 (source file not found). For any other status, 403, 429, 500 and so on, it saves the body as the archive and goes on, so the install fails one step later on a page of HTML, and nothing in the output says the server refused the download or with which status:

  • With a checksum from PreInstall: Checksum error, file: …/repro.zip, then checksum error.
  • Without one: WARNING: Checksum is not provided, skip verify..., then unpack failed, err:zip: not a valid zip file.

This is not hypothetical. Huawei Cloud's mirror of Gradle answers a download from Go-http-client/1.1 with a 403 and a page of HTML, and serves the same file to other agents. With the vfox-gradle plugin and VFOX_GRADLE_MIRRORS=https://repo.huaweicloud.com/gradle, vfox install [email protected] ends in Checksum error, and a user cannot tell that it was an HTTP 403 from the mirror.

Steps to reproduce
No plugin from the registry is needed. A plugin of four small files, whose PreInstall gets the digest with http.get as most plugins do, and a server that answers the status that is the first part of the path.

metadata.lua:

PLUGIN = {}
PLUGIN.name = "repro"
PLUGIN.version = "0.0.1"
PLUGIN.homepage = "https://example.invalid"
PLUGIN.license = "Apache 2.0"
PLUGIN.description = "A download that answers something else than 200 or 404"

hooks/available.lua:

function PLUGIN:Available(ctx)
    return { { version = "403" } }
end

hooks/env_keys.lua:

function PLUGIN:EnvKeys(ctx)
    return { { key = "PATH", value = ctx.path } }
end

hooks/pre_install.lua:

local http = require("http")

-- The version is the status the server answers the download with: 403, 500, ...
function PLUGIN:PreInstall(ctx)
    local base = "http://127.0.0.1:18080/" .. ctx.version
    local result = { version = ctx.version, url = base .. "/repro.zip" }
    if os.getenv("REPRO_NO_SUM") == nil then
        -- A request the plugin makes itself, as most plugins do for a checksum.
        local resp = http.get({ url = base .. "/repro.zip.sha256" })
        result.sha256 = resp.body
    end
    return result
end

server.go:

package main

import (
	"fmt"
	"log"
	"net/http"
	"strconv"
	"strings"
)

// /<status>/repro.zip answers <status> with a page of HTML, and
// /<status>/repro.zip.sha256 answers 200 with a digest of 64 zeros.
func main() {
	log.SetFlags(0)
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		log.Printf("%s %s  User-Agent: %s", r.Method, r.URL.Path, r.UserAgent())
		parts := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
		status, err := strconv.Atoi(parts[0])
		if err != nil || len(parts) != 2 {
			http.NotFound(w, r)
			return
		}
		if strings.HasSuffix(parts[1], ".sha256") {
			fmt.Fprint(w, strings.Repeat("0", 64))
			return
		}
		w.Header().Set("Content-Type", "text/html")
		w.WriteHeader(status)
		fmt.Fprintf(w, "<html><body>HTTP %d</body></html>", status)
	})
	log.Fatal(http.ListenAndServe("127.0.0.1:18080", nil))
}
$ go run server.go &
$ zip -r repro.zip metadata.lua hooks
$ vfox add repro --source ./repro.zip
$ vfox install repro@404
$ vfox install repro@403
$ vfox install repro@500
$ REPRO_NO_SUM=1 vfox install repro@403

Actual behavior
The 404 is reported as what it is:

$ vfox install repro@404
Preinstalling repro@404...
failed to download repro@404 file, err:source file not found
failed to install repro

The 403 is not, and the 500 and the 429 print the same lines as the 403:

$ vfox install repro@403
Preinstalling repro@403...
Verifying checksum 0000000000000000000000000000000000000000000000000000000000000000...
Checksum error, file: …/.vfox/cache/repro/repro.zip
checksum error
failed to install repro

Without a checksum:

$ REPRO_NO_SUM=1 vfox install repro@403
Preinstalling repro@403...
Verifying checksum ...
WARNING: Checksum is not provided, skip verify...
Unpacking …/.vfox/cache/repro/repro.zip...
unpack failed, err:zip: not a valid zip file
failed to install repro

Expected behavior
Like the 404, a status outside 2xx fails the download itself, naming the status and the URL, for example failed to download repro@403 file, err:unexpected HTTP status 403 Forbidden. Nothing is saved, so the checksum and the unpacking never see a page of HTML.

Where
Download is called by moveRemoteFile, which wraps its error as failed to download <label> file, err:…. The 404 is the only status it looks at, and the file is opened and written right after:

https://github.com/version-fox/vfox/blob/cdf07474b2bd49aab82bcdb573ce2fc08e39f644/internal/sdk/sdk.go#L1060-L1084

	if resp.StatusCode == http.StatusNotFound {
		return "", errors.New("source file not found")
	}

A check of resp.StatusCode for 2xx after it, before the file is opened, would do. (Downloader.Download in internal/shared/util/downloader.go has the same check, but nothing calls NewDownloader.)

Related: the User-Agent of a download
The requests a plugin makes with http.get carry vfox/1.0.13 vfox-repro/0.0.1 (ensureUserAgent and ComputeUserAgent), but the download goes out as Go-http-client/1.1, unless PreInstall returns headers. The log of the server above shows both:

GET /403/repro.zip.sha256  User-Agent: vfox/1.0.13 vfox-repro/0.0.1
GET /403/repro.zip  User-Agent: Go-http-client/1.1

Mirrors and CDNs that refuse Go's default agent, like the one above, are what turns this into the bug. A plugin can work around it by returning headers = { ["User-Agent"] = … } from PreInstall, which vfox applies to the download: with it, the install through Huawei Cloud's mirror above succeeds and passes the checksum. If the download used the agent that the plugin's own requests use, no plugin would have to. I can split this into an issue of its own if you prefer.

Lingua principale
Go
Stelle
4k
Fork
167
Merge medio
2g 10h
PR unite (30g)
8

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di version-fox/vfox

Tutte le issue di version-fox/vfox

Issue simili

Altre issue su Go

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.