Passar para o conteúdo principal

Axur Feeds - ServiceNow

Como substituir a integração nativa por um Feed (Push ou Pull) e continuar criando incidentes no ServiceNow no mesmo formato de antes.

Visão geral

A integração nativa enviava os eventos detection.opened diretamente para uma tabela do ServiceNow. Com os Feeds, você escolhe quais tickets serão entregues, e um pequeno script no ServiceNow transforma cada ticket em um registro de Incident.

Figura 1: Os dois modos de entrega. Ambos usam o mesmo JSON e o mesmo script de mapeamento.

  • Push (Webhook 2.0): quase em tempo real

    • A Plataforma Axur envia os tickets para um endpoint que você cria no ServiceNow. É a opção mais próxima da integração nativa, mas sua instância precisa aceitar chamadas de entrada.

  • Pull (polling): mais fácil de aprovar

    • O ServiceNow consulta o feed a cada X minutos. O tráfego é somente de saída, e a latência é igual ao intervalo do agendamento.

Pré-requisitos

Um usuário admin no ServiceNow (ou web_service_admin para Push), um usuário manager na Plataforma Axur para criar o feed e uma API Key da Plataforma Axur (para Pull).


Criar o feed na Plataforma Axur

  1. Acesse one.axur.com/api-integrations → seção Feeds → Add feed → Skip templates.

  2. Escolha o tipo Tickets.

  3. Escolha o modo de entrega: Pull ou Push. Para Push, informe a URL do endpoint que você vai criar na etapa 6.

  4. Em Event date, selecione First detection (ticket.creation.date). Cada ticket chega apenas uma vez, assim como o detection.opened da integração nativa.

  5. Em Feed parameters, deixe em branco para receber tudo ou aplique um filtro, por exemplo: type=phishing,fake-social-media-profile.

  6. Defina um título, por exemplo “ServiceNow – Incidents”, e salve. Anote o ID/URL do feed.

Figura 2: Configuração sugerida do feed (ilustração; a tela real pode ser diferente).

Também quer receber redetecções?

Use Any detection (open.date). O script abaixo atualiza o incidente existente pelo ticketKey (correlation_id), então nenhum incidente é duplicado.


Mapeamento de campos

Cada item em collectionData.tickets[] vira um registro na tabela Incident. As cores mostram de onde vem cada informação:


Tabela de mapeamento

Campo no ServiceNow

Origem no JSON

Observações

state

fixo 1

Definido apenas na criação. Atualizações não reabrem o incidente.

correlation_id

ticket.ticketKey

Usado para evitar duplicidades.

correlation_display

ticket.ticketKey

Igual à integração nativa.

short_description

Workspace - Tipo - customerKey - reference (ticketKey)

Mesmo formato da integração nativa. Limitado a 160 caracteres: apenas a URL é encurtada (com ...).

description

Template nativo (Figura 3b)

Link: https://one.axur.com/brand-protection/tickets/{ticketKey}

Atenção às chaves com ponto

Campos como open.date e creation.date têm um ponto no nome. Em JavaScript, use colchetes: d['open.date'], e não d.open.date.


Script Include: o mapeador

Este script monta os campos e cria ou atualiza o incidente. Você o cria uma única vez, e ele é usado tanto pelo Push quanto pelo Pull.

  1. No ServiceNow, acesse System Definition › Script Includes → New.

  2. Name: AxurFeedMapper (precisa ser exatamente este nome).

  3. Cole o código abaixo no campo Script e salve.

Figura 4: Criação do Script Include (ilustração).

TYPE_LABELS e WORKSPACES contêm os rótulos oficiais usados pela Plataforma Axur, para que o texto fique igual ao da integração nativa. Tipos que não estão na lista usam Axur Surface e o slug original como padrão, então nada quebra.

