135 lines
8.5 KiB
Markdown
135 lines
8.5 KiB
Markdown
---
|
|
name: mcp-insert-headers-footers
|
|
description: MCP dedicado multi-site (node stdio, ligação `insert-headers-footers` em ~/.omp/agent/mcp.json) para ler e escrever os 3 blocos globais de código (header/body/footer) do plugin Insert Headers and Footers (agora rebrandeado "WPCode Lite", slug `insert-headers-and-footers`) via WP-CLI/SSH. ⚠️ Superfície de risco real — estes 3 blocos injectam HTML/JS/CSS arbitrário em TODAS as páginas do site (produção incluída); uma escrita mal formada pode partir o site inteiro ou abrir um vector XSS. Site é um parâmetro em cada tool. Usar quando "insert headers and footers", "wpcode lite", "header footer mcp", "injectar script no site todo", "código global wordpress", "meta pixel site inteiro", "gtm noscript global", "ihaf mcp".
|
|
layer: wiki
|
|
---
|
|
|
|
# /mcp-insert-headers-footers — MCP dedicado multi-site ao Insert Headers and Footers (WPCode Lite)
|
|
|
|
Projecto em `/media/ealmeida/Dados/Dev/mcp-insert-headers-footers/` (TypeScript, SDK MCP oficial,
|
|
stdio). Sem código PHP novo no WordPress — cada tool executa `wp eval` com um snippet PHP estático
|
|
(nunca dados do utilizador interpolados no comando) via `ssh server` sobre o WP-CLI já instalado.
|
|
**`site` é um parâmetro em cada tool**, não uma ligação fixa — o plugin está confirmado `active`
|
|
(WPCode Lite 2.3.8) nos 8 sites reais do bundle.
|
|
|
|
## ⚠️ Superfície de risco
|
|
|
|
Este plugin injecta os 3 blocos em **todas as páginas de todo o site**, via `wp_head`,
|
|
`wp_body_open` e `wp_footer`. Não há "sandbox" nem preview automático — o que estiver gravado sai
|
|
directamente no HTML servido a todos os visitantes assim que a cache de página for purgada (ou
|
|
imediatamente, se não houver cache activa). Riscos concretos:
|
|
|
|
- **Quebra visual/funcional site-wide** — uma tag HTML não fechada, um `<script>` malformado, ou
|
|
CSS inline quebrado no bloco `body` (que corre logo a seguir a `<body>`) pode desalinhar ou
|
|
esconder o site inteiro para todos os visitantes.
|
|
- **Vector XSS real** — qualquer conteúdo aqui corre sem sanitização nenhuma no frontend
|
|
(`echo wp_unslash($code)` directo, sem `esc_html`/`wp_kses`). Nunca aceitar conteúdo de origem
|
|
não confiável para estes blocos.
|
|
- **Perda de conteúdo** — a escrita é **substituição total** do bloco, não merge. Ler sempre
|
|
primeiro com `ihaf_get_blocks`; se o bloco já tiver código em produção (ex. Meta Pixel + GTM
|
|
noscript em `descomplicar`), o conteúdo completo desejado (antigo + novo) tem de ser fornecido
|
|
explicitamente.
|
|
|
|
A tool de escrita (`ihaf_set_block`) está marcada `destructiveHint:true` e a sua descrição já
|
|
inclui este aviso — mas o agente que a chama é responsável por: (1) ler o bloco antes de escrever,
|
|
(2) testar em `starter` (staging) sempre que possível antes de `descomplicar` (produção), (3)
|
|
purgar cache de página e verificar o site visualmente depois de escrever.
|
|
|
|
## Sites conhecidos
|
|
|
|
Chamar `ihaf_list_sites` para a lista actual com path e nota de estado. Todos os 8 sites do bundle
|
|
verificados `active` (WPCode Lite 2.3.8) ao vivo em 19-08-2026: `descomplicar` (produção — bloco
|
|
`body` **já em uso**: Meta Pixel + GTM noscript, não sobrescrever sem ler primeiro),
|
|
`emanuelalmeida`, `starter` (os 3 blocos vazios — site preferencial para testes), `ccv`, `care`,
|
|
`ecommerce-demo`, `e-commerce`, `ecommerce`. Também aceita um path absoluto directamente
|
|
(`/home/ealmeida/<site>`) para sites fora desta lista.
|
|
|
|
## Tools (4)
|
|
|
|
| Tool | Read-only | Uso |
|
|
|---|---|---|
|
|
| `ihaf_list_sites` | sim | Aliases conhecidos, path WP, nota de estado |
|
|
| `ihaf_get_plugin_status` | sim | Instalado/activo + versão — chamar antes se não tiver a certeza de que o plugin está activo nesse site |
|
|
| `ihaf_get_blocks` | sim | Os 3 blocos (`header`/`body`/`footer`) no texto lógico exacto que é servido ao frontend, mais `headersFootersMode` informativo. Chamar SEMPRE antes de `ihaf_set_block` |
|
|
| `ihaf_set_block` | não — `destructiveHint:true` | Substitui por inteiro UM dos 3 blocos (`block`: `header`\|`body`\|`footer`). `content` é o texto final completo, sem escaping adicional (o MCP trata do `wp_slash()` internamente) |
|
|
|
|
Todas as tools exigem `site` como primeiro parâmetro.
|
|
|
|
## Mapeamento de dados (a única documentação destes 3 blocos)
|
|
|
|
Não há skill de conhecimento separada para este plugin — este é o mapeamento completo.
|
|
|
|
Os 3 blocos vivem em **3 `wp_options` planas** (não uma option única, não uma tabela custom):
|
|
|
|
| Bloco | Option | Hook de output | Posição no HTML |
|
|
|---|---|---|---|
|
|
| `header` | `ihaf_insert_header` | `wp_head` | Dentro de `<head>` |
|
|
| `body` | `ihaf_insert_body` | `wp_body_open` (prioridade 1) | Logo a seguir à abertura de `<body>` |
|
|
| `footer` | `ihaf_insert_footer` | `wp_footer` | Antes de `</body>` |
|
|
|
|
Código fonte confirmado (`includes/global-output.php`): cada option é lida com `get_option()` cru
|
|
e impressa com `echo wp_unslash( $code )`, sem qualquer sanitização. Ficam vazias por omissão
|
|
(`get_option()` devolve `''`), o output é simplesmente ignorado se `empty( trim( $code ) )`.
|
|
Também são ignoradas em `is_admin()`, `is_feed()`, `is_robots()`, `is_trackback()`.
|
|
|
|
### ⚠️ Gotcha crítico de formato — slashing, não JSON
|
|
|
|
Diferente do gotcha do WP Fastest Cache (JSON string vs option tipada), aqui o problema é
|
|
**slashing do WordPress**, não JSON. O ecrã de admin do plugin grava directamente a partir de
|
|
`$_REQUEST` (`includes/admin/pages/class-wpcode-admin-page-headers-footers.php`):
|
|
|
|
```php
|
|
update_option( 'ihaf_insert_header', $_REQUEST['ihaf_insert_header'] );
|
|
```
|
|
|
|
Como o WordPress aplica `addslashes()` globalmente a todos os superglobais logo no arranque
|
|
(`wp_magic_quotes()`), o valor **gravado em `wp_options` fica sempre "slashed"** — aspas e
|
|
backslashes escapados. No frontend, o output faz sempre `wp_unslash( $code )` antes de imprimir.
|
|
Ou seja: **a option em repouso na base de dados nunca é o texto lógico da página** — é sempre esse
|
|
texto com `addslashes()` aplicado.
|
|
|
|
Confirmado ao vivo em produção (19-08-2026), bloco `body`:
|
|
|
|
```
|
|
BD (raw): style=\"display:none\"
|
|
Frontend: style="display:none"
|
|
```
|
|
|
|
Por isso este MCP **nunca** usa `wp option get/update` em bruto sobre estas 3 keys. Usa sempre
|
|
`wp eval` com as funções nativas `wp_unslash()` (leitura) e `wp_slash()` (escrita) — nunca
|
|
reimplementa `addslashes`/`stripslashes` em TypeScript. `ihaf_get_blocks` devolve sempre o texto
|
|
lógico (pós-`wp_unslash`, o que realmente sai no HTML); `ihaf_set_block` recebe o texto lógico
|
|
desejado e aplica `wp_slash()` do lado do servidor antes de `update_option()`. Sem este passo,
|
|
qualquer backslash literal no conteúdo (regex JS, `\n` em atributos, etc.) seria corrompido no
|
|
próximo `wp_unslash()` do frontend.
|
|
|
|
Qualquer MCP futuro sobre uma option que seja alimentada a partir de `$_REQUEST`/formulário admin
|
|
deve verificar primeiro se o plugin em causa depende deste padrão de slashing implícito do
|
|
WordPress antes de escolher `wp option get/update` directo.
|
|
|
|
## Segurança
|
|
|
|
Allowlist fixa das 3 option keys (`src/wpcli.ts`, `BLOCK_OPTION_KEYS`) — nunca `wp option update`
|
|
genérico sobre uma chave arbitrária. Conteúdo sempre via base64 em stdin, nunca interpolado no
|
|
comando SSH remoto; o único dado interpolado directamente no snippet PHP é o nome da option, que
|
|
vem de uma allowlist fixa de 3 valores (não de input do utilizador), tal como o padrão já usado em
|
|
`mcp-wpfc`. A escrita também invalida a page cache do próprio plugin quando disponível
|
|
(`wpcode_clear_all_plugins_page_cache('global')`), mas **não** purga WP Fastest Cache/Cloudflare —
|
|
isso é responsabilidade de quem chama a tool (ver `mcp-wpfc`/`mcp-cloudflare-app`).
|
|
|
|
## Verificação
|
|
|
|
Construído e testado ponta-a-ponta 19-08-2026: `ihaf_get_plugin_status` e `ihaf_get_blocks`
|
|
confirmados contra produção (`descomplicar`) — bloco `body` com Meta Pixel + GTM noscript lido
|
|
correctamente já unslashed, idêntico ao confirmado por leitura SSH directa fora do MCP
|
|
(`wp_unslash(get_option(...))`). Write round-trip testado em `starter` (staging, blocos vazios):
|
|
escrita de conteúdo com aspas e backslash duplo via `ihaf_set_block`, releitura via
|
|
`ihaf_get_blocks` confirmou o texto lógico exacto preservado (aspas e backslashes intactos),
|
|
depois revertido para `""` e confirmado por leitura SSH directa (`get_option()` == `""`) e por
|
|
`ihaf_get_blocks` limpo, sem alterar o estado original do site.
|
|
|
|
## Skills relacionadas
|
|
|
|
- `mcp-wpfc` — purga de page cache local, complementar depois de alterar estes blocos.
|
|
- `wp-cli` — padrões gerais de WP-CLI multi-site usados como base deste MCP.
|