Skip to content

Commit

Permalink
fix: Fixed ZCC pagination function and Added ZIA Policy Export Endpoi…
Browse files Browse the repository at this point in the history
…nt (#301)

* fix: Fixed ZCC pagination function and Added ZIA Policy Export Endpoint
  • Loading branch information
willguibr authored Feb 5, 2025
1 parent 290c31f commit 7ad7feb
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 9 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Changelog

# 3.1.3 (January 30, 2025)
# 3.1.3 (February 5, 2025)

## Notes
- Golang: **v1.22**

### ZIA SSL Inspection Rules
[PR #301](https://github.com/zscaler/zscaler-sdk-go/pull/301) - Added the following new ZIA API Endpoints:
- Added `POST /exportPolicies` Exports the rules configured for the specified policy types to JSON files.

### Bug Fixes

[PR #298](https://github.com/zscaler/zscaler-sdk-go/pull/298) - Fixed ZCC `ReadAllPages` pagination function due to panic related to incorrect method reference.
Expand Down
6 changes: 5 additions & 1 deletion docs/guides/release-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,15 @@ Track all Zscaler SDK GO releases. New resources, features, and bug fixes will b

---

# 3.1.3 (January 30, 2025)
# 3.1.3 (February 5, 2025)

## Notes
- Golang: **v1.22**

### ZIA SSL Inspection Rules
[PR #301](https://github.com/zscaler/zscaler-sdk-go/pull/301) - Added the following new ZIA API Endpoints:
- Added `POST /exportPolicies` Exports the rules configured for the specified policy types to JSON files.

### Bug Fixes

[PR #298](https://github.com/zscaler/zscaler-sdk-go/pull/298) - Fixed ZCC `ReadAllPages` pagination function due to panic related to incorrect method reference.
Expand Down
7 changes: 2 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module github.com/zscaler/zscaler-sdk-go/v3

go 1.22.0

toolchain go1.23.1
go 1.23.1

require (
github.com/allegro/bigcache/v3 v3.1.0
Expand All @@ -17,6 +15,7 @@ require (
github.com/okta/okta-sdk-golang/v2 v2.20.0
github.com/olekukonko/tablewriter v0.0.5
github.com/stretchr/testify v1.10.0
github.com/zscaler/zscaler-sdk-go/v2 v2.732.0
golang.org/x/text v0.22.0
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -32,14 +31,12 @@ require (
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/logutils v1.0.0 // indirect
github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/sys v0.29.0 // indirect
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,6 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 h1:pSCLCl6joCFRnjpeojzOpEYs4q7Vditq8fySFG5ap3Y=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -278,7 +277,6 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
github.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
Expand Down Expand Up @@ -315,6 +313,8 @@ github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q
github.com/zclconf/go-cty v1.8.2/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
github.com/zclconf/go-cty-yaml v1.0.2/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=
github.com/zscaler/zscaler-sdk-go/v2 v2.732.0 h1:oyESzPJJswG9dTSH0VcCeZH1ebYUmhIOKeTQg0sLu+w=
github.com/zscaler/zscaler-sdk-go/v2 v2.732.0/go.mod h1:ugDudbyESUrANGw74moJypgVnWuOyLm8NyIJgfUzNNo=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
Expand Down
105 changes: 105 additions & 0 deletions zscaler/zia/services/policy_export/policy_export.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package policy_export

import (
"archive/zip"
"bytes"
"context"
"fmt"
"io"
"os"
"path/filepath"
"strings"

"github.com/zscaler/zscaler-sdk-go/v3/zscaler"
)

const (
policyExportEndpoint = "/zia/api/v1/exportPolicies"
)

// ExportPolicies sends the policyTypes to the export API, unzips the returned data,
// and writes each JSON file to outputDir (e.g., "./exported_policies").
//
// This version includes a fix for the "zip slip" vulnerability:
// 1. Cleans and checks paths to prevent extraction outside outputDir.
// 2. Creates subdirectories, if any, within the archive.
func ExportPolicies(ctx context.Context, service *zscaler.Service, policyTypes []string, outputDir string) error {
// 1) Call CreateWithSlicePayload to POST the slice of policyTypes and get the ZIP data
respBody, err := service.Client.CreateWithSlicePayload(ctx, policyExportEndpoint, policyTypes)
if err != nil {
return fmt.Errorf("failed to export policies: %w", err)
}

service.Client.GetLogger().Printf("[INFO] Successfully triggered export. Unzipping response...")

// 2) Create output directory if it doesn't exist
if err := os.MkdirAll(outputDir, 0o755); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}

// 3) Convert respBody bytes into a zip reader
r, err := zip.NewReader(bytes.NewReader(respBody), int64(len(respBody)))
if err != nil {
return fmt.Errorf("failed to read ZIP from response: %w", err)
}

// 4) Iterate through each file in the ZIP
for _, f := range r.File {
// If it's a directory, just create it (if preserving subfolders)
if f.FileInfo().IsDir() {
// We'll sanitize the folder path too
folderName := filepath.Clean(f.Name)
destDir := filepath.Join(outputDir, folderName)

// Check for zip slip by ensuring the result is still inside outputDir
if !strings.HasPrefix(destDir, filepath.Clean(outputDir)+string(os.PathSeparator)) {
return fmt.Errorf("zip slip attempted with directory %q", f.Name)
}

if err := os.MkdirAll(destDir, 0o755); err != nil {
return fmt.Errorf("failed to create directory %q: %w", destDir, err)
}
continue
}

// 5) Clean the file name to remove any ../ or other path tricks
cleanedName := filepath.Clean(f.Name)

// 6) Combine it with outputDir to get the final path
destPath := filepath.Join(outputDir, cleanedName)

// 7) Check for zip slip
if !strings.HasPrefix(destPath, filepath.Clean(outputDir)+string(os.PathSeparator)) {
return fmt.Errorf("zip slip attempted with file %q", f.Name)
}

// 8) Open each file in the ZIP
zippedFile, err := f.Open()
if err != nil {
return fmt.Errorf("failed to open zipped file %q: %w", f.Name, err)
}
defer zippedFile.Close()

// Create subdirectories if needed (in case the file is inside a subfolder)
if err := os.MkdirAll(filepath.Dir(destPath), 0o755); err != nil {
return fmt.Errorf("failed to create subdirectory for %q: %w", destPath, err)
}

// 9) Create a destination file on disk
outFile, err := os.Create(destPath)
if err != nil {
return fmt.Errorf("failed to create file %q: %w", destPath, err)
}

// 10) Copy the file contents
if _, err := io.Copy(outFile, zippedFile); err != nil {
outFile.Close()
return fmt.Errorf("failed to write file %q: %w", destPath, err)
}
outFile.Close()

service.Client.GetLogger().Printf("[INFO] Extracted %s", destPath)
}

return nil
}

0 comments on commit 7ad7feb

Please sign in to comment.