Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions prowler/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ All notable changes to the **Prowler SDK** are documented in this file.
- `entra_conditional_access_policy_device_registration_mfa_required` check and `entra_intune_enrollment_sign_in_frequency_every_time` enhancement for M365 provider [(#10222)](https://github.com/prowler-cloud/prowler/pull/10222)
- `entra_conditional_access_policy_block_elevated_insider_risk` check for M365 provider [(#10234)](https://github.com/prowler-cloud/prowler/pull/10234)
- `Vercel` provider support with 30 checks [(#10189)](https://github.com/prowler-cloud/prowler/pull/10189)
- `identity_storage_service_level_admins_scoped` check for OCI Provider CIS v3.1 compliance 1.15 [(#10567)](https://github.com/prowler-cloud/prowler/pull/10568)

### 🔄 Changed

Expand Down
4 changes: 3 additions & 1 deletion prowler/compliance/oraclecloud/cis_3.1_oraclecloud.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,9 @@
{
"Id": "1.15",
"Description": "Ensure storage service-level admins cannot delete resources they manage",
"Checks": [],
"Checks": [
"identity_storage_service_level_admins_scoped"
],
"Attributes": [
{
"Section": "1. Identity and Access Management",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"Provider": "oraclecloud",
"CheckID": "identity_storage_service_level_admins_scoped",
"CheckTitle": "Identity policy does not grant delete to storage service level admins",
"CheckType": [],
"ServiceName": "identity",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "high",
"ResourceType": "Policy",
"ResourceGroup": "IAM",
"Description": "To apply the separation of duties security principle, one can restrict service-level administrators from being able to delete resources they are managing. It means service-level administrators can only manage resources of a specific service but not delete resources for that specific service.",
"Risk": "Creating service-level administrators without the ability to delete the resource they are managing helps in tightly controlling access to Oracle Cloud Infrastructure (OCI) services by implementing the separation of duties security principle.",
"RelatedUrl": "",
"AdditionalURLs": [
"https://docs.oracle.com/en/solutions/oci-best-practices/protect-data-rest1.html#GUID-939A5EA1-3057-48E0-9E02-ADAFCB82BA3E",
"https://docs.oracle.com/en-us/iaas/Content/Identity/policyreference/policyreference.htm",
"https://docs.oracle.com/en-us/iaas/Content/Block/home.htm",
"https://docs.oracle.com/en-us/iaas/Content/File/home.htm",
"https://docs.oracle.com/en-us/iaas/Content/Object/home.htm"
],
"Remediation": {
"Code": {
"Console": "1. Login to OCI console.\n2. Go to Identity -> Policies, In the compartment dropdown, choose the compartment.\n3. Open each policy to view the policy statements.\n4. Verify the policies to ensure that the policy statements that grant access to storage service-level administrators have a condition that excludes access to delete the service they are the administrator for.",
"NativeIaC": "",
"CLI": "",
"Terraform": "",
"Other": ""
},
"Recommendation": {
"Text": "Add the appropriate where condition to any policy statement that allows the storage service-level admins to manage the storage service.",
"Url": ""
}
},
"Categories": [
"identity-access"
],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
from prowler.lib.check.models import Check, Check_Report_OCI
from prowler.providers.oraclecloud.services.identity.identity_client import (
identity_client,
)


class identity_storage_service_level_admins_scoped(Check):
"""Ensure storage service-level admins cannot delete resources they manage (CIS 1.15)"""

def execute(self):
"""Ensure service-level administrators can only manage resources of a specific service but not delete resources.

This check ensures that policies don't grant delete permission on storage like "manage volumes in tenancy"
without restricting delete permissions.
"""
findings = []

storage_policies = {
"FILE-FAMILY": ["FILE-SYSTEMS", "MOUNT-TARGETS", "EXPORT-SETS"],
"OBJECT-FAMILY": ["BUCKETS", "OBJECTS"],
"VOLUME-FAMILY": ["VOLUMES", "VOLUME-ATTACHMENTS", "VOLUME-BACKUPS"],
}
all_base_policies = [
item for sublist in storage_policies.values() for item in sublist
]

# Check for policies that violate least privilege by granting manage delete service-level storage privileges
for policy in identity_client.policies:
# Skip non-active policies
if policy.lifecycle_state != "ACTIVE":
continue

# Skip default tenant admin policy
if policy.name.upper() == "TENANT ADMIN POLICY":
continue

region = policy.region if hasattr(policy, "region") else "global"

has_violation = False
offending_statements = []
for statement in policy.statements:
statement_upper = statement.upper()
# Only check groups
if not statement_upper.startswith("ALLOW GROUP"):
continue

# Check for "allow group ... to manage *file-service* resources" without restriction (not restricted to certain buckets or non delete)
if any(
f"MANAGE {global_storage_policy}" in statement_upper
for global_storage_policy in storage_policies
):
if "WHERE" not in statement_upper:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is still too permissive for CIS 3.1 control 1.15.

The control does not require any WHERE clause; it requires a condition that excludes delete access for the storage resource being managed. Right now a statement such as where request.region="iad" or where target.bucket.name="x" would pass, even though it still allows deletion.

Please update the check so it only treats a statement as compliant when it explicitly excludes the relevant delete permission for that resource, for example request.permission!='VOLUME_DELETE', request.permission!='FILE_SYSTEM_DELETE', request.permission!='OBJECT_DELETE', etc. This is the core requirement of CIS 1.15: service-level admins may manage the service, but they must not be able to delete the resources they manage.

Copy link
Copy Markdown
Contributor Author

@rchotacode rchotacode Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally implemented this behavior, but looking at the CIS 1.15 examples it seems service level admins refer to unrestricted manage access to storage services. Other CIS benchmarks also seem to flag it as such. I would think that target.bucket.name="x" would be a resource level of control, not a service.

These are the example policies adjusted to fit within the control within its definition document:

Example policies for global/tenant level for block volume service-administrators:

Allow group VolumeUsers to manage volumes in tenancy where request.permission!='VOLUME_DELETE' 
Allow group VolumeUsers to manage volume-backups in tenancy where request.permission!='VOLUME_BACKUP_DELETE

Example policies for global/tenant level for file storage system service-administrators:

Allow group FileUsers to manage file-systems in tenancy where request.permission!='FILE_SYSTEM_DELETE' 
Allow group FileUsers to manage mount-targets in tenancy where request.permission!='MOUNT_TARGET_DELETE' 
Allow group FileUsers to manage export-sets in tenancy where request.permission!='EXPORT_SET_DELETE'

Example policies for global/tenant level for object storage system service-administrators:

Allow group BucketUsers to manage objects in tenancy where request.permission!='OBJECT_DELETE' 
Allow group BucketUsers to manage buckets in tenancy where request.permission!='BUCKET_DELETE'

Each of these allows manage for a file service storage, but doesn't restrict the permissions at all without the request.permission!='X_DELETE'. In addition, the reference here https://docs.oracle.com/en/solutions/oci-best-practices/protect-data-rest1.html#GUID-EBE9212C-78B0-4795-9FB5-59DB0E91E106 linked in the control says that the permissions should be give "to only the users who need these privileges," which implies that some users should still be able to have delete permissions. I interpreted this as meaning that the CIS benchmark was to limit unrestricted delete permissions given to admins over all storage services.

has_violation = True
offending_statements.append(statement)
break
if any(
f"MANAGE {base_storage_policy}" in statement_upper
for base_storage_policy in all_base_policies
):
if "WHERE" not in statement_upper:
has_violation = True
offending_statements.append(statement)
break
if "MANAGE ALL-RESOURCES" in statement_upper:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also revisit the manage all-resources handling here.

For CIS 1.15, broad statements should not pass just because they contain a WHERE. The implementation should only pass when it can prove that delete is excluded for the storage resources covered by the statement. Otherwise this can create false PASS results for policies that remain non-compliant with the benchmark.

if "WHERE" not in statement_upper:
has_violation = True
offending_statements.append(statement)
break

report = Check_Report_OCI(
metadata=self.metadata(),
resource=policy,
region=region,
resource_id=policy.id,
resource_name=policy.name,
compartment_id=policy.compartment_id,
)

if has_violation:
report.status = "FAIL"
report.status_extended = f"Policy '{policy.name}' grants 'manage' permissions with delete. Service-level storage administrators should not be created with delete permissions.\n{offending_statements}"
else:
report.status = "PASS"
report.status_extended = f"Policy '{policy.name}' does not grant storage service level admins delete permissions."

findings.append(report)

# If no policies found, that's also a finding
if not findings:
region = (
identity_client.audited_regions[0].key
if identity_client.audited_regions
else "global"
)
report = Check_Report_OCI(
metadata=self.metadata(),
resource={},
region=region,
resource_id=identity_client.audited_tenancy,
resource_name="Tenancy",
compartment_id=identity_client.audited_tenancy,
)
report.status = "PASS"
report.status_extended = (
"No active storage-level admin policies found with delete permissions."
)
findings.append(report)

return findings
Loading
Loading