{
  "schema": "aaes/capability-pack/1",
  "provider": "zscaler",
  "version": "1.0.0",
  "description": "Zscaler: read ZIA filtering configuration, stage URL filtering rules and activate all pending tenant changes; read or create ZPA application segments that can grant access through existing policies.",
  "notes": "The endpoint hosts 'zscaler-zia.invalid' and 'zscaler-zpa.invalid' are reserved non-production placeholders (an RFC 2606 reserved TLD): substitute the customer's own ZIA cloud API host (zsapi.<cloud name> for the tenant's cloud) and ZPA gateway host before enabling, in every endpoint, in the resource locators and in every egress allowlist entry — the allowlist matches exactly, so all three must be substituted together, and substitution is a required deployment step.\n\nThis pack registers the legacy ZIA session model: ZIA API authentication is a session-cookie flow in which the client POSTs an admin username, password and the tenant's obfuscated API key to /api/v1/authenticatedSession and receives a session cookie; the stored credential is the resulting cookie value, minted and refreshed by an external custody job — see the credential note. ZIA configuration changes are staged per session and only take enforcement effect when activated: zia.config.activate applies ALL staged changes tenant-wide — including changes staged by other sessions and by console users — and is registered with the deploy action at the minimum R3 tier.\n\nThe ZPA capabilities override the pack credential: ZPA's API uses OAuth2 client-credentials bearer tokens, not the ZIA session cookie, so the ZPA capabilities name a separate reference and the Authorization header. Zscaler's newer Zidentity OneAPI framework (a single OAuth2 client across ZIA/ZPA) exists and is preferred by the vendor for migrated tenants; the retrieved vendor SDK documentation shows OneAPI is NOT only a credential change: it moves authentication to the Zidentity token endpoint (https://<vanity_domain>.zslogin.net/oauth2/v1/token) and serves the products from a different API base authority (api.<cloud_name>.zsapi.net) with its own path contract, so a migrated tenant must re-review authority, paths and credential wiring together rather than swapping only the credential reference.\n\nDocumentation basis: raw endpoint paths and methods are verified against Zscaler's official per-product Swagger/OpenAPI specifications served from help.zscaler.com (retrieved 2026-09-23): the ZIA API Authentication, URL Categories, URL Filtering Policy, Firewall Policies and Activation specifications (host zsapi.[zscaler-cloud-name], basePath /api/v1) and the ZPA Application Segment Management specification (OpenAPI 3.0.0) print every path and method this pack registers. The help site's HTML pages render client-side, but its JSON content API (help.zscaler.com/zapi/fetch-data) exposes each API section's downloadable specification file; the vendor-maintained Python SDK documentation listed in sources corroborated the operation surface in the first phase and remains listed.\n\nDocumented exclusions — this pack does not register: any DELETE endpoint (URL filtering rule, firewall rule, URL category or application segment deletion — removing an enforcement rule can silently reopen blocked traffic, and deletion is not part of this pack), admin user, role and API-key management, PAC file management, ZPA connector provisioning or deletion, sandbox file submission (which sends customer files for analysis), sublocation and location VPN credential management, and ZIA DLP dictionary/policy management.",
  "credential": {
    "secret_ref_id": "secret-ref-zscaler-zia-session",
    "custody": "customer_vault",
    "inject": "header",
    "inject_name": "Cookie",
    "note": "A ZIA API session cookie, held in the client's own secret manager. The stored material must be the complete Cookie header value: 'JSESSIONID=' followed by the session id returned by the ZIA authenticatedSession call — the raw path (POST /api/v1/authenticatedSession) and the cookie name (JSESSIONID, returned in the Set-Cookie header) are printed verbatim in Zscaler's official API Authentication specification and Getting Started article on help.zscaler.com (retrieved 2026-09-23). ZIA's legacy authentication is a derived session, not a static token: an external deployment process POSTs a JSON body containing an API-enabled admin's username, password and the tenant API key — obfuscated with Zscaler's documented timestamp-based obfuscation, which is obfuscation, not encryption — to /api/v1/authenticatedSession on the customer's ZIA cloud host, and stores the resulting session cookie under this reference.\n\nThe three source values (username, password, API key) are separate secrets under their own vault references; the executor injects the stored cookie verbatim and performs no session lifecycle itself, so renewal ownership sits entirely with that external process. Zscaler documents a default JSESSIONID lifetime of 5 minutes and invalidation by an explicit logout (DELETE /authenticatedSession); calls made after expiry or logout fail closed with a 401.\n\nRotating the API key or disabling the admin stops NEW session issuance, but an already-established session should be treated as valid until it expires or is logged out — stopping issuance is not session invalidation, so on suspicion the custody job must explicitly log out the session, not only rotate the key.\n\nLeast privilege: use a dedicated API-only admin whose role permits exactly the registered scope (read-only administration for the reads; URL filtering configuration only where rule creation and activation are enabled), and never a full admin. The ZPA capabilities in this pack override this credential block with their own bearer-token reference (see their inject fields). ZPA legacy authentication is OAuth2 client credentials, verified against Zscaler's ZPA API Getting Started and About API Key Management articles (help.zscaler.com, retrieved 2026-09-23): client_id and client_secret are issued on the ZPA console's API Keys page (Administration > API > Private Access API; earlier SDK documentation named the older Configuration & Control > Public API > API Keys path), the client secret is displayed only when the key is first created, the customer id identifies the tenant and is copyable from the API Keys page menu, and the ZPA cloud value selects the API gateway; the external custody job exchanges the client credentials for an access token at the tenant's ZPA gateway (POST /signin with form-encoded client_id and client_secret, returning token_type Bearer) and stores the complete 'Bearer <token>' Authorization header value.\n\nThe retrieved documentation sets the token's default lifetime at 3,600 seconds (one hour); it still does not state whether deleting the API key invalidates already-issued tokens — treat any captured token as live until expiry.\n\nLeast privilege: a dedicated API key whose console role permits exactly the registered application-segment read/create scope, never a full admin.\n\nExecutor hygiene: the cookie and the bearer value must live only in executor memory for the lifetime of a call, must never be written to logs, traces or diagnostics, and must not appear in crash dumps; a deployment that cannot meet that bar must not enable the pack."
  },
  "capabilities": [
    {
      "capability_id": "zscaler.zia.url_categories.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "zscaler-zia.invalid"
      ],
      "endpoint": "https://zscaler-zia.invalid/api/v1/urlCategories",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-zscaler-zia-session",
      "inject": "header",
      "inject_name": "Cookie",
      "actions": [
        "read"
      ],
      "resource_id": "zscaler-zia-tenant",
      "description": "List the tenant's URL categories from the collection endpoint, including custom categories and their URL membership — the classification data the tenant's web policy is built from. Registered as the collection read.\n\nThe host is a reserved placeholder: substitute the customer's ZIA cloud API host before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "zscaler.zia.url_filtering_rules.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "zscaler-zia.invalid"
      ],
      "endpoint": "https://zscaler-zia.invalid/api/v1/urlFilteringRules",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-zscaler-zia-session",
      "inject": "header",
      "inject_name": "Cookie",
      "actions": [
        "read"
      ],
      "resource_id": "zscaler-zia-tenant",
      "description": "List the tenant's URL filtering rules from the collection endpoint: ordered allow/caution/block rules with their category, user, group, department and location scope. Registered as the collection read.\n\nThe host is a reserved placeholder: substitute before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "zscaler.zia.firewall_rules.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "zscaler-zia.invalid"
      ],
      "endpoint": "https://zscaler-zia.invalid/api/v1/firewallFilteringRules",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-zscaler-zia-session",
      "inject": "header",
      "inject_name": "Cookie",
      "actions": [
        "read"
      ],
      "resource_id": "zscaler-zia-tenant",
      "description": "List the tenant's cloud firewall filtering rules from the collection endpoint: ordered rules with network services, applications, destinations and action. Registered as the collection read.\n\nThe host is a reserved placeholder: substitute before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "zscaler.zia.url_filtering_rule.create",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R2",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "zscaler-zia.invalid"
      ],
      "endpoint": "https://zscaler-zia.invalid/api/v1/urlFilteringRules",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-zscaler-zia-session",
      "inject": "header",
      "inject_name": "Cookie",
      "actions": [
        "create"
      ],
      "resource_id": "zscaler-zia-tenant",
      "description": "Create one URL filtering rule (for example an emergency block or allow rule for a category or URL list). The new rule is staged in the session and takes no enforcement effect until configuration activation (registered separately as zia.config.activate at R3); the rule itself can be updated or deleted afterwards, so the create is registered as a reversible create at R2. Rule order matters for enforcement — the deployment should fix the rule's rank in the request body rather than letting the cloud place it.\n\nThe host is a reserved placeholder: substitute before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "zscaler.zia.config.activate",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "zscaler-zia.invalid"
      ],
      "endpoint": "https://zscaler-zia.invalid/api/v1/status/activate",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-zscaler-zia-session",
      "inject": "header",
      "inject_name": "Cookie",
      "actions": [
        "deploy"
      ],
      "resource_id": "zscaler-zia-tenant",
      "description": "Activate the tenant's staged ZIA configuration and apply it to production enforcement. Activation applies every pending change across the tenant, including changes staged by other administrator sessions or console users.\n\nReview the whole pending change set before activating. GET /api/v1/status reports activation status for saved changes; Zscaler's Activation specification does not describe it as a pending-change inventory. Obtain that inventory from the console or another reviewed source; this package does not provide one.\n\nRegistered as deploy at R3. Replace the reserved host before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "zscaler.zpa.app_segments.read",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R0",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "zscaler-zpa.invalid"
      ],
      "endpoint": "https://zscaler-zpa.invalid/mgmtconfig/v1/admin/customers/CUSTOMER_ID/application",
      "method": "GET",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-zscaler-zpa-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "read"
      ],
      "resource_id": "zscaler-zpa-tenant",
      "description": "List the tenant's ZPA application segments from the collection endpoint: the internal applications (domain/IP and port ranges) published through ZPA. CUSTOMER_ID is a placeholder the deployment binds to the ZPA tenant id before enabling. This capability overrides the pack credential: ZPA authenticates with an OAuth2 bearer token (see the pack's credential note), injected as the Authorization header.\n\nThe host is a reserved placeholder: substitute before enabling.",
      "requires_review": true
    },
    {
      "capability_id": "zscaler.zpa.app_segment.create",
      "version": "1.0.0",
      "kind": "tool",
      "risk_tier": "R3",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN",
      "data_class": "internal",
      "egress_allowlist": [
        "zscaler-zpa.invalid"
      ],
      "endpoint": "https://zscaler-zpa.invalid/mgmtconfig/v1/admin/customers/CUSTOMER_ID/application",
      "method": "POST",
      "connector": "http",
      "custody": "customer_vault",
      "secret_ref_id": "secret-ref-zscaler-zpa-token",
      "inject": "header",
      "inject_name": "Authorization",
      "actions": [
        "create",
        "deploy",
        "share"
      ],
      "resource_id": "zscaler-zpa-tenant",
      "description": "Create a ZPA application segment and publish an internal application through ZPA. The vendor SDK requires name, domain_names, segment_group_id, and server_group_ids, with optional port ranges, bypass type, health reporting, and clientless-app associations. The endpoint's OpenAPI 3.0.0 schema marks only name as required; use the fuller SDK contract for setup.\n\nExisting policies can immediately grant access when the segment joins a group they already cover. Excluding policy writes from this package does not prevent that effect. The capability therefore declares create, deploy, and share at R3.\n\nOn supported AAES-mediated execution paths, deploy and share require approval by an authorized person; this does not prevent calls through bypass or observation-only paths.\n\nBind CUSTOMER_ID and replace the reserved host before enabling. This capability uses its own ZPA bearer-token reference rather than the package credential; see the credential note.",
      "requires_review": true
    }
  ],
  "resources": [
    {
      "resource_id": "zscaler-zia-tenant",
      "kind": "api",
      "locator": "https://zscaler-zia.invalid/api/v1",
      "data_class": "internal",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    },
    {
      "resource_id": "zscaler-zpa-tenant",
      "kind": "api",
      "locator": "https://zscaler-zpa.invalid/mgmtconfig/v1/admin/customers/CUSTOMER_ID",
      "data_class": "internal",
      "owner": "REPLACE-WITH-ACCOUNTABLE-HUMAN"
    }
  ],
  "review": {
    "state": "library",
    "date": "2026-09-23",
    "sources": [
      "https://zscaler-sdk-python.readthedocs.io/en/latest/ (retrieved 2026-09-21)",
      "https://zscaler-sdk-python.readthedocs.io/en/latest/zs/zia/activate.html (retrieved 2026-09-21)",
      "https://zscaler-sdk-python.readthedocs.io/en/latest/zs/zpa/app_segments.html (retrieved 2026-09-21)",
      "https://zscaler-sdk-python.readthedocs.io/en/latest/zs/zia/url_categories.html (retrieved 2026-09-21)",
      "https://zscaler-sdk-python.readthedocs.io/en/latest/zs/zia/url_filtering.html (retrieved 2026-09-21)",
      "https://zscaler-sdk-python.readthedocs.io/en/latest/zs/zia/cloud_firewall_rules.html (retrieved 2026-09-21)",
      "https://help.zscaler.com/sites/default/files/api-docs/API%20Authentication_30.json (retrieved 2026-09-23)",
      "https://help.zscaler.com/sites/default/files/api-docs/URL%20Categories_21.json (retrieved 2026-09-23)",
      "https://help.zscaler.com/sites/default/files/api-docs/URL%20Filtering%20Policy_1.json (retrieved 2026-09-23)",
      "https://help.zscaler.com/sites/default/files/api-docs/Firewall%20Policies_22.json (retrieved 2026-09-23)",
      "https://help.zscaler.com/sites/default/files/api-docs/Activation_11.json (retrieved 2026-09-23)",
      "https://help.zscaler.com/sites/default/files/api-docs/Application%20Segment%20Management_20.json (retrieved 2026-09-23)",
      "https://help.zscaler.com/zia/api-getting-started (retrieved 2026-09-23)",
      "https://help.zscaler.com/zpa/getting-started-zpa-api (retrieved 2026-09-23)",
      "https://help.zscaler.com/zpa/about-api-key-management (retrieved 2026-09-23)"
    ],
    "reviewer_id": "aaes-eng"
  }
}
