diff --git a/docs/resource-specific-documentation.md b/docs/resource-specific-documentation.md index c071d1bff..c36278254 100644 --- a/docs/resource-specific-documentation.md +++ b/docs/resource-specific-documentation.md @@ -931,6 +931,8 @@ NetworkACLs have the following key properties: - `scope`: The scope of the rule ('management', 'authentication', or 'tenant') - `match` or `not_match`: Criteria for matching requests +The `match` and `not_match` criteria also support an `auth0_managed` array for matching Auth0-managed IP ranges (e.g. `auth0.icloud_relay_proxy`, `auth0.low_reputation`). Each value must follow the pattern `^auth0\.[^.\s]+$`. This is an Early Access feature gated behind the `tenant_acl_curated_blocklists` feature flag and requires the `advanced-breached-password-detection` entitlement; the API rejects rules using `auth0_managed` with an HTTP 403 if the tenant is not entitled. + **YAML Example** ```yaml @@ -954,6 +956,15 @@ networkACLs: scope: 'management' not_match: user_agents: ['BadBot/1.0'] + - description: 'Block iCloud Private Relay Exits' + active: true + priority: 4 + rule: + action: + block: true + scope: 'tenant' + match: + auth0_managed: ['auth0.icloud_relay_proxy'] ``` **Directory Example** @@ -964,6 +975,7 @@ Folder structure when in directory mode. ./networkACLs/ ./Allow Specific Countries-p-2.json ./Redirect Specific User Agents-p-3.json + ./Block iCloud Private Relay Exits-p-4.json ``` Contents of `Allow Specific Countries-p-2.json`: @@ -1004,6 +1016,25 @@ Contents of `Redirect Specific User Agents-p-3.json`: } ``` +Contents of `Block iCloud Private Relay Exits-p-4.json`: + +```json +{ + "description": "Block iCloud Private Relay Exits", + "active": true, + "priority": 4, + "rule": { + "action": { + "block": true + }, + "scope": "tenant", + "match": { + "auth0_managed": ["auth0.icloud_relay_proxy"] + } + } +} +``` + ## PhoneProviders When managing phone providers, credentials are never exported. diff --git a/src/tools/auth0/handlers/networkACLs.ts b/src/tools/auth0/handlers/networkACLs.ts index d5b9c1849..95a387a1e 100644 --- a/src/tools/auth0/handlers/networkACLs.ts +++ b/src/tools/auth0/handlers/networkACLs.ts @@ -73,6 +73,15 @@ const MatchSchema = { uniqueItems: true, minItems: 1, }, + auth0_managed: { + type: 'array', + items: { + type: 'string', + pattern: '^auth0\\.[^.\\s]+$', + }, + uniqueItems: true, + minItems: 1, + }, geo_country_codes: { type: 'array', items: { diff --git a/test/tools/auth0/handlers/networkACLs.test.ts b/test/tools/auth0/handlers/networkACLs.test.ts index 88890ee3e..5a539308d 100644 --- a/test/tools/auth0/handlers/networkACLs.test.ts +++ b/test/tools/auth0/handlers/networkACLs.test.ts @@ -1,7 +1,8 @@ import { PromisePoolExecutor } from 'promise-pool-executor'; import { expect } from 'chai'; +import Ajv from 'ajv'; -import NetworkACLsHandler from '../../../../src/tools/auth0/handlers/networkACLs'; +import NetworkACLsHandler, { schema } from '../../../../src/tools/auth0/handlers/networkACLs'; import pageClient from '../../../../src/tools/auth0/client'; import { mockPagedData } from '../../../utils'; @@ -90,6 +91,66 @@ describe('#networkACLs handler', () => { }); }); + describe('#networkACLs schema', () => { + const ajv = new Ajv({ useDefaults: true, nullable: true }); + + it('should pass schema validation for auth0_managed in match', () => { + const valid = ajv.validate(schema, [ + { + description: 'Block iCloud Private Relay Exits', + active: true, + priority: 1, + rule: { + action: { block: true }, + scope: 'tenant', + match: { + auth0_managed: ['auth0.icloud_relay_proxy'], + }, + }, + }, + ]); + expect(valid).to.equal(true); + expect(ajv.errors).to.be.null; + }); + + it('should pass schema validation for auth0_managed in not_match', () => { + const valid = ajv.validate(schema, [ + { + description: 'Allow unless low-reputation', + active: true, + priority: 1, + rule: { + action: { allow: true }, + scope: 'tenant', + not_match: { + auth0_managed: ['auth0.low_reputation'], + }, + }, + }, + ]); + expect(valid).to.equal(true); + expect(ajv.errors).to.be.null; + }); + + it('should fail schema validation for auth0_managed values not matching the pattern', () => { + const valid = ajv.validate(schema, [ + { + description: 'Invalid auth0_managed value', + active: true, + priority: 1, + rule: { + action: { block: true }, + scope: 'tenant', + match: { + auth0_managed: ['icloud_relay_proxy'], + }, + }, + }, + ]); + expect(valid).to.equal(false); + }); + }); + describe('#networkACLs process', () => { it('should return empty if no networkACLs asset', async () => { const auth0 = {