PageSourceSearch

https://catellix.com/static/js/csrf.js

js catellix.com collected 2026-09-24 12:57:08 UTC 6,114 bytes, 152 lines download raw bytes

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.