feat: generate vsa policy value from file

This commit is contained in:
mrjoelkamp
2024-08-14 09:16:24 -05:00
parent 8c6df28540
commit d2a8348ae8
14 changed files with 114 additions and 39 deletions

View File

@@ -28,16 +28,13 @@ func ExampleNewClient_registry() {
// get trusted tuf metadata
trustedMetadata := registryClient.GetMetadata()
if err != nil {
panic(err)
}
// top-level target files
targets := trustedMetadata.Targets[metadata.TARGETS].Signed.Targets
for _, t := range targets {
// download target files
_, _, err := registryClient.DownloadTarget(t.Path, filepath.Join(tufOutputPath, "download"))
_, err := registryClient.DownloadTarget(t.Path, filepath.Join(tufOutputPath, "download"))
if err != nil {
panic(err)
}

View File

@@ -24,10 +24,10 @@ func NewMockTufClient(srcPath string, dstPath string) *MockTufClient {
}
}
func (dc *MockTufClient) DownloadTarget(target string, filePath string) (actualFilePath string, data []byte, err error) {
func (dc *MockTufClient) DownloadTarget(target string, filePath string) (file *TargetFile, err error) {
src, err := os.Open(filepath.Join(dc.srcPath, target))
if err != nil {
return "", nil, err
return nil, err
}
defer src.Close()
@@ -40,11 +40,11 @@ func (dc *MockTufClient) DownloadTarget(target string, filePath string) (actualF
err = os.MkdirAll(filepath.Dir(dstFilePath), os.ModePerm)
if err != nil {
return "", nil, err
return nil, err
}
dst, err := os.Create(dstFilePath)
if err != nil {
return "", nil, err
return nil, err
}
defer dst.Close()
@@ -53,10 +53,10 @@ func (dc *MockTufClient) DownloadTarget(target string, filePath string) (actualF
b, err := io.ReadAll(tee)
if err != nil {
return "", nil, err
return nil, err
}
return dstFilePath, b, nil
return &TargetFile{ActualFilePath: dstFilePath, Data: b}, nil
}
type MockVersionChecker struct {

View File

@@ -81,7 +81,7 @@ func NewRegistryFetcher(metadataRepo, metadataTag, targetsRepo string) *Registry
func (d *RegistryFetcher) DownloadFile(urlPath string, maxLength int64, timeout time.Duration) ([]byte, error) {
d.timeout = timeout
imgRef, fileName, err := d.parseImgRef(urlPath)
imgRef, fileName, err := d.ParseImgRef(urlPath)
if err != nil {
return nil, err
}
@@ -186,7 +186,7 @@ func getDataFromLayer(fileLayer v1.Layer, maxLength int64) ([]byte, error) {
}
// parseImgRef maintains the Fetcher interface by parsing a URL path to an image reference and file name.
func (d *RegistryFetcher) parseImgRef(urlPath string) (imgRef, fileName string, err error) {
func (d *RegistryFetcher) ParseImgRef(urlPath string) (imgRef, fileName string, err error) {
// Check if repo is target or metadata
if strings.Contains(urlPath, d.targetsRepo) {
// determine if the target path contains subdirectories and set image name accordingly

View File

@@ -206,7 +206,7 @@ func TestParseImgRef(t *testing.T) {
metadataTag: LatestTag,
targetsRepo: targetsRepo,
}
imgRef, file, err := d.parseImgRef(tc.ref)
imgRef, file, err := d.ParseImgRef(tc.ref)
assert.NoError(t, err)
assert.Equal(t, tc.expectedRef, imgRef)
assert.Equal(t, tc.expectedFile, file)

View File

@@ -36,7 +36,7 @@ var (
)
type Downloader interface {
DownloadTarget(target, filePath string) (actualFilePath string, data []byte, err error)
DownloadTarget(target, filePath string) (file *TargetFile, err error)
}
type Client struct {
@@ -44,6 +44,13 @@ type Client struct {
cfg *config.UpdaterConfig
}
type TargetFile struct {
ActualFilePath string
TargetURI string
Digest string
Data []byte
}
// NewClient creates a new TUF client.
func NewClient(initialRoot []byte, tufPath, metadataSource, targetsSource string, versionChecker VersionChecker) (*Client, error) {
var tufSource Source
@@ -119,40 +126,73 @@ func NewClient(initialRoot []byte, tufPath, metadataSource, targetsSource string
return client, nil
}
func (t *Client) generateTargetURI(target *metadata.TargetFiles, digest string) (string, error) {
targetBaseURL := ensureTrailingSlash(t.cfg.RemoteTargetsURL)
targetRemotePath := target.Path
if t.cfg.PrefixTargetsWithHash {
baseName := filepath.Base(targetRemotePath)
dirName, ok := strings.CutSuffix(targetRemotePath, "/"+baseName)
if !ok {
// <hash>.<target-name>
targetRemotePath = fmt.Sprintf("%s.%s", digest, baseName)
} else {
// <dir-prefix>/<hash>.<target-name>
targetRemotePath = fmt.Sprintf("%s/%s.%s", dirName, digest, baseName)
}
}
fullURL := fmt.Sprintf("%s%s", targetBaseURL, targetRemotePath)
switch fetcher := t.cfg.Fetcher.(type) {
case *RegistryFetcher:
ref, _, err := fetcher.ParseImgRef(fullURL)
if err != nil {
return "", fmt.Errorf("failed to parse image reference: %w", err)
}
return ref, nil
case *fetcher.DefaultFetcher:
return fullURL, nil
default:
return "", fmt.Errorf("unsupported fetcher type: %T", fetcher)
}
}
// DownloadTarget downloads the target file using Updater. The Updater gets the target
// information, verifies if the target is already cached, and if it is not cached,
// downloads the target file.
func (t *Client) DownloadTarget(target string, filePath string) (actualFilePath string, data []byte, err error) {
func (t *Client) DownloadTarget(target string, filePath string) (file *TargetFile, err error) {
// search if the desired target is available
targetInfo, err := t.updater.GetTargetInfo(target)
if err != nil {
return "", nil, err
return nil, err
}
// check if filePath exists and create the directory if it doesn't
if _, err := os.Stat(filepath.Dir(filePath)); os.IsNotExist(err) {
err = os.MkdirAll(filepath.Dir(filePath), os.ModePerm)
if err != nil {
return "", nil, fmt.Errorf("failed to create target download directory '%s': %w", filepath.Dir(filePath), err)
return nil, fmt.Errorf("failed to create target download directory '%s': %w", filepath.Dir(filePath), err)
}
}
// target is available, so let's see if the target is already present locally
actualFilePath, data, err = t.updater.FindCachedTarget(targetInfo, filePath)
actualFilePath, data, err := t.updater.FindCachedTarget(targetInfo, filePath)
if err != nil {
return "", nil, fmt.Errorf("failed while finding a cached target: %w", err)
return nil, fmt.Errorf("failed while finding a cached target: %w", err)
}
if data != nil {
return actualFilePath, data, err
digest := util.SHA256Hex(data)
uri, err := t.generateTargetURI(targetInfo, digest)
return &TargetFile{ActualFilePath: actualFilePath, TargetURI: uri, Data: data, Digest: digest}, err
}
// target is not present locally, so let's try to download it
actualFilePath, data, err = t.updater.DownloadTarget(targetInfo, filePath, "")
if err != nil {
return "", nil, fmt.Errorf("failed to download target file %s - %w", target, err)
return nil, fmt.Errorf("failed to download target file %s - %w", target, err)
}
return actualFilePath, data, err
digest := util.SHA256Hex(data)
uri, err := t.generateTargetURI(targetInfo, digest)
return &TargetFile{ActualFilePath: actualFilePath, TargetURI: uri, Data: data, Digest: digest}, err
}
func (t *Client) GetMetadata() trustedmetadata.TrustedMetadata {

View File

@@ -122,14 +122,14 @@ func TestDownloadTarget(t *testing.T) {
targets := trustedMetadata.Targets[metadata.TARGETS].Signed.Targets
for _, target := range targets {
// download target files
_, _, err := tufClient.DownloadTarget(target.Path, filepath.Join(tufPath, "download"))
_, err := tufClient.DownloadTarget(target.Path, filepath.Join(tufPath, "download"))
assert.NoErrorf(t, err, "Failed to download target: %v", err)
}
// download delegated target
targetInfo, err := tufClient.updater.GetTargetInfo(delegatedTargetFile)
assert.NoError(t, err)
_, _, err = tufClient.DownloadTarget(targetInfo.Path, filepath.Join(tufPath, targetInfo.Path))
_, err = tufClient.DownloadTarget(targetInfo.Path, filepath.Join(tufPath, targetInfo.Path))
assert.NoError(t, err)
}
}

View File

@@ -67,11 +67,11 @@ func (vc *DefaultVersionChecker) CheckVersion(client Downloader) error {
// see https://github.com/Masterminds/semver/blob/v3.2.1/README.md#checking-version-constraints
// for more information on the expected format of the version constraints in the TUF repo
_, versionConstraintsBytes, err := client.DownloadTarget("version-constraints", "")
target, err := client.DownloadTarget("version-constraints", "")
if err != nil {
return fmt.Errorf("failed to download version-constraints: %w", err)
}
versionConstraints, err := semver.NewConstraint(string(versionConstraintsBytes))
versionConstraints, err := semver.NewConstraint(string(target.Data))
if err != nil {
return fmt.Errorf("failed to parse minimum version: %w", err)
}