func newServerSwizzler(t *testing.T) (map[string][]byte, *testutils.MetadataSwizzler) {
	serverMeta, cs, err := testutils.NewRepoMetadata("docker.com/notary", metadataDelegations...)
	require.NoError(t, err)

	serverSwizzler := testutils.NewMetadataSwizzler("docker.com/notary", serverMeta, cs)
	require.NoError(t, err)

	return serverMeta, serverSwizzler
}
Beispiel #2
0
// we just want sample metadata for a role - so we can build cached metadata
// and use it once.
func getSampleMeta(t *testing.T) (map[string][]byte, string) {
	gun := "docker.com/notary"
	delgNames := []string{"targets/a", "targets/a/b", "targets/a/b/force_parent_metadata"}
	if _cachedMeta == nil {
		meta, _, err := testutils.NewRepoMetadata(gun, delgNames...)
		require.NoError(t, err)

		_cachedMeta = meta
	}
	return _cachedMeta, gun
}
Beispiel #3
0
func TestRepoPrefixDoesNotMatch(t *testing.T) {
	gun := "docker.io/notary"
	meta, cs, err := testutils.NewRepoMetadata(gun)
	require.NoError(t, err)
	s := storage.NewMemStorage()

	ctx := context.WithValue(context.Background(), notary.CtxKeyMetaStore, s)
	ctx = context.WithValue(ctx, notary.CtxKeyKeyAlgo, data.ED25519Key)

	snChecksumBytes := sha256.Sum256(meta[data.CanonicalSnapshotRole])

	// successful gets
	handler := RootHandler(ctx, nil, cs, nil, nil, []string{"nope"})
	ts := httptest.NewServer(handler)

	url := fmt.Sprintf("%s/v2/%s/_trust/tuf/", ts.URL, gun)
	uploader, err := store.NewHTTPStore(url, "", "json", "key", http.DefaultTransport)
	require.NoError(t, err)

	require.Error(t, uploader.SetMulti(meta))

	// update the storage so we don't fail just because the metadata is missing
	for _, roleName := range data.BaseRoles {
		require.NoError(t, s.UpdateCurrent(gun, storage.MetaUpdate{
			Role:    roleName,
			Data:    meta[roleName],
			Version: 1,
		}))
	}

	_, err = uploader.GetSized(data.CanonicalSnapshotRole, notary.MaxDownloadSize)
	require.Error(t, err)

	_, err = uploader.GetSized(
		tufutils.ConsistentName(data.CanonicalSnapshotRole, snChecksumBytes[:]), notary.MaxDownloadSize)
	require.Error(t, err)

	_, err = uploader.GetKey(data.CanonicalTimestampRole)
	require.Error(t, err)

	// the httpstore doesn't actually delete all, so we do it manually
	req, err := http.NewRequest("DELETE", url, nil)
	require.NoError(t, err)
	res, err := http.DefaultTransport.RoundTrip(req)
	require.NoError(t, err)
	defer res.Body.Close()
	require.Equal(t, http.StatusNotFound, res.StatusCode)
}
Beispiel #4
0
// If the parent is not in the store, then the parent must be in the update else
// validation fails.
func TestValidateTargetsParentInUpdate(t *testing.T) {
	gun := "docker.com/notary"
	delgName := "targets/level1"
	metadata, _, err := testutils.NewRepoMetadata(gun, delgName, path.Join(delgName, "other"))
	require.NoError(t, err)
	store := storage.NewMemStorage()

	// load the root into the builder, else we can't load anything else
	builder := tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{})
	require.NoError(t, builder.Load(data.CanonicalRootRole, metadata[data.CanonicalRootRole], 0, false))

	targetsUpdate := storage.MetaUpdate{
		Role:    data.CanonicalTargetsRole,
		Version: 1,
		Data:    []byte("Invalid metadata"),
	}

	delgUpdate := storage.MetaUpdate{
		Role:    delgName,
		Version: 1,
		Data:    metadata[delgName],
	}

	upload := map[string]storage.MetaUpdate{
		"targets/level1":          delgUpdate,
		data.CanonicalTargetsRole: targetsUpdate,
	}

	// parent update not readable - fail
	_, err = loadAndValidateTargets(gun, builder, upload, store)
	require.Error(t, err)
	require.IsType(t, validation.ErrBadTargets{}, err)

	// because we sort the roles, the list of returned updates
	// will contain shallower roles first, in this case "targets",
	// and then "targets/level1"
	targetsUpdate.Data = metadata[data.CanonicalTargetsRole]
	upload[data.CanonicalTargetsRole] = targetsUpdate
	updates, err := loadAndValidateTargets(gun, builder, upload, store)
	require.NoError(t, err)
	require.Equal(t, []storage.MetaUpdate{targetsUpdate, delgUpdate}, updates)
}
Beispiel #5
0
func TestGenerateSnapshotNoKey(t *testing.T) {
	gun := "docker.com/notary"
	metadata, cs, err := testutils.NewRepoMetadata(gun)
	require.NoError(t, err)
	store := storage.NewMemStorage()

	// delete snapshot key in the cryptoservice
	for _, keyID := range cs.ListKeys(data.CanonicalSnapshotRole) {
		require.NoError(t, cs.RemoveKey(keyID))
	}

	builder := tuf.NewRepoBuilder(gun, cs, trustpinning.TrustPinConfig{})
	// only load root and targets
	require.NoError(t, builder.Load(data.CanonicalRootRole, metadata[data.CanonicalRootRole], 0, false))
	require.NoError(t, builder.Load(data.CanonicalTargetsRole, metadata[data.CanonicalTargetsRole], 0, false))

	_, err = generateSnapshot(gun, builder, store)
	require.Error(t, err)
	require.IsType(t, validation.ErrBadHierarchy{}, err)
}
// Update can succeed even if we cannot write any metadata to the repo (assuming
// no data in the repo)
func TestUpdateSucceedsEvenIfCannotWriteNewRepo(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode")
	}

	serverMeta, _, err := testutils.NewRepoMetadata("docker.com/notary", metadataDelegations...)
	require.NoError(t, err)

	ts := readOnlyServer(t, store.NewMemoryStore(serverMeta), http.StatusNotFound, "docker.com/notary")
	defer ts.Close()

	for role := range serverMeta {
		repo := newBlankRepo(t, ts.URL)
		repo.fileStore = &unwritableStore{MetadataStore: repo.fileStore, roleToNotWrite: role}
		_, err := repo.Update(false)

		if role == data.CanonicalRootRole {
			require.Error(t, err) // because checkRoot loads root from cache to check hashes
			continue
		} else {
			require.NoError(t, err)
		}

		for r, expected := range serverMeta {
			actual, err := repo.fileStore.GetMeta(r, -1)
			if r == role {
				require.Error(t, err)
				require.IsType(t, store.ErrMetaNotFound{}, err,
					"expected no data because unable to write for %s", role)
			} else {
				require.NoError(t, err, "problem getting repo metadata for %s", r)
				require.True(t, bytes.Equal(expected, actual),
					"%s: expected to update since only %s was unwritable", r, role)
			}
		}

		os.RemoveAll(repo.baseDir)
	}
}
Beispiel #7
0
// ### Target validation with delegations tests
func TestLoadTargetsLoadsNothingIfNoUpdates(t *testing.T) {
	gun := "docker.com/notary"
	metadata, _, err := testutils.NewRepoMetadata(gun)
	require.NoError(t, err)

	// load the root into the builder, else we can't load anything else
	builder := tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{})
	require.NoError(t, builder.Load(data.CanonicalRootRole, metadata[data.CanonicalRootRole], 0, false))

	store := storage.NewMemStorage()
	store.UpdateCurrent(gun, storage.MetaUpdate{
		Role:    data.CanonicalTargetsRole,
		Version: 1,
		Data:    metadata[data.CanonicalTargetsRole],
	})

	// if no updates, nothing is loaded
	targetsToUpdate, err := loadAndValidateTargets(gun, builder, nil, store)
	require.Empty(t, targetsToUpdate)
	require.NoError(t, err)
	require.False(t, builder.IsLoaded(data.CanonicalTargetsRole))
}
Beispiel #8
0
func TestRepoPrefixMatches(t *testing.T) {
	gun := "docker.io/notary"
	meta, cs, err := testutils.NewRepoMetadata(gun)
	require.NoError(t, err)

	ctx := context.WithValue(context.Background(), notary.CtxKeyMetaStore, storage.NewMemStorage())
	ctx = context.WithValue(ctx, notary.CtxKeyKeyAlgo, data.ED25519Key)

	snChecksumBytes := sha256.Sum256(meta[data.CanonicalSnapshotRole])

	// successful gets
	handler := RootHandler(ctx, nil, cs, nil, nil, []string{"docker.io"})
	ts := httptest.NewServer(handler)

	url := fmt.Sprintf("%s/v2/%s/_trust/tuf/", ts.URL, gun)
	uploader, err := store.NewHTTPStore(url, "", "json", "key", http.DefaultTransport)
	require.NoError(t, err)

	// uploading is cool
	require.NoError(t, uploader.SetMulti(meta))
	// getting is cool
	_, err = uploader.GetSized(data.CanonicalSnapshotRole, notary.MaxDownloadSize)
	require.NoError(t, err)

	_, err = uploader.GetSized(
		tufutils.ConsistentName(data.CanonicalSnapshotRole, snChecksumBytes[:]), notary.MaxDownloadSize)
	require.NoError(t, err)

	_, err = uploader.GetKey(data.CanonicalTimestampRole)
	require.NoError(t, err)

	// the httpstore doesn't actually delete all, so we do it manually
	req, err := http.NewRequest("DELETE", url, nil)
	require.NoError(t, err)
	res, err := http.DefaultTransport.RoundTrip(req)
	require.NoError(t, err)
	defer res.Body.Close()
	require.Equal(t, http.StatusOK, res.StatusCode)
}
Beispiel #9
0
// When a delegation role appears in the update and the parent does not, the
// parent is loaded from the DB if it can
func TestValidateTargetsRequiresStoredParent(t *testing.T) {
	gun := "docker.com/notary"
	delgName := "targets/level1"
	metadata, _, err := testutils.NewRepoMetadata(gun, delgName, path.Join(delgName, "other"))
	require.NoError(t, err)

	// load the root into the builder, else we can't load anything else
	builder := tuf.NewRepoBuilder(gun, nil, trustpinning.TrustPinConfig{})
	require.NoError(t, builder.Load(data.CanonicalRootRole, metadata[data.CanonicalRootRole], 0, false))

	delUpdate := storage.MetaUpdate{
		Role:    delgName,
		Version: 1,
		Data:    metadata[delgName],
	}

	upload := map[string]storage.MetaUpdate{delgName: delUpdate}

	store := storage.NewMemStorage()

	// if the DB has no "targets" role
	_, err = loadAndValidateTargets(gun, builder, upload, store)
	require.Error(t, err)
	require.IsType(t, validation.ErrBadTargets{}, err)

	// ensure the "targets" (the parent) is in the "db"
	store.UpdateCurrent(gun, storage.MetaUpdate{
		Role:    data.CanonicalTargetsRole,
		Version: 1,
		Data:    metadata[data.CanonicalTargetsRole],
	})

	updates, err := loadAndValidateTargets(gun, builder, upload, store)
	require.NoError(t, err)
	require.Len(t, updates, 1)
	require.Equal(t, delgName, updates[0].Role)
	require.Equal(t, metadata[delgName], updates[0].Data)
}
Beispiel #10
0
// This just checks the URL routing is working correctly and cache headers are set correctly.
// More detailed tests for this path including negative
// tests are located in /server/handlers/
func TestGetCurrentRole(t *testing.T) {
	store := storage.NewMemStorage()
	metadata, _, err := testutils.NewRepoMetadata("gun")
	require.NoError(t, err)

	// need both the snapshot and the timestamp, because when getting the current
	// timestamp the server checks to see if it's out of date (there's a new snapshot)
	// and if so, generates a new one
	store.UpdateCurrent("gun", storage.MetaUpdate{
		Role:    data.CanonicalSnapshotRole,
		Version: 1,
		Data:    metadata[data.CanonicalSnapshotRole],
	})
	store.UpdateCurrent("gun", storage.MetaUpdate{
		Role:    data.CanonicalTimestampRole,
		Version: 1,
		Data:    metadata[data.CanonicalTimestampRole],
	})

	ctx := context.WithValue(
		context.Background(), notary.CtxKeyMetaStore, store)

	ctx = context.WithValue(ctx, notary.CtxKeyKeyAlgo, data.ED25519Key)

	ccc := utils.NewCacheControlConfig(10, false)
	handler := RootHandler(ctx, nil, signed.NewEd25519(), ccc, ccc, nil)
	serv := httptest.NewServer(handler)
	defer serv.Close()

	res, err := http.Get(fmt.Sprintf(
		"%s/v2/gun/_trust/tuf/%s.json",
		serv.URL,
		data.CanonicalTimestampRole,
	))
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, res.StatusCode)
	verifyGetResponse(t, res, metadata[data.CanonicalTimestampRole])
}
// If a repo has corrupt metadata (in that the hash doesn't match the snapshot) or
// missing metadata, an update will replace all of it
func TestUpdateReplacesCorruptOrMissingMetadata(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode")
	}
	serverMeta, cs, err := testutils.NewRepoMetadata("docker.com/notary", metadataDelegations...)
	require.NoError(t, err)

	ts := readOnlyServer(t, store.NewMemoryStore(serverMeta), http.StatusNotFound, "docker.com/notary")
	defer ts.Close()

	repo := newBlankRepo(t, ts.URL)
	defer os.RemoveAll(repo.baseDir)

	_, err = repo.Update(false) // ensure we have all metadata to start with
	require.NoError(t, err)

	// we want to swizzle the local cache, not the server, so create a new one
	repoSwizzler := testutils.NewMetadataSwizzler("docker.com/notary", serverMeta, cs)
	repoSwizzler.MetadataCache = repo.fileStore

	for _, role := range repoSwizzler.Roles {
		for _, expt := range waysToMessUpLocalMetadata {
			text, messItUp := expt.desc, expt.swizzle
			for _, forWrite := range []bool{true, false} {
				require.NoError(t, messItUp(repoSwizzler, role), "could not fuzz %s (%s)", role, text)
				_, err := repo.Update(forWrite)
				require.NoError(t, err)
				for r, expected := range serverMeta {
					actual, err := repo.fileStore.GetMeta(r, -1)
					require.NoError(t, err, "problem getting repo metadata for %s", role)
					require.True(t, bytes.Equal(expected, actual),
						"%s for %s: expected to recover after update", text, role)
				}
			}
		}
	}
}