1/** 2 * Proteção CSRF (Double-Submit Cookie) 3 * 4 * Lê o cookie CSRF (__Secure-csrf_token em HTTPS, csrf_token em HTTP) 5 * e adiciona automaticamente no header X-CSRF-Token em todas as 6 * requisições POST, PUT, DELETE e PATCH. 7 * 8 * NOTA: O cookie CSRF é intencionalmente não-httpOnly porque o padrão 9 * double-submit cookie EXIGE que o JavaScript consiga ler o valor do 10 * cookie para enviá-lo como header. Isso é seguro porque: 11 * 1. SameSite=Strict impede envio cross-site 12 * 2. O atacante não consegue ler o cookie de outro domÃnio 13 * 3. O cookie usa prefix __Secure- em HTTPS (proteção contra cookie injection) 14 * 15 * Uso: Incluir este script antes de qualquer código que faça requisições fetch. 16 * Exemplo: <script src="/static/js/csrf.js"></script> 17 */ 18 19(function() { 20 'use strict'; 21 22 /** 23 * Lê o valor de um cookie pelo nome. 24 */ 25 function getCookie(name) { 26 var nameEQ = name + "="; 27 var ca = document.cookie.split(';'); 28 for (var i = 0; i < ca.length; i++) { 29 var c = ca[i]; 30 while (c.charAt(0) === ' ') c = c.substring(1, c.length); 31 if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length); 32 } 33 return null; 34 } 35 36 /** 37 * Obtém o token CSRF do cookie. 38 * Tenta __Secure-csrf_token (HTTPS/produção) primeiro, depois csrf_token (HTTP/dev). 39 */ 40 function getCsrfToken() { 41 return getCookie('__Secure-csrf_token') || getCookie('csrf_token'); 42 } 43 44 /** 45 * Detecta se está no Proxy/Agent (HTTP local). 46 */ 47 function isProxyOrigin() { 48 try { 49 return window.location.protocol === 'http:' && 50 (window.location.hostname === '127.0.0.1' || window.location.hostname === 'localhost'); 51 } catch (e) { return false; } 52 } 53 54 /** 55 * Obtém token JWT do Proxy (fallback quando cookie httpOnly não funciona no webview). 56 */ 57 function getProxyAuthToken() { 58 try { 59 return sessionStorage.getItem('catellix_token') || ''; 60 } catch (e) { return ''; } 61 } 62 63 /** 64 * Intercepta fetch() para adicionar automaticamente o header X-CSRF-Token 65 * em requisições POST, PUT, DELETE e PATCH. 66 * No Proxy: adiciona Authorization: Bearer quando token em sessionStorage (fallback para cookie). 67 */ 68 var originalFetch = window.fetch; 69 window.fetch = function(url, options) { 70 options = options || {}; 71 var method = (options.method || 'GET').toUpperCase(); 72 73 // Proxy/Agent: fallback quando cookie não funciona (webview) - adiciona Authorization 74 if (isProxyOrigin()) { 75 var token = getProxyAuthToken(); 76 if (token && typeof token === 'string' && token.trim()) { 77 var urlStr = (typeof url === 'string') ? url : (url && url.url ? url.url : ''); 78 if (!urlStr || urlStr.startsWith('/') || urlStr.indexOf(window.location.host) >= 0) { 79 if (!options.headers) options.headers = {}; 80 if (options.headers instanceof Headers) { 81 options.headers.set('Authorization', 'Bearer ' + token.trim()); 82 } else { 83 var h = (options.headers instanceof Map) ? Object.fromEntries(options.headers) : (options.headers || {}); 84 h['Authorization'] = 'Bearer ' + token.trim(); 85 options.headers = h; 86 } 87 } 88 } 89 } 90 91 // Adiciona CSRF token em métodos que alteram estado 92 if (method === 'POST' || method === 'PUT' || method === 'DELETE' || method === 'PATCH') { 93 var csrfToken = getCsrfToken(); 94 if (csrfToken) { 95 // Garante que headers existe 96 if (!options.headers) { 97 options.headers = {}; 98 } 99 // Se headers é um objeto simples, adiciona diretamente 100 if (options.headers instanceof Headers) { 101 options.headers.set('X-CSRF-Token', csrfToken); 102 } else { 103 // Se é um objeto plano ou Map, converte para objeto 104 var headersObj = {}; 105 if (options.headers instanceof Map) {
106 options.headers.forEach(function(value, key) { 107 headersObj[key] = value; 108 }); 109 } else { 110 headersObj = options.headers; 111 } 112 headersObj['X-CSRF-Token'] = csrfToken; 113 options.headers = headersObj; 114 } 115 } 116 } 117 118 return originalFetch.call(this, url, options); 119 }; 120 121 /** 122 * Função helper para uso manual (caso necessário). 123 * Retorna um objeto com o header X-CSRF-Token pronto para usar. 124 */ 125 window.getCsrfHeader = function() { 126 var token = getCsrfToken(); 127 return token ? { 'X-CSRF-Token': token } : {}; 128 }; 129 130 // Log de inicialização (apenas em desenvolvimento) 131 if (typeof console !== 'undefined' && console.log && window.location.hostname === 'localhost') { 132 console.log('[CSRF] Proteção CSRF ativada. Token:', getCsrfToken() ? 'presente' : 'ausente (será gerado no próximo GET)'); 133 } 134 135 /* Ãltimas páginas (atalhos no menu do dashboard): registro leve em localStorage */ 136 (function () { 137 try { 138 var p = (window.location.pathname || '').toLowerCase(); 139 if (!p) return; 140 var skip = { 141 '/login': 1, '/register': 1, '/logout': 1, '/reset_password': 1, 142 '/set_new_password': 1, '/confirm_email': 1, '/comece-gratis': 1 143 }; 144 if (skip[p]) return; 145 if (p.indexOf('/static/') === 0) return; 146 var s = document.createElement('script'); 147 s.src = '/static/js/recent-pages.js?v=20260417-recent-ui'; 148 s.async = true; 149 document.head.appendChild(s); 150 } catch (e) {} 151 })(); 152})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.