@@ -45,6 +45,7 @@ const OP_NAME_TO_ACTION = Object.freeze({
4545 get_bucket_object_lock : { regular : "s3:GetBucketObjectLockConfiguration" } ,
4646 get_bucket : { regular : "s3:ListBucket" } ,
4747 get_object_acl : { regular : "s3:GetObjectAcl" } ,
48+ get_object_attributes : { regular : [ "s3:GetObject" , "s3:GetObjectAttributes" ] , versioned : [ "s3:GetObjectVersion" , "s3:GetObjectVersionAttributes" ] } , // Notice - special case
4849 get_object_tagging : { regular : "s3:GetObjectTagging" , versioned : "s3:GetObjectVersionTagging" } ,
4950 get_object_uploadId : { regular : "s3:ListMultipartUploadParts" } ,
5051 get_object_retention : { regular : "s3:GetObjectRetention" } ,
@@ -139,11 +140,16 @@ async function _is_object_tag_fit(req, predicate, value) {
139140async function has_bucket_policy_permission ( policy , account , method , arn_path , req ) {
140141 const [ allow_statements , deny_statements ] = _ . partition ( policy . Statement , statement => statement . Effect === 'Allow' ) ;
141142
143+ // the case where the permission is an array started in op get_object_attributes
144+ const method_arr = Array . isArray ( method ) ? method : [ method ] ;
145+
142146 // look for explicit denies
143- if ( await _is_statements_fit ( deny_statements , account , method , arn_path , req ) ) return 'DENY' ;
147+ const res_arr_deny = await is_statement_fit_of_method_array ( deny_statements , account , method_arr , arn_path , req ) ;
148+ if ( res_arr_deny . every ( item => item ) ) return 'DENY' ;
144149
145150 // look for explicit allows
146- if ( await _is_statements_fit ( allow_statements , account , method , arn_path , req ) ) return 'ALLOW' ;
151+ const res_arr_allow = await is_statement_fit_of_method_array ( allow_statements , account , method_arr , arn_path , req ) ;
152+ if ( res_arr_allow . every ( item => item ) ) return 'ALLOW' ;
147153
148154 // implicit deny
149155 return 'IMPLICIT_DENY' ;
@@ -156,6 +162,7 @@ function _is_action_fit(method, statement) {
156162 dbg . log1 ( 'bucket_policy: ' , statement . Action ? 'Action' : 'NotAction' , ' fit?' , action , method ) ;
157163 if ( ( action === '*' ) || ( action === 's3:*' ) || ( action === method ) ) {
158164 action_fit = true ;
165+ break ;
159166 }
160167 }
161168 return statement . Action ? action_fit : ! action_fit ;
@@ -170,6 +177,7 @@ function _is_principal_fit(account, statement) {
170177 dbg . log1 ( 'bucket_policy: ' , statement . Principal ? 'Principal' : 'NotPrincipal' , ' fit?' , principal , account ) ;
171178 if ( ( principal . unwrap ( ) === '*' ) || ( principal . unwrap ( ) === account ) ) {
172179 principal_fit = true ;
180+ break ;
173181 }
174182 }
175183 return statement . Principal ? principal_fit : ! principal_fit ;
@@ -184,11 +192,17 @@ function _is_resource_fit(arn_path, statement) {
184192 dbg . log1 ( 'bucket_policy: ' , statement . Resource ? 'Resource' : 'NotResource' , ' fit?' , resource_regex , arn_path ) ;
185193 if ( resource_regex . test ( arn_path ) ) {
186194 resource_fit = true ;
195+ break ;
187196 }
188197 }
189198 return statement . Resource ? resource_fit : ! resource_fit ;
190199}
191200
201+ async function is_statement_fit_of_method_array ( statements , account , method_arr , arn_path , req ) {
202+ return Promise . all ( method_arr . map ( method_permission =>
203+ _is_statements_fit ( statements , account , method_permission , arn_path , req ) ) ) ;
204+ }
205+
192206async function _is_statements_fit ( statements , account , method , arn_path , req ) {
193207 for ( const statement of statements ) {
194208 const action_fit = _is_action_fit ( method , statement ) ;
@@ -237,7 +251,7 @@ function _parse_condition_keys(condition_statement) {
237251}
238252
239253async function validate_s3_policy ( policy , bucket_name , get_account_handler ) {
240- const all_op_names = _ . compact ( _ . flatMap ( OP_NAME_TO_ACTION , action => [ action . regular , action . versioned ] ) ) ;
254+ const all_op_names = _ . flatten ( _ . compact ( _ . flatMap ( OP_NAME_TO_ACTION , action => [ action . regular , action . versioned ] ) ) ) ;
241255 for ( const statement of policy . Statement ) {
242256
243257 const statement_principal = statement . Principal || statement . NotPrincipal ;
0 commit comments