Skip to content

Commit ac92fdc

Browse files
authored
Merge pull request #850 from CosmWasm/840-enforce-default-code-permissions
Enforce default code permissions
2 parents 663716a + 27d3051 commit ac92fdc

File tree

4 files changed

+186
-4
lines changed

4 files changed

+186
-4
lines changed

x/wasm/keeper/keeper.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
159159
if !authZ.CanCreateCode(k.getUploadAccessConfig(ctx), creator) {
160160
return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "can not create code")
161161
}
162+
// figure out proper instantiate access
163+
defaultAccessConfig := k.getInstantiateAccessConfig(ctx).With(creator)
164+
if instantiateAccess == nil {
165+
instantiateAccess = &defaultAccessConfig
166+
} else if !instantiateAccess.IsSubset(defaultAccessConfig) {
167+
// we enforce this must be subset of default upload access
168+
return 0, sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "instantiate access must be subset of default upload access")
169+
}
170+
162171
wasmCode, err = ioutils.Uncompress(wasmCode, uint64(types.MaxWasmSize))
163172
if err != nil {
164173
return 0, sdkerrors.Wrap(types.ErrCreateFailed, err.Error())
@@ -175,10 +184,6 @@ func (k Keeper) create(ctx sdk.Context, creator sdk.AccAddress, wasmCode []byte,
175184
}
176185
codeID = k.autoIncrementID(ctx, types.KeyLastCodeID)
177186
k.Logger(ctx).Debug("storing new contract", "features", report.RequiredFeatures, "code_id", codeID)
178-
if instantiateAccess == nil {
179-
defaultAccessConfig := k.getInstantiateAccessConfig(ctx).With(creator)
180-
instantiateAccess = &defaultAccessConfig
181-
}
182187
codeInfo := types.NewCodeInfo(checksum, creator, *instantiateAccess)
183188
k.storeCodeInfo(ctx, codeID, codeInfo)
184189

x/wasm/keeper/keeper_test.go

+78
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,84 @@ func TestCreateWithParamPermissions(t *testing.T) {
183183
}
184184
}
185185

186+
// ensure that the user cannot set the code instantiate permission to something more permissive
187+
// than the default
188+
func TestEnforceValidPermissionsOnCreate(t *testing.T) {
189+
ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
190+
keeper := keepers.WasmKeeper
191+
contractKeeper := keepers.ContractKeeper
192+
193+
deposit := sdk.NewCoins(sdk.NewInt64Coin("denom", 100000))
194+
creator := keepers.Faucet.NewFundedAccount(ctx, deposit...)
195+
other := keepers.Faucet.NewFundedAccount(ctx, deposit...)
196+
197+
onlyCreator := types.AccessTypeOnlyAddress.With(creator)
198+
onlyOther := types.AccessTypeOnlyAddress.With(other)
199+
200+
specs := map[string]struct {
201+
defaultPermssion types.AccessType
202+
requestedPermission *types.AccessConfig
203+
// grantedPermission is set iff no error
204+
grantedPermission types.AccessConfig
205+
// expError is nil iff the request is allowed
206+
expError *sdkerrors.Error
207+
}{
208+
"override everybody": {
209+
defaultPermssion: types.AccessTypeEverybody,
210+
requestedPermission: &onlyCreator,
211+
grantedPermission: onlyCreator,
212+
},
213+
"default to everybody": {
214+
defaultPermssion: types.AccessTypeEverybody,
215+
requestedPermission: nil,
216+
grantedPermission: types.AccessConfig{Permission: types.AccessTypeEverybody},
217+
},
218+
"explicitly set everybody": {
219+
defaultPermssion: types.AccessTypeEverybody,
220+
requestedPermission: &types.AccessConfig{Permission: types.AccessTypeEverybody},
221+
grantedPermission: types.AccessConfig{Permission: types.AccessTypeEverybody},
222+
},
223+
"cannot override nobody": {
224+
defaultPermssion: types.AccessTypeNobody,
225+
requestedPermission: &onlyCreator,
226+
expError: sdkerrors.ErrUnauthorized,
227+
},
228+
"default to nobody": {
229+
defaultPermssion: types.AccessTypeNobody,
230+
requestedPermission: nil,
231+
grantedPermission: types.AccessConfig{Permission: types.AccessTypeNobody},
232+
},
233+
"only defaults to code creator": {
234+
defaultPermssion: types.AccessTypeOnlyAddress,
235+
requestedPermission: nil,
236+
grantedPermission: onlyCreator,
237+
},
238+
"can explicitly set to code creator": {
239+
defaultPermssion: types.AccessTypeOnlyAddress,
240+
requestedPermission: &onlyCreator,
241+
grantedPermission: onlyCreator,
242+
},
243+
"cannot override which address in only": {
244+
defaultPermssion: types.AccessTypeOnlyAddress,
245+
requestedPermission: &onlyOther,
246+
expError: sdkerrors.ErrUnauthorized,
247+
},
248+
}
249+
for msg, spec := range specs {
250+
t.Run(msg, func(t *testing.T) {
251+
params := types.DefaultParams()
252+
params.InstantiateDefaultPermission = spec.defaultPermssion
253+
keeper.SetParams(ctx, params)
254+
codeID, err := contractKeeper.Create(ctx, creator, hackatomWasm, spec.requestedPermission)
255+
require.True(t, spec.expError.Is(err), err)
256+
if spec.expError == nil {
257+
codeInfo := keeper.GetCodeInfo(ctx, codeID)
258+
require.Equal(t, codeInfo.InstantiateConfig, spec.grantedPermission)
259+
}
260+
})
261+
}
262+
}
263+
186264
func TestCreateDuplicate(t *testing.T) {
187265
ctx, keepers := CreateTestInput(t, false, SupportedFeatures)
188266
keeper := keepers.ContractKeeper

x/wasm/types/types.go

+18
Original file line numberDiff line numberDiff line change
@@ -330,3 +330,21 @@ func VerifyAddressLen() func(addr []byte) error {
330330
return nil
331331
}
332332
}
333+
334+
// IsSubset will return true if the caller is the same as the superset,
335+
// or if the caller is more restrictive than the superset.
336+
func (a AccessConfig) IsSubset(superSet AccessConfig) bool {
337+
switch superSet.Permission {
338+
case AccessTypeEverybody:
339+
// Everything is a subset of this
340+
return a.Permission != AccessTypeUnspecified
341+
case AccessTypeNobody:
342+
// Only an exact match is a subset of this
343+
return a.Permission == AccessTypeNobody
344+
case AccessTypeOnlyAddress:
345+
// An exact match or nobody
346+
return a.Permission == AccessTypeNobody || (a.Permission == AccessTypeOnlyAddress && a.Address == superSet.Address)
347+
default:
348+
return false
349+
}
350+
}

