{"id":47740607,"url":"https://github.com/doctena-org/octorules-cloudflare","last_synced_at":"2026-04-18T13:05:37.727Z","repository":{"id":345552711,"uuid":"1185256407","full_name":"doctena-org/octorules-cloudflare","owner":"doctena-org","description":"Cloudflare provider for octorules","archived":false,"fork":false,"pushed_at":"2026-04-07T12:13:05.000Z","size":836,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-07T14:27:21.700Z","etag":null,"topics":["cloudflare","firewall","iac","octorules","security","waf","yaml"],"latest_commit_sha":null,"homepage":"https://github.com/doctena-org/octorules","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/doctena-org.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-18T11:53:47.000Z","updated_at":"2026-04-07T12:13:08.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/doctena-org/octorules-cloudflare","commit_stats":null,"previous_names":["doctena-org/octorules-cloudflare"],"tags_count":23,"template":false,"template_full_name":null,"purl":"pkg:github/doctena-org/octorules-cloudflare","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-cloudflare","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-cloudflare/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-cloudflare/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-cloudflare/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doctena-org","download_url":"https://codeload.github.com/doctena-org/octorules-cloudflare/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-cloudflare/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31969791,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T00:39:45.007Z","status":"online","status_checked_at":"2026-04-18T02:00:07.018Z","response_time":103,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cloudflare","firewall","iac","octorules","security","waf","yaml"],"created_at":"2026-04-02T23:43:22.368Z","updated_at":"2026-04-18T13:05:37.722Z","avatar_url":"https://github.com/doctena-org.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# octorules-cloudflare\n\nCloudflare provider for [octorules](https://github.com/doctena-org/octorules) — manages 23 Cloudflare rule phases, custom rulesets, lists, and Page Shield policies as YAML.\n\n## Installation\n\n```bash\npip install octorules-cloudflare\n```\n\nOr via the octorules extra:\n\n```bash\npip install octorules[cloudflare]\n```\n\nThis installs octorules (core), octorules-cloudflare, and\n[octorules-wirefilter](https://github.com/doctena-org/octorules-wirefilter)\n(Rust FFI bridge to Cloudflare's wirefilter engine for authoritative expression\nparsing and full linter coverage).\n\nPrebuilt wirefilter wheels are available for Linux (x86_64, aarch64; glibc and\nmusl/Alpine), macOS (x86_64, ARM64), and Windows (x86_64).\n\n## Configuration\n\n```yaml\nproviders:\n  cloudflare:\n    token: env/CLOUDFLARE_API_TOKEN\n  rules:\n    directory: ./rules\n\nzones:\n  example.com:\n    sources:\n      - rules\n```\n\nThe `env/` prefix resolves values from environment variables at runtime.\nAll keys under the provider section are forwarded to the provider constructor\nas keyword arguments (octodns-style passthrough).\n\n### Authentication\n\nA [Cloudflare API token](https://developers.cloudflare.com/fundamentals/api/get-started/create-token/)\nis required. See [docs/permissions.md](docs/permissions.md) for the full list of required permissions per phase and extension.\n\n### Provider settings\n\nAll settings below go under the provider section (e.g. `providers.cloudflare`).\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `token` | *(required)* | Cloudflare API token (`env/` prefix supported) |\n| `max_retries` | `2` | API retry count (0-10) |\n| `timeout` | `30` | API timeout in seconds (max 300) |\n\nSafety thresholds are configured under `safety:` (framework-owned, not\nforwarded to the provider):\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `safety.delete_threshold` | `30.0` | Max % of rules that can be deleted |\n| `safety.update_threshold` | `30.0` | Max % of rules that can be updated |\n| `safety.min_existing` | `3` | Min rules before thresholds apply |\n\n## Supported features\n\n| Feature | Status |\n|---------|--------|\n| Phase rules (23 phases) | Supported |\n| Custom rulesets (account-level) | Supported |\n| Lists (IP, ASN, hostname, redirect) | Supported |\n| Page Shield policies (zone-level) | Supported |\n| Zone discovery (`list_zones`) | Supported |\n| Account-level scopes | Supported |\n| Audit IP extraction (`octorules audit`) | Supported |\n| Bot Management settings (`cloudflare_bot_management`) | Supported |\n| URL normalization settings (`cloudflare_url_normalization`) | Supported |\n| Zone security defaults (`cloudflare_zone_security`) | Supported |\n| Leaked Credential Check (`cloudflare_leaked_credential_check`) | Supported |\n| Content Scanning / anti-malware (`cloudflare_content_scanning`) | Supported |\n\n## Supported phases\n\n23 Cloudflare phases — 18 HTTP request/response phases and 5 network-level (Magic Transit) phases. Phases execute in a fixed order:\n\n```\nRequest  -\u003e url_normalization -\u003e redirect_rules -\u003e url_rewrite_rules -\u003e request_header_rules\n         -\u003e origin_rules -\u003e config_rules -\u003e cache_rules\n         -\u003e waf_custom_rules -\u003e waf_managed_rules -\u003e rate_limiting_rules\n         -\u003e bot_fight_rules -\u003e http_ddos_rules\n         -\u003e  Origin fetch  \u003c-\n         -\u003e custom_error_rules -\u003e response_header_rules -\u003e compression_rules\n         -\u003e sensitive_data_detection -\u003e log_custom_fields -\u003e Response\n```\n\nPhases with a default action (e.g., `redirect_rules` -\u003e `redirect`) don't need `action` in the YAML — it's injected automatically. For phases without a default (e.g., `waf_custom_rules`), you must specify `action` explicitly.\n\nPhases marked as both Zone and Account work at either scope. Account-only phases are skipped for zone scopes and vice versa, eliminating wasted API calls.\n\nFor the full phase reference — execution order diagram, valid actions per phase, field/function availability, and key behaviors — see [docs/lint/README.md](docs/lint/README.md).\n\n\u003e **Note:** `waf_managed_exceptions` was renamed to `waf_managed_rules`. The old name still works as an alias but is deprecated — update your YAML files to use the new name.\n\n## Expression syntax\n\nRule expressions use [Cloudflare's ruleset expression language](https://developers.cloudflare.com/ruleset-engine/rules-language/expressions/). When [octorules-wirefilter](https://github.com/doctena-org/octorules-wirefilter) is installed (included automatically with `octorules-cloudflare`), expressions are parsed by Cloudflare's actual wirefilter engine, providing authoritative type checking, field validation, and syntax verification. Without it, a regex-based fallback parser extracts fields, functions, operators, and literals but cannot perform type checking.\n\nThe linter logs which parser is active at startup (`Expression parser: wirefilter` or `Expression parser: regex fallback`).\n\n\u003e **Rule-level metadata:** All Cloudflare rules support the `octorules:` key for per-rule metadata — `ignored: true` to skip a rule during plan/sync, and `included`/`excluded` to restrict rules to specific providers. See [octorules core docs](https://github.com/doctena-org/octorules#rule-level-metadata) for syntax and examples.\n\n## Custom rulesets (account-level)\n\nAt the account level, WAF custom rules and rate limiting rules use a two-tier structure: the phase entrypoint contains **deploy rules** (`action: execute`) that reference child **custom rulesets** by ID. The individual blocking/logging rules live inside those child rulesets.\n\noctorules manages both tiers. Deploy rules are managed via the normal phase sections (`waf_custom_rules`, `rate_limiting_rules`). The individual rules inside each custom ruleset are managed via a separate `custom_rulesets` section:\n\n```yaml\n# Account rules file (e.g. rules/my-account.yaml)\n\n# Deploy rules (phase entrypoint — references child rulesets by ID)\nwaf_custom_rules:\n  - ref: deploy-known-attackers\n    description: Deploy known attackers ruleset\n    action: execute\n    action_parameters:\n      id: abc12345def67890abc12345def67890\n      version: latest\n    enabled: true\n    expression: (http.host eq \"api.example.com\")\n\n# Individual rules inside each custom ruleset\ncustom_rulesets:\n  - id: abc12345def67890abc12345def67890\n    name: Known attackers\n    phase: http_request_firewall_custom\n    rules:\n      - ref: block-bad-asn\n        description: Block by AS number\n        action: block\n        expression: (ip.geoip.asnum in {12345 67890})\n      - ref: block-bad-ua\n        description: Block by user-agent\n        action: block\n        expression: (http.user_agent contains \"BadBot\")\n```\n\nThe `id` field in each `custom_rulesets` entry links it to the deploy rule's `action_parameters.id`. Rules inside use `ref` for identification (same pattern as phase rules). Every rule must specify an `action` explicitly.\n\nUse `octorules dump --scope account` to export existing custom rulesets to YAML.\n\n\u003e **Note:** octorules manages rules *within* existing custom rulesets. Creating or deleting rulesets themselves must be done via the Cloudflare dashboard. Zone-level rulesets do not have `kind=custom` children — this is account-level only.\n\n## Lists (account-level)\n\nCloudflare account-level [Lists](https://developers.cloudflare.com/waf/tools/lists/) (IP lists, ASN lists, hostname lists, redirect lists) can be referenced in rule expressions via `$list_name` syntax. octorules manages full lifecycle of lists declaratively: create, delete, update metadata, and manage items.\n\nAdd a top-level `lists` key to your account rules file:\n\n```yaml\n# rules/my-account.yaml\nlists:\n  - name: blocked_ips\n    kind: ip\n    description: \"Known bad IPs\"\n    items:\n      - ip: \"1.2.3.4\"\n        comment: \"Scanner\"\n      - ip: \"5.6.7.0/24\"\n        comment: \"Botnet range\"\n\n  - name: partner_asns\n    kind: asn\n    description: \"Partner AS numbers\"\n    items:\n      - asn: 12345\n        comment: \"Partner A\"\n      - asn: 67890\n        comment: \"Partner B\"\n```\n\nEach list entry requires:\n\n| Field | Description |\n|-------|-------------|\n| `name` | List name — matches CF list name and `$list_name` in expressions |\n| `kind` | One of `ip`, `asn`, `hostname`, `redirect` |\n| `description` | Optional — updated if changed |\n| `items` | List of items (can be empty `[]` to clear all items) |\n\n**How it works:**\n\n- The presence of a `lists:` key means ALL lists are managed — lists in Cloudflare not in YAML are planned for deletion (subject to safety thresholds).\n- If the `lists:` key is absent, lists are ignored entirely.\n- Item updates are asynchronous — octorules polls the bulk operation until completion.\n- During sync, lists are applied **before** rulesets and phases, so newly created lists are available for rule expressions that reference them.\n- Use `octorules dump --scope account` to export existing lists to YAML. The dump externalizes list items into separate files (referenced via `!include` tags) under `providers.lists.directory` (default: `{rules_dir}/custom_lists`).\n\nReference lists in rule expressions:\n\n```yaml\nwaf_custom_rules:\n  - ref: block-bad-ips\n    description: Block IPs from blocklist\n    action: block\n    expression: (ip.src in $blocked_ips)\n```\n\n## Page Shield policies (zone-level)\n\nCloudflare [Page Shield](https://developers.cloudflare.com/page-shield/) manages Content Security Policies (CSP) at the zone level. octorules manages full lifecycle of Page Shield policies declaratively: create, update, and delete.\n\nAdd a top-level `page_shield_policies` key to your zone rules file:\n\n```yaml\n# rules/example.com.yaml\npage_shield_policies:\n  - description: \"CSP on all example.com\"\n    action: allow\n    expression: \"true\"\n    enabled: true\n    value: \u003e-\n      script-src 'self' 'unsafe-inline' 'unsafe-eval' https:;\n      worker-src 'self' blob:\n\n  - description: \"Log CSP on staging\"\n    action: log\n    expression: '(http.host eq \"staging.example.com\")'\n    enabled: true\n    value: \"default-src 'self'\"\n```\n\nEach policy entry requires:\n\n| Field | Description |\n|-------|-------------|\n| `description` | Policy description — used as the identity key for matching |\n| `action` | `allow` or `log` |\n| `expression` | Cloudflare filter expression |\n| `enabled` | Boolean |\n| `value` | CSP directive string |\n\n**How it works:**\n\n- The `description` field is the identity key (like `ref` for rules and `name` for lists). Policies are matched between YAML and Cloudflare by description.\n- The presence of a `page_shield_policies:` key means ALL policies are managed — policies in Cloudflare not in YAML are planned for deletion.\n- If the `page_shield_policies:` key is absent, policies are ignored entirely.\n- During sync, policies are applied **after** lists and **before** custom rulesets and phases.\n- Use `octorules dump` to export existing Page Shield policies to YAML.\n\n**CSP source normalization:** The order of sources within a CSP directive is not significant — `script-src 'self' example.com` and `script-src example.com 'self'` are semantically identical. octorules normalizes source order (sorted alphabetically within each directive) before comparing `value` fields, so reordering sources in your YAML will not trigger an upstream change on Cloudflare. You can freely reorganize sources for readability without causing a sync.\n\n## Linting\n\n144 Cloudflare-specific lint rules (CF prefix) across 6 ranges:\n\n| Range | Category | Rules |\n|-------|----------|-------|\n| CF001–CF026 | Structure \u0026 parse | 26 |\n| CF100–CF104 | Cross-rule ordering | 5 |\n| CF200–CF222 | Action validation | 23 |\n| CF300–CF309 | Expression, function \u0026 type | 10 |\n| CF400–CF478 | Domain-specific (rate limit, cache, config, redirect, transform, origin, page shield, list) | 45 |\n| CF500–CF545 | Plan limits, style \u0026 value constraints | 35 |\n\nSee [docs/lint/README.md](docs/lint/README.md) for the full rule reference.\n\n## Development\n\n```bash\ngit clone git@github.com:doctena-org/octorules-cloudflare.git\ncd octorules-cloudflare\npython -m venv .venv\nsource .venv/bin/activate\npip install -e \".[dev]\"\nln -sf ../../scripts/hooks/pre-commit .git/hooks/pre-commit\n```\n\nThe pre-commit hook auto-regenerates `schemas.json` (the frozen schema fallback\nfor users without wirefilter) whenever `overlay.toml` or `pyproject.toml`\nchanges. See [docs/schemas.md](docs/schemas.md) for the full schema\narchitecture.\n\n## License\n\noctorules-cloudflare is licensed under the [Apache License 2.0](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoctena-org%2Foctorules-cloudflare","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoctena-org%2Foctorules-cloudflare","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoctena-org%2Foctorules-cloudflare/lists"}