@@ -59,12 +59,14 @@ func extractTarGzFile(archivePath, wantPath, dest string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open archive: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
gz, err := gzip.NewReader(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot read gzip: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = gz.Close() }()
|
||||
|
||||
tr := tar.NewReader(gz)
|
||||
@@ -82,7 +84,7 @@ func extractTarGzFile(archivePath, wantPath, dest string) error {
|
||||
continue
|
||||
}
|
||||
|
||||
if hdr.Typeflag != tar.TypeReg && hdr.Typeflag != tar.TypeRegA {
|
||||
if hdr.Typeflag != tar.TypeReg {
|
||||
return fmt.Errorf("update: %s is not a regular file", wantPath)
|
||||
}
|
||||
|
||||
@@ -97,6 +99,7 @@ func extractZipFile(archivePath, wantPath, dest string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open zip: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = r.Close() }()
|
||||
|
||||
for _, f := range r.File {
|
||||
|
||||
@@ -54,6 +54,7 @@ func LayoutFor(goos, goarch string) (AssetLayout, error) {
|
||||
binary := "probo-agent"
|
||||
isZip := false
|
||||
ext := "tar.gz"
|
||||
|
||||
if goos == "windows" {
|
||||
binary += ".exe"
|
||||
isZip = true
|
||||
|
||||
@@ -26,6 +26,7 @@ func copyFile(src, dst string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open %s: %w", src, err)
|
||||
}
|
||||
|
||||
defer func() { _ = in.Close() }()
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||
@@ -40,11 +41,14 @@ func copyFile(src, dst string) error {
|
||||
if _, err := io.Copy(out, in); err != nil {
|
||||
_ = out.Close()
|
||||
_ = os.Remove(dst)
|
||||
|
||||
return fmt.Errorf("cannot copy to %s: %w", dst, err)
|
||||
}
|
||||
|
||||
if err := out.Sync(); err != nil {
|
||||
_ = out.Close()
|
||||
_ = os.Remove(dst)
|
||||
|
||||
return fmt.Errorf("cannot fsync %s: %w", dst, err)
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ func replaceBinary(dst, src string) error {
|
||||
if err := copyFile(src, staging); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Chmod(staging, 0o755); err != nil {
|
||||
_ = os.Remove(staging)
|
||||
return fmt.Errorf("cannot chmod staged binary: %w", err)
|
||||
|
||||
@@ -135,6 +135,7 @@ func (j *jsonTimestamp) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
*j = jsonTimestamp(t)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -191,6 +192,7 @@ func (u *Updater) CheckLatest(ctx context.Context) (*Release, error) {
|
||||
current := normalizeSemver(u.CurrentVersion)
|
||||
|
||||
var best *Release
|
||||
|
||||
for i := range releases {
|
||||
rel := &releases[i]
|
||||
if rel.Draft || rel.Prerelease {
|
||||
@@ -217,10 +219,12 @@ func (u *Updater) CheckLatest(ctx context.Context) (*Release, error) {
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
checksumURL, ok := findAssetURL(rel.Assets, checksumFileName)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
bundleURL, ok := findAssetURL(rel.Assets, checksumBundleFileName)
|
||||
if !ok {
|
||||
// Releases without a Sigstore bundle predate the
|
||||
@@ -281,6 +285,7 @@ func (u *Updater) Apply(ctx context.Context, rel *Release) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create update workdir: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = os.RemoveAll(workDir) }()
|
||||
|
||||
archivePath := filepath.Join(workDir, layout.ArchiveName)
|
||||
@@ -353,6 +358,7 @@ func (u *Updater) resolveVerifier() (Verifier, error) {
|
||||
}
|
||||
|
||||
u.Verifier = v
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
@@ -381,6 +387,7 @@ func (u *Updater) listReleases(ctx context.Context) ([]githubRelease, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot build releases request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/vnd.github+json")
|
||||
req.Header.Set("User-Agent", u.userAgent())
|
||||
|
||||
@@ -388,6 +395,7 @@ func (u *Updater) listReleases(ctx context.Context) ([]githubRelease, error) {
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot fetch releases: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
@@ -408,6 +416,7 @@ func (u *Updater) downloadFile(ctx context.Context, src, dst string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot build download request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Accept", "application/octet-stream")
|
||||
req.Header.Set("User-Agent", u.userAgent())
|
||||
|
||||
@@ -415,6 +424,7 @@ func (u *Updater) downloadFile(ctx context.Context, src, dst string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot fetch %s: %w", src, err)
|
||||
}
|
||||
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
if resp.StatusCode >= 400 {
|
||||
@@ -423,6 +433,7 @@ func (u *Updater) downloadFile(ctx context.Context, src, dst string) error {
|
||||
}
|
||||
|
||||
tmp := dst + ".part"
|
||||
|
||||
f, err := os.OpenFile(tmp, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot create %s: %w", tmp, err)
|
||||
@@ -432,6 +443,7 @@ func (u *Updater) downloadFile(ctx context.Context, src, dst string) error {
|
||||
_ = f.Close()
|
||||
return fmt.Errorf("cannot stream %s: %w", src, err)
|
||||
}
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
return fmt.Errorf("cannot close %s: %w", tmp, err)
|
||||
}
|
||||
@@ -440,6 +452,7 @@ func (u *Updater) downloadFile(ctx context.Context, src, dst string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot stat %s: %w", tmp, err)
|
||||
}
|
||||
|
||||
if stat.Size() > defaultDownloadLimit {
|
||||
_ = os.Remove(tmp)
|
||||
return fmt.Errorf("download %s exceeds %d bytes", src, defaultDownloadLimit)
|
||||
@@ -537,6 +550,7 @@ func verifyChecksum(archivePath, checksumPath, archiveName string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open archive: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = f.Close() }()
|
||||
|
||||
h := sha256.New()
|
||||
@@ -568,6 +582,7 @@ func readChecksum(path, archiveName string) (string, error) {
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// `sha256sum` output is `<hex> <name>`; the GNU tool also
|
||||
// supports a single-space separator and a leading `*` flag
|
||||
// for binary mode. Handle both.
|
||||
|
||||
@@ -163,6 +163,7 @@ func newFakeReleaseServer(t *testing.T, tag, version string, layout AssetLayout,
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/repos/getprobo/probo/releases", func(w http.ResponseWriter, r *http.Request) {
|
||||
base := "http://" + r.Host
|
||||
|
||||
assets := []map[string]any{
|
||||
{
|
||||
"name": layout.ArchiveName,
|
||||
@@ -179,6 +180,7 @@ func newFakeReleaseServer(t *testing.T, tag, version string, layout AssetLayout,
|
||||
"browser_download_url": base + "/download/" + checksumBundleFileName,
|
||||
})
|
||||
}
|
||||
|
||||
body := []map[string]any{
|
||||
{
|
||||
"tag_name": frs.tag,
|
||||
@@ -187,6 +189,7 @@ func newFakeReleaseServer(t *testing.T, tag, version string, layout AssetLayout,
|
||||
"assets": assets,
|
||||
},
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(body)
|
||||
_ = version
|
||||
@@ -206,6 +209,7 @@ func newFakeReleaseServer(t *testing.T, tag, version string, layout AssetLayout,
|
||||
|
||||
frs.server = httptest.NewServer(mux)
|
||||
t.Cleanup(frs.server.Close)
|
||||
|
||||
return frs
|
||||
}
|
||||
|
||||
@@ -213,9 +217,11 @@ func (f *fakeReleaseServer) URL() string { return f.server.URL }
|
||||
|
||||
func buildArchive(t *testing.T, layout AssetLayout, binary []byte) []byte {
|
||||
t.Helper()
|
||||
|
||||
if layout.IsZip {
|
||||
return buildZip(t, layout, binary)
|
||||
}
|
||||
|
||||
return buildTarGz(t, layout, binary)
|
||||
}
|
||||
|
||||
@@ -246,6 +252,7 @@ func buildTarGz(t *testing.T, layout AssetLayout, binary []byte) []byte {
|
||||
|
||||
data, err := os.ReadFile(out)
|
||||
require.NoError(t, err)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -268,6 +275,7 @@ func buildZip(t *testing.T, layout AssetLayout, binary []byte) []byte {
|
||||
|
||||
data, err := os.ReadFile(out)
|
||||
require.NoError(t, err)
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -106,12 +106,15 @@ func NewCosignVerifier(cfg CosignVerifierConfig) (*CosignVerifier, error) {
|
||||
if cfg.Repo == "" {
|
||||
return nil, fmt.Errorf("update: cosign verifier requires Repo")
|
||||
}
|
||||
|
||||
if cfg.WorkflowPath == "" {
|
||||
cfg.WorkflowPath = expectedWorkflowPath
|
||||
}
|
||||
|
||||
if cfg.TagPrefix == "" {
|
||||
cfg.TagPrefix = DefaultTagPrefix
|
||||
}
|
||||
|
||||
if cfg.CacheDir == "" {
|
||||
return nil, fmt.Errorf("update: cosign verifier requires CacheDir")
|
||||
}
|
||||
@@ -169,6 +172,7 @@ func (v *CosignVerifier) Verify(_ context.Context, artifactPath, bundlePath stri
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open artifact for verification: %w", err)
|
||||
}
|
||||
|
||||
defer func() { _ = artifact.Close() }()
|
||||
|
||||
policy := verify.NewPolicy(
|
||||
|
||||
Reference in New Issue
Block a user