vpzscalergitdemo11 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.
- package/package.json +21 -0
- package/test3.py +207 -0
package/package.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vpzscalergitdemo11",
|
|
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()
|