Merge pull request #5510 from cli/gist-delete-check

Avoid crash when deleting gist with no owner
This commit is contained in:
Mislav Marohnić 2022-04-25 18:32:11 +02:00 committed by GitHub
commit adc9abd5ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 72 deletions

View file

@ -2,6 +2,7 @@ package delete
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"strings" "strings"
@ -58,8 +59,6 @@ func deleteRun(opts *DeleteOptions) error {
return err return err
} }
apiClient := api.NewClientFromHTTP(client)
cfg, err := opts.Config() cfg, err := opts.Config()
if err != nil { if err != nil {
return err return err
@ -70,21 +69,11 @@ func deleteRun(opts *DeleteOptions) error {
return err return err
} }
gist, err := shared.GetGist(client, host, gistID) apiClient := api.NewClientFromHTTP(client)
if err != nil { if err := deleteGist(apiClient, host, gistID); err != nil {
return err if errors.Is(err, shared.NotFoundErr) {
} return fmt.Errorf("unable to delete gist %s: either the gist is not found or it is not owned by you", gistID)
username, err := api.CurrentLoginName(apiClient, host) }
if err != nil {
return err
}
if username != gist.Owner.Login {
return errors.New("you do not own this gist")
}
err = deleteGist(apiClient, host, gistID)
if err != nil {
return err return err
} }
@ -95,6 +84,10 @@ func deleteGist(apiClient *api.Client, hostname string, gistID string) error {
path := "gists/" + gistID path := "gists/" + gistID
err := apiClient.REST(hostname, "DELETE", path, nil, nil) err := apiClient.REST(hostname, "DELETE", path, nil, nil)
if err != nil { if err != nil {
var httpErr api.HTTPError
if errors.As(err, &httpErr) && httpErr.StatusCode == 404 {
return shared.NotFoundErr
}
return err return err
} }
return nil return nil

View file

@ -6,7 +6,6 @@ import (
"testing" "testing"
"github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/config"
"github.com/cli/cli/v2/pkg/cmd/gist/shared"
"github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/cmdutil"
"github.com/cli/cli/v2/pkg/httpmock" "github.com/cli/cli/v2/pkg/httpmock"
"github.com/cli/cli/v2/pkg/iostreams" "github.com/cli/cli/v2/pkg/iostreams"
@ -57,87 +56,59 @@ func TestNewCmdDelete(t *testing.T) {
func Test_deleteRun(t *testing.T) { func Test_deleteRun(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
opts *DeleteOptions opts DeleteOptions
gist *shared.Gist
httpStubs func(*httpmock.Registry) httpStubs func(*httpmock.Registry)
nontty bool
wantErr bool wantErr bool
wantStdout string
wantStderr string wantStderr string
wantParams map[string]interface{}
}{ }{
{ {
name: "no such gist",
wantErr: true,
}, {
name: "another user's gist",
gist: &shared.Gist{
ID: "1234",
Files: map[string]*shared.GistFile{
"cicada.txt": {
Filename: "cicada.txt",
Content: "bwhiizzzbwhuiiizzzz",
Type: "text/plain",
},
},
Owner: &shared.GistOwner{Login: "octocat2"},
},
wantErr: true,
wantStderr: "you do not own this gist",
}, {
name: "successfully delete", name: "successfully delete",
gist: &shared.Gist{ opts: DeleteOptions{
ID: "1234", Selector: "1234",
Files: map[string]*shared.GistFile{
"cicada.txt": {
Filename: "cicada.txt",
Content: "bwhiizzzbwhuiiizzzz",
Type: "text/plain",
},
},
Owner: &shared.GistOwner{Login: "octocat"},
}, },
httpStubs: func(reg *httpmock.Registry) { httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("DELETE", "gists/1234"), reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StringResponse("{}")) httpmock.StatusStringResponse(200, "{}"))
}, },
wantErr: false, wantErr: false,
wantStdout: "",
wantStderr: "",
},
{
name: "not found",
opts: DeleteOptions{
Selector: "1234",
},
httpStubs: func(reg *httpmock.Registry) {
reg.Register(httpmock.REST("DELETE", "gists/1234"),
httpmock.StatusStringResponse(404, "{}"))
},
wantErr: true,
wantStdout: "",
wantStderr: "",
}, },
} }
for _, tt := range tests { for _, tt := range tests {
reg := &httpmock.Registry{} reg := &httpmock.Registry{}
if tt.gist == nil {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.StatusStringResponse(404, "Not Found"))
} else {
reg.Register(httpmock.REST("GET", "gists/1234"),
httpmock.JSONResponse(tt.gist))
reg.Register(httpmock.GraphQL(`query UserCurrent\b`),
httpmock.StringResponse(`{"data":{"viewer":{"login":"octocat"}}}`))
}
if tt.httpStubs != nil { if tt.httpStubs != nil {
tt.httpStubs(reg) tt.httpStubs(reg)
} }
if tt.opts == nil {
tt.opts = &DeleteOptions{}
}
tt.opts.HttpClient = func() (*http.Client, error) { tt.opts.HttpClient = func() (*http.Client, error) {
return &http.Client{Transport: reg}, nil return &http.Client{Transport: reg}, nil
} }
tt.opts.Config = func() (config.Config, error) { tt.opts.Config = func() (config.Config, error) {
return config.NewBlankConfig(), nil return config.NewBlankConfig(), nil
} }
io, _, _, _ := iostreams.Test() ios, _, _, _ := iostreams.Test()
io.SetStdoutTTY(!tt.nontty) ios.SetStdoutTTY(false)
io.SetStdinTTY(!tt.nontty) ios.SetStdinTTY(false)
tt.opts.IO = io tt.opts.IO = ios
tt.opts.Selector = "1234"
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
err := deleteRun(tt.opts) err := deleteRun(&tt.opts)
reg.Verify(t) reg.Verify(t)
if tt.wantErr { if tt.wantErr {
assert.Error(t, err) assert.Error(t, err)