{
  "steward": "infra-networking-steward",
  "project": "Azure-AI-RAG-CSharp-Semantic-Kernel-Functions",
  "runDate": "2026-03-22",
  "runId": "2026-03-22T00-00-00",
  "findings": [
    {
      "id": "INET-PVTEP-001",
      "title": "Storage Account publicly accessible with no IP restriction",
      "severity": "critical",
      "category": "PVTEP",
      "file": "infra/core/storage/blob-storage-account.bicep",
      "line": 15,
      "description": "The storage account is deployed with publicNetworkAccess: 'Enabled' and allowSharedKeyAccess: true. No networkAcls property is defined, meaning the account is reachable from any internet source with a valid key or SAS token. The account holds documents used for RAG ingestion and Function App blob triggers.",
      "recommendation": "Add networkAcls with defaultAction: 'Deny' and bypass: 'AzureServices'. Set allowSharedKeyAccess: false since the Function App already authenticates via managed identity. Add a private endpoint once VNet integration is in place.",
      "status": "open"
    },
    {
      "id": "INET-PVTEP-002",
      "title": "Cosmos DB publicly accessible with no network restriction",
      "severity": "critical",
      "category": "PVTEP",
      "file": "infra/core/database/cosmos-db/account.bicep",
      "description": "The Cosmos DB account has no publicNetworkAccess property (defaults to enabled), no ipRules, no virtualNetworkRules, and no private endpoint. Additionally, disableLocalAuth: false is hardcoded in the resource body, overriding the caller's disableKeyBasedAuth parameter — key-based auth is always enabled regardless of caller intent.",
      "recommendation": "Add publicNetworkAccess: 'Disabled' or at minimum ipRules to restrict to known App Service outbound IPs. Fix the hardcoded disableLocalAuth: false override to respect the parameter value. Add a private endpoint as the target state.",
      "status": "open"
    },
    {
      "id": "INET-PVTEP-003",
      "title": "Azure AI Search publicly accessible with no IP restriction or private endpoint",
      "severity": "critical",
      "category": "PVTEP",
      "file": "infra/core/search/search-services.bicep",
      "line": 30,
      "description": "publicNetworkAccess defaults to 'enabled'. The networkRuleSet parameter defaults to { bypass: 'None', ipRules: [] }. The empty ipRules with publicNetworkAccess enabled does not restrict access. The search index holds vectorised RAG document data and is fully internet-accessible.",
      "recommendation": "Change the publicNetworkAccess parameter default to 'disabled'. Add a private endpoint, or add explicit ipRules in networkRuleSet to restrict access to known App Service and Function App outbound IPs.",
      "status": "open"
    },
    {
      "id": "INET-PVTEP-004",
      "title": "Azure OpenAI publicly accessible with no network restriction",
      "severity": "critical",
      "category": "PVTEP",
      "file": "infra/core/ai/openai/openai-account.bicep",
      "line": 28,
      "description": "The OpenAI Cognitive Services account has publicNetworkAccess: 'Enabled' and no networkAcls. The endpoint is callable from any internet source. While managed identity is used for authentication, the endpoint is exposed to probing and rate-limit abuse from external actors.",
      "recommendation": "Add networkAcls with defaultAction: 'Deny' and allowlist the App Service and Function App outbound IPs. Add a private endpoint with privatelink.openai.azure.com once VNet integration is in place.",
      "status": "open"
    },
    {
      "id": "INET-VNET-001",
      "title": "No VNet deployed — entire workload runs on public internet",
      "severity": "notable",
      "category": "VNET",
      "file": "infra/main.bicep",
      "description": "No Microsoft.Network/virtualNetworks resource is deployed. All compute-to-data connectivity traverses Azure's public network. There is no integration subnet for App Service outbound traffic and no private endpoint subnet for data services.",
      "recommendation": "Add a VNet module with at minimum two subnets: an integration subnet (delegated to Microsoft.Web/serverFarms) for App Service and Function App outbound routing, and a private endpoint subnet for data service private endpoints.",
      "status": "open"
    },
    {
      "id": "INET-VNET-002",
      "title": "App Service (API) has no VNet integration",
      "severity": "notable",
      "category": "VNET",
      "file": "infra/app/api-app.bicep",
      "line": 106,
      "description": "The API App Service sets publicNetworkAccess: 'Enabled' with no virtualNetworkSubnetId in siteConfig. Without VNet integration, even if private endpoints were added to backend services, the API cannot route outbound traffic through them.",
      "recommendation": "Add virtualNetworkSubnetId referencing a subnet delegated to Microsoft.Web/serverFarms and set vnetRouteAllEnabled: true on siteConfig. Requires App Service Plan upgrade to Standard tier (see INET-VNET-004).",
      "status": "open"
    },
    {
      "id": "INET-VNET-003",
      "title": "Function App has no VNet integration",
      "severity": "notable",
      "category": "VNET",
      "file": "infra/app/loader-function.bicep",
      "description": "The loader Function App has no virtualNetworkSubnetId and no publicNetworkAccess restriction. Blob trigger connections, Storage API calls, and AI Search/OpenAI outbound calls all traverse public endpoints. This is the document ingestion pipeline handling raw document content.",
      "recommendation": "Configure virtualNetworkSubnetId and vnetRouteAllEnabled: true. Requires App Service Plan upgrade to Standard tier (see INET-VNET-004).",
      "status": "open"
    },
    {
      "id": "INET-VNET-004",
      "title": "App Service Plan Basic tier prevents VNet integration",
      "severity": "notable",
      "category": "VNET",
      "file": "infra/core/host/app-service.bicep",
      "description": "Both App Service Plans are provisioned at B2 (Basic tier). VNet integration for outbound traffic requires Standard (S1+) or Premium tier. This is a structural blocker for INET-VNET-002 and INET-VNET-003.",
      "recommendation": "Upgrade both App Service Plans from B2 (Basic) to at minimum S2 (Standard) or P1v3 (Premium). This is a prerequisite for adding VNet integration to all hosted apps and functions.",
      "status": "open"
    },
    {
      "id": "INET-KV-001",
      "title": "Key Vault module file is empty — Key Vault is not deployed",
      "severity": "notable",
      "category": "KV",
      "file": "infra/core/security/key-vault.bicep",
      "description": "The Key Vault Bicep module file is effectively empty (1 line). The loader Function App has a KeyVaultUri app setting with an empty string value. The API App has CosmosDb_ConnectionString as an empty app setting. Key Vault was clearly intended but never implemented, leaving secrets unmanaged.",
      "recommendation": "Implement the Key Vault module with publicNetworkAccess: 'Disabled', a private endpoint, RBAC role assignments for the managed identity, and network ACLs. Populate KeyVaultUri in the Function App and migrate secrets to Key Vault references.",
      "status": "open"
    },
    {
      "id": "INET-CORS-001",
      "title": "Storage Account blob service has no explicit CORS policy",
      "severity": "notable",
      "category": "CORS",
      "file": "infra/core/storage/blob-storage-account.bicep",
      "line": 22,
      "description": "The blobServices child resource has no corsRules property defined. While Azure defaults to no CORS rules (blocking browser cross-origin access), the absence of an explicit policy in IaC means any CORS rule added manually — including a wildcard — would not be tracked or reviewed through the IaC pipeline.",
      "recommendation": "Add an explicit corsRules: [] to the blobServices resource if no cross-origin access is needed. If specific origins need access, list them explicitly. Never use allowedOrigins: ['*'].",
      "status": "open"
    },
    {
      "id": "INET-STOR-001",
      "title": "Blob containers missing explicit publicAccess: None",
      "severity": "minor",
      "category": "STOR",
      "file": "infra/core/storage/blob-storage-account.bicep",
      "description": "The load, completed, and images containers are created without a properties.publicAccess property. Only the archive container explicitly sets publicAccess: 'None'. Inconsistent explicit settings create configuration drift risk.",
      "recommendation": "Add properties: { publicAccess: 'None' } to the load, completed, and images container resources for consistency and defence-in-depth.",
      "status": "open"
    },
    {
      "id": "INET-NSG-001",
      "title": "No NSG resources exist in the infrastructure",
      "severity": "minor",
      "category": "NSG",
      "file": "infra/main.bicep",
      "description": "No Microsoft.Network/networkSecurityGroups resources are defined anywhere in the Bicep codebase. When a VNet is introduced, NSGs must be defined and attached to subnets. There is no NSG module or baseline to build from.",
      "recommendation": "Create an NSG module alongside the VNet module. Define explicit allow rules, add descriptions to every rule, and ensure no rule uses source: '*' for management ports (SSH/RDP).",
      "status": "open"
    },
    {
      "id": "INET-DNS-001",
      "title": "No private DNS zones scaffolded for future private endpoint readiness",
      "severity": "minor",
      "category": "DNS",
      "file": "infra/main.bicep",
      "description": "No Microsoft.Network/privateDnsZones resources are defined. While not required today (no private endpoints exist), private DNS zones are a prerequisite for any future private endpoint implementation.",
      "recommendation": "When adding private endpoints, create and link private DNS zones: privatelink.blob.core.windows.net, privatelink.documents.azure.com, privatelink.search.windows.net, privatelink.openai.azure.com, and privatelink.vaultcore.azure.net.",
      "status": "open"
    },
    {
      "id": "INET-AUTH-001",
      "title": "Managed Identity RBAC used consistently across all services",
      "severity": "info",
      "category": "AUTH",
      "description": "All compute resources (API App Service, Function App) use a User-Assigned Managed Identity for authentication to Storage, AI Search, OpenAI, and Cosmos DB. RBAC role assignments are defined in Bicep for all data services. This is the correct authentication posture and significantly reduces credential exposure risk.",
      "status": "open"
    },
    {
      "id": "INET-AUTH-002",
      "title": "Azure AI Search local auth correctly disabled",
      "severity": "info",
      "category": "AUTH",
      "file": "infra/core/search/search-services.bicep",
      "description": "The search module is called in main.bicep with disableLocalAuth: true, which is correctly propagated to the search resource. This prevents API key-based access to the search service, enforcing RBAC-only access.",
      "status": "open"
    }
  ],
  "summary": {
    "critical": 4,
    "notable": 6,
    "minor": 3,
    "info": 2,
    "total": 15
  }
}
