E22 · Kit audit
10 minWith the holes from lessons 2 and 3 fixed, run the audit block and open relatorio-seguranca.md.
Done when: AUDITORIA: 6/6 ok and saída: 0.
Lesson 8 · 20 min
Six checks, one per lesson, in a single run with a dated report. You run the audit on the kit, plant a secret to see the break get caught, and leave with the review plan: the spec changes, the dependencies change, and the audit runs again.
Start the lesson
Goal: run the full audit (page, fallback, exposure, catalog, server, secrets), see a break get caught, and leave with the dated plan.
One per course lesson. Click each one.
Web Bot Auth (lesson 6) is not included: it is a policy decision, checked in the threat card and in politica-bots.md.
With the two holes fixed (lessons 2 and 3). Writes kit/relatorio-seguranca.md with the date.
cd ~/projetos/formacao-webmcp-seguranca-v72/kit node scripts/auditar.mjs; echo "saída: $?"
// auditar.mjs — the security audit for the whole course in one run (lesson 8).
// 6 checks, each from one lesson; writes relatorio-seguranca.md with date and result.
// 1 page checar.mjs: third-party text marked; consequential action asks for confirmation (lessons 2–3)
// 2 fallback the page works without WebMCP (the case for most visitors)
// 3 exposure every .html page that is not index sends Permissions-Policy with tools=() (lesson 4)
// 4 catalog ameacas.mjs with no findings (lesson 7)
// 5 server atacar-mcp.mjs seguro: 0 of 6 attacks get through (lesson 5)
// 6 secrets segredos.mjs on the kit folder: 0 secrets (lesson 5)
// Usage: node scripts/auditar.mjs (PAGINA=<folder> and CATALOGO=<file> for your copy)
// Exit 0 = 6/6; 1 = at least one failed.
import { spawnSync } from 'node:child_process';
import { readdirSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { KIT, pastaPagina, servir, abrir } from '../lib/navegador.mjs';
const node = (...args) => spawnSync(process.execPath, args, { cwd: KIT, encoding: 'utf8', timeout: 240000 });
const ultima = r => (r.stdout || r.stderr || '').trim().split('\n').at(-1);
const res = [];
const anotar = (n, nome, ok, det) => { res.push({ n, nome, ok, det }); console.log(`${ok ? 'ok ' : 'FALHA'} ${n} ${nome}: ${det}`); };
const pasta = pastaPagina();
const s0 = await abrir('sem');
console.log(`== ${s0.versao.replace(' · modo: sem', '')} · ${new Date().toISOString().slice(0, 10)}`);
let r = node('scripts/checar.mjs');
anotar(1, 'página', r.status === 0, ultima(r));
const fb = await s0.page.evaluate(() => ({ modo: document.documentElement.dataset.webmcp, itens: document.querySelectorAll('li').length }));
anotar(2, 'fallback', fb.modo === 'sem' && fb.itens > 0 && !s0.erros.length, `sem WebMCP: ${fb.itens} itens na tela, ${s0.erros.length} erros de JS`);
await s0.fechar();
const srv = await servir(pasta);
const paginas = readdirSync(pasta).filter(f => f.endsWith('.html') && f !== 'index.html');
const sem = [];
for (const p of paginas) {
const pp = (await fetch(srv.url + p)).headers.get('permissions-policy') || '';
if (!/\btools=\(\)/.test(pp)) sem.push(p);
}
await srv.fechar();
anotar(3, 'exposição', sem.length === 0, sem.length ? `sem Permissions-Policy tools=(): ${sem.join(', ')}` : `${paginas.length} página(s) sem ferramenta com tools=()`);
r = node('scripts/ameacas.mjs', process.env.CATALOGO || resolve(pasta, 'ferramentas.mjs'));
anotar(4, 'catálogo', r.status === 0, ultima(r));
r = node('scripts/atacar-mcp.mjs', 'seguro');
anotar(5, 'servidor', r.status === 0, ultima(r));
r = node('scripts/segredos.mjs', '.');
anotar(6, 'segredos', r.status === 0, ultima(r));
const okN = res.filter(x => x.ok).length;
const hoje = new Date().toISOString().slice(0, 10);
writeFileSync(resolve(KIT, 'relatorio-seguranca.md'),
`# Relatório de segurança — ${hoje}\n\nSpec WebMCP: rascunho CG de 16/10/2026 · MCP 2026-07-28\n\n| # | verificação | resultado | detalhe |\n|---|---|---|---|\n` +
res.map(x => `| ${x.n} | ${x.nome} | ${x.ok ? 'ok' : 'FALHA'} | ${x.det.replace(/\|/g, '/')} |`).join('\n') +
`\n\nTotal: ${okN}/6. Rode de novo a cada mudança de ferramenta, de dependência ou da spec.\n`);
console.log(`AUDITORIA: ${okN}/6 ok · relatório: relatorio-seguranca.md`);
process.exit(okN === 6 ? 0 : 1);== Chromium 156.0.8078.4 (Playwright) · 2026-10-19 ok 1 página: RESULTADO: 0 falhas ok 2 fallback: sem WebMCP: 4 itens na tela, 0 erros de JS ok 3 exposição: 1 página(s) sem ferramenta com tools=() ok 4 catálogo: 5 ferramentas · 0 achados ok 5 servidor: passaram: 0 de 6 ok 6 segredos: 70 arquivos lidos · 0 segredos AUDITORIA: 6/6 ok · relatório: relatorio-seguranca.md saída: 0
The number of files read changes with what you add to the kit. What counts is the last line.
A forgotten file with a token inside the page folder. The block plants it, audits, and deletes it.
cd ~/projetos/formacao-webmcp-seguranca-v72/kit
printf "export const TOKEN = '%s';\n" $(openssl rand -hex 16) > pagina/config-esquecida.mjs
node scripts/auditar.mjs | tail -2; echo "saída: ${PIPESTATUS[0]}"
rm pagina/config-esquecida.mjsFALHA 6 segredos: 71 arquivos lidos · 1 segredo AUDITORIA: 5/6 ok · relatório: relatorio-seguranca.md saída: 1
Exit 1: you can use the audit as a gate before publishing (in CI or in a pre-push hook).
A local copy of your page that registers the tools and sets document.documentElement.dataset.webmcp at the end. PAGINA=[folder] changes the folder; CANARIO and CONFIRMAR change the canary and the selector of your dialog.
CATALOGO=[file] points to the module with the definitions, if it is not ferramentas.mjs.
atacar-mcp.mjs attacks the kit's server. For yours, copy the six attacks and replace subir() with the URL of your test server, using test tokens from your provider.
With every new tool, every dependency update, and whenever the spec changes. WebMCP is a draft: the origin trial runs through Chrome 156 (on October 19, 2026), and the API has already been renamed once, from navigator.modelContext to document.modelContext.
Four deliverables, eight lessons. Click an exercise to see the "done when".
confirmation + origin
3 exercises
An action asks for a person; only the right people see it.
See the lessonAccept that there is no filter and mark what comes from visitors.
Confirmation with target and effect, measured exposure, tools=() where it isn't needed.
Audience, scope, handle with an owner; signature verified.
Model the threats and run the battery after every change.
Each pair of lessons uses the fixed kit from the previous one.
Pick a start date and how much time you have per week.
The audit on the kit and on your site.
With the holes from lessons 2 and 3 fixed, run the audit block and open relatorio-seguranca.md.
Done when: AUDITORIA: 6/6 ok and saída: 0.
Run the break block.
Done when: FALHA 6 segredos, AUDITORIA: 5/6 ok and saída: 1; after the rm, the audit goes back to 6/6.
Run the audit with PAGINA and CATALOGO from your site, generate the plan above, and mark the next review on your calendar.
Done when: PAGINA=[your folder] node scripts/auditar.mjs and then head -1 relatorio-seguranca.md → # Relatório de segurança — [today's date]; ls [your downloads folder]/plano-*.ics → the plan file.
Question 1 of 4
The audit scored 5/6 with FALHA 6 segredos. What should you do before publishing?skill
Question 2 of 4
Why does check 2 open the page WITHOUT WebMCP?
Question 3 of 4
When does the audit need to run again?
navigator.modelContext to document.modelContext).navigator.modelContext to document.modelContext).Review: Audit your siteQuestion 4 of 4
Which lesson in the course does the audit NOT check automatically?
Choose an answer. The explanation appears after you try.