vpzscalergitdemo22 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +21 -0
  2. package/test3.py +207 -0
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "vpzscalergitdemo22",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/vivek2727-ship-it/demorepo.git"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "ISC",
16
+ "type": "commonjs",
17
+ "bugs": {
18
+ "url": "https://github.com/vivek2727-ship-it/demorepo/issues"
19
+ },
20
+ "homepage": "https://github.com/vivek2727-ship-it/demorepo#readme"
21
+ }
package/test3.py ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Zscaler Workflow Automation (ZWA) - Incident Export to Excel
4
+ ================================================================
5
+
6
+ Pulls incidents from the Zscaler Workflow Automation API and exports
7
+ them to a formatted Excel workbook.
8
+
9
+ API DOCS:
10
+ ZWA (Zscaler Workflow Automation) uses its own legacy auth scheme:
11
+ POST {API_BASE_URL}/auth/v1/token with key_id + key_secret (HMAC-signed
12
+ with a timestamp) returns a Bearer token used for subsequent calls.
13
+ Base URL is cloud-specific, e.g. https://api.us1.zsworkflow.net
14
+
15
+ SETUP:
16
+ 1. In ZWA admin console, create an API key (key_id + key_secret).
17
+ 2. Set the environment variables below (or edit the CONFIG section).
18
+ 3. pip install requests openpyxl --break-system-packages
19
+ 4. python export_incidents.py --start 2026-05-01 --end 2026-06-11
20
+
21
+ USAGE EXAMPLES:
22
+ python export_incidents.py
23
+ python export_incidents.py --start 2026-06-01 --end 2026-06-11 --status OPEN
24
+ python export_incidents.py --output my_incidents.xlsx --page-size 200
25
+ """
26
+
27
+ import os
28
+ import sys
29
+ import time
30
+ import argparse
31
+ import requests
32
+ from datetime import datetime, timedelta
33
+ from openpyxl import Workbook
34
+ from openpyxl.styles import Font, PatternFill, Alignment
35
+ from openpyxl.utils import get_column_letter
36
+
37
+ # ---------------------------------------------------------------------------
38
+ # CONFIG - set via environment variables (recommended) or edit directly
39
+ # ---------------------------------------------------------------------------
40
+ KEY_ID = os.environ.get("ZWA_CLIENT_ID", "")
41
+ KEY_SECRET = os.environ.get("ZWA_CLIENT_SECRET", "")
42
+ API_BASE_URL = os.environ.get("ZWA_API_BASE_URL", "https://us1.zsworkflow.net")
43
+ AUTH_ENDPOINT = "/auth/v1/token"
44
+ INCIDENTS_ENDPOINT = "/dlp/v1/incidents/transactions" # adjust per docs if needed
45
+
46
+ VERIFY_SSL = True
47
+ TIMEOUT = 30
48
+
49
+
50
+ # ---------------------------------------------------------------------------
51
+ # Auth
52
+ # ---------------------------------------------------------------------------
53
+ def obfuscate_secret(key_secret, timestamp_ms):
54
+ """Zscaler legacy-style obfuscation of the API secret using a timestamp."""
55
+ high = str(timestamp_ms)[-6:]
56
+ low = bin(int(high))[2:].zfill(20)
57
+ obf = ""
58
+ for ch in high:
59
+ obf += key_secret[int(ch)]
60
+ j = len(high) - 1
61
+ for bit in low:
62
+ if bit == "1" and j >= 0:
63
+ obf += key_secret[j]
64
+ j -= 1
65
+ return obf
66
+
67
+
68
+ def get_access_token():
69
+ if not KEY_ID or not KEY_SECRET:
70
+ sys.exit("ERROR: Set ZWA_CLIENT_ID and ZWA_CLIENT_SECRET environment variables.")
71
+
72
+ timestamp_ms = int(time.time() * 1000)
73
+ obf_secret = obfuscate_secret(KEY_SECRET, timestamp_ms)
74
+ payload = {
75
+ "key_id": KEY_ID,
76
+ "key_secret": obf_secret,
77
+ "timestamp": str(timestamp_ms),
78
+ }
79
+ url = API_BASE_URL.rstrip("/") + AUTH_ENDPOINT
80
+ resp = requests.post(url, json=payload, timeout=TIMEOUT, verify=VERIFY_SSL)
81
+ if resp.status_code in (401, 403):
82
+ sys.exit(f"ERROR: Auth failed ({resp.status_code}). Response: {resp.text}")
83
+ resp.raise_for_status()
84
+ data = resp.json()
85
+ token = data.get("token") or data.get("access_token") or data.get("accessToken")
86
+ if not token:
87
+ sys.exit(f"ERROR: No token in auth response: {resp.text}")
88
+ return token
89
+
90
+
91
+ # ---------------------------------------------------------------------------
92
+ # Fetch incidents (paginated)
93
+ # ---------------------------------------------------------------------------
94
+ def fetch_incidents(token, start_date=None, end_date=None, status=None, page_size=100):
95
+ headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"}
96
+ all_incidents = []
97
+ page = 1
98
+
99
+ while True:
100
+ params = {"pageSize": page_size, "page": page}
101
+ if start_date:
102
+ params["startTime"] = start_date
103
+ if end_date:
104
+ params["endTime"] = end_date
105
+ if status:
106
+ params["status"] = status
107
+
108
+ url = API_BASE_URL.rstrip("/") + INCIDENTS_ENDPOINT
109
+ resp = requests.get(url, headers=headers, params=params, timeout=TIMEOUT, verify=VERIFY_SSL)
110
+ if resp.status_code == 401:
111
+ sys.exit("ERROR: Unauthorized - check client credentials/scopes.")
112
+ resp.raise_for_status()
113
+
114
+ data = resp.json()
115
+ # Response shape varies; handle common patterns
116
+ items = data.get("incidents") or data.get("results") or data.get("data") or []
117
+ if not items:
118
+ break
119
+
120
+ all_incidents.extend(items)
121
+ print(f" Fetched page {page}: {len(items)} incidents (total so far: {len(all_incidents)})")
122
+
123
+ total_pages = data.get("totalPages")
124
+ if total_pages and page >= total_pages:
125
+ break
126
+ if not total_pages and len(items) < page_size:
127
+ break
128
+
129
+ page += 1
130
+
131
+ return all_incidents
132
+
133
+
134
+ # ---------------------------------------------------------------------------
135
+ # Excel export
136
+ # ---------------------------------------------------------------------------
137
+ def export_to_excel(incidents, output_path):
138
+ wb = Workbook()
139
+ ws = wb.active
140
+ ws.title = "Incidents"
141
+
142
+ if not incidents:
143
+ ws["A1"] = "No incidents found for the given filters."
144
+ wb.save(output_path)
145
+ return
146
+
147
+ # Collect a consistent set of columns across all incidents
148
+ columns = []
149
+ for inc in incidents:
150
+ for k in inc.keys():
151
+ if k not in columns:
152
+ columns.append(k)
153
+
154
+ header_font = Font(name="Arial", bold=True, color="FFFFFF")
155
+ header_fill = PatternFill("solid", start_color="2F5496")
156
+ header_align = Alignment(horizontal="center", vertical="center", wrap_text=True)
157
+
158
+ for col_idx, col_name in enumerate(columns, start=1):
159
+ cell = ws.cell(row=1, column=col_idx, value=col_name)
160
+ cell.font = header_font
161
+ cell.fill = header_fill
162
+ cell.alignment = header_align
163
+
164
+ for row_idx, inc in enumerate(incidents, start=2):
165
+ for col_idx, col_name in enumerate(columns, start=1):
166
+ value = inc.get(col_name, "")
167
+ if isinstance(value, (dict, list)):
168
+ value = str(value)
169
+ cell = ws.cell(row=row_idx, column=col_idx, value=value)
170
+ cell.font = Font(name="Arial")
171
+
172
+ ws.freeze_panes = "A2"
173
+ for col_idx in range(1, len(columns) + 1):
174
+ ws.column_dimensions[get_column_letter(col_idx)].width = 22
175
+
176
+ wb.save(output_path)
177
+
178
+
179
+ # ---------------------------------------------------------------------------
180
+ # Main
181
+ # ---------------------------------------------------------------------------
182
+ def main():
183
+ parser = argparse.ArgumentParser(description="Export Zscaler Workflow Automation incidents to Excel")
184
+ parser.add_argument("--start", help="Start date YYYY-MM-DD (default: 7 days ago)")
185
+ parser.add_argument("--end", help="End date YYYY-MM-DD (default: today)")
186
+ parser.add_argument("--status", help="Filter by incident status (e.g. OPEN, CLOSED)")
187
+ parser.add_argument("--page-size", type=int, default=100)
188
+ parser.add_argument("--output", default="zwa_incidents.xlsx")
189
+ args = parser.parse_args()
190
+
191
+ end_date = args.end or datetime.utcnow().strftime("%Y-%m-%d")
192
+ start_date = args.start or (datetime.utcnow() - timedelta(days=7)).strftime("%Y-%m-%d")
193
+
194
+ print("Authenticating...")
195
+ token = get_access_token()
196
+
197
+ print(f"Fetching incidents from {start_date} to {end_date}...")
198
+ incidents = fetch_incidents(token, start_date, end_date, args.status, args.page_size)
199
+
200
+ print(f"Total incidents fetched: {len(incidents)}")
201
+ print(f"Writing to {args.output}...")
202
+ export_to_excel(incidents, args.output)
203
+ print("Done.")
204
+
205
+
206
+ if __name__ == "__main__":
207
+ main()