{"record":{"id":"dcbab428a5fd7ad6","repo":"infiniflow/ragflow","slug":"webhook-security-is-required-set-allow-anonymous","errorCode":null,"errorMessage":"Webhook security is required. Set allow_anonymous to true to permit unauthenticated webhooks.","messagePattern":"Webhook security is required\\. Set allow_anonymous to true to permit unauthenticated webhooks\\.","errorType":"exception","errorClass":"Exception","httpStatus":400,"severity":"error","filePath":"api/apps/restful_apis/agent_api.py","lineNumber":1892,"sourceCode":"    if not webhook_cfg:\n        return get_data_error_result(code=RetCode.BAD_REQUEST, message=\"Webhook not configured for this agent.\"), RetCode.BAD_REQUEST\n\n    # 5. Validate request method against webhook_cfg.methods\n    allowed_methods = webhook_cfg.get(\"methods\", [])\n    request_method = request.method.upper()\n    if allowed_methods and request_method not in allowed_methods:\n        return get_data_error_result(code=RetCode.BAD_REQUEST, message=f\"HTTP method '{request_method}' not allowed for this webhook.\"), RetCode.BAD_REQUEST\n\n    async def validate_webhook_security(security_cfg: dict):\n        \"\"\"Validate webhook security rules based on security configuration.\"\"\"\n\n        if not isinstance(security_cfg, dict) or not security_cfg:\n            logging.warning(\n                \"Webhook denied: missing security config agent_id=%s method=%s\",\n                agent_id,\n                request.method,\n            )\n            raise Exception(\"Webhook security is required. Set allow_anonymous to true to permit unauthenticated webhooks.\")\n\n        # 1. Validate max body size\n        await _validate_max_body_size(security_cfg)\n\n        # 2. Validate IP whitelist\n        _validate_ip_whitelist(security_cfg)\n\n        # # 3. Validate rate limiting\n        _validate_rate_limit(security_cfg)\n\n        # 4. Validate authentication\n        auth_type = security_cfg.get(\"auth_type\", \"none\")\n\n        if auth_type == \"none\":\n            if not _allow_anonymous_webhook(security_cfg):\n                logging.warning(\n                    \"Webhook denied: anonymous access missing explicit opt-in agent_id=%s method=%s\",\n                    agent_id,","sourceCodeStart":1874,"sourceCodeEnd":1910,"githubUrl":"https://github.com/infiniflow/ragflow/blob/554fb1133ac3861732235ad9c377eb5e0a770665/api/apps/restful_apis/agent_api.py#L1874-L1910","documentation":"The webhook endpoint's validate_webhook_security requires a non-empty dict as the agent's security configuration (api/apps/restful_apis/agent_api.py:1892). A missing, None, empty, or non-dict security_cfg logs a warning and raises Exception('Webhook security is required. Set allow_anonymous to true to permit unauthenticated webhooks.'). Unauthenticated webhooks are denied by default.","triggerScenarios":"Publishing/running an agent as a webhook without any security block in its configuration; security_cfg serialized as null or {} (e.g. a template agent, or an API client that strips the field); sending a test request right after creating a webhook before configuring security.","commonSituations":"Quick trial of the webhook feature skipping the security step; agent JSON imported from a template without a security section; automation tools that drop empty objects on export.","solutions":["Add a security configuration object to the webhook agent (auth_type plus its required fields, e.g. token/basic/jwt).","If the webhook is intentionally public, set allow_anonymous: true inside the security config to explicitly opt in.","Re-save/publish the agent after editing so the running webhook picks up the new security config.","Inspect the request payload you send - the error is server-side config, not headers; headers matter only after a security block exists."],"exampleFix":"// before: agent webhook config\n\"security\": {}\n\n// after\n\"security\": {\"auth_type\": \"token\", \"token\": \"s3cr3t...\", \"allow_anonymous\": false}","handlingStrategy":"validation","validationCode":"security_cfg = agent_config.get(\"webhook\", {}).get(\"security\")\nif not isinstance(security_cfg, dict) or not security_cfg:\n    raise ValueError(\"Webhook requires a security config; set allow_anonymous=true only if public access is intended\")","typeGuard":"def has_webhook_security(cfg: dict) -> bool:\n    sec = cfg.get(\"webhook\", {}).get(\"security\")\n    return isinstance(sec, dict) and len(sec) > 0","tryCatchPattern":"try:\n    resp = await invoke_webhook(session, url, payload)\nexcept WebhookRejected as e:\n    if \"security is required\" in str(e):\n        raise ConfigError(\"Configure webhook security on the agent before calling it\") from e\n    raise","preventionTips":["Make webhook security a required field in agent templates that expose webhooks.","Decide exposure posture up front: authenticated (token/basic/jwt) or explicitly allow_anonymous with rate/IP limits.","Add a config-time lint that rejects webhook publication with an empty security block."],"tags":["webhook","agent","security","configuration","api"],"backgroundTag":null,"analyzedSha":"554fb1133ac3861732235ad9c377eb5e0a770665","analyzedAt":"2026-08-15T09:20:16.380Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}