var AxurFeedMapper = {
TABLE: 'incident',
TICKET_URL: 'https://one.axur.com/brand-protection/tickets/',
SHORT_DESCRIPTION_LIMIT: 160,
DEFAULT_WORKSPACE: 'Axur Surface',

WORKSPACES: {
'digital-fraud': 'Digital Fraud',
'online-piracy': 'Online Piracy',
'data-leakage': 'Data Leakage',
'deep-dark-web': 'Deep & Dark Web',
'executives-vips': 'Executives & VIPs'
},

TYPE_LABELS: {
'phishing': { workspace: 'digital-fraud', name: 'Phishing' },
'paid-search': { workspace: 'digital-fraud', name: 'Brand use in paid search' },
'malware': { workspace: 'digital-fraud', name: 'Malware' },
'fake-social-media-profile': { workspace: 'digital-fraud', name: 'Fake social media profile' },
'similar-domain-name': { workspace: 'digital-fraud', name: 'Similar domain name' },
'fake-mobile-app': { workspace: 'digital-fraud', name: 'Fake mobile app' },
'fraudulent-brand-use': { workspace: 'digital-fraud', name: 'Fraudulent brand use' },
'unauthorized-sale': { workspace: 'online-piracy', name: 'Counterfeit or irregular sale' },
'unauthorized-distribution': { workspace: 'online-piracy', name: 'Content piracy' },
'corporate-credential-leak': { workspace: 'data-leakage', name: 'Corporate credential leak' },
'code-secret-leak': { workspace: 'data-leakage', name: 'Code secret leak' },
'database-exposure': { workspace: 'data-leakage', name: 'Database exposure' },
'document-leak': { workspace: 'data-leakage', name: 'Document leak' },
'other-sensitive-data': { workspace: 'data-leakage', name: 'Other sensitive data' },
'infostealer-credential': { workspace: 'data-leakage', name: 'Infostealer credentials' },
'dw-activity': { workspace: 'deep-dark-web', name: 'Deep & Dark Web activity' },
'data-exposure-website': { workspace: 'deep-dark-web', name: 'Data exposure in website' },
'data-sale-website': { workspace: 'deep-dark-web', name: 'Data sale in website' },
'fraud-tool-scheme-website': { workspace: 'deep-dark-web', name: 'Fraud tool or scheme in website' },
'suspicious-activity-website': { workspace: 'deep-dark-web', name: 'Suspicious activity in website' },
'data-exposure-message': { workspace: 'deep-dark-web', name: 'Data exposure in messaging app' },
'data-sale-message': { workspace: 'deep-dark-web', name: 'Data sale in messaging app' },
'fraud-tool-scheme-message': { workspace: 'deep-dark-web', name: 'Fraud tool or scheme in messaging app' },
'suspicious-activity-message': { workspace: 'deep-dark-web', name: 'Suspicious activity in messaging app' },
'infrastructure-exposure': { workspace: 'deep-dark-web', name: 'Infrastructure exposure' },
'ransomware-attack': { workspace: 'deep-dark-web', name: 'Ransomware attack alert' },
'executive-fake-social-media-profile': { workspace: 'executives-vips', name: 'Fake social media profile' },
'executive-personalinfo-leak': { workspace: 'executives-vips', name: 'Personal information leak' },
'executive-credential-leak': { workspace: 'executives-vips', name: 'Credential leak' },
'executive-mobile-phone': { workspace: 'executives-vips', name: 'Phone number leak' },
'executive-card-leak': { workspace: 'executives-vips', name: 'Credit card leak' }
},

buildFields: function (item) {
var t = item.ticket || {};
var d = item.detection || {};
var ticketKey = t.ticketKey;
var type = d.type || '';
var label = this.TYPE_LABELS[type] || { workspace: '', name: type };
var workspace = this.WORKSPACES[label.workspace] || this.DEFAULT_WORKSPACE;
var threatUrl = t.reference || '';

var prefix = workspace + ' - ' + label.name + ' - ' + t.customerKey + ' - ';
var suffix = ' (' + ticketKey + ')';
var shortDescription = prefix + this.fitReference(threatUrl, prefix.length + suffix.length) + suffix;

var description = [
'Axur - one.axur.com',
'',
'New threat detected (' + this.TICKET_URL + ticketKey + ')',
'',
'Ticket Key: ' + ticketKey,
this.assetsLine(d.assets || []),
'Ticket Type: ' + label.name + ' (' + type + ')',
'Threat URL: ' + threatUrl,
'Detection Date: ' + this.formatDate(d['open.date'] || t['creation.date'])
].join('\n');

return { ticketKey: ticketKey, shortDescription: shortDescription, description: description };
},

fitReference: function (reference, usedLength) {
var available = this.SHORT_DESCRIPTION_LIMIT - usedLength;
if (reference.length <= available) return reference;
return reference.substring(0, Math.max(available - 3, 0)) + '...';
},

assetsLine: function (assets) {
return assets.length === 1 ? 'Asset: ' + assets[0] : 'Assets: (' + assets.join(', ') + ')';
},

formatDate: function (iso) {
if (!iso) return '';
return iso.substring(8, 10) + '/' + iso.substring(5, 7) + '/' + iso.substring(0, 4);
},

upsert: function (item) {
var f = this.buildFields(item);
if (!f.ticketKey) return;

var gr = new GlideRecord(this.TABLE);
gr.addQuery('correlation_id', f.ticketKey);
gr.query();
var exists = gr.next();
if (!exists) {
gr.initialize();
gr.state = 1;
}
gr.correlation_id = f.ticketKey;
gr.correlation_display = f.ticketKey;
gr.short_description = f.shortDescription;
gr.description = f.description;
if (exists) { gr.update(); } else { gr.insert(); }
},

processPayload: function (payload) {
var tickets = (payload && payload.collectionData && payload.collectionData.tickets) || [];
for (var i = 0; i < tickets.length; i++) {
this.upsert(tickets[i]);
}
return tickets.length;
}
};

Outra tabela?

