Comandos #
cat.commands.register('lab.analisar', {'pt-BR': 'Analisar', en: 'Analyze'}, async args => {
await cat.storage.set('ultimaAnalise', args);
return {ok: true};
});
const execucao = await cat.commands.execute('lab.analisar', {origem: 'aba'});IDs seguem [a-z][a-z0-9_.-]{1,79} e não se repetem. cat.commands.execute inicia outro comando da mesma extensão e devolve o identificador da execução. Exige commands.
Menus de mensagem #
cat.ui.menu.register({
id: 'lab.menu',
title: {'pt-BR': 'Analisar com o laboratório', en: 'Analyze with the lab'},
command: 'lab.analisar',
contexts: ['request', 'response']
});Ações de menu recebem a request e a response brutas, a url e o source. Exige ui.menu, commands e, para conteúdo HTTP, traffic.read.
Abas nativas #
cat.ui.tab.register({
id: 'lab.painel',
title: {'pt-BR': 'Laboratório', en: 'Laboratory'},
components: [
{type: 'text', text: {'pt-BR': 'Pronto para analisar.', en: 'Ready to analyze.'}},
{type: 'field', id: 'nota', label: {'pt-BR': 'Nota', en: 'Note'}},
{type: 'button', label: {'pt-BR': 'Salvar nota', en: 'Save note'}, command: 'lab.salvar'}
]
});
cat.commands.register('lab.salvar', {'pt-BR': 'Salvar nota', en: 'Save note'}, async args => {
await cat.storage.set('nota', args.nota || '');
cat.ui.update('lab.painel', [{type: 'text', text: {'pt-BR': 'Nota salva.', en: 'Note saved.'}}]);
});Os valores dos campos são enviados como argumentos do comando do botão. Exige ui.tab.
Componentes #
Os componentes são renderizados de forma nativa, sem HTML ou JavaScript visual.
| Tipo | Campos | Desde |
|---|---|---|
text | text | v1 |
button | label, command, args | v1 |
field e filter | id, label, value | v1 |
list | items | v1 |
table | columns, rows | v1 |
http | message | v1 |
progress | value | v1 |
json | label, value | SDK 1.2 |
diff | label, before, after | SDK 1.2 |
timeline | label, items com title, detail e time | SDK 1.2 |
chart | label, values com label e value | SDK 1.2 |
graph | label, nodes e edges com from e to | SDK 1.2 |
Textos humanos precisam de pt-BR e en. Campos técnicos e identificadores permanecem como estão.
Armazenamento #
await cat.storage.set('contador', 1);
const contador = await cat.storage.get('contador');
await cat.storage.delete('contador');Os dados JSON ficam separados por extensão, com quota de 10 MiB e até 128 KiB por valor. O aplicativo usa um banco versionado com transações, e os dados sobrevivem às atualizações. Exige storage.
Configurações #
const laboratorio = cat.settings.get('laboratory');cat.settings.get lê os valores de settings, validados pelo settingsSchema do manifesto.
Credenciais #
Cadastre credenciais na tela da extensão e informe apenas o identificador no código ou no formulário. Elas ficam protegidas pelo Android Keystore, são adicionadas pelo host como Bearer e nunca entram nas exportações.
Achados #
cat.events.on('http.response', async message => {
const cache = message.headers.find(h => h.name.toLowerCase() === 'cache-control');
if (!cache || !cache.value.includes('public')) return;
await cat.findings.add({
fingerprint: 'cache-publico:' + cat.http.parseUrl(message.url).pathname,
title: {'pt-BR': 'Resposta com cache público', en: 'Response with public cache'},
description: {'pt-BR': 'A resposta declara Cache-Control público.', en: 'The response declares public Cache-Control.'},
severity: 'low',
confidence: 'observed',
url: message.url,
evidence: {request: message.request, response: message}
});
});| Campo | Valores |
|---|---|
severity | info, low, medium, high, critical |
confidence | observed, confirmed, hypothesis |
status | open, resolved, false_positive |
O fingerprint consolida achados repetidos e preserva o estado escolhido pelo usuário. Exige findings e, neste exemplo, traffic.read.
Exportar com .catdata #
Um .catdata contém dados e achados selecionados. Evidências são opcionais; headers sensíveis, corpos e parâmetros de URL são ocultados na exportação, e credenciais nunca são exportadas. A importação valida o formato e exige que a extensão correspondente já esteja instalada.