Commands #
cat.commands.register('lab.analyze', {'pt-BR': 'Analisar', en: 'Analyze'}, async args => {
await cat.storage.set('lastAnalysis', args);
return {ok: true};
});
const run = await cat.commands.execute('lab.analyze', {origin: 'tab'});IDs follow [a-z][a-z0-9_.-]{1,79} and must be unique. cat.commands.execute starts another command of the same extension and returns the run identifier. Requires commands.
Message menus #
cat.ui.menu.register({
id: 'lab.menu',
title: {'pt-BR': 'Analisar com o laboratório', en: 'Analyze with the lab'},
command: 'lab.analyze',
contexts: ['request', 'response']
});Menu actions receive the raw request and response, the url and the source. Requires ui.menu, commands and, for HTTP content, traffic.read.
Native tabs #
cat.ui.tab.register({
id: 'lab.panel',
title: {'pt-BR': 'Laboratório', en: 'Laboratory'},
components: [
{type: 'text', text: {'pt-BR': 'Pronto para analisar.', en: 'Ready to analyze.'}},
{type: 'field', id: 'note', label: {'pt-BR': 'Nota', en: 'Note'}},
{type: 'button', label: {'pt-BR': 'Salvar nota', en: 'Save note'}, command: 'lab.save'}
]
});
cat.commands.register('lab.save', {'pt-BR': 'Salvar nota', en: 'Save note'}, async args => {
await cat.storage.set('note', args.note || '');
cat.ui.update('lab.panel', [{type: 'text', text: {'pt-BR': 'Nota salva.', en: 'Note saved.'}}]);
});Field values are sent as arguments to the button command. Requires ui.tab.
Components #
Components are rendered natively, with no visual HTML or JavaScript.
| Type | Fields | Since |
|---|---|---|
text | text | v1 |
button | label, command, args | v1 |
field and 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 with title, detail and time | SDK 1.2 |
chart | label, values with label and value | SDK 1.2 |
graph | label, nodes and edges with from and to | SDK 1.2 |
Human-facing labels need pt-BR and en. Technical fields and identifiers stay as they are.
Storage #
await cat.storage.set('counter', 1);
const counter = await cat.storage.get('counter');
await cat.storage.delete('counter');JSON data is kept per extension, with a 10 MiB quota and up to 128 KiB per value. The app uses a versioned database with transactions, and data survives updates. Requires storage.
Settings #
const laboratory = cat.settings.get('laboratory');cat.settings.get reads the values from settings, validated by the settingsSchema of the manifest.
Credentials #
Register credentials on the extension screen and reference only their identifier in code or forms. They are protected by Android Keystore, added by the host as Bearer and never included in exports.
Findings #
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: 'public-cache:' + 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}
});
});| Field | Values |
|---|---|
severity | info, low, medium, high, critical |
confidence | observed, confirmed, hypothesis |
status | open, resolved, false_positive |
The fingerprint consolidates repeated findings and preserves the status chosen by the user. Requires findings and, in this example, traffic.read.
Exporting with .catdata #
A .catdata file contains selected data and findings. Evidence is optional; sensitive headers, bodies and URL parameters are redacted on export, and credentials are never exported. Importing validates the format and requires the matching extension to be installed.