Unverified Commit 16f41352 authored by Cristian Maglie's avatar Cristian Maglie Committed by GitHub

Check gpg signature for library_index.json (#1307)

* Download gzipped version of the library index

* Check gpg signature for library_index.json

* Update docs/UPGRADING.md
parent 93ae80cb
...@@ -17,16 +17,13 @@ package librariesmanager ...@@ -17,16 +17,13 @@ package librariesmanager
import ( import (
"net/url" "net/url"
"go.bug.st/downloader/v2"
) )
// LibraryIndexURL is the URL where to get library index. // LibraryIndexURL is the URL where to get the library index.
var LibraryIndexURL, _ = url.Parse("https://downloads.arduino.cc/libraries/library_index.json") var LibraryIndexURL, _ = url.Parse("https://downloads.arduino.cc/libraries/library_index.json")
// UpdateIndex downloads the libraries index file from Arduino repository. // LibraryIndexGZURL is the URL where to get the gzipped library index.
func (lm *LibrariesManager) UpdateIndex(config *downloader.Config) (*downloader.Downloader, error) { var LibraryIndexGZURL, _ = url.Parse("https://downloads.arduino.cc/libraries/library_index.json.gz")
lm.IndexFile.Parent().MkdirAll()
// TODO: Download from gzipped URL index // LibraryIndexSignature is the URL where to get the library index signature.
return downloader.DownloadWithConfig(lm.IndexFile.String(), LibraryIndexURL.String(), *config, downloader.NoResume) var LibraryIndexSignature, _ = url.Parse("https://downloads.arduino.cc/libraries/library_index.json.sig")
}
...@@ -35,9 +35,10 @@ type LibrariesManager struct { ...@@ -35,9 +35,10 @@ type LibrariesManager struct {
LibrariesDir []*LibrariesDir LibrariesDir []*LibrariesDir
Libraries map[string]*LibraryAlternatives `json:"libraries"` Libraries map[string]*LibraryAlternatives `json:"libraries"`
Index *librariesindex.Index Index *librariesindex.Index
IndexFile *paths.Path IndexFile *paths.Path
DownloadsDir *paths.Path IndexFileSignature *paths.Path
DownloadsDir *paths.Path
} }
// LibrariesDir is a directory containing libraries // LibrariesDir is a directory containing libraries
...@@ -95,15 +96,17 @@ func (lm LibrariesManager) Names() []string { ...@@ -95,15 +96,17 @@ func (lm LibrariesManager) Names() []string {
// NewLibraryManager creates a new library manager // NewLibraryManager creates a new library manager
func NewLibraryManager(indexDir *paths.Path, downloadsDir *paths.Path) *LibrariesManager { func NewLibraryManager(indexDir *paths.Path, downloadsDir *paths.Path) *LibrariesManager {
var indexFile *paths.Path var indexFile, indexFileSignature *paths.Path
if indexDir != nil { if indexDir != nil {
indexFile = indexDir.Join("library_index.json") indexFile = indexDir.Join("library_index.json")
indexFileSignature = indexDir.Join("library_index.json.sig")
} }
return &LibrariesManager{ return &LibrariesManager{
Libraries: map[string]*LibraryAlternatives{}, Libraries: map[string]*LibraryAlternatives{},
IndexFile: indexFile, IndexFile: indexFile,
DownloadsDir: downloadsDir, IndexFileSignature: indexFileSignature,
Index: librariesindex.EmptyIndex, DownloadsDir: downloadsDir,
Index: librariesindex.EmptyIndex,
} }
} }
......
...@@ -17,7 +17,6 @@ package commands ...@@ -17,7 +17,6 @@ package commands
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url" "net/url"
...@@ -37,6 +36,7 @@ import ( ...@@ -37,6 +36,7 @@ import (
"github.com/arduino/arduino-cli/configuration" "github.com/arduino/arduino-cli/configuration"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1" rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
paths "github.com/arduino/go-paths-helper" paths "github.com/arduino/go-paths-helper"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"go.bug.st/downloader/v2" "go.bug.st/downloader/v2"
) )
...@@ -179,14 +179,62 @@ func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequ ...@@ -179,14 +179,62 @@ func UpdateLibrariesIndex(ctx context.Context, req *rpc.UpdateLibrariesIndexRequ
if err != nil { if err != nil {
return err return err
} }
d, err := lm.UpdateIndex(config)
if err := lm.IndexFile.Parent().MkdirAll(); err != nil {
return err
}
// Create a temp dir to stage all downloads
tmp, err := paths.MkTempDir("", "library_index_download")
if err != nil { if err != nil {
return err return err
} }
Download(d, "Updating index: library_index.json", downloadCB) defer tmp.RemoveAll()
if d.Error() != nil {
return d.Error() // Download gzipped library_index
tmpIndexGz := tmp.Join("library_index.json.gz")
if d, err := downloader.DownloadWithConfig(tmpIndexGz.String(), librariesmanager.LibraryIndexGZURL.String(), *config, downloader.NoResume); err == nil {
if err := Download(d, "Updating index: library_index.json.gz", downloadCB); err != nil {
return errors.Wrap(err, "downloading library_index.json.gz")
}
} else {
return err
} }
// Download signature
tmpSignature := tmp.Join("library_index.json.sig")
if d, err := downloader.DownloadWithConfig(tmpSignature.String(), librariesmanager.LibraryIndexSignature.String(), *config, downloader.NoResume); err == nil {
if err := Download(d, "Updating index: library_index.json.sig", downloadCB); err != nil {
return errors.Wrap(err, "downloading library_index.json.sig")
}
} else {
return err
}
// Extract the real library_index
tmpIndex := tmp.Join("library_index.json")
if err := paths.GUnzip(tmpIndexGz, tmpIndex); err != nil {
return errors.Wrap(err, "unzipping library_index.json.gz")
}
// Check signature
if ok, _, err := security.VerifyArduinoDetachedSignature(tmpIndex, tmpSignature); err != nil {
return errors.Wrap(err, "verifying signature")
} else if !ok {
return errors.New("library_index.json has an invalid signature")
}
// Copy extracted library_index and signature to final destination
lm.IndexFile.Remove()
lm.IndexFileSignature.Remove()
if err := tmpIndex.CopyTo(lm.IndexFile); err != nil {
return errors.Wrap(err, "writing library_index.json")
}
if err := tmpSignature.CopyTo(lm.IndexFileSignature); err != nil {
return errors.Wrap(err, "writing library_index.json.sig")
}
// Rescan libraries
if _, err := Rescan(req.GetInstance().GetId()); err != nil { if _, err := Rescan(req.GetInstance().GetId()); err != nil {
return fmt.Errorf("rescanning filesystem: %s", err) return fmt.Errorf("rescanning filesystem: %s", err)
} }
......
...@@ -2,6 +2,18 @@ ...@@ -2,6 +2,18 @@
Here you can find a list of migration guides to handle breaking changes between releases of the CLI. Here you can find a list of migration guides to handle breaking changes between releases of the CLI.
## Unreleased
### Removed rarely used golang API
The following function from the `github.com/arduino/arduino-cli/arduino/libraries` module is no longer available:
```go
func (lm *LibrariesManager) UpdateIndex(config *downloader.Config) (*downloader.Downloader, error) {
```
We recommend using the equivalent gRPC API to perform the update of the index.
## 0.18.0 ## 0.18.0
### Breaking changes in gRPC API and CLI JSON output. ### Breaking changes in gRPC API and CLI JSON output.
......
...@@ -404,7 +404,9 @@ def test_install_with_zip_path(run_command, data_dir, downloads_dir): ...@@ -404,7 +404,9 @@ def test_install_with_zip_path(run_command, data_dir, downloads_dir):
def test_update_index(run_command): def test_update_index(run_command):
result = run_command("lib update-index") result = run_command("lib update-index")
assert result.ok assert result.ok
assert "Updating index: library_index.json downloaded" == result.stdout.splitlines()[-1].strip() lines = [l.strip() for l in result.stdout.splitlines()]
assert "Updating index: library_index.json.gz downloaded" in lines
assert "Updating index: library_index.json.sig downloaded" in lines
def test_uninstall(run_command): def test_uninstall(run_command):
...@@ -454,7 +456,8 @@ def test_search(run_command): ...@@ -454,7 +456,8 @@ def test_search(run_command):
result = run_command("lib search --names") result = run_command("lib search --names")
assert result.ok assert result.ok
lines = [l.strip() for l in result.stdout.strip().splitlines()] lines = [l.strip() for l in result.stdout.strip().splitlines()]
assert "Updating index: library_index.json downloaded" in lines assert "Updating index: library_index.json.gz downloaded" in lines
assert "Updating index: library_index.json.sig downloaded" in lines
libs = [l[6:].strip('"') for l in lines if "Name:" in l] libs = [l[6:].strip('"') for l in lines if "Name:" in l]
expected = {"WiFi101", "WiFi101OTA", "Firebase Arduino based on WiFi101"} expected = {"WiFi101", "WiFi101OTA", "Firebase Arduino based on WiFi101"}
......
...@@ -23,7 +23,8 @@ def test_update(run_command): ...@@ -23,7 +23,8 @@ def test_update(run_command):
assert "Updating index: package_index.json downloaded" in lines assert "Updating index: package_index.json downloaded" in lines
assert "Updating index: package_index.json.sig downloaded" in lines assert "Updating index: package_index.json.sig downloaded" in lines
assert "Updating index: library_index.json downloaded" in lines assert "Updating index: library_index.json.gz downloaded" in lines
assert "Updating index: library_index.json.sig downloaded" in lines
def test_update_showing_outdated(run_command): def test_update_showing_outdated(run_command):
...@@ -46,7 +47,8 @@ def test_update_showing_outdated(run_command): ...@@ -46,7 +47,8 @@ def test_update_showing_outdated(run_command):
assert "Updating index: package_index.json downloaded" in lines assert "Updating index: package_index.json downloaded" in lines
assert "Updating index: package_index.json.sig downloaded" in lines assert "Updating index: package_index.json.sig downloaded" in lines
assert "Updating index: library_index.json downloaded" in lines assert "Updating index: library_index.json.gz downloaded" in lines
assert "Updating index: library_index.json.sig downloaded" in lines
assert lines[-5].startswith("Arduino AVR Boards") assert lines[-5].startswith("Arduino AVR Boards")
assert lines[-2].startswith("USBHost") assert lines[-2].startswith("USBHost")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment