{"id":48676066,"url":"https://github.com/doctena-org/octorules-bunny","last_synced_at":"2026-04-18T13:03:29.250Z","repository":{"id":349376345,"uuid":"1200468177","full_name":"doctena-org/octorules-bunny","owner":"doctena-org","description":"Bunny.net Shield WAF provider for octorules","archived":false,"fork":false,"pushed_at":"2026-04-13T11:25:11.000Z","size":246,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T13:36:38.603Z","etag":null,"topics":["bunny","firewall","iac","octorules","security","waf","yaml"],"latest_commit_sha":null,"homepage":null,"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-04-03T12:57:20.000Z","updated_at":"2026-04-13T11:25:14.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/doctena-org/octorules-bunny","commit_stats":null,"previous_names":["doctena-org/octorules-bunny"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/doctena-org/octorules-bunny","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-bunny","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-bunny/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-bunny/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-bunny/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/doctena-org","download_url":"https://codeload.github.com/doctena-org/octorules-bunny/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/doctena-org%2Foctorules-bunny/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31969773,"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":["bunny","firewall","iac","octorules","security","waf","yaml"],"created_at":"2026-04-10T15:05:05.103Z","updated_at":"2026-04-18T13:03:29.207Z","avatar_url":"https://github.com/doctena-org.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# octorules-bunny\n\nBunny.net Shield WAF provider for [octorules](https://github.com/doctena-org/octorules) — manages Bunny Shield custom WAF rules, rate limits, access lists, edge rules, managed rule overrides, pull zone security, and bot/DDoS configuration as YAML.\n\n\u003e **Alpha** — this provider has comprehensive offline test coverage (512 tests)\n\u003e but has not yet been tested against live Bunny Shield APIs. Use with caution\n\u003e and report any issues.\n\n## Installation\n\n```bash\npip install octorules-bunny\n```\n\nThis installs octorules (core) and octorules-bunny. The provider is auto-discovered — no `class:` needed in config.\n\n## Configuration\n\n```yaml\n# config.yaml\nproviders:\n  bunny:\n    api_key: env/BUNNY_API_KEY\n  rules:\n    directory: ./rules\n\nzones:\n  my-blog:\n    sources:\n      - rules\n  api-backend:\n    sources:\n      - rules\n```\n\nEach zone name maps to a Bunny Pull Zone name. The provider resolves pull zone names to Shield Zone IDs at runtime. The `env/` prefix resolves values from environment variables at runtime. All keys under the provider section are forwarded to the provider constructor as keyword arguments (octodns-style passthrough).\n\n### Authentication\n\nSet `BUNNY_API_KEY` to your Bunny.net account API key (found at https://dash.bunny.net under Account Settings). The key is account-scoped — one key covers all pull zones.\n\n### Provider settings\n\nAll settings below go under the provider section (e.g. `providers.bunny`).\n\n| Key | Default | Description |\n|-----|---------|-------------|\n| `api_key` | `BUNNY_API_KEY` env var | Bunny.net API key |\n| `timeout` | `30` | API timeout in seconds |\n| `max_retries` | `2` | API retry count (retries on 429/5xx) |\n| `max_workers` | `1` | Parallel workers for multi-zone operations |\n| `plan` | *(none)* | Bunny account tier (`free` or `advanced`) for plan-aware lint rules (BN501). |\n\nSafety thresholds are configured under `safety:` (framework-owned, not forwarded 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 (4 phases) | Supported |\n| Edge rules (redirects, headers, force SSL, blocking) | Supported |\n| Pull zone security (blocked IPs/countries/referrers, token auth, CORS) | Supported |\n| Managed WAF rule overrides | Supported |\n| Shield config (bot/DDoS) | Supported |\n| Zone discovery (`list_zones`) | Supported |\n| Audit IP extraction (`octorules audit`) | Supported |\n| Custom rulesets | Not supported (Bunny API limitation) |\n| Lists | Not supported (use access list phase instead) |\n\n## Zone File Example\n\n```yaml\n# rules/my-cdn.yaml\n---\nbunny_waf_custom_rules:\n  - ref: Block SQLi\n    action: block\n    severity: error\n    description: Detect SQL injection in request body\n    conditions:\n      - variable: request_body\n        operator: detect_sqli\n    transformations:\n      - lowercase\n      - url_decode\n\n  - ref: Block bad bots\n    action: challenge\n    severity: warning\n    description: Challenge requests from known bot user agents\n    conditions:\n      - variable: request_headers\n        variable_value: User-Agent\n        operator: rx\n        value: \"(curl|wget|python-requests)\"\n    transformations:\n      - lowercase\n\n  - ref: Block admin from CN\n    action: block\n    severity: error\n    description: Block access to admin panel from China\n    conditions:\n      - variable: request_uri\n        operator: begins_with\n        value: /admin\n      - variable: geo\n        variable_value: COUNTRY_CODE\n        operator: str_eq\n        value: CN\n\nbunny_waf_rate_limit_rules:\n  - ref: API rate limit\n    action: block\n    description: Rate limit API endpoints\n    request_count: 100\n    timeframe: 1m\n    block_time: 5m\n    counter_key_type: ip\n    conditions:\n      - variable: request_uri\n        operator: begins_with\n        value: /api/\n\nbunny_waf_access_list_rules:\n  - ref: \"42\"\n    type: country\n    action: block\n    enabled: true\n    content: |\n      CN\n      RU\n\n  - ref: \"43\"\n    type: ip\n    action: allow\n    enabled: true\n    content: |\n      203.0.113.0/24\n\nbunny_waf_managed_rules:\n  disabled:\n    - \"941100\"\n    - \"942100\"\n  log_only:\n    - \"930100\"\n\nbunny_shield_config:\n  bot_detection:\n    execution_mode: block\n    request_integrity_sensitivity: medium\n    ip_sensitivity: medium\n    fingerprint_sensitivity: high\n    complex_fingerprinting: true\n  ddos:\n    shield_sensitivity: medium\n    execution_mode: block\n    challenge_window: 300\n```\n\n## Phases\n\n| Phase | YAML key | Description |\n|-------|----------|-------------|\n| Custom WAF | `bunny_waf_custom_rules` | Custom WAF rules with conditions, operators, and actions |\n| Rate Limit | `bunny_waf_rate_limit_rules` | Rate limiting rules with thresholds and block times |\n| Access List | `bunny_waf_access_list_rules` | IP/CIDR/ASN/Country/Org/JA4 block/allow lists |\n| Edge Rules | `bunny_edge_rules` | CDN-level edge rules (redirects, header manipulation, force SSL, blocking) |\n\n## Non-Phase Sections\n\n| YAML key | Description |\n|----------|-------------|\n| `bunny_waf_managed_rules` | Disable or log-only individual managed WAF rules |\n| `bunny_shield_config` | Bot detection and DDoS protection configuration |\n| `bunny_pullzone_security` | Pull zone security: blocked IPs/countries/referrers, token auth, CORS |\n\n## Actions\n\n| Action | API Value | Description |\n|--------|:---------:|-------------|\n| `block` | 1 | Block the request |\n| `log` | 2 | Log only (no enforcement) |\n| `challenge` | 3 | JavaScript proof-of-work challenge |\n| `allow` | 4 | Explicitly allow the request |\n| `bypass` | 5 | Bypass all further WAF processing |\n\n## Operators\n\n| Operator | API Value | Description |\n|----------|:---------:|-------------|\n| `begins_with` | 0 | String prefix match |\n| `ends_with` | 1 | String suffix match |\n| `contains` | 2 | Substring match |\n| `contains_word` | 3 | Word-level match |\n| `str_match` | 4 | Case-sensitive exact match |\n| `eq` | 5 | Integer equality |\n| `ge` | 6 | Greater or equal |\n| `gt` | 7 | Greater than |\n| `le` | 8 | Less or equal |\n| `lt` | 9 | Less than |\n| `within` | 12 | Substring containment |\n| `rx` | 14 | Regular expression |\n| `str_eq` | 15 | Case-insensitive exact match |\n| `detect_sqli` | 17 | SQL injection detection (no value needed) |\n| `detect_xss` | 18 | XSS detection (no value needed) |\n\n## Variables\n\n| Variable | API Value | Sub-value? | Description |\n|----------|:---------:|:----------:|-------------|\n| `request_uri` | 0 | No | Request URI |\n| `request_uri_raw` | 1 | No | Raw request URI |\n| `args` | 2 | Optional | All arguments |\n| `args_combined_size` | 3 | No | Total argument size |\n| `args_get` | 4 | Optional | GET parameters |\n| `args_get_names` | 5 | No | GET parameter names |\n| `args_post` | 6 | Optional | POST parameters |\n| `args_post_names` | 7 | No | POST parameter names |\n| `files_names` | 8 | No | Uploaded file names |\n| `geo` | 9 | **Required** | Geographic data (COUNTRY_CODE, ASN, CITY, CONTINENT, LATITUDE, LONGITUDE) |\n| `remote_addr` | 10 | No | Client IP address |\n| `query_string` | 11 | No | Query string |\n| `request_basename` | 12 | No | Request URI basename |\n| `request_body` | 13 | No | Request body |\n| `request_cookies_names` | 14 | No | Cookie names |\n| `request_cookies` | 15 | **Required** | Cookie (specify cookie name as variable_value) |\n| `request_filename` | 16 | No | Request filename |\n| `request_headers_names` | 17 | No | Header names |\n| `request_headers` | 18 | **Required** | Header (specify header name as variable_value) |\n| `request_line` | 19 | No | Full request line |\n| `request_method` | 20 | No | HTTP method |\n| `request_protocol` | 21 | No | Request protocol |\n| `response_body` | 22 | No | Response body |\n| `response_headers` | 23 | Optional | Response header (specify header name as variable_value) |\n| `response_status` | 24 | No | Response status code |\n| `fingerprint` | 25 | No | Client fingerprint |\n\n## Rule Name Constraints\n\nCustom WAF and rate limit rule names (`ref`) must match `[a-zA-Z0-9 ]+` (alphanumeric characters and spaces only). No hyphens, underscores, or special characters. This is an API constraint enforced by Bunny.\n\nAccess list `ref` values are API-assigned numeric IDs. Use `octorules dump` to discover IDs after initial creation in the Bunny dashboard.\n\n## Sync Behavior\n\nBunny Shield does not support atomic bulk rule replacement. The provider uses a diff-and-reconcile strategy:\n\n1. **Patch** existing rules (ref exists in both old and new)\n2. **Add** new rules (ref only in desired YAML)\n3. **Remove** stale rules (ref only in current API state)\n\nThis order ensures the zone never has *fewer* rules than intended during sync.\n\n## Plan Limits\n\n| Plan | Custom WAF Rules | Rate Limits | Access Lists |\n|------|:----------------:|:-----------:|:------------:|\n| Free | 0 | 2 | Managed only |\n| Advanced ($9.50/mo) | 10 | 10 | Custom + managed |\n| Enterprise | Unlimited | Unlimited | Full |\n\n## Linting\n\n58 lint rules with the `BN` prefix validate your rules offline before sync. See [docs/lint.md](docs/lint.md) for the full reference.\n\n```bash\noctorules lint\n```\n\n## Known limitations\n\n- **Non-atomic sync**: Bunny Shield has no bulk-replace API. `octorules sync` uses diff-and-reconcile (patch existing, add new, remove stale) — three separate API calls per changed rule. A failure mid-sync leaves the zone in an intermediate state; re-running sync will converge.\n- **Rule name constraints**: Bunny requires alphanumeric characters and spaces only — hyphens, underscores, and special characters are rejected by the API.\n- **No custom rulesets or lists**: Bunny Shield has no equivalent of AWS Rule Groups or Cloudflare Lists. Use the access list phase for IP/CIDR/ASN/country blocking.\n- **Access list entries are individual**: Each access list entry is a separate API object; there is no batch create/delete. Large changeset syncs make many API calls.\n- **Plan tier limits**: Free plans cannot create custom WAF rules and are limited to 2 rate-limit rules. The linter warns about these limits (BN501). Set `plan: advanced` in the provider config to check only against Advanced tier limits.\n\n## Development\n\n```bash\npython -m venv .venv\n.venv/bin/pip install -e \".[dev]\"\n.venv/bin/pytest tests/ -v\n```\n\nPre-commit hook:\n\n```bash\nln -sf ../../scripts/hooks/pre-commit .git/hooks/pre-commit\n```\n\n## License\n\noctorules-bunny is licensed under the [Apache License 2.0](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoctena-org%2Foctorules-bunny","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdoctena-org%2Foctorules-bunny","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdoctena-org%2Foctorules-bunny/lists"}