From 0331e20e85c4bc11313fa19abd99712b6dec0e0a Mon Sep 17 00:00:00 2001 From: jesb1n Date: Sat, 21 Mar 2026 16:01:04 +0530 Subject: [PATCH 1/4] add support for Zero Trust Identity Providers and Access Policies --- resources/accessIdentityProvider.go | 66 +++++++++++++++++++++++++++++ resources/accessPolicy.go | 66 +++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 resources/accessIdentityProvider.go create mode 100644 resources/accessPolicy.go diff --git a/resources/accessIdentityProvider.go b/resources/accessIdentityProvider.go new file mode 100644 index 0000000..cda0c93 --- /dev/null +++ b/resources/accessIdentityProvider.go @@ -0,0 +1,66 @@ +package resources + +import ( + "context" + + "github.com/cloudflare/cloudflare-go/v6" + "github.com/cloudflare/cloudflare-go/v6/zero_trust" + + "github.com/arafato/cf-nuke/infrastructure" + "github.com/arafato/cf-nuke/types" + "github.com/arafato/cf-nuke/utils" +) + +func init() { + infrastructure.RegisterAccountCollector("zt-identity-provider", CollectZTIdentityProviders) +} + +type ZTIdentityProvider struct { + Client *zero_trust.IdentityProviderService +} + +func CollectZTIdentityProviders(creds *types.Credentials) (types.Resources, error) { + client := utils.CreateCFClient(creds) + + page, err := client.ZeroTrust.IdentityProviders.List(context.TODO(), zero_trust.IdentityProviderListParams{ + AccountID: cloudflare.F(creds.AccountID), + }) + if err != nil { + return nil, err + } + + var allIdPs []zero_trust.IdentityProviderListResponse + for page != nil && len(page.Result) != 0 { + allIdPs = append(allIdPs, page.Result...) + page, err = page.GetNextPage() + if err != nil { + break + } + } + + var allResources types.Resources + for _, idp := range allIdPs { + displayName := idp.Name + if displayName == "" { + displayName = idp.ID + } + res := types.Resource{ + Removable: ZTIdentityProvider{Client: client.ZeroTrust.IdentityProviders}, + ResourceID: idp.ID, + ResourceName: displayName, + AccountID: creds.AccountID, + ProductName: "ZTIdentityProvider", + } + allResources = append(allResources, &res) + } + + return allResources, nil +} + +func (c ZTIdentityProvider) Remove(accountID string, resourceID string, resourceName string) error { + _, err := c.Client.Delete(context.TODO(), resourceID, zero_trust.IdentityProviderDeleteParams{ + AccountID: cloudflare.F(accountID), + }) + + return err +} diff --git a/resources/accessPolicy.go b/resources/accessPolicy.go new file mode 100644 index 0000000..e03b030 --- /dev/null +++ b/resources/accessPolicy.go @@ -0,0 +1,66 @@ +package resources + +import ( + "context" + + "github.com/cloudflare/cloudflare-go/v6" + "github.com/cloudflare/cloudflare-go/v6/zero_trust" + + "github.com/arafato/cf-nuke/infrastructure" + "github.com/arafato/cf-nuke/types" + "github.com/arafato/cf-nuke/utils" +) + +func init() { + infrastructure.RegisterAccountCollector("zt-access-policy", CollectZTAccessPolicies) +} + +type ZTAccessPolicy struct { + Client *zero_trust.AccessPolicyService +} + +func CollectZTAccessPolicies(creds *types.Credentials) (types.Resources, error) { + client := utils.CreateCFClient(creds) + + page, err := client.ZeroTrust.Access.Policies.List(context.TODO(), zero_trust.AccessPolicyListParams{ + AccountID: cloudflare.F(creds.AccountID), + }) + if err != nil { + return nil, err + } + + var allPolicies []zero_trust.AccessPolicyListResponse + for page != nil && len(page.Result) != 0 { + allPolicies = append(allPolicies, page.Result...) + page, err = page.GetNextPage() + if err != nil { + break + } + } + + var allResources types.Resources + for _, policy := range allPolicies { + displayName := policy.Name + if displayName == "" { + displayName = policy.ID + } + res := types.Resource{ + Removable: ZTAccessPolicy{Client: client.ZeroTrust.Access.Policies}, + ResourceID: policy.ID, + ResourceName: displayName, + AccountID: creds.AccountID, + ProductName: "ZTAccessPolicy", + } + allResources = append(allResources, &res) + } + + return allResources, nil +} + +func (c ZTAccessPolicy) Remove(accountID string, resourceID string, resourceName string) error { + _, err := c.Client.Delete(context.TODO(), resourceID, zero_trust.AccessPolicyDeleteParams{ + AccountID: cloudflare.F(accountID), + }) + + return err +} From d64b40957b1dfdc2a630fdae118693c4251730ba Mon Sep 17 00:00:00 2001 From: jesb1n Date: Sat, 21 Mar 2026 17:06:43 +0530 Subject: [PATCH 2/4] add ZTDeviceProfile resource handling and collection logic --- resources/deviceProfile.go | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 resources/deviceProfile.go diff --git a/resources/deviceProfile.go b/resources/deviceProfile.go new file mode 100644 index 0000000..bdcf8da --- /dev/null +++ b/resources/deviceProfile.go @@ -0,0 +1,61 @@ +package resources + +import ( + "context" + + "github.com/cloudflare/cloudflare-go/v6" + "github.com/cloudflare/cloudflare-go/v6/zero_trust" + + "github.com/arafato/cf-nuke/infrastructure" + "github.com/arafato/cf-nuke/types" + "github.com/arafato/cf-nuke/utils" +) + +func init() { + infrastructure.RegisterAccountCollector("zt-device-profile", CollectZTDeviceProfiles) +} + +type ZTDeviceProfile struct { + Client *zero_trust.DevicePolicyCustomService +} + +func CollectZTDeviceProfiles(creds *types.Credentials) (types.Resources, error) { + client := utils.CreateCFClient(creds) + + page, err := client.ZeroTrust.Devices.Policies.Custom.List(context.TODO(), zero_trust.DevicePolicyCustomListParams{ + AccountID: cloudflare.F(creds.AccountID), + }) + if err != nil { + return nil, err + } + + var allResources types.Resources + for _, profile := range page.Result { + // Skip the default profile — it cannot be deleted + if profile.Default { + continue + } + displayName := profile.Name + if displayName == "" { + displayName = profile.PolicyID + } + res := types.Resource{ + Removable: ZTDeviceProfile{Client: client.ZeroTrust.Devices.Policies.Custom}, + ResourceID: profile.PolicyID, + ResourceName: displayName, + AccountID: creds.AccountID, + ProductName: "ZTDeviceProfile", + } + allResources = append(allResources, &res) + } + + return allResources, nil +} + +func (c ZTDeviceProfile) Remove(accountID string, resourceID string, resourceName string) error { + _, err := c.Client.Delete(context.TODO(), resourceID, zero_trust.DevicePolicyCustomDeleteParams{ + AccountID: cloudflare.F(accountID), + }) + + return err +} From 85854636212ea9e5ec303a6457e0aa6b96118bf9 Mon Sep 17 00:00:00 2001 From: jesb1n Date: Sat, 21 Mar 2026 17:07:44 +0530 Subject: [PATCH 3/4] add log file pattern to .gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3e45cdc..e23555e 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,6 @@ go.work.sum # Editor/IDE .zed + +# Logs +cf-nuke-scan-*.log \ No newline at end of file From fac0f390604a234d345fe33d7f8b800444bd5718 Mon Sep 17 00:00:00 2001 From: jesb1n Date: Sat, 21 Mar 2026 17:26:47 +0530 Subject: [PATCH 4/4] add ZTAccessUser resource handling and collection logic --- resources/accessUser.go | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 resources/accessUser.go diff --git a/resources/accessUser.go b/resources/accessUser.go new file mode 100644 index 0000000..411a47d --- /dev/null +++ b/resources/accessUser.go @@ -0,0 +1,80 @@ +package resources + +import ( + "context" + + "github.com/cloudflare/cloudflare-go/v6" + "github.com/cloudflare/cloudflare-go/v6/zero_trust" + + "github.com/arafato/cf-nuke/infrastructure" + "github.com/arafato/cf-nuke/types" + "github.com/arafato/cf-nuke/utils" +) + +func init() { + infrastructure.RegisterAccountCollector("zt-access-user", CollectZTAccessUsers) +} + +type ZTAccessUser struct { + Client *zero_trust.SeatService + SeatUID string +} + +func CollectZTAccessUsers(creds *types.Credentials) (types.Resources, error) { + client := utils.CreateCFClient(creds) + + page, err := client.ZeroTrust.Access.Users.List(context.TODO(), zero_trust.AccessUserListParams{ + AccountID: cloudflare.F(creds.AccountID), + }) + if err != nil { + return nil, err + } + + var allUsers []zero_trust.AccessUserListResponse + for page != nil && len(page.Result) != 0 { + allUsers = append(allUsers, page.Result...) + page, err = page.GetNextPage() + if err != nil { + break + } + } + + var allResources types.Resources + for _, user := range allUsers { + // Only include users that occupy a seat + if !user.AccessSeat && !user.GatewaySeat { + continue + } + displayName := user.Email + if displayName == "" { + displayName = user.Name + } + if displayName == "" { + displayName = user.ID + } + res := types.Resource{ + Removable: ZTAccessUser{Client: client.ZeroTrust.Seats, SeatUID: user.SeatUID}, + ResourceID: user.ID, + ResourceName: displayName, + AccountID: creds.AccountID, + ProductName: "ZTAccessUser", + } + allResources = append(allResources, &res) + } + + return allResources, nil +} + +func (c ZTAccessUser) Remove(accountID string, resourceID string, resourceName string) error { + _, err := c.Client.Edit(context.TODO(), zero_trust.SeatEditParams{ + AccountID: cloudflare.F(accountID), + Body: []zero_trust.SeatEditParamsBody{ + { + SeatUID: cloudflare.F(c.SeatUID), + AccessSeat: cloudflare.F(false), + GatewaySeat: cloudflare.F(false), + }, + }, + }) + return err +}