feat(wordpress): EMCP Tools workflow skills + widget catalogs + pending wordpress skills

- emcp-page-building, emcp-content-ops, emcp-site-audit: EMCP Tools MCP
  workflows verified live (atomic/legacy interplay, apply-template overwrite
  risk, create-theme-template goes live immediately, false-positive malware
  pattern in scan-security, change ledger + rollback)
- elementor-pro-widgets: 30+5 native Elementor Pro widgets (curated catalog)
- elementskit-widgets / powerpack-widgets: 42 + 97 third-party widgets,
  widgetType extracted from plugin source (not guessed by convention)
- plugin.json bumped 1.2.0 -> 1.3.0, keywords + description updated
- commits pending wordpress skills already present as untracked files
  (emcp-tools, wordfence, wp-activity-log, seguranca-descomplicar,
  webp-express, wp-fastest-cache, wp-font-perf, wp-meteor,
  wp-activity-log, redis-object-cache, app-for-cloudflare) and pending
  edits (rank-math, wp-cli, wp-content-seo-gate)
This commit is contained in:
Claude Code
2026-08-19 03:44:00 +01:00
parent aa981e8089
commit 4a55d51329
22 changed files with 6432 additions and 29 deletions
+492
View File
@@ -0,0 +1,492 @@
---
name: wp-activity-log
description: Gestão e auditoria do WP Activity Log (Melapress, slug wp-security-audit-log — rebranding do antigo "WP Security Audit Log") via WP-CLI/SQL em servidores CWP. Cobre consulta ao log de eventos (tabelas wsal_occurrences/wsal_metadata), replicação do padrão stealth mode + log restrito a um único utilizador, confirmação do gotcha "wp-cli bypassa restrições de UI", e gestão de retenção/volume do log. Usar quando "wp activity log", "wsal", "wp security audit log", "log de auditoria wordpress", "quem alterou isto no site", "histórico de acções wordpress", "esconder plugin de auditoria", "stealth mode plugin", "restringir log a um utilizador".
---
# /wp-activity-log — WP Activity Log (Melapress) via WP-CLI/SQL
Plugin de log de auditoria WordPress (equivalente a Syslog para WP). Nome
comercial mudou de "WP Security Audit Log" para **"WP Activity Log"**
(rebranding Melapress) mas o **slug mantém-se `wp-security-audit-log`** — usar
sempre este slug em `wp plugin get/activate/deactivate`, nunca o nome
comercial.
**Fonte:** `CONFIG-Plugins-Referencia.md` §9 + verificação SSH ao vivo nesta
sessão (`emanuelalmeida.pt`, versão `5.6.5`, prefixo de tabelas `wpne_`).
---
## Contexto CWP — sempre obrigatório
```bash
PATH=/home/USER/public_html
PREFIX=$(wp db prefix --allow-root --path=$PATH) # ex.: wpne_, wp_, wpah_
```
**Acesso via SSH:** `server.descomplicar.pt`, porta 9443. **Existe sim um
comando WP-CLI nativo** — corrige o que estava documentado antes: o plugin
regista `WP_CLI::add_command( 'wsal_cli_commands', ... )` em
`classes/class-wp-security-audit-log.php`. Ver §1.1 para a lista completa
de subcomandos. Fora desses 7 comandos, o resto passa por `wp option`,
`wp db query` e `wp eval`.
---
## 1. Identificação e configuração (18 opções `wp_options`)
Prefixos: `wsal_*` (config principal), `Wsal*` (raro), `fs_wsalp` (estado
Freemius). Ler tudo de uma vez:
```bash
wp option list --search='wsal*' --format=table --fields=option_name,option_value --allow-root --path=$PATH
wp option get fs_wsalp --allow-root --path=$PATH
wp plugin get wp-security-audit-log --format=json --allow-root --path=$PATH
```
### Tabela de configuração real (`emanuelalmeida.pt`, verificado ao vivo)
| Opção | Valor | O que faz |
|---|---|---|
| `wsal_restrict-log-viewer` | `only_me` | Só o utilizador em `only-me-user-id` vê o log no wp-admin |
| `wsal_restrict-plugin-settings` | `only_me` | Só esse utilizador altera a configuração do plugin |
| `wsal_only-me-user-id` | `17` | ID do utilizador autorizado — confirmar sempre com `wp user get <ID>` que é a conta de IT correcta, não um utilizador órfão/comprometido |
| `wsal_hide-plugin` | `yes` | **Stealth mode** — esconde o plugin da lista `wp-admin/plugins.php` (filtro `all_plugins`); um atacante com sessão de browser comprometida não sabe que o site está a ser auditado |
| `wsal_mwp-child-stealth-mode` | `yes` | Idem, mas para dashboards multisite externos (ManageWP e similares) |
| `wsal_disabled-alerts` | array de IDs (ver §3) | Tipos de evento que NÃO são registados |
| `wsal_plugin_version` | `5.6.5` | Versão instalada |
| `wsal_freemius_state` / `fs_wsalp` | `skipped` / `no` | Não activou licenciamento Freemius (versão free) |
| `wsal_built-in-notifications` | array serializado | Config de e-mail de resumo diário (`daily_summary_notification`, `daily_email_address`) |
| `wsal_notified_plugin_updates`, `wsal_notified_theme_updates`, `wsal_notified_wp_core_update` | arrays/strings | Cache interna de versões já notificadas — não é config de segurança, ignorar em auditorias |
| `wsal_feature-highlight-notice-show`, `wsal_upgrade-notice-show`, `wsal_notification-modal-dismissed`, `wsal_free-search-try` | flags de UI | Estado de notices dispensados — irrelevante para segurança |
### 1.1 Comandos WP-CLI nativos (`wp wsal_cli_commands <subcomando>`)
Confirmado em `classes/Controllers/WP_CLI/class-wp-cli-commands.php`
(registados desde a v4.6, `set_retention` inclusive). Todos aceitam
`--allow-root --path=$PATH`.
| Subcomando | Argumentos | O que faz |
|---|---|---|
| `remove_wizard` | nenhum | Marca o wizard de setup inicial como dispensado (`setup-modal-dismissed=true`) |
| `remove_daily_notification` | nenhum | Desactiva o resumo diário por e-mail (`built-in-notifications.daily_summary_notification=false`) — dispara evento `6310` |
| `remove_weekly_notification` | nenhum | Desactiva o resumo semanal por e-mail (`weekly_summary_notification=false`) — dispara evento `6319` |
| `disable_enable_alert` | `<alert_id>` | Activa/desactiva um `alert_id` específico (alternativa CLI ao array `wsal_disabled-alerts`) |
| `set_hide_plugin` | `<1\|true\|0\|false>` | Equivalente CLI a `wp option update wsal_hide-plugin` (stealth mode) |
| `login_page_notification` | `--enabled=<bool>` `--text=<html>` | Activa/desactiva o aviso "este site tem log de actividade" na página de login (`wsal_login-page-notification` + `wsal_login-page-notification-text`) — **não confirmado activo em nenhum site auditado**; texto aceita HTML |
| `set_retention` | `--enabled=<bool>` `--pruning-value=<int>` `--pruning-unit=<days\|months\|years>` | Configura a purga automática (pruning) — ver §6 |
```bash
wp wsal_cli_commands disable_enable_alert 1002 --allow-root --path=$PATH
wp wsal_cli_commands set_hide_plugin 1 --allow-root --path=$PATH
wp wsal_cli_commands set_retention --enabled=true --pruning-value=6 --pruning-unit=months --allow-root --path=$PATH
```
---
## 2. Consultar o log de eventos (`wsal_occurrences` + `wsal_metadata`)
O log real vive em **duas tabelas dedicadas** (não em `postmeta`/`options`):
```bash
wp db query "SHOW TABLES LIKE '%wsal%';" --allow-root --path=$PATH
```
### Estrutura de `${PREFIX}wsal_occurrences` (1 linha = 1 evento)
| Campo | Tipo | Nota |
|---|---|---|
| `id` | bigint PK | |
| `site_id` | bigint | Para multisite |
| `alert_id` | bigint | Tipo de evento (ver tabela §3) |
| `created_on` | double | Unix timestamp com microssegundos — usar `FROM_UNIXTIME(created_on)` |
| `client_ip` | varchar | IP de origem |
| `severity` | varchar | Código numérico (ver §3) |
| `object` | varchar | Categoria (`user`, `post`, `plugins`, `system`, ...) |
| `event_type` | varchar | `created`/`modified`/`deleted`/`login`/... |
| `username`, `user_id`, `user_roles` | | Quem fez a acção — `user_id=0` é sistema/anónimo |
| `session_id`, `user_agent` | | |
| `post_status`, `post_type`, `post_id` | | Preenchido só em eventos de conteúdo |
Detalhe extra (nome do plugin alterado, ficheiro, etc.) fica em
`${PREFIX}wsal_metadata` (`occurrence_id`, `name`, `value` — schema
key/value, `LEFT JOIN` por `occurrence_id`).
### Queries úteis
```bash
PREFIX=$(wp db prefix --allow-root --path=$PATH)
# Volume total + janela temporal coberta
wp db query "SELECT COUNT(*) total, FROM_UNIXTIME(MIN(created_on)) primeiro, FROM_UNIXTIME(MAX(created_on)) ultimo FROM ${PREFIX}wsal_occurrences;" --allow-root --path=$PATH
# Últimos 50 eventos de um utilizador específico
wp db query "SELECT FROM_UNIXTIME(created_on) quando, alert_id, object, event_type, client_ip FROM ${PREFIX}wsal_occurrences WHERE username='it' ORDER BY id DESC LIMIT 50;" --allow-root --path=$PATH
# Eventos num intervalo de datas
wp db query "SELECT FROM_UNIXTIME(created_on) quando, username, object, event_type FROM ${PREFIX}wsal_occurrences WHERE created_on BETWEEN UNIX_TIMESTAMP('2026-08-01') AND UNIX_TIMESTAMP('2026-08-16') ORDER BY id;" --allow-root --path=$PATH
# Filtrar por tipo de evento (alert_id) — ex.: falhas de login (1002)
wp db query "SELECT FROM_UNIXTIME(created_on) quando, username, client_ip FROM ${PREFIX}wsal_occurrences WHERE alert_id=1002 ORDER BY id DESC LIMIT 30;" --allow-root --path=$PATH
# Distribuição por tipo de evento (para ver o que domina o log)
wp db query "SELECT alert_id, COUNT(*) total FROM ${PREFIX}wsal_occurrences GROUP BY alert_id ORDER BY total DESC LIMIT 20;" --allow-root --path=$PATH
# Distribuição por severidade
wp db query "SELECT severity, COUNT(*) total FROM ${PREFIX}wsal_occurrences GROUP BY severity ORDER BY total DESC;" --allow-root --path=$PATH
# Ver metadados de um evento específico (ex.: qual plugin foi alterado)
wp db query "SELECT name, value FROM ${PREFIX}wsal_metadata WHERE occurrence_id=<ID>;" --allow-root --path=$PATH
```
---
## 3. Tipos de evento e severidade (dados reais observados, `emanuelalmeida.pt`)
`severity` (código Melapress padrão): `200`=informational, `250`=user
activity, `300`=notification, `400`=warning, `500`=critical.
Top `alert_id` observados em 60.337 eventos (~1,5 anos de histórico) — usar
como referência de "normal" ao auditar um site novo, não como lista
exaustiva (o WSAL tem 100+ tipos de evento definidos):
| `alert_id` | Ocorrências | Interpretação provável |
|---|---|---|
| `6070` | 26.476 | Verificação periódica de sistema (cron interno do WSAL) |
| `1003` | 20.276 | Actividade de sessão/login |
| `2008` | 3.202 | Conteúdo modificado (post/página) |
| `6066` | 2.615 | Verificação periódica de sistema |
| `6069` | 2.329 | Verificação periódica de sistema |
| `1002` | 1.965 | Login falhado |
| `1000` | 389 | Login com sucesso |
| `2053`/`2054` | 305/133 | Conteúdo criado/eliminado |
**Não confiar em interpretações de `alert_id` sem confirmar** contra a
documentação oficial Melapress (`https://melapress.com/support/kb/`) ou o
código do plugin (`wp-content/plugins/wp-security-audit-log/`) — os números
acima são inferência a partir do padrão de volume, não confirmados
individualmente nesta sessão.
### 3.1 Categorias de eventos (mapa completo, `defaults.php`)
Ficheiro central de definições: `defaults.php` (raiz do plugin, função
`set_wsal_alerts()`, ~3.760 linhas). Define **as categorias core** — os
eventos de integrações de terceiros (WooCommerce, Yoast, ACF, Rank Math,
etc.) vivem em ficheiros separados (ver §3.2). Categorias core, por ordem
de aparição no ficheiro (nome + intervalo aproximado de `alert_id`):
| Categoria (grupo topo) | Subcategorias | Intervalo `alert_id` | Conteúdo |
|---|---|---|---|
| Users Logins & Sessions Events | User Activity | `1000`–`1099` | Login/logout, falhas de login, sessões, alteração de password |
| Content & Comments | Content, Categories, Custom Fields, Custom Fields (ACF), Comments, Widgets, Menus, Custom Post Types, Pages | `2000`–`2999` | Posts/páginas criados/editados/eliminados, categorias, comentários, widgets, menus, custom post types, ACF |
| User Accounts | User Profiles, Multisite User Profiles | `4000`–`4999` | Utilizadores criados/editados/eliminados, mudança de role, password reset |
| Plugins & Themes | Plugins, Themes, Themes on Multisite | `5000`–`5599` | Instalação/activação/desactivação/actualização de plugins e temas |
| Activity Logs | Activity log plugin | `6000`–`6099`, `6300`–`6399` | Config do próprio WSAL alterada (ex.: `6310`/`6311`/`6312`/`6319` já usados pelos comandos WP-CLI de notificação — ver §1.1); também cobre eventos de "Notifications & Integrations" (Slack/SMS/e-mail custom) |
| WordPress & System | System | `6000`–`6099` | Cron interno do WSAL, verificações periódicas (os IDs `6066`/`6069`/`6070` de alto volume observados em §3 pertencem aqui) |
| — | WordPress Site Settings | `4700`–`4799` (aprox.) | Alteração de opções core do WP (site title, timezone, permalinks, etc.) |
| — | Email Events | `8800`–`8899` (aprox.) | Falhas de envio de e-mail via `wp_mail` |
| — | Database Events | `9000`–`9099` (aprox.) | Erros de query, updates de schema da BD |
**Nota:** as gamas de `alert_id` acima são aproximadas (extraídas da ordem
de definição em `defaults.php`, não documentadas oficialmente pela
Melapress como ranges fixos) — para confirmar a categoria exacta de um
`alert_id` específico, o método fiável é `grep -A6 "^\s*$ALERT_ID,$"
defaults.php` no código-fonte do servidor.
### 3.2 Alertas de integrações de terceiros (activados só se o plugin correspondente estiver presente)
Ficheiros em `classes/WPSensors/Alerts/` (18 ficheiros, um por integração
suportada) — cada um define o próprio bloco de `alert_id`, só carregado se
o plugin alvo estiver activo no site:
| Ficheiro | Integração | Volume aprox. de alertas |
|---|---|---|
| `class-woocommerce-custom-alerts.php` | WooCommerce | ~417 linhas de definição (produtos, encomendas, cupões, definições da loja) |
| `class-yoast-custom-alerts.php` | Yoast SEO | Meta SEO, sitemaps, redirects |
| `class-rank-math-custom-alerts.php` | Rank Math | Meta SEO, schema, redirects |
| `class-acf-custom-alerts.php` | Advanced Custom Fields | Grupos de campos, campos criados/editados |
| `class-gravity-forms-custom-alerts.php` | Gravity Forms | Formulários, entradas, notificações |
| `class-wpforms-custom-alerts.php` | WPForms | Formulários, entradas |
| `class-wp-2fa-custom-alerts.php` | WP 2FA (Melapress) | Activação/desactivação de 2FA por utilizador |
| `class-memberpress-custom-alerts.php` | MemberPress | Subscrições, membros |
| `class-paid-memberships-pro-custom-alerts.php` | Paid Memberships Pro | Níveis, membros |
| `class-learndash-custom-alerts.php` | LearnDash | Cursos, inscrições |
| `class-bbpress-custom-alerts.php` | bbPress | Fóruns, tópicos |
| `class-tablepress-custom-alerts.php` | TablePress | Tabelas |
| `class-redirection-custom-alerts.php` | Redirection | Regras de redirect |
| `class-termly-custom-alerts.php` | Termly | Consentimento de cookies |
| `class-ai-wp-plugin-alerts.php` | AI (Melapress AI features) | Uso de funcionalidades AI do próprio WSAL |
| `class-multisite-custom-alerts.php` | WordPress Multisite | Criação/eliminação de sites na rede |
| `class-mainwp-server-custom-alerts.php` | MainWP (servidor gerido) | Propagação de definições via dashboard MainWP — ver aviso em `defaults.php:2593` ("definições do WSAL foram sobrepostas a partir do MainWP") |
Confirmar quais estão activos num site: `wp plugin list --status=active
--allow-root --path=$PATH` e cruzar com a tabela acima — só os que
correspondem a um plugin activo geram eventos.
---
## 4. Replicar o padrão de segurança num site novo (checklist)
Baseado na configuração real confirmada em `emanuelalmeida.pt` (avaliada
como "acima da média"):
```bash
PATH=/home/USER/public_html
# 1. Instalar/activar (slug correcto — nome comercial mudou, slug não)
wp plugin install wp-security-audit-log --activate --allow-root --path=$PATH
# 2. Identificar o ID do utilizador de IT/admin de confiança (NÃO um admin genérico partilhado)
wp user list --role=administrator --fields=ID,user_login,user_email --allow-root --path=$PATH
# 3. Restringir visualização do log e config a esse utilizador
wp option update wsal_restrict-log-viewer only_me --allow-root --path=$PATH
wp option update wsal_restrict-plugin-settings only_me --allow-root --path=$PATH
wp option update wsal_only-me-user-id <ID_do_utilizador_IT> --allow-root --path=$PATH
# 4. Activar stealth mode — esconder o plugin da lista wp-admin
wp option update wsal_hide-plugin yes --allow-root --path=$PATH
wp option update wsal_mwp-child-stealth-mode yes --allow-root --path=$PATH
# 5. Confirmar (ler de volta, nunca confiar só no exit code)
wp option get wsal_restrict-log-viewer --allow-root --path=$PATH
wp option get wsal_only-me-user-id --allow-root --path=$PATH
wp user get <ID_do_utilizador_IT> --field=user_login --allow-root --path=$PATH
```
Não mexer em `wsal_disabled-alerts` por defeito — deixar todos os ~900+
tipos de evento activos (contando módulos de terceiros — ver §3.1). O padrão
observado de apenas 13 IDs desligados, todos de baixo valor informativo, é a
postura recomendada; desligar mais do que isso reduz cobertura de auditoria
sem ganho real.
---
## 5. GOTCHA CRÍTICO — stealth mode e "only_me" não protegem contra WP-CLI/SQL
**Confirmado nesta sessão por teste directo:** apesar de `hide-plugin=yes`
esconder o plugin da lista em `wp-admin/plugins.php` (via hook PHP no ecrã
de admin), o plugin continua **totalmente visível e operável via WP-CLI**:
```bash
wp plugin list --allow-root --path=$PATH | grep security-audit
# → wp-security-audit-log,active,none,5.6.5,,on (aparece normalmente)
wp plugin status wp-security-audit-log --allow-root --path=$PATH
# → Status: Active (nenhuma restrição aplicada)
```
**Implicação de segurança real:** o padrão "esconder + restringir a
`only_me`" protege apenas contra um atacante que compromete uma **sessão de
browser wp-admin** de um utilizador administrador genérico (não vê o plugin
na UI, não consegue desactivá-lo nem ler o log pela interface). **Não
protege** contra:
- Acesso SSH/WP-CLI ao servidor (qualquer comando `wp plugin deactivate`,
`wp option update wsal_hide-plugin no`, ou `wp db query "DELETE FROM
wsal_occurrences"` funciona sem qualquer verificação de `only-me-user-id`);
- Acesso directo à base de dados (phpMyAdmin, mysql CLI);
- Um plugin malicioso que corra `update_option()`/`deactivate_plugins()` em
PHP no mesmo processo WordPress (o hook de stealth só filtra a
*renderização* da lista, não a capability real).
Estas restrições são **apenas de UI**, implementadas por filtros como
`all_plugins`/menu — não são um controlo de acesso ao nível dos dados. Ao
avaliar a robustez do log de auditoria de um site, tratar sempre acesso
SSH/WP-CLI e acesso à BD como "full trust" que ignora completamente esta
protecção — a defesa real contra esses vectores é a segurança do próprio
servidor (SSH keys, `mysql` sem exposição externa), não a config do plugin.
---
## 6. Retenção, arquivo e volume do log
O plugin **não faz purga agressiva por defeito** — confirmado:
`emanuelalmeida.pt` tinha 60.337 eventos cobrindo Mar/2025 a Ago/2026 (~1,5
anos) sem gaps aparentes, ocupando `~13 MB` (`wsal_occurrences`) + `~50 MB`
(`wsal_metadata`, guarda os detalhes de cada evento — cresce mais rápido
que a tabela principal).
```bash
# Verificar tamanho actual antes de decidir sobre arquivo/purga
wp db query "SELECT table_name, ROUND(((data_length+index_length)/1024/1024),2) size_mb, table_rows FROM information_schema.TABLES WHERE table_schema=DATABASE() AND table_name LIKE '%wsal%';" --allow-root --path=$PATH
```
### 6.1 Purga automática (pruning) — `wsal_pruning-*`, classe `Plugin_Settings_Helper`/`Settings_Helper`
Ao contrário do que a auditoria pontual anterior sugeria, a purga
automática **não é exclusiva da versão paga** — a lógica e as opções vivem
no core (`classes/Helpers/class-plugin-settings-helper.php` +
`class-settings-helper.php`) e há um comando WP-CLI dedicado (`set_retention`,
ver §1.1). Confirmado nesta expansão: nenhuma destas opções está definida em
`emanuelalmeida.pt` (usa os defaults do código, purga desligada).
| Opção | Default no código | O que faz |
|---|---|---|
| `wsal_pruning-date-e` | `false` (desligado) | Activa/desactiva a purga automática por data |
| `wsal_pruning-date` | calculado por `get_default_pruning_date()` | Data-limite (`strtotime`-compatible, ex. `"6 months"`) — eventos mais antigos são apagados no próximo cron |
| `wsal_pruning-unit` | `months` | Unidade usada no picker da UI (`days`/`months`/`years`) |
| `wsal_pruning-limit-e` | não confirmado | Activa purga por **limite de contagem** (em vez de por data) |
| `wsal_pruning-limit` | `1` (mínimo) | Nº máximo de eventos a manter, quando `pruning-limit-e=true` |
| `wsal_delete-data` | `false` | Se `true`, ao desinstalar o plugin todos os dados (`wsal_occurrences`/`wsal_metadata`/opções) são apagados; se `false`, ficam órfãos na BD |
```bash
# Ler estado actual de purga
wp option list --search='wsal_pruning*' --format=table --allow-root --path=$PATH
# Activar purga a 6 meses via WP-CLI (equivalente ao form da UI)
wp wsal_cli_commands set_retention --enabled=true --pruning-value=6 --pruning-unit=months --allow-root --path=$PATH
wp option get wsal_pruning-date-e --allow-root --path=$PATH
wp option get wsal_pruning-date --allow-root --path=$PATH
```
### 6.2 Arquivo (archiving) para BD externa — `wsal_archiving-*`
Funcionalidade distinta da purga: em vez de apagar, **move** eventos
antigos para uma segunda ligação de base de dados (`Archive_Records::archive()`
em `classes/Entities/Archive/class-archive-records.php`) via cron. Requer
configurar previamente uma "connection" de arquivo (ver §6.3) — sem
connection definida, `is_archiving_set_and_enabled()` devolve `false` e o
cron não corre, mesmo com `archiving-e=yes`.
| Opção | O que faz |
|---|---|
| `wsal_archiving-e` | Liga/desliga o arquivo automático |
| `wsal_archiving-date` + `wsal_archiving-date-type` (`days`/`months`/`years`, default `1`/`months`) | Antiguidade a partir da qual um evento é arquivado |
| `wsal_archiving-run-every` | Frequência do cron de arquivo (default `hourly`) |
| `wsal_archiving-stop` | Pausa temporária do arquivo sem desligar a config |
| `wsal_archiving-cron-started` | Flag interna — cron já agendado |
```bash
wp option list --search='wsal_archiving*' --format=table --allow-root --path=$PATH
```
**Não confirmado activo em nenhum site auditado** (`emanuelalmeida.pt` não
tem nenhuma destas opções definidas).
### 6.3 Espelhamento (mirroring) para BD/serviço externo — `wsal_wsal-mirror-*`
Envia uma cópia em tempo real de cada evento para uma ligação externa
(outra tabela MySQL, ou syslog/SIEM de terceiros via plugin de mirror
dedicado — **o WSAL não tem integração nativa de syslog/SIEM no core**, só
o mecanismo genérico de "connections" reutilizado por mirror e archiving).
`Settings_Helper::get_all_mirrors()` lê todas as opções com prefixo
`WSAL_MIRROR_PREFIX` (`wsal-mirror-`); cada uma é um array serializado
(`connection`, filtros de `alert_id`, etc.).
```bash
wp option list --search='wsal-mirror-*' --format=table --allow-root --path=$PATH
wp option list --search='wsal_connection-*' --format=table --allow-root --path=$PATH
```
Se `database_logging_enabled` for `false` E existir pelo menos um mirror
configurado, o WSAL passa a **não gravar mais em `wsal_occurrences` local**
— todo o log vai só para o(s) destino(s) espelhados. Confirmar sempre
`Settings_Helper::is_database_logging_enabled()` (via `wp eval`) antes de
assumir que as queries SQL do §2 devolvem o histórico completo num site com
mirror activo.
**Nota operacional (aplica-se a §6.1–§6.3):** a ausência de purga é boa
para auditoria (histórico completo disponível), mas em sites com muita
actividade o crescimento é linear e sem limite. Se `wsal_metadata`
ultrapassar dimensões incómodas (centenas de MB), considerar:
- Activar `wsal_pruning-date-e` com uma janela realista (6–12 meses) via
`set_retention`;
- Arquivar eventos antigos para uma tabela `_archive` antes de apagar
(nunca `DELETE` directo sem backup — perde-se rasto de auditoria);
- Configurar uma connection de arquivo (§6.2) se o volume justificar
automação em vez de purga manual.
Não foi necessário agir em `emanuelalmeida.pt` nesta sessão — volume actual
(~63 MB total) é irrelevante para performance.
---
## 7. Notificações, relatórios e integrações
### 7.1 Notificações incorporadas (resumo diário/semanal por e-mail — gratuito)
Opção `wsal_built-in-notifications` (array serializado, constante
`Notifications::BUILT_IN_NOTIFICATIONS_SETTINGS_NAME`). Confirmado em
`emanuelalmeida.pt`: só contém config de resumo diário
(`daily_summary_notification`, `daily_email_address`); a v5.3+ acrescentou
resumo **semanal** (`weekly_summary_notification`, evento `6319` ao
desactivar — ver §1.1 `remove_weekly_notification`).
```bash
wp option get wsal_built-in-notifications --format=json --allow-root --path=$PATH
```
### 7.2 Notificações customizadas (regra "se `alert_id` X ocorrer, notifica Y") — Premium
Opção `wsal_custom-notifications` (constante
`CUSTOM_NOTIFICATIONS_SETTINGS_NAME`) — permite regras condicionais por
utilizador/IP/tipo de evento, com entrega por e-mail, **Slack**
(`classes/Controllers/slack/`) ou **SMS via Twilio**
(`classes/Controllers/twilio/`). Não confirmado activo em nenhum site
auditado; `wsal_notifications` (nota: nome sem `custom-`, é outra opção)
só guarda config global de encurtamento de URLs (`shorten_notification_urls`,
`notification_bitly_shorten_key`) — confirmado vazio/desligado em
`emanuelalmeida.pt`.
```bash
wp option get wsal_custom-notifications --format=json --allow-root --path=$PATH
wp option get wsal_notifications --format=json --allow-root --path=$PATH
```
### 7.3 Exportação e relatórios
- **CSV** — gratuito, core. Acção AJAX `wsal_export_csv_results`
(`classes/Writers/class-csv-writer.php`), disponível no ecrã do log em
wp-admin (botão "Export"). Não há comando WP-CLI dedicado — a exportação
é sempre via browser/AJAX; para extrair dados por CLI usar as queries SQL
do §2 e `wp db query ... --format=csv` como alternativa equivalente.
- **Relatórios agendados** (geração automática + entrega por e-mail,
white-label) — feature **Premium** (`classes/Views/class-premium-features.php`,
`AuditLog.php:470` mostra link de upsell "Get scheduled reports... with
Premium" nos eventos de tipo `2000`/`2001`/`2100`/`4000`). Não existe em
wp-options nem endpoint WP-CLI na versão gratuita.
### 7.4 SIEM/Syslog externo
**Não existe integração nativa de Syslog/SIEM no core do plugin** —
confirmado por ausência de qualquer classe/ficheiro com `syslog`/`SIEM` no
código-fonte (`grep -rl` devolveu zero resultados). A única via de exportar
eventos em tempo real para um sistema externo é o mecanismo de
**espelhamento (mirroring)** descrito em §6.3, que só suporta ligações
configuradas como "connections" do próprio plugin (outra BD MySQL) — não um
protocolo Syslog/CEF nativo. Para integração SIEM real, a Melapress
documenta um add-on comercial separado; confirmar sempre no site do
fornecedor antes de assumir que existe suporte nativo.
---
## 8. Erros comuns
| Sintoma | Causa | Solução |
|---|---|---|
| `wp plugin activate wp-activity-log` falha ("plugin not found") | Usou o nome comercial em vez do slug | Slug é sempre `wp-security-audit-log`, independentemente do rebranding |
| Query em `wsal_occurrences` devolve datas erradas | `created_on` é Unix timestamp `double` (com microssegundos), não `DATETIME` | Usar sempre `FROM_UNIXTIME(created_on)` |
| Achar que o plugin "não está lá" num site com `hide-plugin=yes` | Confundir stealth mode de UI com ausência real | Confirmar sempre via `wp plugin list`/`wp plugin status` — o WP-CLI nunca respeita esta flag (ver §5) |
| `only-me-user-id` aponta para um ID que já não existe/foi desactivado | Utilizador de IT mudou de conta e a opção não foi actualizada | Confirmar sempre com `wp user get <ID>` antes de assumir que a restrição é válida — um ID órfão equivale a log inacessível a todos via UI |
| Achar `wsal_disabled-alerts` vazio = tudo activo | O array serializado inclui sempre um `s:0:""` inicial (artefacto de serialização PHP), não é um alerta desligado real | Ignorar a primeira entrada vazia ao contar IDs desligados |
| Achar que "não há WP-CLI para este plugin" | Confusão histórica — o WSAL regista `wp wsal_cli_commands` desde a v4.6 | Usar `wp cli cmd-dump \| grep wsal` para confirmar; ver §1.1 |
| Achar que purga automática (`pruning`) é só Premium | `set_retention`/`wsal_pruning-*` são core, disponíveis na versão gratuita | Confirmar sempre no código (`class-plugin-settings-helper.php`) antes de recomendar upgrade pago só para ligar retenção |
| Confundir `wsal_notifications` com `wsal_custom-notifications` | Nomes parecidos, funções diferentes — a primeira é só config de encurtamento de URL, a segunda são as regras de alerta customizado (Premium) | Ler sempre as duas e não assumir que uma vazia implica a outra desligada |
| Assumir que o log SQL (§2) tem o histórico completo num site com mirroring activo | Se `database_logging_enabled=false` com mirror configurado, o WSAL deixa de escrever localmente | Confirmar `Settings_Helper::is_database_logging_enabled()` via `wp eval` antes de tirar conclusões de volume/gaps |
---
**Fonte:** `CONFIG-Plugins-Referencia.md` §9 (WP Security Audit Log / WP
Activity Log) + verificação SSH ao vivo nesta sessão (`emanuelalmeida.pt`,
opções `wsal_*`, estrutura e contagem de `wsal_occurrences`/`wsal_metadata`,
teste de bypass `wp plugin list`) + **leitura completa do código-fonte do
plugin nesta expansão** (`wp-content/plugins/wp-security-audit-log/`,
versão `5.6.5`, ~99.000 linhas PHP): `defaults.php` (definições de todos os
eventos core, função `set_wsal_alerts()`), `classes/WPSensors/Alerts/*`
(18 integrações de terceiros), `classes/Controllers/WP_CLI/class-wp-cli-commands.php`
(comandos WP-CLI nativos), `classes/Helpers/class-settings-helper.php` +
`class-plugin-settings-helper.php` (todas as opções `wsal_*` — pruning,
archiving, mirroring, notificações), `classes/Entities/Archive/class-archive-records.php`
(mecanismo de arquivo), `classes/notification/*` (notificações
incorporadas e customizadas, Slack/Twilio), `classes/Views/class-premium-features.php`
e `AuditLog.php` (features gated como Premium: relatórios agendados,
notificações custom, mirroring/archiving).