diff --git a/cmd/workspace/up/up_client.go b/cmd/workspace/up/up_client.go index 822f9aa5c..5dbd53fcb 100644 --- a/cmd/workspace/up/up_client.go +++ b/cmd/workspace/up/up_client.go @@ -99,10 +99,12 @@ func (cmd *UpCmd) prepareClient( // client's resolved WorkspaceConfig().Source, not the possibly-nil // source parsed above. if err := cmd.prepareResolvedWorkspaceSecrets(ctx, devsyConfig, client); err != nil { + _ = client.Delete(ctx, client2.DeleteOptions{Force: true, IgnoreNotFound: true}) return nil, err } if err := cmd.checkProviderUpdate(ctx, devsyConfig, client); err != nil { + _ = client.Delete(ctx, client2.DeleteOptions{Force: true, IgnoreNotFound: true}) return nil, err } return client, nil diff --git a/e2e/tests/up/sops.go b/e2e/tests/up/sops.go index f2b62aa6c..e4869485c 100644 --- a/e2e/tests/up/sops.go +++ b/e2e/tests/up/sops.go @@ -14,6 +14,7 @@ import ( ) const ( + sourceSubCmd = "source" sopsE2EPlaintext = "SUPER_SECRET_TEST_VALUE_7B91" sopsE2EMounted = "mounted-value-77" sopsE2EAgeIdentity = "AGE-SECRET-KEY-12UWYSAH2MRDQ5K4EWC4253PDTCSCS32Y5EFQ8TEN2SL3QYU2GN2SG88CZX" // gitleaks:allow @@ -163,12 +164,13 @@ func unsetSOPSEnv(name string) { } func registerSOPSSource(ctx context.Context, dtc *dockerTestContext, name, filePath string) { + _, _ = dtc.f.ExecCommandOutput(ctx, []string{secretCmd, sourceSubCmd, "remove", name}) _, err := dtc.f.ExecCommandOutput( ctx, - []string{secretCmd, "source", "add", "sops", name, filePath}, + []string{secretCmd, sourceSubCmd, "add", "sops", name, filePath}, ) framework.ExpectNoError(err) ginkgo.DeferCleanup(func() { - _, _ = dtc.f.ExecCommandOutput(ctx, []string{secretCmd, "source", "remove", name}) + _, _ = dtc.f.ExecCommandOutput(ctx, []string{secretCmd, sourceSubCmd, "remove", name}) }) } diff --git a/pkg/git/inspect.go b/pkg/git/inspect.go index 8ac6c974e..b3ea0b118 100644 --- a/pkg/git/inspect.go +++ b/pkg/git/inspect.go @@ -112,10 +112,23 @@ func fetchInspectionPR( } func fetchInspectionCommit(ctx context.Context, repo *Repo, commit string) (string, error) { - if _, err := repo.run(ctx, "fetch", "--depth=1", "origin", commit); err != nil { - return "", fmt.Errorf("fetch commit %q: %w", commit, err) + if _, err := repo.run(ctx, "cat-file", "-e", commit+"^{commit}"); err == nil { + return commit, nil } - return "FETCH_HEAD", nil + if _, err := repo.run(ctx, "fetch", "--depth=1", "origin", commit); err == nil { + return "FETCH_HEAD", nil + } + if _, err := repo.run(ctx, "fetch", "origin", "+refs/heads/*:refs/remotes/origin/*"); err == nil { + if _, err := repo.run(ctx, "cat-file", "-e", commit+"^{commit}"); err == nil { + return commit, nil + } + } + if _, err := repo.run(ctx, "fetch", "--unshallow", "origin"); err == nil { + if _, err := repo.run(ctx, "cat-file", "-e", commit+"^{commit}"); err == nil { + return commit, nil + } + } + return "", fmt.Errorf("fetch commit %q: commit not found in remote repository", commit) } // ReadFile returns the bytes for a path relative to the selected subpath @@ -152,12 +165,10 @@ func (i *Inspection) ReadFile(ctx context.Context, filePath string) ([]byte, err // another repository-relative path. func cleanRepoRelativePath(kind, value string) (string, error) { value = strings.TrimSpace(strings.ReplaceAll(value, "\\", "/")) + value = strings.TrimPrefix(value, "/") if value == "" { return "", nil } - if strings.HasPrefix(value, "/") { - return "", fmt.Errorf("git %s %q must be relative to the repository root", kind, value) - } clean := path.Clean(value) if clean == "." { return "", nil diff --git a/pkg/git/inspect_test.go b/pkg/git/inspect_test.go index 3392e671c..ed92301a8 100644 --- a/pkg/git/inspect_test.go +++ b/pkg/git/inspect_test.go @@ -51,7 +51,7 @@ func TestCleanInspectionSubPath(t *testing.T) { {name: "dot", value: ".", want: ""}, {name: "simple", value: testSubPath, want: testSubPath}, {name: "trailing slash", value: testSubPath + "/", want: testSubPath}, - {name: "absolute rejected", value: "/apps/bar", wantErr: true}, + {name: "leading slash stripped", value: "/apps/bar", want: "apps/bar"}, {name: "parent escape rejected", value: "../bar", wantErr: true}, {name: "parent only rejected", value: "..", wantErr: true}, } { diff --git a/pkg/secrets/project_config.go b/pkg/secrets/project_config.go index c9cfbbc9b..d410308e0 100644 --- a/pkg/secrets/project_config.go +++ b/pkg/secrets/project_config.go @@ -100,12 +100,10 @@ func validateProjectSecret(value string, sources map[string]struct{}) error { // never be able to read arbitrary host files. func CleanProjectSourcePath(value string) (string, error) { value = strings.TrimSpace(strings.ReplaceAll(value, "\\", "/")) + value = strings.TrimPrefix(value, "/") if value == "" { return "", fmt.Errorf("source path must not be empty") } - if strings.HasPrefix(value, "/") { - return "", fmt.Errorf("source path %q must be relative to the repository root", value) - } clean := path.Clean(value) if clean == "." || clean == ".." || strings.HasPrefix(clean, "../") { return "", fmt.Errorf("source path %q escapes the repository root", value) diff --git a/pkg/secrets/project_config_test.go b/pkg/secrets/project_config_test.go index d7eb2b8f4..47c312e0f 100644 --- a/pkg/secrets/project_config_test.go +++ b/pkg/secrets/project_config_test.go @@ -34,7 +34,11 @@ func TestCleanProjectSourcePath(t *testing.T) { require.NoError(t, err) require.Equal(t, "config/secrets.enc.yaml", clean) - for _, bad := range []string{"", "/etc/passwd", "../secret", "a/../../secret"} { + cleanAbs, err := CleanProjectSourcePath("/config/secrets.enc.yaml") + require.NoError(t, err) + require.Equal(t, "config/secrets.enc.yaml", cleanAbs) + + for _, bad := range []string{"", "../secret", "a/../../secret"} { _, err := CleanProjectSourcePath(bad) require.Error(t, err, bad) }