Se os registros não devem ir para incident, altere o valor de TABLE. A tabela precisa ter os campos correlation_id, short_description e description.


Opção A: Pull com um Scheduled Job

Escolha esta opção se a sua instância não pode receber chamadas externas.

Armazenar a URL e a API Key

  1. Acesse sys_properties.list (digite no filtro de navegação) → New.

  2. Crie axur.feed.url com a URL do feed, por exemplo: https://api.axur.com/gateway/1.0/api/integration-feed/feeds/feed/<ID>.

  3. Crie axur.feed.api_key com a API Key da Plataforma Axur e restrinja os Read roles a admin.

Segurança

Nunca escreva a API Key diretamente no script. Se o seu time de segurança preferir, use Connection & Credential Aliases em vez da system property.

Criar o agendamento

  1. Acesse System Definition › Scheduled Jobs → New → Automatically run a script of your choosing.

  2. Name: Axur Feed → Incident. Run: Periodically. Repeat interval: 00:05:00 (5 min).

  3. Cole o script abaixo e salve.

Figura 5: Scheduled Job executando a cada 5 minutos (ilustração).

var FEED_URL = gs.getProperty('axur.feed.url');
var API_KEY = gs.getProperty('axur.feed.api_key');
var MAX_PAGES = 20;

for (var page = 0; page < MAX_PAGES; page++) {
var request = new sn_ws.RESTMessageV2();
request.setEndpoint(FEED_URL);
request.setHttpMethod('GET');
request.setRequestHeader('Authorization', 'Bearer ' + API_KEY);
request.setRequestHeader('Accept', 'application/json');

var response = request.execute();
if (response.getStatusCode() != 200) {
gs.error('Axur feed: HTTP ' + response.getStatusCode() + ' - ' + response.getBody());
break;
}

var processed = AxurFeedMapper.processPayload(JSON.parse(response.getBody()));
if (processed === 0) break;
}

Cada chamada ao feed avança o ponteiro e retorna a próxima página. O loop continua até que uma página volte vazia, com limite de 20 páginas por execução para que o job nunca trave. Consulte a documentação da API para ver o formato do header de autenticação.


Opção B: Push com uma Scripted REST API

Escolha esta opção para receber os tickets quase em tempo real. Sua instância precisa aceitar chamadas de entrada vindas da Plataforma Axur.

  1. Acesse System Web Services › Scripted Web Services › Scripted REST APIs → New.

  2. Name: Axur Feed, API ID: axur_feed. Salve.

  3. Na aba Resources → New: Name tickets, HTTP method POST, Relative path /tickets.

  4. Cole o script abaixo no resource e salve.

  5. Copie o Resource path (por exemplo: https://<instance>.service-now.com/api/<scope>/axur_feed/tickets) e informe-o como endpoint do feed Push na Plataforma Axur.

Figura 6: Resource POST que recebe o Webhook 2.0 (ilustração).

(function process(request, response) {
var processed = AxurFeedMapper.processPayload(request.body.data);
response.setStatus(200);
response.setBody({ processed: processed });
})(request, response);

Autenticação do endpoint

Por padrão, o resource exige autenticação do ServiceNow (Requires authentication). Configure-a de acordo com as opções de autenticação disponíveis no feed Push da Plataforma Axur (por exemplo, um usuário de integração dedicado que só pode gravar na tabela Incident). Não desative a autenticação.


Testar e validar

  1. Teste sem avançar o ponteiro: no Pull, adicione ?dry-run=true ao final de axur.feed.url. Você recebe sempre os mesmos tickets e nada se perde.

  2. Execute o job manualmente: abra o Scheduled Job → Execute Now.

  3. Confira os incidentes em Incident › All, filtrando por Correlation ID não vazio.

  4. Execute novamente. Graças ao upsert, o número de incidentes não deve aumentar.

  5. Verifique se há erros em System Logs › Errors (mensagens que começam com Axur feed:).

  6. Tudo certo? Remova o ?dry-run=true para que o ponteiro comece a avançar.

Dica

Valide em uma instância de subprodução ou de desenvolvimento antes de ativar em produção.


Limitações e checklist

Integração nativa

Com Feeds

detection.opened cria o registro

✅ Coberto (data do evento First detection ou Any detection)

Campos short_description, description, correlation_display, state

✅ Mesmo formato, montado pelo script

Somente novas detecções

✅ E mais: filtros por tipo, incidente, takedown, qualquer atualização…


Checklist de go-live

  • Feed criado na Plataforma Axur com a data do evento e os filtros corretos

  • Script Include AxurFeedMapper criado

  • Tabela TYPE_LABELS atualizada com os tipos que você usa

  • Pull: properties axur.feed.url e axur.feed.api_key criadas (com leitura restrita)

  • Pull: Scheduled Job ativo · Push: endpoint cadastrado no feed

  • Testado com ?dry-run=true, sem duplicidades

  • ?dry-run=true removido


Respondeu à sua pergunta?