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

Release finding #585

Merged
merged 9 commits into from
Feb 2, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
16 changes: 9 additions & 7 deletions proto/shentu/bounty/v1/bounty.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ message Finding {
uint64 finding_id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = "yaml:\"id\""];
string title = 2 [(gogoproto.moretags) = "yaml:\"title\""];
google.protobuf.Any encrypted_desc = 3 [(cosmos_proto.accepts_interface) = "EncryptedDesc",(gogoproto.moretags) = "yaml:\"encrypted_desc\""];
uint64 program_id = 4 [(gogoproto.moretags) = "yaml:\"program_id\""];
SeverityLevel severity_level = 5 [(gogoproto.moretags) = "yaml:\"severity_level\""];;
google.protobuf.Any encrypted_poc = 6 [(cosmos_proto.accepts_interface) = "EncryptedPoc",(gogoproto.moretags) = "yaml:\"encrypted_poc\""];
string submitter_address = 7 [(gogoproto.moretags) = "yaml:\"submitter_address\""];
FindingStatus finding_status = 8 [(gogoproto.moretags) = "yaml:\"finding_status\""];

google.protobuf.Any encrypted_comment = 9 [(cosmos_proto.accepts_interface) = "EncryptedComment",(gogoproto.moretags) = "yaml:\"encrypted_comment\""];
string desc = 4 [(gogoproto.moretags) = "yaml:\"desc\""];
uint64 program_id = 5 [(gogoproto.moretags) = "yaml:\"program_id\""];
SeverityLevel severity_level = 6 [(gogoproto.moretags) = "yaml:\"severity_level\""];;
google.protobuf.Any encrypted_poc = 7 [(cosmos_proto.accepts_interface) = "EncryptedPoc",(gogoproto.moretags) = "yaml:\"encrypted_poc\""];
string poc = 8 [(gogoproto.moretags) = "yaml:\"poc\""];
string submitter_address = 9 [(gogoproto.moretags) = "yaml:\"submitter_address\""];
FindingStatus finding_status = 10 [(gogoproto.moretags) = "yaml:\"finding_status\""];
google.protobuf.Any encrypted_comment = 11 [(cosmos_proto.accepts_interface) = "EncryptedComment",(gogoproto.moretags) = "yaml:\"encrypted_comment\""];
string comment = 12 [(cosmos_proto.accepts_interface) = "Comment",(gogoproto.moretags) = "yaml:\"comment\""];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think encrypted_poc & poc or encrypted_comment & comment pairs can be merged to accept an interface?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I will merge these.

}

