Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option to disable integrity checks in core install (for development purposes) #2740

Merged
merged 4 commits into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions commands/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import (
"google.golang.org/grpc/status"
)

func installTool(ctx context.Context, pm *packagemanager.PackageManager, tool *cores.ToolRelease, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB) error {
func installTool(ctx context.Context, pm *packagemanager.PackageManager, tool *cores.ToolRelease, downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB, checks resources.IntegrityCheckMode) error {
pme, release := pm.NewExplorer()
defer release()

Expand All @@ -56,7 +56,7 @@ func installTool(ctx context.Context, pm *packagemanager.PackageManager, tool *c
return errors.New(i18n.Tr("downloading %[1]s tool: %[2]s", tool, err))
}
taskCB(&rpc.TaskProgress{Completed: true})
if err := pme.InstallTool(tool, taskCB, true); err != nil {
if err := pme.InstallTool(tool, taskCB, true, checks); err != nil {
return errors.New(i18n.Tr("installing %[1]s tool: %[2]s", tool, err))
}
return nil
Expand Down Expand Up @@ -282,7 +282,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor
// Install builtin tools if necessary
if len(builtinToolsToInstall) > 0 {
for _, toolRelease := range builtinToolsToInstall {
if err := installTool(ctx, pmb.Build(), toolRelease, downloadCallback, taskCallback); err != nil {
if err := installTool(ctx, pmb.Build(), toolRelease, downloadCallback, taskCallback, resources.IntegrityCheckFull); err != nil {
e := &cmderrors.InitFailedError{
Code: codes.Internal,
Cause: err,
Expand Down Expand Up @@ -394,7 +394,7 @@ func (s *arduinoCoreServerImpl) Init(req *rpc.InitRequest, stream rpc.ArduinoCor

// Install library
taskCallback(&rpc.TaskProgress{Name: i18n.Tr("Installing library %s", libraryRef)})
if err := libRelease.Resource.Install(pme.DownloadDir, libRoot, libDir); err != nil {
if err := libRelease.Resource.Install(pme.DownloadDir, libRoot, libDir, resources.IntegrityCheckFull); err != nil {
taskCallback(&rpc.TaskProgress{Name: i18n.Tr("Error installing library %s", libraryRef)})
e := &cmderrors.FailedLibraryInstallError{Cause: err}
responseError(e.GRPCStatus())
Expand Down
7 changes: 4 additions & 3 deletions commands/service_library_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/arduino/arduino-cli/internal/arduino/libraries"
"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesindex"
"github.com/arduino/arduino-cli/internal/arduino/libraries/librariesmanager"
"github.com/arduino/arduino-cli/internal/arduino/resources"
"github.com/arduino/arduino-cli/internal/i18n"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/arduino/go-paths-helper"
Expand Down Expand Up @@ -159,7 +160,7 @@ func (s *arduinoCoreServerImpl) LibraryInstall(req *rpc.LibraryInstallRequest, s
if err := downloadLibrary(ctx, downloadsDir, libRelease, downloadCB, taskCB, downloadReason, s.settings); err != nil {
return err
}
if err := installLibrary(lmi, downloadsDir, libRelease, installTask, taskCB); err != nil {
if err := installLibrary(lmi, downloadsDir, libRelease, installTask, taskCB, resources.IntegrityCheckFull); err != nil {
return err
}
}
Expand All @@ -179,7 +180,7 @@ func (s *arduinoCoreServerImpl) LibraryInstall(req *rpc.LibraryInstallRequest, s
return nil
}

func installLibrary(lmi *librariesmanager.Installer, downloadsDir *paths.Path, libRelease *librariesindex.Release, installTask *librariesmanager.LibraryInstallPlan, taskCB rpc.TaskProgressCB) error {
func installLibrary(lmi *librariesmanager.Installer, downloadsDir *paths.Path, libRelease *librariesindex.Release, installTask *librariesmanager.LibraryInstallPlan, taskCB rpc.TaskProgressCB, checks resources.IntegrityCheckMode) error {
taskCB(&rpc.TaskProgress{Name: i18n.Tr("Installing %s", libRelease)})
logrus.WithField("library", libRelease).Info("Installing library")

Expand All @@ -193,7 +194,7 @@ func installLibrary(lmi *librariesmanager.Installer, downloadsDir *paths.Path, l

installPath := installTask.TargetPath
tmpDirPath := installPath.Parent()
if err := libRelease.Resource.Install(downloadsDir, tmpDirPath, installPath); err != nil {
if err := libRelease.Resource.Install(downloadsDir, tmpDirPath, installPath, checks); err != nil {
return &cmderrors.FailedLibraryInstallError{Cause: err}
}

Expand Down
7 changes: 6 additions & 1 deletion commands/service_platform_install.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/arduino/arduino-cli/commands/cmderrors"
"github.com/arduino/arduino-cli/commands/internal/instances"
"github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
"github.com/arduino/arduino-cli/internal/arduino/resources"
"github.com/arduino/arduino-cli/internal/i18n"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
)
Expand Down Expand Up @@ -95,7 +96,11 @@ func (s *arduinoCoreServerImpl) PlatformInstall(req *rpc.PlatformInstallRequest,
}
}

if err := pme.DownloadAndInstallPlatformAndTools(ctx, platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall()); err != nil {
checks := resources.IntegrityCheckFull
if s.settings.BoardManagerEnableUnsafeInstall() {
checks = resources.IntegrityCheckNone
}
if err := pme.DownloadAndInstallPlatformAndTools(ctx, platformRelease, tools, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall(), checks); err != nil {
return err
}

Expand Down
7 changes: 6 additions & 1 deletion commands/service_platform_upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/arduino/arduino-cli/commands/internal/instances"
"github.com/arduino/arduino-cli/internal/arduino/cores"
"github.com/arduino/arduino-cli/internal/arduino/cores/packagemanager"
"github.com/arduino/arduino-cli/internal/arduino/resources"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
)

Expand Down Expand Up @@ -75,7 +76,11 @@ func (s *arduinoCoreServerImpl) PlatformUpgrade(req *rpc.PlatformUpgradeRequest,
Package: req.GetPlatformPackage(),
PlatformArchitecture: req.GetArchitecture(),
}
platform, err := pme.DownloadAndInstallPlatformUpgrades(ctx, ref, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall())
checks := resources.IntegrityCheckFull
if s.settings.BoardManagerEnableUnsafeInstall() {
checks = resources.IntegrityCheckNone
}
platform, err := pme.DownloadAndInstallPlatformUpgrades(ctx, ref, downloadCB, taskCB, req.GetSkipPostInstall(), req.GetSkipPreUninstall(), checks)
if err != nil {
return platform, err
}
Expand Down
2 changes: 2 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

- `board_manager`
- `additional_urls` - the URLs to any additional Boards Manager package index files needed for your boards platforms.
- `enable_unsafe_install` - set to `true` to allow installation of packages that do not pass the checksum test. This
is considered an unsafe installation method and should be used only for development purposes.
- `daemon` - options related to running Arduino CLI as a [gRPC] server.
- `port` - TCP port used for gRPC client connections.
- `directories` - directories used by Arduino CLI.
Expand Down
11 changes: 5 additions & 6 deletions internal/arduino/cores/packageindex/index.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,12 @@ package packageindex

import (
"encoding/json"
"errors"
"fmt"
"slices"

"github.com/arduino/arduino-cli/internal/arduino/cores"
"github.com/arduino/arduino-cli/internal/arduino/resources"
"github.com/arduino/arduino-cli/internal/arduino/security"
"github.com/arduino/arduino-cli/internal/i18n"
"github.com/arduino/go-paths-helper"
easyjson "github.com/mailru/easyjson"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -273,14 +271,15 @@ func (inPlatformRelease indexPlatformRelease) extractPlatformIn(outPackage *core
outPlatform.Deprecated = inPlatformRelease.Deprecated
}

size, err := inPlatformRelease.Size.Int64()
if err != nil {
return errors.New(i18n.Tr("invalid platform archive size: %s", err))
}
outPlatformRelease := outPlatform.GetOrCreateRelease(inPlatformRelease.Version)
outPlatformRelease.Name = inPlatformRelease.Name
outPlatformRelease.Category = inPlatformRelease.Category
outPlatformRelease.IsTrusted = trusted
size, err := inPlatformRelease.Size.Int64()
if err != nil {
logrus.Warningf("invalid platform %s archive size: %s", outPlatformRelease, err)
size = 0
}
outPlatformRelease.Resource = &resources.DownloadResource{
ArchiveFileName: inPlatformRelease.ArchiveFileName,
Checksum: inPlatformRelease.Checksum,
Expand Down
22 changes: 12 additions & 10 deletions internal/arduino/cores/packagemanager/install_uninstall.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/arduino/arduino-cli/commands/cmderrors"
"github.com/arduino/arduino-cli/internal/arduino/cores"
"github.com/arduino/arduino-cli/internal/arduino/cores/packageindex"
"github.com/arduino/arduino-cli/internal/arduino/resources"
"github.com/arduino/arduino-cli/internal/i18n"
rpc "github.com/arduino/arduino-cli/rpc/cc/arduino/cli/commands/v1"
"github.com/arduino/go-paths-helper"
Expand All @@ -40,6 +41,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
taskCB rpc.TaskProgressCB,
skipPostInstall bool,
skipPreUninstall bool,
checks resources.IntegrityCheckMode,
) (*cores.PlatformRelease, error) {
if platformRef.PlatformVersion != nil {
return nil, &cmderrors.InvalidArgumentError{Message: i18n.Tr("Upgrade doesn't accept parameters with version")}
Expand All @@ -64,7 +66,7 @@ func (pme *Explorer) DownloadAndInstallPlatformUpgrades(
if err != nil {
return nil, &cmderrors.PlatformNotFoundError{Platform: platformRef.String()}
}
if err := pme.DownloadAndInstallPlatformAndTools(ctx, platformRelease, tools, downloadCB, taskCB, skipPostInstall, skipPreUninstall); err != nil {
if err := pme.DownloadAndInstallPlatformAndTools(ctx, platformRelease, tools, downloadCB, taskCB, skipPostInstall, skipPreUninstall, checks); err != nil {
return nil, err
}

Expand All @@ -78,7 +80,7 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
ctx context.Context,
platformRelease *cores.PlatformRelease, requiredTools []*cores.ToolRelease,
downloadCB rpc.DownloadProgressCB, taskCB rpc.TaskProgressCB,
skipPostInstall bool, skipPreUninstall bool) error {
skipPostInstall bool, skipPreUninstall bool, checks resources.IntegrityCheckMode) error {
log := pme.log.WithField("platform", platformRelease)

// Prerequisite checks before install
Expand Down Expand Up @@ -106,7 +108,7 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(

// Install tools first
for _, tool := range toolsToInstall {
if err := pme.InstallTool(tool, taskCB, skipPostInstall); err != nil {
if err := pme.InstallTool(tool, taskCB, skipPostInstall, checks); err != nil {
return err
}
}
Expand Down Expand Up @@ -138,7 +140,7 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
}

// Install
if err := pme.InstallPlatform(platformRelease); err != nil {
if err := pme.InstallPlatform(platformRelease, checks); err != nil {
log.WithError(err).Error("Cannot install platform")
return &cmderrors.FailedInstallError{Message: i18n.Tr("Cannot install platform"), Cause: err}
}
Expand Down Expand Up @@ -196,18 +198,18 @@ func (pme *Explorer) DownloadAndInstallPlatformAndTools(
}

// InstallPlatform installs a specific release of a platform.
func (pme *Explorer) InstallPlatform(platformRelease *cores.PlatformRelease) error {
func (pme *Explorer) InstallPlatform(platformRelease *cores.PlatformRelease, checks resources.IntegrityCheckMode) error {
destDir := pme.PackagesDir.Join(
platformRelease.Platform.Package.Name,
"hardware",
platformRelease.Platform.Architecture,
platformRelease.Version.String())
return pme.InstallPlatformInDirectory(platformRelease, destDir)
return pme.InstallPlatformInDirectory(platformRelease, destDir, checks)
}

// InstallPlatformInDirectory installs a specific release of a platform in a specific directory.
func (pme *Explorer) InstallPlatformInDirectory(platformRelease *cores.PlatformRelease, destDir *paths.Path) error {
if err := platformRelease.Resource.Install(pme.DownloadDir, pme.tempDir, destDir); err != nil {
func (pme *Explorer) InstallPlatformInDirectory(platformRelease *cores.PlatformRelease, destDir *paths.Path, checks resources.IntegrityCheckMode) error {
if err := platformRelease.Resource.Install(pme.DownloadDir, pme.tempDir, destDir, checks); err != nil {
return errors.New(i18n.Tr("installing platform %[1]s: %[2]s", platformRelease, err))
}
if d, err := destDir.Abs(); err == nil {
Expand Down Expand Up @@ -320,7 +322,7 @@ func (pme *Explorer) UninstallPlatform(platformRelease *cores.PlatformRelease, t
}

// InstallTool installs a specific release of a tool.
func (pme *Explorer) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB, skipPostInstall bool) error {
func (pme *Explorer) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.TaskProgressCB, skipPostInstall bool, checks resources.IntegrityCheckMode) error {
log := pme.log.WithField("Tool", toolRelease)

if toolRelease.IsInstalled() {
Expand All @@ -343,7 +345,7 @@ func (pme *Explorer) InstallTool(toolRelease *cores.ToolRelease, taskCB rpc.Task
"tools",
toolRelease.Tool.Name,
toolRelease.Version.String())
err := toolResource.Install(pme.DownloadDir, pme.tempDir, destDir)
err := toolResource.Install(pme.DownloadDir, pme.tempDir, destDir, checks)
if err != nil {
log.WithError(err).Warn("Cannot install tool")
return &cmderrors.FailedInstallError{Message: i18n.Tr("Cannot install tool %s", toolRelease), Cause: err}
Expand Down
4 changes: 2 additions & 2 deletions internal/arduino/cores/packagemanager/profiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ func (pmb *Builder) installMissingProfilePlatform(ctx context.Context, platformR

// Perform install
taskCB(&rpc.TaskProgress{Name: i18n.Tr("Installing platform %s", tmpPlatformRelease)})
if err := tmpPme.InstallPlatformInDirectory(tmpPlatformRelease, destDir); err != nil {
if err := tmpPme.InstallPlatformInDirectory(tmpPlatformRelease, destDir, resources.IntegrityCheckFull); err != nil {
taskCB(&rpc.TaskProgress{Name: i18n.Tr("Error installing platform %s", tmpPlatformRelease)})
return &cmderrors.FailedInstallError{Message: i18n.Tr("Error installing platform %s", tmpPlatformRelease), Cause: err}
}
Expand Down Expand Up @@ -183,7 +183,7 @@ func (pmb *Builder) installMissingProfileTool(ctx context.Context, toolRelease *

// Install tool
taskCB(&rpc.TaskProgress{Name: i18n.Tr("Installing tool %s", toolRelease)})
if err := toolResource.Install(pmb.DownloadDir, tmp, destDir); err != nil {
if err := toolResource.Install(pmb.DownloadDir, tmp, destDir, resources.IntegrityCheckFull); err != nil {
taskCB(&rpc.TaskProgress{Name: i18n.Tr("Error installing tool %s", toolRelease)})
return &cmderrors.FailedInstallError{Message: i18n.Tr("Error installing tool %s", toolRelease), Cause: err}
}
Expand Down
21 changes: 15 additions & 6 deletions internal/arduino/resources/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,27 @@ import (
"go.bug.st/cleanup"
)

type IntegrityCheckMode int

const (
IntegrityCheckFull IntegrityCheckMode = iota
IntegrityCheckNone
)

// Install installs the resource in three steps:
// - the archive is unpacked in a temporary subdir of tempPath
// - there should be only one root dir in the unpacked content
// - the only root dir is moved/renamed to/as the destination directory
// Note that tempPath and destDir must be on the same filesystem partition
// otherwise the last step will fail.
func (release *DownloadResource) Install(downloadDir, tempPath, destDir *paths.Path) error {
// Check the integrity of the package
if ok, err := release.TestLocalArchiveIntegrity(downloadDir); err != nil {
return errors.New(i18n.Tr("testing local archive integrity: %s", err))
} else if !ok {
return errors.New(i18n.Tr("checking local archive integrity"))
func (release *DownloadResource) Install(downloadDir, tempPath, destDir *paths.Path, checks IntegrityCheckMode) error {
if checks != IntegrityCheckNone {
// Check the integrity of the package
if ok, err := release.TestLocalArchiveIntegrity(downloadDir); err != nil {
return errors.New(i18n.Tr("testing local archive integrity: %s", err))
} else if !ok {
return errors.New(i18n.Tr("checking local archive integrity"))
}
}

// Create a temporary dir to extract package
Expand Down
4 changes: 2 additions & 2 deletions internal/arduino/resources/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func TestInstallPlatform(t *testing.T) {
Size: 157,
}

require.NoError(t, r.Install(downloadDir, tempPath, destDir))
require.NoError(t, r.Install(downloadDir, tempPath, destDir, IntegrityCheckFull))
})

tests := []struct {
Expand Down Expand Up @@ -82,7 +82,7 @@ func TestInstallPlatform(t *testing.T) {
require.NoError(t, err)
require.NoError(t, os.WriteFile(path.Join(downloadDir.String(), testFileName), origin, 0644))

err = test.downloadResource.Install(downloadDir, tempPath, destDir)
err = test.downloadResource.Install(downloadDir, tempPath, destDir, IntegrityCheckFull)
require.Error(t, err)
require.Contains(t, err.Error(), test.error)
})
Expand Down
7 changes: 7 additions & 0 deletions internal/cli/configuration/board_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,10 @@ func (settings *Settings) BoardManagerAdditionalUrls() []string {
}
return settings.Defaults.GetStringSlice("board_manager.additional_urls")
}

func (settings *Settings) BoardManagerEnableUnsafeInstall() bool {
if v, ok, _ := settings.GetBoolOk("board_manager.enable_unsafe_install"); ok {
return v
}
return settings.Defaults.GetBool("board_manager.enable_unsafe_install")
}
4 changes: 4 additions & 0 deletions internal/cli/configuration/configuration.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
"type": "string",
"format": "uri"
}
},
"enable_unsafe_install": {
"description": "set to `true` to allow installation of packages that do not pass the checksum test. This is considered an unsafe installation method and should be used only for development purposes.",
"type": "boolean"
}
},
"type": "object"
Expand Down
1 change: 1 addition & 0 deletions internal/cli/configuration/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func SetDefaults(settings *Settings) {

// Boards Manager
setDefaultValueAndKeyTypeSchema("board_manager.additional_urls", []string{})
setDefaultValueAndKeyTypeSchema("board_manager.enable_unsafe_install", false)

// arduino directories
setDefaultValueAndKeyTypeSchema("directories.data", getDefaultArduinoDataDir())
Expand Down
17 changes: 17 additions & 0 deletions internal/integrationtest/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1366,3 +1366,20 @@ func TestCoreInstallWithWrongArchiveSize(t *testing.T) {
_, _, err = cli.Run("--additional-urls", "https://raw.githubusercontent.com/geolink/opentracker-arduino-board/bf6158ebab0402db217bfb02ea61461ddc6f2940/package_opentracker_index.json", "core", "install", "opentracker:[email protected]")
require.NoError(t, err)
}

func TestCoreInstallWithMissingOrInvalidChecksumAndUnsafeInstallEnabled(t *testing.T) {
// See: https://github.com/arduino/arduino-cli/issues/1468
env, cli := integrationtest.CreateArduinoCLIWithEnvironment(t)
defer env.CleanUp()

_, _, err := cli.Run("--additional-urls", "https://raw.githubusercontent.com/keyboardio/ArduinoCore-GD32-Keyboardio/refs/heads/main/package_gd32_index.json", "core", "update-index")
require.NoError(t, err)

_, _, err = cli.Run("--additional-urls", "https://raw.githubusercontent.com/keyboardio/ArduinoCore-GD32-Keyboardio/refs/heads/main/package_gd32_index.json", "core", "install", "GD32Community:gd32")
require.Error(t, err)

_, _, err = cli.RunWithCustomEnv(
map[string]string{"ARDUINO_BOARD_MANAGER_ENABLE_UNSAFE_INSTALL": "true"},
"--additional-urls", "https://raw.githubusercontent.com/keyboardio/ArduinoCore-GD32-Keyboardio/refs/heads/main/package_gd32_index.json", "core", "install", "GD32Community:gd32")
require.NoError(t, err)
}
Loading