Spaces:
Sleeping
Sleeping
File size: 5,236 Bytes
10bd457 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | """Pydantic V2 models for CloudGuard-S3-Auditor environment."""
from __future__ import annotations
import enum
from typing import Literal
from pydantic import BaseModel, Field, field_validator
# ββ Enums ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class BucketPolicy(str, enum.Enum):
PUBLIC = "public"
PRIVATE = "private"
class EncryptionType(str, enum.Enum):
NONE = "none"
AES256 = "aes256"
AWS_KMS = "aws-kms"
class ActionType(str, enum.Enum):
MAKE_PRIVATE = "make_private"
MAKE_PUBLIC = "make_public"
ENABLE_ENCRYPTION = "enable_encryption"
NOOP = "noop"
# ββ Bucket State βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class S3Bucket(BaseModel):
"""Single S3 bucket in the simulated environment."""
name: str = Field(..., description="Bucket identifier, e.g. 'website-css' or 'user-pii-db'")
policy: BucketPolicy = Field(default=BucketPolicy.PUBLIC)
encryption: EncryptionType = Field(default=EncryptionType.NONE)
contains_pii: bool = Field(default=False)
is_required_public: bool = Field(default=False)
def policy_json(self) -> dict:
return {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow" if self.policy == BucketPolicy.PUBLIC else "Deny",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": f"arn:aws:s3:::{self.name}/*",
}
],
}
# ββ Action βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class Action(BaseModel):
"""Agent action submitted each step."""
action_type: ActionType
bucket_name: str | None = Field(
default=None,
description="Target bucket name (required for all actions except noop)",
)
encryption_algo: EncryptionType | None = Field(
default=None,
description="Encryption algorithm (required only for enable_encryption)",
)
@field_validator("bucket_name")
@classmethod
def require_bucket_for_non_noop(cls, v: str | None, info) -> str | None:
if info.data.get("action_type") != ActionType.NOOP and v is None:
raise ValueError("bucket_name is required for non-noop actions")
return v
def to_str(self) -> str:
if self.action_type == ActionType.NOOP:
return "noop"
if self.action_type == ActionType.ENABLE_ENCRYPTION:
algo = self.encryption_algo.value if self.encryption_algo else "aes256"
return f"enable_encryption:{self.bucket_name}:{algo}"
return f"{self.action_type.value}:{self.bucket_name}"
@classmethod
def from_str(cls, raw: str) -> "Action":
raw = raw.strip()
if raw == "noop":
return cls(action_type=ActionType.NOOP)
parts = raw.split(":")
action_type = ActionType(parts[0])
if action_type == ActionType.ENABLE_ENCRYPTION:
if len(parts) < 3:
raise ValueError("enable_encryption requires bucket_name and algo")
return cls(
action_type=action_type,
bucket_name=parts[1],
encryption_algo=EncryptionType(parts[2]),
)
return cls(action_type=action_type, bucket_name=parts[1])
# ββ Observation ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class BucketObservation(BaseModel):
"""Visible state of a single bucket (sent to agent)."""
name: str
policy: BucketPolicy
encryption: EncryptionType
contains_pii: bool
is_required_public: bool
class Observation(BaseModel):
"""Full observation returned to agent each step."""
buckets: list[BucketObservation]
step_number: int = Field(ge=0)
max_steps: int = Field(ge=1)
done: bool = False
message: str = ""
# ββ Reward / Step Result βββββββββββββββββββββββββββββββββββββββββββββββββββββ
class StepResult(BaseModel):
"""Result of a single env.step() call."""
observation: Observation
reward: float = Field(ge=-10.0, le=10.0)
done: bool = False
info: dict = Field(default_factory=dict)
# ββ Grading ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
class GradeResult(BaseModel):
"""Output of the SuccessGrader."""
success: bool
score: float = Field(ge=0.0, le=1.0)
details: dict = Field(default_factory=dict) |