x/wasm/types/types_test.go

+81
Original file line numberDiff line numberDiff line change
@@ -372,3 +372,84 @@ func TestVerifyAddressLen(t *testing.T) {
372372
})
373373
}
374374
}
375+
376+
func TestAccesConfigSubset(t *testing.T) {
377+
specs := map[string]struct {
378+
check AccessConfig
379+
superSet AccessConfig
380+
isSubSet bool
381+
}{
382+
"nobody <= nobody": {
383+
superSet: AccessConfig{Permission: AccessTypeNobody},
384+
check: AccessConfig{Permission: AccessTypeNobody},
385+
isSubSet: true,
386+
},
387+
"only > nobody": {
388+
superSet: AccessConfig{Permission: AccessTypeNobody},
389+
check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "foobar"},
390+
isSubSet: false,
391+
},
392+
"everybody > nobody": {
393+
superSet: AccessConfig{Permission: AccessTypeNobody},
394+
check: AccessConfig{Permission: AccessTypeEverybody},
395+
isSubSet: false,
396+
},
397+
"unspecified > nobody": {
398+
superSet: AccessConfig{Permission: AccessTypeNobody},
399+
check: AccessConfig{Permission: AccessTypeUnspecified},
400+
isSubSet: false,
401+
},
402+
"nobody <= everybody": {
403+
superSet: AccessConfig{Permission: AccessTypeEverybody},
404+
check: AccessConfig{Permission: AccessTypeNobody},
405+
isSubSet: true,
406+
},
407+
"only <= everybody": {
408+
superSet: AccessConfig{Permission: AccessTypeEverybody},
409+
check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "foobar"},
410+
isSubSet: true,
411+
},
412+
"everybody <= everybody": {
413+
superSet: AccessConfig{Permission: AccessTypeEverybody},
414+
check: AccessConfig{Permission: AccessTypeEverybody},
415+
isSubSet: true,
416+
},
417+
"unspecified > everybody": {
418+
superSet: AccessConfig{Permission: AccessTypeEverybody},
419+
check: AccessConfig{Permission: AccessTypeUnspecified},
420+
isSubSet: false,
421+
},
422+
"nobody <= only": {
423+
superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"},
424+
check: AccessConfig{Permission: AccessTypeNobody},
425+
isSubSet: true,
426+
},
427+
"only <= only(same)": {
428+
superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"},
429+
check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"},
430+
isSubSet: true,
431+
},
432+
"only > only(other)": {
433+
superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"},
434+
check: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "other"},
435+
isSubSet: false,
436+
},
437+
"everybody > only": {
438+
superSet: AccessConfig{Permission: AccessTypeOnlyAddress, Address: "owner"},
439+
check: AccessConfig{Permission: AccessTypeEverybody},
440+
isSubSet: false,
441+
},
442+
"nobody > unspecified": {
443+
superSet: AccessConfig{Permission: AccessTypeUnspecified},
444+
check: AccessConfig{Permission: AccessTypeNobody},
445+
isSubSet: false,
446+
},
447+
}
448+
449+
for name, spec := range specs {
450+
t.Run(name, func(t *testing.T) {
451+
subset := spec.check.IsSubset(spec.superSet)
452+
require.Equal(t, spec.isSubSet, subset)
453+
})
454+
}
455+
}

0 commit comments

Comments
 (0)