Skip to content
Back to skills

C2f Javascript Ajax

ASecurity

MANDATORY READ before AJAX calls, fetch requests, multipart uploads or Gestor AJAX backend handlers. Prevents CSRF 403 errors, invalid JSON envelopes and stuck interfaces.

  • 4 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added November 7, 2026
developmentjavascriptgojavaphptestinggitapifrontendbackend

Works with

  • cli
  • api
  • mcp

Security analysis

A100/100

Scanned November 7, 2026

npx -y skills add otavioserra/conn2flow-ai-workspace --skill c2f-javascript-ajax --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of C2f Javascript Ajax?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for C2f Javascript Ajax
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/otavioserra-c2f-javascript-ajax-conn2flow-ai-workspace/badge)](https://www.skillsdirectory.com/skills/otavioserra-c2f-javascript-ajax-conn2flow-ai-workspace)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: c2f-javascript-ajax
description: "MANDATORY READ before AJAX calls, fetch requests, multipart uploads or Gestor AJAX backend handlers. Prevents CSRF 403 errors, invalid JSON envelopes and stuck interfaces."
user-invocable: false
---

# Governance and Integration Architecture AJAX in Gestor Conn2Flow

# ⚡ MANDATORY TRIGGER
- **TRIGGER**: Write, edit or migrate frontend JavaScript code (Vanilla Fetch, jQuery) or backend PHP endpoints that perform asynchronous communication (AJAX) in Gestor modules.
- **SKIP ONLY IF**: Pure local visual utility Scripts (e.g. simple CSS animations without network calls).
- **CONSEQUENCE OF IGNORING**: 403 Forbidden Errors (Token CSRF invalid or missing) for not activating core AJAX mode, broken JSON envelopes, dimmer/spinner locking and silent session failures (401).

---

## 🔍 1. Diagnosis: Like Gestor Detects AJAX & 403 Calls

At the core of the framework (`gestor.php`), the AJAX mode is only activated when the request POST/GET explicitly sends `ajax: 'sim'`:
* **With `ajax: 'sim'`**: The Gestor popula `$_GESTOR['ajax'] = 'sim'`, reads `$_GESTOR['ajax-opcao']` (from the `ajaxOpcao` field) and **disables token CSRF synchronous form requirement HTML**.
* **No `ajax: 'sim'`**: Gestor assumes that this is a traditional HTML form submission, requires `$_GESTOR['token']` and **rejects the request immediately with 403 Forbidden (`{"status":"error","message":"Token CSRF inválido ou ausente."}`)**.

### Automatic CSRF and 401 coverage by `global.js`

`gestor/assets/global/global.js` injects `X-CSRF-Token` into changeable requests (`POST`, `PUT`, `PATCH`, `DELETE`) from **same origin** on ALL channels — do not type the header by hand:

|Channel|Mechanism|
| --- | --- |
|`$.ajax`|`jQuery.ajaxPrefilter`|
|`fetch`|`window.fetch` envelope|
|Forms|`submit` listener in capture + `HTMLFormElement.prototype.submit` envelope + jQuery delegate handler|
|**Raw `XMLHttpRequest`** (req-163)|`XMLHttpRequest.prototype.open` / `setRequestHeader` / `send` (guard `__c2fCsrf`)|

- **Uploads with progress bar**: `fetch` does not expose `upload.onprogress`; use `new XMLHttpRequest()` normally — the token enters alone. Before req-163 this path returned **403** with the logged-in user.
- Token manually defined (`xhr.setRequestHeader('X-CSRF-Token', ...)`, in any box) **is not duplicated or overwritten**. `GET` and cross-origin never receive the token.
- **401** response with `X-Gestor-Auth-Redirect` redirects to login in `$.ajax`, `fetch` and `XMLHttpRequest`. Still treat the 401 in your handler to unlock dimmers/spinners.

---

## 🏆 2. Frontend Canon Standard in JavaScript Vanilla (Gold Standard)

In modern modules with pure JavaScript (Vanilla JS), always use the canonical standard with `URLSearchParams` or `FormData`:

### A) Structured Data Requests (`application/x-www-form-urlencoded`):
```javascript
/**
 * Padrão Canônico de Requisição AJAX no Gestor (Vanilla JS)
 * @param {Object} state - Estado do módulo (deve conter opcao e moduleBaseUrl)
 * @param {string} ajaxOpcao - Ação a ser executada no backend (ex: 'salvar-dados', 'listar')
 * @param {Object} [extraData] - Parâmetros adicionais (objetos/arrays são serializados em JSON)
 * @returns {Promise<Object>} Resposta JSON do backend
 */
function gestorAjax(state, ajaxOpcao, extraData) {
    var params = new URLSearchParams({
        opcao: state.opcao || 'dashboard',
        ajax: 'sim',
        ajaxOpcao: ajaxOpcao
    });

    if (extraData) {
        Object.keys(extraData).forEach(function (key) {
            var val = extraData[key];
            params.set(key, typeof val === 'object' && val !== null ? JSON.stringify(val) : val);
        });
    }

    return fetch(state.moduleBaseUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: params
    })
    .then(function (response) {
        if (response.status === 401) {
            window.location.href = (window.gestor && window.gestor.raiz ? window.gestor.raiz : '/') + 'signin/';
            throw new Error('Sessão expirada. Redirecionando...');
        }
        if (!response.ok) {
            throw new Error('HTTP ' + response.status);
        }
        return response.json();
    })
    .then(function (json) {
        if (!json || json.status !== 'Ok') {
            throw new Error((json && json.message) || 'Erro na resposta do servidor.');
        }
        return json;
    });
}
```

### B) Multipart File Uploads (`FormData`):
```javascript
/**
 * Padrão Canônico para Uploads de Arquivo Multipart (Vanilla JS)
 */
function gestorUpload(state, ajaxOpcao, extraData, file) {
    var form = new FormData();
    form.append('opcao', state.opcao || 'dashboard');
    form.append('ajax', 'sim');
    form.append('ajaxOpcao', ajaxOpcao);

    if (extraData) {
        Object.keys(extraData).forEach(function (key) {
            var val = extraData[key];
            form.append(key, typeof val === 'object' && val !== null ? JSON.stringify(val) : val);
        });
    }
    if (file) {
        form.append('file', file);
    }

    return fetch(state.moduleBaseUrl, {
        method: 'POST',
        body: form
    })
    .then(function (response) {
        if (response.status === 401) {
            window.location.href = (window.gestor && window.gestor.raiz ? window.gestor.raiz : '/') + 'signin/';
            throw new Error('Sessão expirada. Redirecionando...');
        }
        if (!response.ok) {
            throw new Error('HTTP ' + response.status);
        }
        return response.json();
    })
    .then(function (json) {
        if (!json || json.status !== 'Ok') {
            throw new Error((json && json.message) || 'Erro no upload.');
        }
        return json;
    });
}
```

---

## 🏛️ 3. PHP Backend Canon Pattern (`modulo.php`)

In the module PHP controller, the entire AJAX route should be intercepted at the beginning of the cycle with `interface_ajax_iniciar()` and completed with `interface_ajax_finalizar()`:

```php
function modulo_start() {
    global $_GESTOR;

    gestor_incluir_bibliotecas();

    // 1. Interceptador de Chamadas AJAX
    if (!empty($_GESTOR['ajax'])) {
        interface_ajax_iniciar();

        switch ($_GESTOR['ajax-opcao']) {
            case 'minha-acao':
                modulo_ajax_minha_acao();
                break;
            default:
                $_GESTOR['ajax-json'] = array(
                    'status' => 'Erro',
                    'message' => 'Ação AJAX desconhecida: ' . ($_GESTOR['ajax-opcao'] ?? '')
                );
                break;
        }

        interface_ajax_finalizar();
        return;
    }

    // 2. Roteamento de Páginas Síncronas Normais
    switch ($_GESTOR['opcao']) {
        case 'dashboard':
        default:
            modulo_dashboard();
            break;
    }
}

function modulo_ajax_minha_acao() {
    global $_GESTOR;

    // Recupera dados enviados
    $param1 = $_REQUEST['param1'] ?? '';

    // Lógica de negócio / banco
    // ...

    // Retorno de Sucesso padronizado
    $_GESTOR['ajax-json'] = array(
        'status' => 'Ok',
        'data' => array(
            'id' => 123,
            'resultado' => 'Processado com sucesso'
        )
    );
}
```

---

## 📦 4. Legacy pattern with jQuery (`ajaxDefault`)

For legacy modules and interfaces using jQuery and Semantic UI:

```javascript
var ajaxDefault = {
    type: 'POST',
    url: gestor.raiz + gestor.moduloCaminho + '/',
    ajaxOpcao: 'ajaxOpcao',
    data: {
        opcao: gestor.moduloOpcao,
        ajax: 'sim'
    },
    dataType: 'json',
    beforeSend: function () {
        loadDimmer(true);
        msg_erro_resetar();
    },
    success: function (dados) {
        if (dados.status === 'Ok') {
            this.successCallback(dados);
        } else {
            this.successNotOkCallback(dados);
            console.log('ERROR - ' + this.ajaxOpcao + ' - ' + dados.status);
        }
        loadDimmer(false);
    },
    error: function (txt) {
        if (txt.status === 401) {
            window.open(gestor.raiz + (txt.responseJSON.redirect ? txt.responseJSON.redirect : "signin/"), "_self");
        } else {
            console.log('ERROR AJAX - ' + this.ajaxOpcao + ' - Dados:', txt);
            loadDimmer(false);
        }
    },
    successCallback: function (response) { },
    successNotOkCallback: function (response) { }
};

// Uso:
var ajax = Object.assign({}, ajaxDefault);
ajax.ajaxOpcao = 'minha-acao';
ajax.data = Object.assign({}, ajaxDefault.data, {
    ajaxOpcao: ajax.ajaxOpcao,
    param1: 'valor1'
});
ajax.successCallback = function (response) {
    // response.data ...
};
$.ajax(ajax);
```

---

## 5. `<head>` (`gestor_pagina_javascript_incluir`) Injection Trap

The PHP `gestor_pagina_javascript_incluir()` function injects module scripts into the `<head>` element of the HTML page. This means the script is **run before `<body>` is parsed**.

### 5.1 Prohibition of Direct DOM Queries on Script Root

```javascript
// ❌ ERRADO — DOM ainda não foi parseado, retorna null
var form = document.getElementById('form-checkout');
form.addEventListener('submit', handler); // TypeError: null

// ✅ CORRETO — aguardar DOMContentLoaded
document.addEventListener('DOMContentLoaded', function() {
    var form = document.getElementById('form-checkout');
    form.addEventListener('submit', handler);
});
```

### 5.2 Event Delegation Preferences

For elements that can be inserted dynamically (widgets, AJAX components), use **delegation of events** in `document`:

```javascript
// ✅ CORRETO — delegação funciona mesmo para elementos futuros
document.addEventListener('click', function(e) {
    if (e.target.matches('.btn-adicionar-carrinho')) {
        adicionarAoCarrinho(e.target.dataset.produtoId);
    }
});

// ✅ CORRETO — submit com delegação
document.addEventListener('submit', function(e) {
    if (e.target.matches('#form-checkout')) {
        e.preventDefault();
        processarCheckout(e.target);
    }
});
```

> [!WARNING]
> Without `DOMContentLoaded` or delegation, the form will be submitted natively (synchronous POST) instead of being intercepted via AJAX, causing full page reload and state loss.

---

## ⛔ AJAX Inviolable Rules:
1. **Always send `ajax: 'sim'`**: Without this field, the backend fires `403 Forbidden` error by CSRF.
2. **Always send `opcao` and `ajaxOpcao`**: Identify the context of the page and the action to be routed in `switch ($_GESTOR['ajax-opcao'])`.
3. **Backend MUST use `interface_ajax_iniciar()` and `interface_ajax_finalizar()`**: Ensures headers Correct JSON, buffer cleaning and standardized envelope.
4. **Treat error 401 (Unauthorized)**: Redirect immediately to `signin/` in case of expired session.
5. **Required version Bump**: By changing any JavaScript script in `resources/`, increase the version (`versao: "X.Y.Z"`) in `<id>.json` or in the module manifest before running `c2f resources:sync` to invalidate the browser cache.

## Asynchronous traffic clearance with Chrome DevTools MCP

In an isolated local session prepared according to `c2f-agent-visual-inspection`, play the actual action of the interface before evaluating console and network. See the schema negotiated by the installed server; do not suppose that the parameters are identical between versions.

1. Collect `list_console_messages` before/after action. Use `get_console_message` by ID to detail available errors and stack traces; register relevant file, line, and frames, including source maps when solved. A bug-free console doesn't prove AJAX worked.
2. Use `list_network_requests` to locate `fetch`/XHR of the action, including `/_ajax/`, `/_api/` and module routes. Correlate ID, method, URL and sequence; scan each failure with `get_network_request`, checking HTTP status, headers, payload and response body.
3. In Gestor, check `ajax=sim`, `opcao` and `ajaxOpcao` where the route contract requires them. In changeable methods of the same origin, check the presence of `X-CSRF-Token` injected by `global.js` and valid session. The `ajax=sim` field does not eliminate CSRF protection; do not disable protection to fix a 403. Do not expose token, Authorization, Cookie or Set-Cookie values in the records.
4. Differentiate 401 (session/`X-Gestor-Auth-Redirect`), 403 (authorization/CSRF), 500 (backend) and HTTP 200 with invalid HTML login, PHP warns or JSON. Check Content-Type and JSON envelope expected by the route, and release loading/dimmer after success and failure.
5. For multipart, check fields and browser-generated boldness; do not manually set Content-Type on `FormData`. Register observed latency and duplicate requests after repeated clicks. Check DOM results with new snapshot/screenshot.

Prefer to observe the original request. Resending mutations like `fetch()` can duplicate recordings/payments and requires authorized testing scope. Evidence should contain IDs and sanitized data, never dumps comprehensive sensitive headers. Reference: [official tools](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/tool-reference.md).

## REP-002 — observable HTTP failures

Callbacks `.fail()` calls AJAX critical must explicitly register `xhr.status`, step and time to correlate with access log, without tokens or sensitive content:
```javascript
.fail(function (xhr) {
    console.error('HTTP', xhr.status);
});
```
Preserve spinner release and session treatment/CSRF. Message “Failure” alone does not support timeout diagnosis or memory.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.