{"id":"s3-bucket-policy-and-cors","version":"1.0.0","primitive":"code_execution","description":"AWS region","registry_refs":["boto3"],"tags":[],"solves":[],"auth_required":true,"verified":false,"last_verified":"null","next_check":"2026-07-30","eval_result":"null","eval_env":"null","mast":[],"ref":"https://arxiv.org/abs/2503.13657","inputs":[{"name":"AWS_ACCESS_KEY_ID","required":true,"description":"AWS access key ID"},{"name":"AWS_SECRET_ACCESS_KEY","required":true,"description":"AWS secret access key"},{"name":"S3_BUCKET","required":true,"description":"S3 bucket name to configure"},{"name":"AWS_REGION","default":"us-east-1","required":false,"description":"AWS region"}],"executable":"# ============================================\n# checklist:     s3-bucket-policy-and-cors\n# version:       1.0.0\n# primitive:     code_execution\n# description:   Set and verify S3 bucket CORS configuration and bucket policy using boto3\n# registry_refs: boto3\n# auth_required: true\n# verified:      false\n# last_verified: null\n# next_check:    2026-07-30\n# eval_result:   null\n# eval_env:      null\n#\n# inputs:\n#   - name: AWS_ACCESS_KEY_ID\n#     required: true\n#     description: AWS access key ID\n#   - name: AWS_SECRET_ACCESS_KEY\n#     required: true\n#     description: AWS secret access key\n#   - name: S3_BUCKET\n#     required: true\n#     description: S3 bucket name to configure\n#   - name: AWS_REGION\n#     required: false\n#     default: \"us-east-1\"\n#     description: AWS region\n#\n# OUTPUTS:\n#   cors_set_ok      — true if CORS config was applied\n#   cors_verified    — true if CORS config read back matches what was set\n#   policy_set_ok    — true if bucket policy was applied\n#   policy_verified  — true if policy read back is valid JSON\n#\n# MAST FAILURE MODES ADDRESSED:\n# FM-1.1 Disobey Task Specification        — CORS uses dict not XML (boto3 handles translation)\n# FM-3.3 Incorrect Verification            — config read back and compared, not just set\n#\n# ref: https://arxiv.org/abs/2503.13657\n# ============================================\n\nimport sys\nimport os\nimport subprocess\nimport time\nimport urllib.request\nimport json\n\n# ─────────────────────────────────────────\n# PRE_EXECUTION\n# ─────────────────────────────────────────\n\nfor attempt in range(2):\n    try:\n        req = urllib.request.Request(\n            \"https://checklist.day/api/registry/boto3\",\n            headers={\"User-Agent\": \"checklist-agent/1.0\"}\n        )\n        with urllib.request.urlopen(req, timeout=10) as resp:\n            registry = json.loads(resp.read())\n            break\n    except Exception as e:\n        if attempt == 1:\n            print(f\"ABORT: registry unreachable — {e}\")\n            sys.exit(1)\n        time.sleep(2)\n\nwarnings = registry.get(\"warnings\", [])\nif warnings:\n    print(\"[boto3] WARNINGS:\")\n    for w in warnings if isinstance(warnings, list) else [warnings]:\n        print(f\"  ⚠ {w}\")\n\n# ─────────────────────────────────────────\n# EXECUTION\n# ─────────────────────────────────────────\n\nsubprocess.check_call([sys.executable, \"-m\", \"pip\", \"install\", \"-q\", \"boto3>=1.26.0\"])\n\nimport boto3\nfrom botocore.exceptions import ClientError\n\nAWS_ACCESS_KEY_ID     = os.environ.get(\"AWS_ACCESS_KEY_ID\")\nAWS_SECRET_ACCESS_KEY = os.environ.get(\"AWS_SECRET_ACCESS_KEY\")\nAWS_REGION            = os.environ.get(\"AWS_REGION\", \"us-east-1\")\nS3_BUCKET             = os.environ.get(\"S3_BUCKET\")\n\nif not AWS_ACCESS_KEY_ID:\n    print(\"ABORT: AWS_ACCESS_KEY_ID not set\"); sys.exit(1)\nif not AWS_SECRET_ACCESS_KEY:\n    print(\"ABORT: AWS_SECRET_ACCESS_KEY not set\"); sys.exit(1)\nif not S3_BUCKET:\n    print(\"ABORT: S3_BUCKET not set\"); sys.exit(1)\n\nclient = boto3.client(\n    \"s3\",\n    aws_access_key_id=AWS_ACCESS_KEY_ID,\n    aws_secret_access_key=AWS_SECRET_ACCESS_KEY,\n    region_name=AWS_REGION,\n)\n\n# 1. CORS Configuration\n# FOOTGUN: boto3 uses dict with CORSRules key — NOT raw XML\n# FOOTGUN: AllowedMethods must be a list of strings, not a single string\ncors_config = {\n    \"CORSRules\": [\n        {\n            \"AllowedHeaders\": [\"*\"],\n            \"AllowedMethods\": [\"GET\", \"PUT\", \"POST\", \"DELETE\"],\n            \"AllowedOrigins\": [\"https://checklist.day\", \"http://localhost:3000\"],\n            \"ExposeHeaders\":  [\"ETag\"],\n            \"MaxAgeSeconds\":  3600,\n        }\n    ]\n}\n\ncors_set_ok   = False\ncors_verified = False\n\ntry:\n    client.put_bucket_cors(Bucket=S3_BUCKET, CORSConfiguration=cors_config)\n    cors_set_ok = True\n    print(f\"  CORS config applied\")\n\n    # Verify readback\n    response     = client.get_bucket_cors(Bucket=S3_BUCKET)\n    rules        = response.get(\"CORSRules\", [])\n    cors_verified = (\n        len(rules) > 0 and\n        \"GET\" in rules[0].get(\"AllowedMethods\", []) and\n        \"https://checklist.day\" in rules[0].get(\"AllowedOrigins\", [])\n    )\n    print(f\"  CORS verified: {cors_verified} ({len(rules)} rule(s))\")\n\n    # Cleanup CORS\n    client.delete_bucket_cors(Bucket=S3_BUCKET)\n\nexcept ClientError as e:\n    print(f\"  CORS error: {e.response['Error']['Code']}: {e.response['Error']['Message']}\")\n\n# 2. Bucket Policy\n# FOOTGUN: policy must be a JSON string, not a dict\npolicy_set_ok   = False\npolicy_verified = False\n\npolicy_dict = {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\":       \"ChecklistReadOnly\",\n            \"Effect\":    \"Deny\",\n            \"Principal\": \"*\",\n            \"Action\":    \"s3:DeleteBucket\",\n            \"Resource\":  f\"arn:aws:s3:::{S3_BUCKET}\",\n        }\n    ],\n}\n\ntry:\n    client.put_bucket_policy(\n        Bucket=S3_BUCKET,\n        Policy=json.dumps(policy_dict),  # FOOTGUN: must be string\n    )\n    policy_set_ok = True\n    print(f\"  bucket policy applied\")\n\n    # Verify readback\n    response        = client.get_bucket_policy(Bucket=S3_BUCKET)\n    returned_policy = json.loads(response[\"Policy\"])\n    policy_verified = \"Statement\" in returned_policy and len(returned_policy[\"Statement\"]) > 0\n    print(f\"  policy verified: {policy_verified}\")\n\n    # Cleanup policy\n    client.delete_bucket_policy(Bucket=S3_BUCKET)\n\nexcept ClientError as e:\n    print(f\"  policy error: {e.response['Error']['Code']}: {e.response['Error']['Message']}\")\n\n# ─────────────────────────────────────────\n# POST_EXECUTION\n# ─────────────────────────────────────────\n\nassert cors_set_ok, \"FAIL: CORS config could not be applied\"\nassert cors_verified, \"FAIL: CORS config readback did not match\"\n\nresult = {\n    \"cors_set_ok\":     cors_set_ok,\n    \"cors_verified\":   cors_verified,\n    \"policy_set_ok\":   policy_set_ok,\n    \"policy_verified\": policy_verified,\n}\nprint(json.dumps(result, indent=2))\nprint(\"PASS\")\n"}