diff --git a/tests/characterization/golden/web_endpoint_inventory.json b/tests/characterization/golden/web_endpoint_inventory.json new file mode 100644 index 0000000..7498dd5 --- /dev/null +++ b/tests/characterization/golden/web_endpoint_inventory.json @@ -0,0 +1,1883 @@ +{ + "/": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "index", + "methods": [ + "GET" + ] + }, + "/admin/config": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/admin/config", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "admin_config", + "methods": [ + "GET" + ] + }, + "/api-explorer": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/api-explorer", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_explorer", + "methods": [ + "GET" + ] + }, + "/api/admin/radio-offline-clear": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_admin_radio_offline_clear", + "methods": [ + "POST" + ] + }, + "/api/admin/zombie-recover": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_admin_zombie_recover", + "methods": [ + "POST" + ] + }, + "/api/banner-status": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_banner_status", + "methods": [ + "GET" + ] + }, + "/api/cache": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_cache", + "methods": [ + "GET" + ] + }, + "/api/channel-operations/": { + "endpoint": "api_get_operation_status", + "methods": [ + "GET" + ] + }, + "/api/channels": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_create_channel", + "methods": [ + "POST" + ] + }, + "/api/channels/": { + "PUT locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_update_channel", + "methods": [ + "PUT" + ] + }, + "/api/channels//feeds": { + "endpoint": "api_channel_feeds", + "methods": [ + "GET" + ] + }, + "/api/channels/stats": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_channel_stats", + "methods": [ + "GET" + ] + }, + "/api/channels/validate": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_validate_channel", + "methods": [ + "POST" + ] + }, + "/api/config/logging": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_logging_post", + "methods": [ + "POST" + ] + }, + "/api/config/maintenance": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_maintenance_post", + "methods": [ + "POST" + ] + }, + "/api/config/notifications": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_notifications_post", + "methods": [ + "POST" + ] + }, + "/api/config/notifications/test": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_notifications_test", + "methods": [ + "POST" + ] + }, + "/api/config/radio-debug": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_radio_debug_post", + "methods": [ + "POST" + ] + }, + "/api/config/radio-offline-alert": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_radio_offline_alert_post", + "methods": [ + "POST" + ] + }, + "/api/config/radio-probe": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_radio_probe_post", + "methods": [ + "POST" + ] + }, + "/api/config/zombie-alert": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_config_zombie_alert_post", + "methods": [ + "POST" + ] + }, + "/api/connected_clients": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_connected_clients", + "methods": [ + "GET" + ] + }, + "/api/contact-detail": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 400, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 400, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_contact_detail", + "methods": [ + "GET" + ] + }, + "/api/contacts": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_contacts", + "methods": [ + "GET" + ] + }, + "/api/contacts/purge": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_contacts_purge", + "methods": [ + "POST" + ] + }, + "/api/contacts/purge-preview": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_contacts_purge_preview", + "methods": [ + "GET" + ] + }, + "/api/dashboard/refresh": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_dashboard_refresh", + "methods": [ + "POST" + ] + }, + "/api/dashboard/series": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_dashboard_series", + "methods": [ + "GET" + ] + }, + "/api/dashboard/summary": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 503, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 503, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_dashboard_summary", + "methods": [ + "GET" + ] + }, + "/api/dashboard/top": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_dashboard_top", + "methods": [ + "GET" + ] + }, + "/api/dashboard/windows": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_dashboard_windows", + "methods": [ + "GET" + ] + }, + "/api/database": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_database", + "methods": [ + "GET" + ] + }, + "/api/decode-path": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 400, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_decode_path", + "methods": [ + "POST" + ] + }, + "/api/delete-contact": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_delete_contact", + "methods": [ + "POST" + ] + }, + "/api/export/contacts": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_export_contacts", + "methods": [ + "GET" + ] + }, + "/api/export/paths": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_export_paths", + "methods": [ + "GET" + ] + }, + "/api/feeds": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_feeds", + "methods": [ + "GET" + ] + }, + "/api/feeds/": { + "PUT locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_update_feed", + "methods": [ + "PUT" + ] + }, + "/api/feeds//activity": { + "endpoint": "api_feed_activity", + "methods": [ + "GET" + ] + }, + "/api/feeds//errors": { + "endpoint": "api_feed_errors", + "methods": [ + "GET" + ] + }, + "/api/feeds//errors/reset": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_reset_feed_errors", + "methods": [ + "POST" + ] + }, + "/api/feeds//refresh": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_refresh_feed", + "methods": [ + "POST" + ] + }, + "/api/feeds/default-format": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_get_default_format", + "methods": [ + "GET" + ] + }, + "/api/feeds/errors/reset": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_reset_all_feed_errors", + "methods": [ + "POST" + ] + }, + "/api/feeds/preview": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_preview_feed", + "methods": [ + "POST" + ] + }, + "/api/feeds/stats": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_feed_stats", + "methods": [ + "GET" + ] + }, + "/api/feeds/test": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_test_feed", + "methods": [ + "POST" + ] + }, + "/api/geocode-contact": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_geocode_contact", + "methods": [ + "POST" + ] + }, + "/api/greeter": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_greeter", + "methods": [ + "GET" + ] + }, + "/api/greeter/end-rollout": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_end_rollout", + "methods": [ + "POST" + ] + }, + "/api/greeter/ungreet": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_ungreet_user", + "methods": [ + "POST" + ] + }, + "/api/health": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_health", + "methods": [ + "GET" + ] + }, + "/api/maintenance/backup_now": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_maintenance_backup_now", + "methods": [ + "POST" + ] + }, + "/api/maintenance/list_backups": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_maintenance_list_backups", + "methods": [ + "GET" + ] + }, + "/api/maintenance/purge": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_maintenance_purge", + "methods": [ + "POST" + ] + }, + "/api/maintenance/restore": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_maintenance_restore", + "methods": [ + "POST" + ] + }, + "/api/maintenance/status": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_maintenance_status", + "methods": [ + "GET" + ] + }, + "/api/mesh/edges": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_mesh_edges", + "methods": [ + "GET" + ] + }, + "/api/mesh/nodes": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_mesh_nodes", + "methods": [ + "GET" + ] + }, + "/api/mesh/resolve-path": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 400, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_resolve_path", + "methods": [ + "POST" + ] + }, + "/api/mesh/stats": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_mesh_stats", + "methods": [ + "GET" + ] + }, + "/api/multibyte-rollout": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 404, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_multibyte_rollout", + "methods": [ + "GET" + ] + }, + "/api/optimize-database": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_optimize_database", + "methods": [ + "POST" + ] + }, + "/api/plugins": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_plugins_get", + "methods": [ + "GET" + ] + }, + "/api/plugins//": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_plugins_save", + "methods": [ + "POST" + ] + }, + "/api/plugins/reload-status": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_plugins_reload_status", + "methods": [ + "GET" + ] + }, + "/api/radio/advert": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_radio_advert", + "methods": [ + "POST" + ] + }, + "/api/radio/connect": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_radio_connect", + "methods": [ + "POST" + ] + }, + "/api/radio/firmware/config/read": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_firmware_config_read", + "methods": [ + "POST" + ] + }, + "/api/radio/firmware/config/write": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_firmware_config_write", + "methods": [ + "POST" + ] + }, + "/api/radio/params": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_radio_params_write", + "methods": [ + "POST" + ] + }, + "/api/radio/reboot": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_radio_reboot", + "methods": [ + "POST" + ] + }, + "/api/radio/status": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_radio_status", + "methods": [ + "GET" + ] + }, + "/api/recent_commands": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_recent_commands", + "methods": [ + "GET" + ] + }, + "/api/region-scopes": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_region_scopes_save", + "methods": [ + "POST" + ] + }, + "/api/region-warnings": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_region_warnings", + "methods": [ + "GET" + ] + }, + "/api/region-warnings/settings": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_region_warnings_save", + "methods": [ + "POST" + ] + }, + "/api/scheduled-messages": { + "PUT locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_update_scheduled_message", + "methods": [ + "PUT" + ] + }, + "/api/scheduled-messages/preview": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_scheduled_messages_preview", + "methods": [ + "POST" + ] + }, + "/api/stats": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_stats", + "methods": [ + "GET" + ] + }, + "/api/stats/rate_limiters": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_rate_limiter_stats", + "methods": [ + "GET" + ] + }, + "/api/stream_data": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 400, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_stream_data", + "methods": [ + "POST" + ] + }, + "/api/system-health": { + "GET locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_system_health", + "methods": [ + "GET" + ] + }, + "/api/toggle-star-contact": { + "POST locked anon": { + "content_type": "application/json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 401, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "api_toggle_star_contact", + "methods": [ + "POST" + ] + }, + "/apple-touch-icon.png": { + "GET locked anon": { + "content_type": "image/png", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "image/png", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "apple_touch_icon", + "methods": [ + "GET" + ] + }, + "/cache": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/cache", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/config#database", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "cache", + "methods": [ + "GET" + ] + }, + "/config": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/config", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "config_page", + "methods": [ + "GET" + ] + }, + "/contacts": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "contacts", + "methods": [ + "GET" + ] + }, + "/favicon-16x16.png": { + "GET locked anon": { + "content_type": "image/png", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "image/png", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "favicon_16x16", + "methods": [ + "GET" + ] + }, + "/favicon-32x32.png": { + "GET locked anon": { + "content_type": "image/png", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "image/png", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "favicon_32x32", + "methods": [ + "GET" + ] + }, + "/favicon.ico": { + "GET locked anon": { + "content_type": "image/x-icon", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "image/x-icon", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "favicon", + "methods": [ + "GET" + ] + }, + "/feeds": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/feeds", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "feeds", + "methods": [ + "GET" + ] + }, + "/greeter": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/greeter", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "greeter", + "methods": [ + "GET" + ] + }, + "/login": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "POST locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "login", + "methods": [ + "GET", + "POST" + ] + }, + "/logout": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "logout", + "methods": [ + "GET" + ] + }, + "/logs": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/logs", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "logs", + "methods": [ + "GET" + ] + }, + "/mesh": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "mesh", + "methods": [ + "GET" + ] + }, + "/multibyte-rollout": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/multibyte-rollout", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 404, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "multibyte_rollout", + "methods": [ + "GET" + ] + }, + "/plugins": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/plugins", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "plugins_page", + "methods": [ + "GET" + ] + }, + "/radio": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/radio", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "radio", + "methods": [ + "GET" + ] + }, + "/realtime": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "realtime", + "methods": [ + "GET" + ] + }, + "/region-warnings": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/region-warnings", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'nonce-X' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "region_warnings_page", + "methods": [ + "GET" + ] + }, + "/schedule": { + "GET locked anon": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "/login?next=/schedule", + "status": 302, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "text/html", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "schedule_page", + "methods": [ + "GET" + ] + }, + "/site.webmanifest": { + "GET locked anon": { + "content_type": "application/manifest+json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "GET open": { + "content_type": "application/manifest+json", + "csp_script_src": "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdnjs.cloudflare.com https://unpkg.com", + "location": "", + "status": 200, + "x_content_type_options": "nosniff", + "x_frame_options": "SAMEORIGIN" + }, + "endpoint": "site_webmanifest", + "methods": [ + "GET" + ] + } +} diff --git a/tests/characterization/golden_util.py b/tests/characterization/golden_util.py index 63326ea..8ad61f5 100644 --- a/tests/characterization/golden_util.py +++ b/tests/characterization/golden_util.py @@ -53,7 +53,7 @@ def _diff(exp: Any, got: Any, path: str = "") -> list[str]: if len(exp) != len(got): return [f"{path}: length {len(exp)} -> {len(got)}"] out = [] - for i, (a, b) in enumerate(zip(exp, got)): + for i, (a, b) in enumerate(zip(exp, got, strict=True)): out.extend(_diff(a, b, f"{path}[{i}]")) return out return [] if exp == got else [f"{path}: {exp!r} -> {got!r}"] diff --git a/tests/characterization/test_bot_identity_matrix.py b/tests/characterization/test_bot_identity_matrix.py index 72aefd8..0ee3737 100644 --- a/tests/characterization/test_bot_identity_matrix.py +++ b/tests/characterization/test_bot_identity_matrix.py @@ -35,7 +35,7 @@ class _Cmd(BaseCommand): def _shapes(): yield "self_info=None", None for combo in itertools.product(VALUES, repeat=len(FIELDS)): - present = {f: (f"{v}-{f}" if v == "Dev" else v) for f, v in zip(FIELDS, combo) if v is not None} + present = {f: (f"{v}-{f}" if v == "Dev" else v) for f, v in zip(FIELDS, combo, strict=True) if v is not None} label = ",".join(f"{k}={v!r}" for k, v in present.items()) or "empty" yield f"dict[{label}]", dict(present) yield f"obj[{label}]", SimpleNamespace(**present) diff --git a/tests/characterization/test_packaging_coverage.py b/tests/characterization/test_packaging_coverage.py new file mode 100644 index 0000000..f229968 --- /dev/null +++ b/tests/characterization/test_packaging_coverage.py @@ -0,0 +1,69 @@ +"""Every tracked runtime file must ship in the wheel. + +``pyproject.toml`` uses ``include-package-data = false`` and lists package data +explicitly, and package discovery is the regular (non-namespace) kind, so a new +subpackage without ``__init__.py`` or a new data file outside the globs is +silently left out of an installed bot. This checks the configuration against +``git ls-files`` without building a wheel (the dev venv has no setuptools). +""" + +from __future__ import annotations + +import fnmatch +import subprocess +from pathlib import Path + +import pytest + +tomllib = pytest.importorskip("tomllib") # Python 3.11+; CI's 3.11-3.13 jobs cover it + +ROOT = Path(__file__).resolve().parents[2] + + +def _tracked(prefix: str) -> list[str]: + try: + out = subprocess.run( + ["git", "ls-files", prefix], cwd=ROOT, capture_output=True, text=True, check=True + ).stdout + except (OSError, subprocess.CalledProcessError): + pytest.skip("not a git checkout") + return [line for line in out.splitlines() if line] + + +def _config(): + return tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))["tool"]["setuptools"] + + +def test_every_python_package_directory_is_a_regular_package(): + missing = sorted( + { + str(Path(f).parent) + for f in _tracked("modules") + if f.endswith(".py") and not (ROOT / Path(f).parent / "__init__.py").exists() + } + ) + assert missing == [], f"directories with modules but no __init__.py (not packaged): {missing}" + + +def test_every_non_python_runtime_file_matches_a_package_data_glob(): + package_data: dict[str, list[str]] = _config()["package-data"] + uncovered = [] + for prefix, package in (("modules", "modules"), ("translations", "translations"), ("data/randomlines", "data.randomlines")): + globs = package_data.get(package, []) + base = ROOT / package.replace(".", "/") + for f in _tracked(prefix): + if f.endswith((".py", ".pyc", ".md")) or "/__pycache__/" in f: + continue + rel = (ROOT / f).relative_to(base).as_posix() + # setuptools globs: '*' does not cross '/', '**' does. + if not any(_glob_match(rel, g) for g in globs): + uncovered.append(f) + assert uncovered == [], f"runtime files not covered by [tool.setuptools.package-data]: {uncovered}" + + +def _glob_match(rel: str, pattern: str) -> bool: + if "**" in pattern: + head, _, tail = pattern.partition("**/") + return rel.startswith(head) and fnmatch.fnmatch(rel[len(head):].split("/")[-1], tail) if tail else rel.startswith(head) + rel_parts, pat_parts = rel.split("/"), pattern.split("/") + return len(rel_parts) == len(pat_parts) and all(fnmatch.fnmatch(r, p) for r, p in zip(rel_parts, pat_parts, strict=True)) diff --git a/tests/characterization/test_web_endpoint_inventory.py b/tests/characterization/test_web_endpoint_inventory.py new file mode 100644 index 0000000..3a74567 --- /dev/null +++ b/tests/characterization/test_web_endpoint_inventory.py @@ -0,0 +1,94 @@ +"""Characterization: the web viewer's route table and how each route answers. + +Records every rule (path, methods, endpoint name) and, for parameterless GET +routes, the status, content type and security-relevant headers of a request +made anonymously with and without a password configured. It also records, for +every mutating route, what an anonymous request gets when a password is set. +Helper extraction or route regrouping in ``app.py`` must leave this unchanged: +endpoint names feed the CSP nonce allowlist and ``url_for``, and auth is +path-based. +""" + +from __future__ import annotations + +import configparser +import re +from unittest.mock import patch + +import pytest + +from tests.characterization.golden_util import assert_golden + +SKIP_GET = { + # Streams or long-polls; not a single request/response. + "/socket.io/", +} + + +def _viewer(tmp_path, password: str): + from modules.web_viewer.app import BotDataViewer + + tmp_path.mkdir(parents=True, exist_ok=True) + config = configparser.ConfigParser() + config.add_section("Bot") + config.set("Bot", "db_path", str(tmp_path / "meshcore_bot.db")) + config.set("Bot", "bot_name", "InvBot") + config.add_section("Web_Viewer") + for key, value in [ + ("host", "127.0.0.1"), ("port", "8080"), ("enabled", "false"), + ("auto_start", "false"), ("debug", "false"), + ("cors_allowed_origins", "*"), ("web_viewer_password", password), + ]: + config.set("Web_Viewer", key, value) + config_path = str(tmp_path / "config.ini") + with open(config_path, "w") as handle: + config.write(handle) + with patch.object(BotDataViewer, "_start_database_polling"), \ + patch.object(BotDataViewer, "_start_log_tailing"), \ + patch.object(BotDataViewer, "_start_cleanup_scheduler"), \ + patch.object(BotDataViewer, "_start_dashboard_refresher"), \ + patch.object(BotDataViewer, "_setup_socketio_handlers"), \ + patch("modules.web_viewer.app.RepeaterManager"): + viewer = BotDataViewer(db_path=str(tmp_path / "meshcore_bot.db"), config_path=config_path) + viewer.app.testing = True + return viewer + + +def _headers(resp): + csp = resp.headers.get("Content-Security-Policy", "") + script_src = next((p.strip() for p in csp.split(";") if p.strip().startswith("script-src")), "") + return { + "status": resp.status_code, + "content_type": (resp.headers.get("Content-Type") or "").split(";")[0], + "location": re.sub(r"https?://[^/]+", "", resp.headers.get("Location", "")), + "csp_script_src": re.sub(r"'nonce-[^']+'", "'nonce-X'", script_src), + "x_frame_options": resp.headers.get("X-Frame-Options"), + "x_content_type_options": resp.headers.get("X-Content-Type-Options"), + } + + +@pytest.mark.timeout(120) +def test_route_table_and_anonymous_responses(tmp_path): + open_viewer = _viewer(tmp_path / "open", "") + locked_viewer = _viewer(tmp_path / "locked", "s3cret-pass") + rules = sorted(open_viewer.app.url_map.iter_rules(), key=lambda r: (r.rule, r.endpoint)) + inventory = {} + open_client = open_viewer.app.test_client() + locked_client = locked_viewer.app.test_client() + for rule in rules: + if rule.endpoint == "static": + continue + methods = sorted(m for m in rule.methods if m not in ("HEAD", "OPTIONS")) + entry = {"endpoint": rule.endpoint, "methods": methods} + has_params = bool(rule.arguments) + if "GET" in methods and not has_params and rule.rule not in SKIP_GET: + entry["GET open"] = _headers(open_client.get(rule.rule)) + entry["GET locked anon"] = _headers(locked_client.get(rule.rule)) + for method in methods: + if method == "GET": + continue + path = re.sub(r"<(?:[^:>]+:)?[^>]+>", "1", rule.rule) + resp = locked_client.open(path, method=method, json={}) + entry[f"{method} locked anon"] = _headers(resp) + inventory[f"{rule.rule}"] = entry + assert_golden("web_endpoint_inventory", inventory)