Ir al contenido principal

Axur Feeds - ServiceNow

Cómo reemplazar la integración nativa por un Feed (Push o Pull) y seguir creando incidentes en ServiceNow con el mismo formato de antes.

Descripción general

La integración nativa enviaba los eventos detection.opened directamente a una tabla de ServiceNow. Con los Feeds, usted elige qué tickets se entregan, y un pequeño script en ServiceNow convierte cada ticket en un registro de Incident.

Figura 1: Los dos modos de entrega. Ambos usan el mismo JSON y el mismo script de mapeo.

  • Push (Webhook 2.0): casi en tiempo real

    • La Plataforma Axur envía los tickets a un endpoint que usted crea en ServiceNow. Es la opción más parecida a la integración nativa, pero su instancia debe aceptar llamadas entrantes.

  • Pull (polling): más fácil de aprobar

    • ServiceNow consulta el feed cada X minutos. El tráfico es solo saliente, y la latencia es igual al intervalo de la programación.

Requisitos previos

Un usuario admin en ServiceNow (o web_service_admin para Push), un usuario manager en la Plataforma Axur para crear el feed y una API Key de la Plataforma Axur (para Pull).


Crear el feed en la Plataforma Axur

  1. Vaya a one.axur.com/api-integrations → sección Feeds → Add feed → Skip templates.

  2. Elija el tipo Tickets.

  3. Elija el modo de entrega: Pull o Push. Para Push, ingrese la URL del endpoint que creará en el paso 6.

  4. En Event date, seleccione First detection (ticket.creation.date). Cada ticket llega una sola vez, igual que el detection.opened de la integración nativa.

  5. En Feed parameters, déjelo en blanco para recibir todo o aplique un filtro, por ejemplo: type=phishing,fake-social-media-profile.

  6. Defina un título, por ejemplo “ServiceNow – Incidents”, y guarde. Anote el ID/URL del feed.

Figura 2: Configuración sugerida del feed (ilustración; la pantalla real puede ser diferente).

¿También quiere recibir redetecciones?

Use Any detection (open.date). El script de abajo actualiza el incidente existente por ticketKey (correlation_id), así que ningún incidente se duplica.


Mapeo de campos

Cada elemento de collectionData.tickets[] se convierte en un registro de la tabla Incident. Los colores muestran de dónde viene cada dato:


Tabla de mapeo

Campo en ServiceNow

Origen en el JSON

Observaciones

state

fijo 1

Se define solo al crear. Las actualizaciones no reabren el incidente.

correlation_id

ticket.ticketKey

Se usa para evitar duplicados.

correlation_display

ticket.ticketKey

Igual que en la integración nativa.

short_description

Workspace - Tipo - customerKey - reference (ticketKey)

Mismo formato que la integración nativa. Limitado a 160 caracteres: solo se acorta la URL (con ...).

description

Plantilla nativa (Figura 3b)

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

Cuidado con las claves con punto

Campos como open.date y creation.date tienen un punto en el nombre. En JavaScript, use corchetes: d['open.date'], no d.open.date.


Script Include: el mapeador

Este script arma los campos y crea o actualiza el incidente. Se crea una sola vez y lo usan tanto Push como Pull.

  1. En ServiceNow, vaya a System Definition › Script Includes → New.

  2. Name: AxurFeedMapper (debe ser exactamente este nombre).

  3. Pegue el código de abajo en el campo Script y guarde.

Figura 4: Creación del Script Include (ilustración).

TYPE_LABELS y WORKSPACES contienen las etiquetas oficiales que usa la Plataforma Axur, para que el texto coincida con el de la integración nativa. Los tipos que no están en la lista usan Axur Surface y el slug original por defecto, así que nada se rompe.

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;
}
};

¿Otra tabla?

Si los registros no deben ir a incident, cambie el valor de TABLE. La tabla debe tener los campos correlation_id, short_description y description.


Opción A: Pull con un Scheduled Job

Elija esta opción si su instancia no puede recibir llamadas externas.

Guardar la URL y la API Key

  1. Vaya a sys_properties.list (escríbalo en el filtro de navegación) → New.

  2. Cree axur.feed.url con la URL del feed, por ejemplo: https://api.axur.com/gateway/1.0/api/integration-feed/feeds/feed/<ID>.

  3. Cree axur.feed.api_key con la API Key de la Plataforma Axur y restrinja los Read roles a admin.

Seguridad

Nunca escriba la API Key directamente en el script. Si su equipo de seguridad lo prefiere, use Connection & Credential Aliases en lugar de la system property.

Crear la programación

  1. Vaya a 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. Pegue el script de abajo y guarde.

Figura 5: Scheduled Job que se ejecuta cada 5 minutos (ilustración).

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 llamada al feed avanza el puntero y devuelve la página siguiente. El loop continúa hasta que una página vuelve vacía, con un límite de 20 páginas por ejecución para que el job nunca se cuelgue. Consulte la documentación de la API para ver el formato del header de autenticación.


Opción B: Push con una Scripted REST API

Elija esta opción para recibir los tickets casi en tiempo real. Su instancia debe aceptar llamadas entrantes desde la Plataforma Axur.

  1. Vaya a System Web Services › Scripted Web Services › Scripted REST APIs → New.

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

  3. En la pestaña Resources → New: Name tickets, HTTP method POST, Relative path /tickets.

  4. Pegue el script de abajo en el resource y guarde.

  5. Copie el Resource path (por ejemplo: https://<instance>.service-now.com/api/<scope>/axur_feed/tickets) e ingréselo como endpoint del feed Push en la Plataforma Axur.

Figura 6: Resource POST que recibe el Webhook 2.0 (ilustración).

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

Autenticación del endpoint

Por defecto, el resource exige autenticación de ServiceNow (Requires authentication). Configúrela según las opciones de autenticación disponibles en el feed Push de la Plataforma Axur (por ejemplo, un usuario de integración dedicado que solo pueda escribir en la tabla Incident). No desactive la autenticación.


Probar y validar

  1. Pruebe sin avanzar el puntero: en Pull, agregue ?dry-run=true al final de axur.feed.url. Recibirá siempre los mismos tickets y no se pierde nada.

  2. Ejecute el job manualmente: abra el Scheduled Job → Execute Now.

  3. Revise los incidentes en Incident › All, filtrando por Correlation ID no vacío.

  4. Ejecútelo de nuevo. Gracias al upsert, la cantidad de incidentes no debería aumentar.

  5. Busque errores en System Logs › Errors (mensajes que empiezan con Axur feed:).

  6. ¿Todo bien? Quite el ?dry-run=true para que el puntero empiece a avanzar.

Consejo

Valide en una instancia de subproducción o de desarrollo antes de activarlo en producción.


Limitaciones y checklist

Integración nativa

Con Feeds

detection.opened crea el registro

✅ Cubierto (fecha del evento First detection o Any detection)

Campos short_description, description, correlation_display, state

✅ Mismo formato, armado por el script

Solo nuevas detecciones

✅ Y más: filtros por tipo, incidente, takedown, cualquier actualización…


Checklist de go-live

  • Feed creado en la Plataforma Axur con la fecha del evento y los filtros correctos

  • Script Include AxurFeedMapper creado

  • Tabla TYPE_LABELS actualizada con los tipos que usa

  • Pull: properties axur.feed.url y axur.feed.api_key creadas (con lectura restringida)

  • Pull: Scheduled Job activo · Push: endpoint registrado en el feed

  • Probado con ?dry-run=true, sin duplicados

  • ?dry-run=true quitado


¿Ha quedado contestada tu pregunta?