feat(misconf): Update Azure network schema for new checks (#9791)

Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
Co-authored-by: Nikita Pivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
yagreut
2025-11-19 20:57:51 +02:00
committed by GitHub
parent c6d95d7cd2
commit ea2dc586b8
13 changed files with 426 additions and 288 deletions

View File

@@ -2,6 +2,7 @@ package compute
import (
"github.com/aquasecurity/trivy/pkg/iac/providers/azure/compute"
"github.com/aquasecurity/trivy/pkg/iac/providers/azure/network"
"github.com/aquasecurity/trivy/pkg/iac/scanners/azure"
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
)
@@ -93,25 +94,6 @@ func adaptLinuxVirtualMachine(resource azure.Resource) compute.LinuxVirtualMachi
}
func extractNetworkInterfaces(networkProfile azure.Value, metadata iacTypes.Metadata) []compute.NetworkInterface {
var networkInterfaces []compute.NetworkInterface
nicsArray := networkProfile.GetMapValue("networkInterfaces").AsList()
for _, nic := range nicsArray {
nicID := nic.GetMapValue("id").AsStringValue("", metadata)
if nicID.Value() != "" {
// Create a minimal NetworkInterface object with the ID information
// In ARM templates, we don't have direct access to subnet details like in Terraform
networkInterface := compute.NetworkInterface{
Metadata: nicID.GetMetadata(),
SubnetID: iacTypes.StringDefault("", nicID.GetMetadata()),
SecurityGroups: nil,
HasPublicIP: iacTypes.BoolDefault(false, nicID.GetMetadata()),
PublicIPAddress: iacTypes.StringDefault("", nicID.GetMetadata()),
}
networkInterfaces = append(networkInterfaces, networkInterface)
}
}
return networkInterfaces
func extractNetworkInterfaces(_ azure.Value, _ iacTypes.Metadata) []network.NetworkInterface {
return nil
}

View File

@@ -116,22 +116,6 @@ func TestAdapt(t *testing.T) {
LinuxVirtualMachines: []compute.LinuxVirtualMachine{{
VirtualMachine: compute.VirtualMachine{
CustomData: types.StringTest("test"),
NetworkInterfaces: []compute.NetworkInterface{
{
Metadata: types.NewTestMetadata(),
SubnetID: types.StringTest(""),
SecurityGroups: nil,
HasPublicIP: types.BoolTest(false),
PublicIPAddress: types.StringTest(""),
},
{
Metadata: types.NewTestMetadata(),
SubnetID: types.StringTest(""),
SecurityGroups: nil,
HasPublicIP: types.BoolTest(false),
PublicIPAddress: types.StringTest(""),
},
},
},
OSProfileLinuxConfig: compute.OSProfileLinuxConfig{
DisablePasswordAuthentication: types.BoolTest(false),
@@ -140,22 +124,6 @@ func TestAdapt(t *testing.T) {
WindowsVirtualMachines: []compute.WindowsVirtualMachine{{
VirtualMachine: compute.VirtualMachine{
CustomData: types.StringTest("test"),
NetworkInterfaces: []compute.NetworkInterface{
{
Metadata: types.NewTestMetadata(),
SubnetID: types.StringTest(""),
SecurityGroups: nil,
HasPublicIP: types.BoolTest(false),
PublicIPAddress: types.StringTest(""),
},
{
Metadata: types.NewTestMetadata(),
SubnetID: types.StringTest(""),
SecurityGroups: nil,
HasPublicIP: types.BoolTest(false),
PublicIPAddress: types.StringTest(""),
},
},
},
}},
},

View File

@@ -15,6 +15,7 @@ func Adapt(deployment azure.Deployment) network.Network {
return network.Network{
SecurityGroups: adaptSecurityGroups(deployment),
NetworkWatcherFlowLogs: adaptNetworkWatcherFlowLogs(deployment),
NetworkInterfaces: adaptNetworkInterfaces(deployment),
}
}
@@ -42,7 +43,9 @@ func adaptSecurityGroupRules(deployment azure.Deployment) (rules []network.Secur
func adaptSecurityGroupRule(resource azure.Resource) network.SecurityGroupRule {
sourceAddressPrefixes := resource.Properties.GetMapValue("sourceAddressPrefixes").AsStringValuesList("")
sourceAddressPrefixes = append(sourceAddressPrefixes, resource.Properties.GetMapValue("sourceAddressPrefix").AsStringValue("", resource.Metadata))
if prefix := resource.Properties.GetMapValue("sourceAddressPrefix").AsStringValue("", resource.Metadata); prefix.IsNotEmpty() {
sourceAddressPrefixes = append(sourceAddressPrefixes, prefix)
}
var sourcePortRanges []common.PortRange
for _, portRange := range resource.Properties.GetMapValue("sourcePortRanges").AsList() {
@@ -56,7 +59,9 @@ func adaptSecurityGroupRule(resource azure.Resource) network.SecurityGroupRule {
}
destinationAddressPrefixes := resource.Properties.GetMapValue("destinationAddressPrefixes").AsStringValuesList("")
destinationAddressPrefixes = append(destinationAddressPrefixes, resource.Properties.GetMapValue("destinationAddressPrefix").AsStringValue("", resource.Metadata))
if prefix := resource.Properties.GetMapValue("destinationAddressPrefix").AsStringValue("", resource.Metadata); prefix.IsNotEmpty() {
destinationAddressPrefixes = append(destinationAddressPrefixes, prefix)
}
var destinationPortRanges []common.PortRange
for _, portRange := range resource.Properties.GetMapValue("destinationPortRanges").AsList() {
@@ -99,12 +104,57 @@ func adaptNetworkWatcherFlowLogs(deployment azure.Deployment) (flowLogs []networ
}
func adaptNetworkWatcherFlowLog(resource azure.Resource) network.NetworkWatcherFlowLog {
enabled := resource.Properties.GetMapValue("enabled").AsBoolValue(false, resource.Metadata)
retentionPolicy := resource.Properties.GetMapValue("retentionPolicy")
return network.NetworkWatcherFlowLog{
Metadata: resource.Metadata,
Enabled: enabled,
RetentionPolicy: network.RetentionPolicy{
Metadata: resource.Metadata,
Enabled: resource.Properties.GetMapValue("retentionPolicy").GetMapValue("enabled").AsBoolValue(false, resource.Metadata),
Days: resource.Properties.GetMapValue("retentionPolicy").GetMapValue("days").AsIntValue(0, resource.Metadata),
Enabled: retentionPolicy.GetMapValue("enabled").AsBoolValue(false, resource.Metadata),
Days: retentionPolicy.GetMapValue("days").AsIntValue(0, resource.Metadata),
},
}
}
func adaptNetworkInterfaces(deployment azure.Deployment) []network.NetworkInterface {
var networkInterfaces []network.NetworkInterface
for _, resource := range deployment.GetResourcesByType("Microsoft.Network/networkInterfaces") {
networkInterfaces = append(networkInterfaces, adaptNetworkInterface(resource, deployment))
}
return networkInterfaces
}
func adaptNetworkInterface(resource azure.Resource, _ azure.Deployment) network.NetworkInterface {
ni := network.NetworkInterface{
Metadata: resource.Metadata,
EnableIPForwarding: resource.Properties.GetMapValue("enableIPForwarding").AsBoolValue(false, resource.Metadata),
HasPublicIP: iacTypes.BoolDefault(false, resource.Metadata),
PublicIPAddress: iacTypes.StringDefault("", resource.Metadata),
SubnetID: iacTypes.StringDefault("", resource.Metadata),
}
ipConfigs := resource.Properties.GetMapValue("ipConfigurations").AsList()
ni.IPConfigurations = make([]network.IPConfiguration, 0, len(ipConfigs))
for _, ipConfig := range ipConfigs {
if ipConfig.IsNull() {
continue
}
ipConfigProps := ipConfig.GetMapValue("properties")
ni.IPConfigurations = append(ni.IPConfigurations, network.IPConfiguration{
Metadata: resource.Metadata,
PublicIPAddress: ipConfigProps.GetMapValue("publicIPAddress").
GetMapValue("id").AsStringValue("", resource.Metadata),
SubnetID: ipConfigProps.GetMapValue("subnet").
GetMapValue("id").AsStringValue("", resource.Metadata),
Primary: ipConfigProps.GetMapValue("primary").AsBoolValue(false, resource.Metadata),
})
}
ni.Setup()
// Note: SecurityGroups are not resolved for ARM templates as related resource search
// is not yet implemented for ARM (parser cannot evaluate expressions/references)
return ni
}

View File

@@ -35,16 +35,10 @@ func TestAdapt(t *testing.T) {
}`,
expected: network.Network{
NetworkWatcherFlowLogs: []network.NetworkWatcherFlowLog{{
RetentionPolicy: network.RetentionPolicy{
Days: types.IntTest(0),
Enabled: types.BoolTest(false),
},
RetentionPolicy: network.RetentionPolicy{},
}},
SecurityGroups: []network.SecurityGroup{{
Rules: []network.SecurityGroupRule{{
DestinationAddresses: []types.StringValue{types.StringTest("")},
SourceAddresses: []types.StringValue{types.StringTest("")},
}},
Rules: []network.SecurityGroupRule{{}},
}},
},
},
@@ -147,6 +141,71 @@ func TestAdapt(t *testing.T) {
}},
},
},
{
name: "network interface with ip configurations",
source: `{
"resources": [
{
"type": "Microsoft.Network/networkInterfaces",
"properties": {
"enableIPForwarding": true,
"ipConfigurations": [
{
"name": "primary-ip",
"properties": {
"primary": true,
"subnet": {
"id": "/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet/subnets/subnet-primary"
},
"publicIPAddress": {
"id": "/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip-primary"
}
}
},
{
"name": "secondary-ip",
"properties": {
"primary": false,
"subnet": {
"id": "/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet/subnets/subnet-secondary"
}
}
}
]
}
}
]
}`,
expected: network.Network{
NetworkInterfaces: []network.NetworkInterface{
{
EnableIPForwarding: types.BoolTest(true),
// backward compatibility — filled from primary config
SubnetID: types.StringTest("/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet/subnets/subnet-primary"),
HasPublicIP: types.BoolTest(true),
PublicIPAddress: types.StringTest("/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip-primary"),
IPConfigurations: []network.IPConfiguration{
{
Primary: types.BoolTest(true),
SubnetID: types.StringTest(
"/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet/subnets/subnet-primary",
),
HasPublicIP: types.BoolTest(true),
PublicIPAddress: types.StringTest("/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/publicIPAddresses/pip-primary"),
},
{
Primary: types.BoolTest(false),
SubnetID: types.StringTest("/subscriptions/abc/resourceGroups/rg/providers/Microsoft.Network/virtualNetworks/vnet/subnets/subnet-secondary"),
HasPublicIP: types.BoolTest(false),
PublicIPAddress: types.StringTest(""),
},
},
},
},
},
},
}
for _, tt := range tests {

View File

@@ -1,10 +1,9 @@
package appservice
import (
"github.com/samber/lo"
"github.com/aquasecurity/trivy/pkg/iac/providers/azure/appservice"
"github.com/aquasecurity/trivy/pkg/iac/terraform"
"github.com/aquasecurity/trivy/pkg/iac/types"
)
func Adapt(modules terraform.Modules) appservice.AppService {
@@ -31,30 +30,42 @@ func adaptFunctionApps(modules terraform.Modules) []appservice.FunctionApp {
}
func adaptService(resource *terraform.Block) appservice.Service {
siteBlock := resource.GetBlock("site_config")
identityBlock := resource.GetBlock("identity")
authBlock := resource.GetBlock("auth_settings")
return appservice.Service{
service := appservice.Service{
Metadata: resource.GetMetadata(),
EnableClientCert: resource.GetAttribute("client_cert_enabled").AsBoolValueOrDefault(false, resource),
HTTPSOnly: resource.GetAttribute("https_only").AsBoolValueOrDefault(false, resource),
Identity: appservice.Identity{
Metadata: lo.TernaryF(identityBlock.IsNil(), resource.GetMetadata, identityBlock.GetMetadata),
Type: identityBlock.GetAttribute("type").AsStringValueOrDefault("", identityBlock),
},
Authentication: appservice.Authentication{
Metadata: lo.TernaryF(identityBlock.IsNil(), resource.GetMetadata, authBlock.GetMetadata),
Enabled: authBlock.GetAttribute("enabled").AsBoolValueOrDefault(false, authBlock),
},
Site: appservice.Site{
Metadata: lo.TernaryF(identityBlock.IsNil(), resource.GetMetadata, siteBlock.GetMetadata),
Metadata: resource.GetMetadata(),
MinimumTLSVersion: types.StringDefault("1.2", resource.GetMetadata()),
},
}
if identityBlock := resource.GetBlock("identity"); identityBlock.IsNotNil() {
service.Identity = appservice.Identity{
Metadata: identityBlock.GetMetadata(),
Type: identityBlock.GetAttribute("type").AsStringValueOrDefault("", identityBlock),
}
}
if authBlock := resource.GetBlock("auth_settings"); authBlock.IsNotNil() {
service.Authentication = appservice.Authentication{
Metadata: authBlock.GetMetadata(),
Enabled: authBlock.GetAttribute("enabled").AsBoolValueOrDefault(false, authBlock),
}
}
if siteBlock := resource.GetBlock("site_config"); siteBlock.IsNotNil() {
service.Site = appservice.Site{
Metadata: siteBlock.GetMetadata(),
EnableHTTP2: siteBlock.GetAttribute("http2_enabled").AsBoolValueOrDefault(false, siteBlock),
MinimumTLSVersion: siteBlock.GetAttribute("min_tls_version").AsStringValueOrDefault("1.2", siteBlock),
PHPVersion: siteBlock.GetAttribute("php_version").AsStringValueOrDefault("", siteBlock),
PythonVersion: siteBlock.GetAttribute("python_version").AsStringValueOrDefault("", siteBlock),
FTPSState: siteBlock.GetAttribute("ftps_state").AsStringValueOrDefault("", siteBlock),
},
}
}
return service
}
func adaptFunctionApp(resource *terraform.Block) appservice.FunctionApp {

View File

@@ -142,25 +142,24 @@ func adaptWindowsVM(resource *terraform.Block, modules terraform.Modules) comput
}
}
func resolveNetworkInterfaces(resource *terraform.Block, modules terraform.Modules) []compute.NetworkInterface {
var networkInterfaces []compute.NetworkInterface
func resolveNetworkInterfaces(resource *terraform.Block, modules terraform.Modules) []network.NetworkInterface {
nicIDsAttr := resource.GetAttribute("network_interface_ids")
if nicIDsAttr.IsNil() {
return networkInterfaces
return nil
}
var networkInterfaces []network.NetworkInterface
for _, nicIDVal := range nicIDsAttr.AsStringValues() {
if referencedNIC, err := modules.GetReferencedBlock(nicIDsAttr, resource); err == nil {
ni := adaptNetworkInterface(referencedNIC, modules)
ni := anetwork.AdaptNetworkInterface(referencedNIC, modules)
networkInterfaces = append(networkInterfaces, ni)
continue
}
networkInterfaces = append(networkInterfaces, compute.NetworkInterface{
networkInterfaces = append(networkInterfaces, network.NetworkInterface{
Metadata: iacTypes.NewUnmanagedMetadata(),
EnableIPForwarding: iacTypes.BoolDefault(false, nicIDVal.GetMetadata()),
SubnetID: iacTypes.StringDefault("", nicIDVal.GetMetadata()),
SecurityGroups: nil,
HasPublicIP: iacTypes.BoolDefault(false, nicIDVal.GetMetadata()),
PublicIPAddress: iacTypes.StringDefault("", nicIDVal.GetMetadata()),
})
@@ -168,44 +167,3 @@ func resolveNetworkInterfaces(resource *terraform.Block, modules terraform.Modul
return networkInterfaces
}
func adaptNetworkInterface(resource *terraform.Block, modules terraform.Modules) compute.NetworkInterface {
ni := compute.NetworkInterface{
Metadata: resource.GetMetadata(),
SubnetID: iacTypes.StringDefault("", resource.GetMetadata()),
SecurityGroups: nil,
HasPublicIP: iacTypes.BoolDefault(false, resource.GetMetadata()),
PublicIPAddress: iacTypes.StringDefault("", resource.GetMetadata()),
}
if nsgAttr := resource.GetAttribute("network_security_group_id"); nsgAttr.IsNotNil() {
if referencedNSG, err := modules.GetReferencedBlock(nsgAttr, resource); err == nil {
ni.SecurityGroups = []network.SecurityGroup{adaptSecurityGroupFromBlock(referencedNSG)}
}
}
ipConfigs := resource.GetBlocks("ip_configuration")
if len(ipConfigs) > 0 {
ipConfig := ipConfigs[0]
if subnetAttr := ipConfig.GetAttribute("subnet_id"); subnetAttr.IsNotNil() {
ni.SubnetID = subnetAttr.AsStringValueOrDefault("", ipConfig)
}
if publicIPAttr := ipConfig.GetAttribute("public_ip_address_id"); publicIPAttr.IsNotNil() {
ni.HasPublicIP = iacTypes.Bool(true, publicIPAttr.GetMetadata())
}
}
return ni
}
func adaptSecurityGroupFromBlock(resource *terraform.Block) network.SecurityGroup {
var rules []network.SecurityGroupRule
for _, ruleBlock := range resource.GetBlocks("security_rule") {
rules = append(rules, anetwork.AdaptSGRule(ruleBlock))
}
return network.SecurityGroup{
Metadata: resource.GetMetadata(),
Rules: rules,
}
}

View File

@@ -29,10 +29,8 @@ resource "azurerm_managed_disk" "example" {
}
}`,
expected: compute.ManagedDisk{
Metadata: iacTypes.NewTestMetadata(),
Encryption: compute.Encryption{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
Enabled: iacTypes.BoolTest(false),
},
},
},
@@ -42,10 +40,8 @@ resource "azurerm_managed_disk" "example" {
resource "azurerm_managed_disk" "example" {
}`,
expected: compute.ManagedDisk{
Metadata: iacTypes.NewTestMetadata(),
Encryption: compute.Encryption{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
Enabled: iacTypes.BoolTest(true),
},
},
},
@@ -87,13 +83,9 @@ resource "azurerm_virtual_machine" "example" {
`,
expected: compute.LinuxVirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
VirtualMachine: compute.VirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
CustomData: iacTypes.String("", iacTypes.NewTestMetadata()),
},
VirtualMachine: compute.VirtualMachine{},
OSProfileLinuxConfig: compute.OSProfileLinuxConfig{
Metadata: iacTypes.NewTestMetadata(),
DisablePasswordAuthentication: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
DisablePasswordAuthentication: iacTypes.BoolTest(true),
},
},
},
@@ -112,17 +104,11 @@ export DATABASE_PASSWORD=\"SomeSortOfPassword\"
}
}`,
expected: compute.LinuxVirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
VirtualMachine: compute.VirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
CustomData: iacTypes.String(
`export DATABASE_PASSWORD=\"SomeSortOfPassword\"
`, iacTypes.NewTestMetadata()),
},
OSProfileLinuxConfig: compute.OSProfileLinuxConfig{
Metadata: iacTypes.NewTestMetadata(),
DisablePasswordAuthentication: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
CustomData: iacTypes.StringTest(
"export DATABASE_PASSWORD=\\\"SomeSortOfPassword\\\"\n"),
},
OSProfileLinuxConfig: compute.OSProfileLinuxConfig{},
},
},
{
@@ -149,13 +135,6 @@ resource "azurerm_linux_virtual_machine" "example" {
}
}
resource "azurerm_public_ip" "example" {
name = "acceptanceTestPublicIp1"
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
allocation_method = "Static"
}
resource "azurerm_network_interface" "example" {
name = "example-nic"
location = azurerm_resource_group.example.location
@@ -163,7 +142,7 @@ resource "azurerm_network_interface" "example" {
ip_configuration {
name = "internal"
public_ip_address_id = azurerm_public_ip.example.id
public_ip_address_id = "test-public-ip-id"
}
network_security_group_id = azurerm_network_security_group.example.id
@@ -188,15 +167,17 @@ resource "azurerm_network_security_group" "example" {
}
`,
expected: compute.LinuxVirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
VirtualMachine: compute.VirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
CustomData: iacTypes.String("", iacTypes.NewTestMetadata()),
NetworkInterfaces: []compute.NetworkInterface{
NetworkInterfaces: []network.NetworkInterface{
{
Metadata: iacTypes.NewTestMetadata(),
HasPublicIP: iacTypes.BoolTest(true),
PublicIPAddress: iacTypes.String("", iacTypes.NewTestMetadata()),
PublicIPAddress: iacTypes.StringTest("test-public-ip-id"),
IPConfigurations: []network.IPConfiguration{
{
HasPublicIP: iacTypes.BoolTest(true),
PublicIPAddress: iacTypes.StringTest("test-public-ip-id"),
},
},
SecurityGroups: []network.SecurityGroup{
{
Rules: []network.SecurityGroupRule{
@@ -215,8 +196,7 @@ resource "azurerm_network_security_group" "example" {
},
},
OSProfileLinuxConfig: compute.OSProfileLinuxConfig{
Metadata: iacTypes.NewTestMetadata(),
DisablePasswordAuthentication: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
DisablePasswordAuthentication: iacTypes.BoolTest(true),
},
},
},
@@ -237,16 +217,11 @@ resource "azurerm_linux_virtual_machine" "example" {
}
}`,
expected: compute.LinuxVirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
VirtualMachine: compute.VirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
CustomData: iacTypes.String("", iacTypes.NewTestMetadata()),
// Empty array in Terraform is parsed as nil
NetworkInterfaces: nil,
},
OSProfileLinuxConfig: compute.OSProfileLinuxConfig{
Metadata: iacTypes.NewTestMetadata(),
DisablePasswordAuthentication: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
DisablePasswordAuthentication: iacTypes.BoolTest(true),
},
},
},
@@ -281,11 +256,8 @@ export DATABASE_PASSWORD=\"SomeSortOfPassword\"
}
}`,
expected: compute.WindowsVirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
VirtualMachine: compute.VirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
CustomData: iacTypes.String(`export DATABASE_PASSWORD=\"SomeSortOfPassword\"
`, iacTypes.NewTestMetadata()),
CustomData: iacTypes.StringTest("export DATABASE_PASSWORD=\\\"SomeSortOfPassword\\\"\n"),
},
},
},
@@ -299,11 +271,8 @@ export GREETING="Hello there"
EOF
}`,
expected: compute.WindowsVirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
VirtualMachine: compute.VirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
CustomData: iacTypes.String(`export GREETING="Hello there"
`, iacTypes.NewTestMetadata()),
CustomData: iacTypes.StringTest("export GREETING=\"Hello there\"\n"),
},
},
},
@@ -325,25 +294,10 @@ resource "azurerm_windows_virtual_machine" "example" {
}
}`,
expected: compute.WindowsVirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
VirtualMachine: compute.VirtualMachine{
Metadata: iacTypes.NewTestMetadata(),
CustomData: iacTypes.String("", iacTypes.NewTestMetadata()),
NetworkInterfaces: []compute.NetworkInterface{
{
Metadata: iacTypes.NewTestMetadata(),
SubnetID: iacTypes.String("", iacTypes.NewTestMetadata()),
SecurityGroups: nil,
HasPublicIP: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
PublicIPAddress: iacTypes.String("", iacTypes.NewTestMetadata()),
},
{
Metadata: iacTypes.NewTestMetadata(),
SubnetID: iacTypes.String("", iacTypes.NewTestMetadata()),
SecurityGroups: nil,
HasPublicIP: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
PublicIPAddress: iacTypes.String("", iacTypes.NewTestMetadata()),
},
NetworkInterfaces: []network.NetworkInterface{
{},
{},
},
},
},

View File

@@ -2,6 +2,7 @@ package network
import (
"github.com/google/uuid"
"github.com/samber/lo"
"github.com/aquasecurity/trivy/pkg/iac/adapters/common"
"github.com/aquasecurity/trivy/pkg/iac/providers/azure/network"
@@ -20,6 +21,7 @@ func Adapt(modules terraform.Modules) network.Network {
groups: make(map[string]network.SecurityGroup),
}).adaptSecurityGroups(),
NetworkWatcherFlowLogs: adaptWatcherLogs(modules),
NetworkInterfaces: adaptNetworkInterfaces(modules),
}
}
@@ -90,6 +92,7 @@ func (a *adapter) adaptSecurityGroup(resource *terraform.Block) {
func adaptWatcherLog(resource *terraform.Block) network.NetworkWatcherFlowLog {
flowLog := network.NetworkWatcherFlowLog{
Metadata: resource.GetMetadata(),
Enabled: resource.GetAttribute("enabled").AsBoolValueOrDefault(false, resource),
RetentionPolicy: network.RetentionPolicy{
Metadata: resource.GetMetadata(),
Enabled: iacTypes.BoolDefault(false, resource.GetMetadata()),
@@ -98,14 +101,68 @@ func adaptWatcherLog(resource *terraform.Block) network.NetworkWatcherFlowLog {
}
if retentionPolicyBlock := resource.GetBlock("retention_policy"); retentionPolicyBlock.IsNotNil() {
flowLog.RetentionPolicy.Metadata = retentionPolicyBlock.GetMetadata()
enabledAttr := retentionPolicyBlock.GetAttribute("enabled")
flowLog.RetentionPolicy.Enabled = enabledAttr.AsBoolValueOrDefault(false, retentionPolicyBlock)
daysAttr := retentionPolicyBlock.GetAttribute("days")
flowLog.RetentionPolicy.Days = daysAttr.AsIntValueOrDefault(0, retentionPolicyBlock)
flowLog.RetentionPolicy = network.RetentionPolicy{
Metadata: retentionPolicyBlock.GetMetadata(),
Enabled: retentionPolicyBlock.GetAttribute("enabled").
AsBoolValueOrDefault(false, retentionPolicyBlock),
Days: retentionPolicyBlock.GetAttribute("days").
AsIntValueOrDefault(0, retentionPolicyBlock),
}
}
return flowLog
}
func adaptNetworkInterfaces(modules terraform.Modules) []network.NetworkInterface {
var networkInterfaces []network.NetworkInterface
for _, module := range modules {
for _, resource := range module.GetResourcesByType("azurerm_network_interface") {
networkInterfaces = append(networkInterfaces, AdaptNetworkInterface(resource, modules))
}
}
return networkInterfaces
}
func AdaptNetworkInterface(resource *terraform.Block, modules terraform.Modules) network.NetworkInterface {
ni := network.NetworkInterface{
Metadata: resource.GetMetadata(),
// Support both ip_forwarding_enabled (new) and enable_ip_forwarding (old) attributes
EnableIPForwarding: resource.GetFirstAttributeOf("ip_forwarding_enabled", "enable_ip_forwarding").
AsBoolValueOrDefault(false, resource),
HasPublicIP: iacTypes.BoolDefault(false, resource.GetMetadata()),
PublicIPAddress: iacTypes.StringDefault("", resource.GetMetadata()),
SubnetID: iacTypes.StringDefault("", resource.GetMetadata()),
}
if nsgAttr := resource.GetAttribute("network_security_group_id"); nsgAttr.IsNotNil() {
if referencedNSG, err := modules.GetReferencedBlock(nsgAttr, resource); err == nil {
ni.SecurityGroups = []network.SecurityGroup{adaptSecurityGroupFromBlock(referencedNSG)}
}
}
ipConfigs := resource.GetBlocks("ip_configuration")
ni.IPConfigurations = make([]network.IPConfiguration, 0, len(ipConfigs))
for _, ipConfig := range ipConfigs {
ni.IPConfigurations = append(ni.IPConfigurations, network.IPConfiguration{
Metadata: ipConfig.GetMetadata(),
PublicIPAddress: ipConfig.GetAttribute("public_ip_address_id").AsStringValueOrDefault("", ipConfig),
SubnetID: ipConfig.GetAttribute("subnet_id").AsStringValueOrDefault("", ipConfig),
Primary: ipConfig.GetAttribute("primary").AsBoolValueOrDefault(false, ipConfig),
})
}
ni.Setup()
return ni
}
func adaptSecurityGroupFromBlock(resource *terraform.Block) network.SecurityGroup {
return network.SecurityGroup{
Metadata: resource.GetMetadata(),
Rules: lo.Map(resource.GetBlocks("security_rule"),
func(b *terraform.Block, _ int) network.SecurityGroupRule {
return AdaptSGRule(b)
},
),
}
}

View File

@@ -41,6 +41,7 @@ func Test_Adapt(t *testing.T) {
resource "azurerm_network_watcher_flow_log" "example" {
resource_group_name = azurerm_resource_group.example.name
name = "example-log"
enabled = true
retention_policy {
enabled = true
@@ -51,44 +52,38 @@ func Test_Adapt(t *testing.T) {
expected: network.Network{
SecurityGroups: []network.SecurityGroup{
{
Metadata: iacTypes.NewTestMetadata(),
Rules: []network.SecurityGroupRule{
{
Metadata: iacTypes.NewTestMetadata(),
Outbound: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
Allow: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
Allow: iacTypes.BoolTest(true),
SourceAddresses: []iacTypes.StringValue{
iacTypes.String("4.53.160.75", iacTypes.NewTestMetadata()),
iacTypes.StringTest("4.53.160.75"),
},
DestinationAddresses: []iacTypes.StringValue{
iacTypes.String("*", iacTypes.NewTestMetadata()),
iacTypes.StringTest("*"),
},
SourcePorts: []common.PortRange{
{
Metadata: iacTypes.NewTestMetadata(),
Start: iacTypes.IntTest(0),
End: iacTypes.IntTest(65535),
},
},
DestinationPorts: []common.PortRange{
{
Metadata: iacTypes.NewTestMetadata(),
Start: iacTypes.IntTest(3389),
End: iacTypes.IntTest(3389),
},
},
Protocol: iacTypes.String("TCP", iacTypes.NewTestMetadata()),
Protocol: iacTypes.StringTest("TCP"),
},
},
},
},
NetworkWatcherFlowLogs: []network.NetworkWatcherFlowLog{
{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.BoolTest(true),
RetentionPolicy: network.RetentionPolicy{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
Days: iacTypes.Int(7, iacTypes.NewTestMetadata()),
Enabled: iacTypes.BoolTest(true),
Days: iacTypes.IntTest(7),
},
},
},
@@ -96,23 +91,66 @@ func Test_Adapt(t *testing.T) {
},
{
name: "defaults",
terraform: `
resource "azurerm_network_security_group" "example" {
terraform: `resource "azurerm_network_security_group" "example" {
name = "tf-appsecuritygroup"
security_rule {
}
}
security_rule {}
}
`,
expected: network.Network{
SecurityGroups: []network.SecurityGroup{
{
Metadata: iacTypes.NewTestMetadata(),
Rules: []network.SecurityGroupRule{
{
Allow: iacTypes.BoolTest(true),
},
},
},
},
},
},
{
name: "network interface",
terraform: `resource "azurerm_network_interface" "example" {
name = "example-nic"
location = "eastus"
resource_group_name = "example-rg"
ip_configuration {
name = "primary-ip"
primary = true
subnet_id = "subnet-primary-id"
public_ip_address_id = "public-ip-primary-id"
}
ip_configuration {
name = "secondary-ip"
subnet_id = "subnet-secondary-id"
}
}
`,
expected: network.Network{
NetworkInterfaces: []network.NetworkInterface{
{
Metadata: iacTypes.NewTestMetadata(),
Outbound: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
Allow: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
Protocol: iacTypes.String("", iacTypes.NewTestMetadata()),
// legacy fields filled from primary
SubnetID: iacTypes.String("subnet-primary-id", iacTypes.NewTestMetadata()),
HasPublicIP: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
PublicIPAddress: iacTypes.String("public-ip-primary-id", iacTypes.NewTestMetadata()),
IPConfigurations: []network.IPConfiguration{
{
Metadata: iacTypes.NewTestMetadata(),
SubnetID: iacTypes.String("subnet-primary-id", iacTypes.NewTestMetadata()),
Primary: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
HasPublicIP: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
PublicIPAddress: iacTypes.String("public-ip-primary-id", iacTypes.NewTestMetadata()),
},
{
Metadata: iacTypes.NewTestMetadata(),
SubnetID: iacTypes.String("subnet-secondary-id", iacTypes.NewTestMetadata()),
Primary: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
HasPublicIP: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
PublicIPAddress: iacTypes.String("", iacTypes.NewTestMetadata()),
},
},
},
@@ -140,6 +178,7 @@ func Test_adaptWatcherLog(t *testing.T) {
name: "defined",
terraform: `
resource "azurerm_network_watcher_flow_log" "watcher" {
enabled = true
retention_policy {
enabled = true
days = 90
@@ -147,11 +186,10 @@ func Test_adaptWatcherLog(t *testing.T) {
}
`,
expected: network.NetworkWatcherFlowLog{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.BoolTest(true),
RetentionPolicy: network.RetentionPolicy{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.Bool(true, iacTypes.NewTestMetadata()),
Days: iacTypes.Int(90, iacTypes.NewTestMetadata()),
Enabled: iacTypes.BoolTest(true),
Days: iacTypes.IntTest(90),
},
},
},
@@ -164,11 +202,10 @@ func Test_adaptWatcherLog(t *testing.T) {
}
`,
expected: network.NetworkWatcherFlowLog{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.BoolTest(false),
RetentionPolicy: network.RetentionPolicy{
Metadata: iacTypes.NewTestMetadata(),
Enabled: iacTypes.Bool(false, iacTypes.NewTestMetadata()),
Days: iacTypes.Int(0, iacTypes.NewTestMetadata()),
Enabled: iacTypes.BoolTest(false),
Days: iacTypes.IntTest(0),
},
},
},

View File

@@ -14,15 +14,7 @@ type Compute struct {
type VirtualMachine struct {
Metadata iacTypes.Metadata
CustomData iacTypes.StringValue // NOT base64 encoded
NetworkInterfaces []NetworkInterface
}
type NetworkInterface struct {
Metadata iacTypes.Metadata
SubnetID iacTypes.StringValue
SecurityGroups []network.SecurityGroup
HasPublicIP iacTypes.BoolValue
PublicIPAddress iacTypes.StringValue
NetworkInterfaces []network.NetworkInterface
}
type LinuxVirtualMachine struct {

View File

@@ -1,6 +1,8 @@
package network
import (
"github.com/samber/lo"
"github.com/aquasecurity/trivy/pkg/iac/adapters/common"
iacTypes "github.com/aquasecurity/trivy/pkg/iac/types"
)
@@ -8,6 +10,7 @@ import (
type Network struct {
SecurityGroups []SecurityGroup
NetworkWatcherFlowLogs []NetworkWatcherFlowLog
NetworkInterfaces []NetworkInterface
}
type SecurityGroup struct {
@@ -28,6 +31,7 @@ type SecurityGroupRule struct {
type NetworkWatcherFlowLog struct {
Metadata iacTypes.Metadata
Enabled iacTypes.BoolValue
RetentionPolicy RetentionPolicy
}
@@ -36,3 +40,57 @@ type RetentionPolicy struct {
Enabled iacTypes.BoolValue
Days iacTypes.IntValue
}
type NetworkInterface struct {
Metadata iacTypes.Metadata
EnableIPForwarding iacTypes.BoolValue
SecurityGroups []SecurityGroup
// Backward compatibility fields.
// These fields represent the primary IP configuration
// (or the first one if 'primary' was not explicitly set).
SubnetID iacTypes.StringValue
HasPublicIP iacTypes.BoolValue
PublicIPAddress iacTypes.StringValue
IPConfigurations []IPConfiguration
}
func (ni *NetworkInterface) Setup() {
for i := range ni.IPConfigurations {
c := &ni.IPConfigurations[i]
publicIP := c.PublicIPAddress
c.HasPublicIP = lo.Ternary(
publicIP.GetMetadata().IsResolvable(),
iacTypes.Bool(publicIP.IsNotEmpty(), publicIP.GetMetadata()),
iacTypes.BoolUnresolvable(c.Metadata),
)
}
if primaryIpConfig, exists := ni.findPrimaryIpConfig(); exists {
ni.SubnetID = primaryIpConfig.SubnetID
ni.PublicIPAddress = primaryIpConfig.PublicIPAddress
ni.HasPublicIP = primaryIpConfig.HasPublicIP
}
}
func (ni *NetworkInterface) findPrimaryIpConfig() (IPConfiguration, bool) {
for _, c := range ni.IPConfigurations {
if c.Primary.Value() {
return c, true
}
}
if len(ni.IPConfigurations) > 0 {
return ni.IPConfigurations[0], true
}
return IPConfiguration{}, false
}
type IPConfiguration struct {
Metadata iacTypes.Metadata
SubnetID iacTypes.StringValue
Primary iacTypes.BoolValue
HasPublicIP iacTypes.BoolValue
PublicIPAddress iacTypes.StringValue
}

View File

@@ -4737,34 +4737,6 @@
}
}
},
"github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.NetworkInterface": {
"type": "object",
"properties": {
"__defsec_metadata": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata"
},
"haspublicip": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue"
},
"publicipaddress": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue"
},
"securitygroups": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.SecurityGroup"
}
},
"subnetid": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue"
}
}
},
"github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.OSProfileLinuxConfig": {
"type": "object",
"properties": {
@@ -4793,7 +4765,7 @@
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.compute.NetworkInterface"
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.NetworkInterface"
}
}
}
@@ -5364,6 +5336,13 @@
"github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.Network": {
"type": "object",
"properties": {
"networkinterfaces": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.NetworkInterface"
}
},
"networkwatcherflowlogs": {
"type": "array",
"items": {
@@ -5380,6 +5359,38 @@
}
}
},
"github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.NetworkInterface": {
"type": "object",
"properties": {
"__defsec_metadata": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata"
},
"enableipforwarding": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue"
},
"haspublicip": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue"
},
"publicipaddress": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue"
},
"securitygroups": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.SecurityGroup"
}
},
"subnetid": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.StringValue"
}
}
},
"github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.NetworkWatcherFlowLog": {
"type": "object",
"properties": {
@@ -5387,6 +5398,10 @@
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.Metadata"
},
"enabled": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.types.BoolValue"
},
"retentionpolicy": {
"type": "object",
"$ref": "#/definitions/github.com.aquasecurity.trivy.pkg.iac.providers.azure.network.RetentionPolicy"

View File

@@ -138,9 +138,6 @@ func (b *Block) Reference() Reference {
}
func (b *Block) GetMetadata() iacTypes.Metadata {
if b.IsNil() {
return iacTypes.NewUnmanagedMetadata()
}
return b.metadata
}