Compare full native bucket policies independent of array order

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a17f16-d99c-7ea3-95f0-8212897fba6b
This commit is contained in:
codex 2026-10-29 21:56:10 +01:00
parent 61a82ac74c
commit ccc0f8b578
2 changed files with 25 additions and 2 deletions

View file

@ -97,6 +97,17 @@ def denied(call):
raise Refused("native_boundary_unexpectedly_allowed")
def canonical_policy(value):
# Policy array ordering is not significant. Scaleway sorts actions and
# principal arrays on write. Preserve every value (including duplicates)
# and compare all fields; never discard unknown permission/condition data.
if isinstance(value, dict):
return {k: canonical_policy(v) for k, v in value.items()}
if isinstance(value, list):
return sorted((canonical_policy(v) for v in value), key=lambda v: json.dumps(v, sort_keys=True))
return value
def policy(bucket, prefix, apps, owner):
def statement(sid, principal, actions, resources, condition=None):
value = {"Sid": sid, "Effect": "Allow", "Principal": {"SCW": principal},
@ -200,13 +211,13 @@ class Resources:
if status(error) != 404:
raise
live = None
if live is not None and live != intended:
if live is not None and canonical_policy(live) != canonical_policy(intended):
raise Refused("existing_policy_drift_requires_review")
if live is None:
self.mutate("put_policy:" + bucket, lambda: client.put_bucket_policy(
Bucket=bucket, Policy=json.dumps(intended)))
self.save()
if json.loads(client.get_bucket_policy(Bucket=bucket)["Policy"]) != intended:
if canonical_policy(json.loads(client.get_bucket_policy(Bucket=bucket)["Policy"])) != canonical_policy(intended):
raise Refused("policy_readback_differs")
if client.get_bucket_versioning(Bucket=bucket).get("Status") != "Enabled":
self.mutate("versioning:" + bucket, lambda: client.put_bucket_versioning(