message EciesEncryptedDesc {
Expand Down
22 changes: 21 additions & 1 deletion proto/shentu/bounty/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ service Msg {

// HostRejectFinding defines a method for host reject a finding.
rpc HostRejectFinding(MsgHostRejectFinding) returns (MsgHostRejectFindingResponse);

// MsgReleaseFinding defines a method for release a finding.
rpc ReleaseFinding(MsgReleaseFinding) returns (MsgReleaseFindingResponse);
}

// MsgCreateProgram defines a SDK message for creating a new program.
Expand All @@ -48,6 +51,8 @@ message MsgCreateProgram {
message MsgCreateProgramResponse {
uint64 program_id = 1 [(gogoproto.jsontag) = "id", (gogoproto.moretags) = "yaml:\"id\""];
}

// MsgSubmitFinding defines a message to submit a finding.
message MsgSubmitFinding {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
Expand All @@ -60,6 +65,7 @@ message MsgSubmitFinding {
string submitter_address = 6 [(gogoproto.moretags) = "yaml:\"submitter_address\""];
}

// MsgSubmitFindingResponse defines the MsgSubmitFinding response type.
message MsgSubmitFindingResponse {
option (gogoproto.goproto_getters) = false;
uint64 finding_id = 1 [(gogoproto.jsontag) = "finding_id", (gogoproto.moretags) = "yaml:\"finding_id\""];
Expand Down Expand Up @@ -89,4 +95,18 @@ message MsgHostRejectFinding {
}

// MsgHostRejectFindingResponse defines the Msg/ostRejectFinding response type.
message MsgHostRejectFindingResponse {}
message MsgHostRejectFindingResponse {}

// MsgReleaseFinding defines a message to release a finding.
message MsgReleaseFinding {
option (gogoproto.equal) = false;

uint64 finding_id = 1 [(gogoproto.moretags) = "yaml:\"finding_id\""];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might also need release all or some findings of a given program.

string desc = 2 [(gogoproto.moretags) = "yaml:\"desc\""];
string poc = 3 [(gogoproto.moretags) = "yaml:\"poc\""];
string comment = 4 [(gogoproto.moretags) = "yaml:\"comment\""];
string host_address = 5 [(gogoproto.moretags) = "yaml:\"host_address\""];
}

// MsgReleaseFindingResponse defines the MsgReleaseFinding response type.
message MsgReleaseFindingResponse {}
1 change: 1 addition & 0 deletions x/bounty/client/cli/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ const (

FlagFindingAddress = "finding-address"
FlagSubmitterAddress = "submitter-address"
FlagFindingID = "finding-id"
)
81 changes: 81 additions & 0 deletions x/bounty/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func NewTxCmd() *cobra.Command {
NewSubmitFindingCmd(),
NewHostAcceptFindingCmd(),
NewHostRejectFindingCmd(),
NewReleaseFindingCmd(),
)

return bountyTxCmds
Expand Down Expand Up @@ -317,3 +318,83 @@ func HostProcessFinding(cmd *cobra.Command, args []string) (fid uint64,

return fid, commentAny, hostAddr, nil
}

func NewReleaseFindingCmd() *cobra.Command {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was deprecated in favor of providing the decrypted plaintext for each finding to be verified by encrypting it again and matching it with the on-chain encrypted ciphertext

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so you'd only be able to release findings one by one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sense

cmd := &cobra.Command{
Use: "release-finding",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use release-finding 1 instead of release-finding --finding-id 1 , it may be more user-friendly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all commands should use the same command format

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are currently 2 formats, and should be unified.

Short: "release encrypted part of a finding ",
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
hostAddr := clientCtx.GetFromAddress()

fid, err := cmd.Flags().GetUint64(FlagFindingID)
if err != nil {
return err
}

encKeyFile, err := cmd.Flags().GetString(FlagEncKeyFile)
if err != nil {
return err
}

findingDesc, findingPoc, findingComment, err := GetFindingPlainText(cmd, fid, encKeyFile)
if err != nil {
return err
}

msg := types.NewReleaseFinding(
hostAddr.String(),
fid,
findingDesc,
findingPoc,
findingComment,
)

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

cmd.Flags().String(FlagEncKeyFile, "", "The program's encryption key file to decrypt findings")
cmd.Flags().Uint64(FlagFindingID, 0, "The program's ID")
flags.AddTxFlagsToCmd(cmd)

_ = cmd.MarkFlagRequired(flags.FlagFrom)
_ = cmd.MarkFlagRequired(FlagFindingID)
_ = cmd.MarkFlagRequired(FlagEncKeyFile)

return cmd
}

func GetFindingPlainText(cmd *cobra.Command, fid uint64, encKeyFile string) (
desc, poc, comment string, err error) {
// get finding info
finding, err := GetFinding(cmd, fid)
if err != nil {
return "", "", "", err
}

prvKey := LoadPrvKey(encKeyFile)

encryptedDescBytes := finding.EncryptedDesc.GetValue()
descBytes, err := prvKey.Decrypt(encryptedDescBytes[2:], nil, nil)
if err != nil {
return "", "", "", err
}

encryptedPocBytes := finding.EncryptedPoc.GetValue()
pocBytes, err := prvKey.Decrypt(encryptedPocBytes[2:], nil, nil)
if err != nil {
return "", "", "", err
}

encryptedCommentBytes := finding.EncryptedComment.GetValue()
commentBytes, err := prvKey.Decrypt(encryptedCommentBytes[2:], nil, nil)
if err != nil {
return "", "", "", err
}

return string(descBytes), string(pocBytes), string(commentBytes), nil
}
29 changes: 29 additions & 0 deletions x/bounty/client/cli/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package cli
import (
"bytes"
"crypto/rand"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/shentufoundation/shentu/v2/x/bounty/types"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be sorted later

"testing"

"github.com/ethereum/go-ethereum/crypto"
Expand All @@ -13,6 +15,33 @@ const (
keyFile = "./dec-key.json"
)

func TestAnyToBytes(t *testing.T) {
decKey, err := ecies.GenerateKey(rand.Reader, ecies.DefaultCurve, nil)
if err != nil {
t.Fatal(err.Error())
}
desc := "test"
encryptedDesc, err := ecies.Encrypt(rand.Reader, &decKey.PublicKey, []byte(desc), nil, nil)
if err != nil {
t.Fatal(err)
}

var descAny *codectypes.Any
encDesc := types.EciesEncryptedDesc{
EncryptedDesc: encryptedDesc,
}
if descAny, err = codectypes.NewAnyWithValue(&encDesc); err != nil {
t.Fatal(err)
}

descBytes := descAny.GetValue()[2:]
descDecrypt, err := decKey.Decrypt(descBytes, nil, nil)

if string(descDecrypt) != desc {
t.Fatal("error")
}
}

func TestSaveLoadKey(t *testing.T) {
decKey, err := ecies.GenerateKey(rand.Reader, ecies.DefaultCurve, nil)
if err != nil {
Expand Down
3 changes: 3 additions & 0 deletions x/bounty/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func NewHandler(k keeper.Keeper) sdk.Handler {
case *types.MsgHostRejectFinding:
res, err := msgServer.HostRejectFinding(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
case *types.MsgReleaseFinding:
res, err := msgServer.ReleaseFinding(sdk.WrapSDKContext(ctx), msg)
return sdk.WrapServiceResult(ctx, res, err)
default:
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", types.ModuleName, msg)
}
Expand Down
43 changes: 43 additions & 0 deletions x/bounty/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,46 @@ func (k msgServer) hostProcess(ctx sdk.Context, fid uint64, hostAddr string, enc
finding.EncryptedComment = encryptedCommentAny
return &finding, nil
}

func (k msgServer) ReleaseFinding(goCtx context.Context, msg *types.MsgReleaseFinding) (*types.MsgReleaseFindingResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)

// get finding
finding, isExist := k.GetFinding(ctx, msg.FindingId)
if !isExist {
return nil, fmt.Errorf("no finding id:%d", msg.FindingId)
}
// get program
program, isExist := k.GetProgram(ctx, finding.ProgramId)
if !isExist {
return nil, fmt.Errorf("no program id:%d", finding.ProgramId)
}
if !program.Active {
return nil, fmt.Errorf("program id:%d is closed", finding.ProgramId)
}

// only creator can update finding comment
if program.CreatorAddress != msg.HostAddress {
return nil, fmt.Errorf("%s not the program creator, expect %s", msg.HostAddress, program.CreatorAddress)
}

finding.Desc = msg.Desc
finding.Poc = msg.Poc
finding.Comment = msg.Comment

k.SetFinding(ctx, finding)

ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
types.EventTypeReleaseFinding,
sdk.NewAttribute(types.AttributeKeyFindingID, strconv.FormatUint(finding.FindingId, 10)),
),
sdk.NewEvent(
sdk.EventTypeMessage,
sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory),
sdk.NewAttribute(sdk.AttributeKeySender, msg.HostAddress),
),
})

return &types.MsgReleaseFindingResponse{}, nil
}
57 changes: 57 additions & 0 deletions x/bounty/keeper/msg_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,60 @@ func (suite *KeeperTestSuite) TestHostRejectFinding() {
})
}
}

func (suite *KeeperTestSuite) TestReleaseFinding() {
programId := suite.InitCreateProgram()
findingId := suite.InitSubmitFinding(programId)

testCases := []struct {
name string
req *types.MsgReleaseFinding
expPass bool
}{
{
"empty request",
&types.MsgReleaseFinding{},
false,
},
{
"valid request => plain text is valid",
&types.MsgReleaseFinding{
FindingId: findingId,
Desc: "test desc",
Poc: "test poc",
Comment: "test comment",
HostAddress: suite.address[0].String(),
},
true,
},
{
"invalid request => host address is invalid",
&types.MsgReleaseFinding{
FindingId: findingId,
Desc: "test desc",
Poc: "test poc",
Comment: "test comment",
HostAddress: suite.address[1].String(),
},
false,
},
}

for _, testCase := range testCases {
suite.Run(fmt.Sprintf("Case %s", testCase.name), func() {
ctx := types1.WrapSDKContext(suite.ctx)
_, err := suite.msgServer.ReleaseFinding(ctx, testCase.req)

finding, _ := suite.keeper.GetFinding(suite.ctx, findingId)

if testCase.expPass {
suite.Require().NoError(err)
suite.Require().Equal(finding.Desc, testCase.req.Desc)
suite.Require().Equal(finding.Poc, testCase.req.Poc)
suite.Require().Equal(finding.Comment, testCase.req.Comment)
} else {
suite.Require().Error(err)
}
})
}
}
Loading