メインコンテンツにスキップ

Axur Feeds - ServiceNow

ネイティブ連携を Feed(Push または Pull)に置き換え、これまでと同じ形式で ServiceNow のインシデントを作成し続ける方法を説明します。

概要

ネイティブ連携では、detection.opened イベントが ServiceNow のテーブルに直接送信されていました。Feeds を使うと、配信するチケットを自分で選択でき、ServiceNow 側の小さなスクリプトが各チケットを Incident レコードに変換します。

図 1:2 つの配信モード。どちらも同じ JSON と同じマッピングスクリプトを使用します。

  • Push (Webhook 2.0): ほぼリアルタイム

    • Axur プラットフォームが、ServiceNow に作成したエンドポイントへチケットを送信します。ネイティブ連携に最も近い方式ですが、インスタンスがインバウンド通信を受け付ける必要があります。

  • Pull (polling): 承認を得やすい

    • ServiceNow が X 分ごとにフィードを照会します。通信はアウトバウンドのみで、遅延はスケジュール間隔と同じになります。

前提条件

ServiceNow の admin ユーザー(Push の場合は web_service_admin)、フィード作成用の Axur プラットフォームの manager ユーザー、および Axur プラットフォームの API Key(Pull の場合)が必要です。


Axur プラットフォームでフィードを作成

  1. one.axur.com/api-integrations を開き、Feeds セクション → Add feed → Skip templates の順に進みます。

  2. タイプとして Tickets を選択します。

  3. 配信モードとして Pull または Push を選択します。Push の場合は、手順 6 で作成するエンドポイントの URL を入力します。

  4. Event date で First detection(ticket.creation.date) を選択します。ネイティブ連携の detection.opened と同様に、各チケットは 1 回だけ届きます。

  5. Feed parameters は、すべて受信する場合は空欄のままにします。絞り込む場合は、例:type=phishing,fake-social-media-profile のように指定します。

  6. タイトル(例:“ServiceNow – Incidents”)を設定して保存します。フィードの ID/URL を控えておいてください。

図 2:推奨フィード設定(イメージ図。実際の画面とは異なる場合があります)。

再検出も受信したい場合

Any detection(open.date) を使用してください。以下のスクリプトは ticketKey(correlation_id)で既存のインシデントを更新するため、インシデントが重複することはありません。


フィールドマッピング

collectionData.tickets[] の各項目が Incident テーブルの 1 レコードになります。色は各情報の取得元を示しています:


マッピング表

ServiceNow のフィールド

JSON 内の取得元

備考

state

固定値 1

作成時のみ設定されます。更新時にインシデントが再オープンされることはありません。

correlation_id

ticket.ticketKey

重複を防ぐために使用します。

correlation_display

ticket.ticketKey

ネイティブ連携と同じです。

short_description

ワークスペース - タイプ - customerKey - reference (ticketKey)

ネイティブ連携と同じ形式です。上限は 160 文字で、URL のみが(... で)短縮されます。

description

ネイティブのテンプレート(図 3b)

リンク:https://one.axur.com/brand-protection/tickets/{ticketKey}

ドットを含むキーに注意

open.date や creation.date などのフィールドは、名前にドットが含まれています。JavaScript では d.open.date ではなく、角括弧を使って d['open.date'] と記述してください。


Script Include:マッパー

このスクリプトはフィールドを組み立て、インシデントを作成または更新します。作成は 1 回だけ で、Push と Pull の両方で使用されます。

  1. ServiceNow で System Definition › Script Includes → New を開きます。

  2. Name:AxurFeedMapper(必ずこの名前にしてください)。

  3. 以下のコードを Script フィールドに貼り付けて保存します。

図 4:Script Include の作成(イメージ図)。

TYPE_LABELS と WORKSPACES には Axur プラットフォームで使われている公式ラベルが含まれているため、テキストがネイティブ連携と一致します。リストにないタイプは Axur Surface と元のスラッグが使われるため、処理が失敗することはありません。

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

別のテーブルを使う場合

レコードの格納先を incident 以外にする場合は、TABLE の値を変更してください。対象テーブルには correlation_id、short_description、description フィールドが必要です。


オプション A:Scheduled Job による Pull

インスタンスが外部からの通信を 受け付けられない 場合は、このオプションを選択してください。

URL と API Key を保存する

  1. sys_properties.list(ナビゲーションフィルターに入力)→ New を開きます。

  2. axur.feed.url を作成し、フィードの URL を設定します。例:https://api.axur.com/gateway/1.0/api/integration-feed/feeds/feed/<ID>.

  3. axur.feed.api_key を作成して Axur プラットフォームの API Key を設定し、Read roles を admin に制限します。

セキュリティ

API Key をスクリプトに直接記述しないでください。セキュリティチームの方針に応じて、system property の代わりに Connection & Credential Aliases を使用することもできます。

スケジュールを作成する

  1. 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 分)。

  3. 以下のスクリプトを貼り付けて保存します。

図 5:5 分ごとに実行される Scheduled Job(イメージ図)。

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

フィードを呼び出すたびにポインターが進み、次のページが返されます。ループは空のページが返るまで続きますが、ジョブが止まらないよう 1 回の実行につき最大 20 ページに制限しています。認証ヘッダーの形式については API ドキュメント を参照してください。


オプション B:Scripted REST API による Push

チケットを ほぼリアルタイム で受信したい場合は、このオプションを選択してください。インスタンスが Axur プラットフォームからのインバウンド通信を受け付ける必要があります。

  1. System Web Services › Scripted Web Services › Scripted REST APIs → New を開きます。

  2. Name:Axur Feed、API ID:axur_feed を入力して保存します。

  3. Resources タブ → New で、Name に tickets、HTTP method に POST、Relative path に /tickets を設定します。

  4. 以下のスクリプトをリソースに貼り付けて保存します。

  5. Resource path(例:https://<instance>.service-now.com/api/<scope>/axur_feed/tickets)をコピーし、Axur プラットフォームの Push フィードのエンドポイントとして入力します。

図 6:Webhook 2.0 を受信する POST リソース(イメージ図)。

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

エンドポイントの認証

デフォルトでは、このリソースには ServiceNow の認証(Requires authentication)が必要です。Axur プラットフォームの Push フィードで利用できる認証方式に合わせて設定してください(例:Incident テーブルへの書き込み権限のみを持つ専用の連携ユーザー)。認証は無効にしないでください。


テストと検証

  1. ポインターを進めずにテスト:Pull の場合は、axur.feed.url の末尾に ?dry-run=true を追加します。毎回同じチケットが返され、データが失われることはありません。

  2. ジョブを手動で実行します:Scheduled Job を開き → Execute Now。

  3. Incident › All で、Correlation ID が空でないもので絞り込んでインシデントを確認します。

  4. もう一度実行します。upsert により、インシデントの件数は 増えないはず です。

  5. System Logs › Errors でエラーを確認します(Axur feed: で始まるメッセージ)。

  6. 問題がなければ、ポインターが進むように ?dry-run=true を削除 します。

ヒント本番環境で有効にする前に、サブ本番環境または開発環境のインスタンスで検証してください。


制限事項とチェックリスト

ネイティブ連携

Feeds の場合

detection.opened でレコードを作成

✅ 対応(イベント日付 First detection または Any detection)

short_description, description, correlation_display, state フィールド

✅ 同じ形式(スクリプトで生成)

新規検出のみ

✅ さらに:タイプ、インシデント、テイクダウン、あらゆる更新などでのフィルタリングが可能…


本番稼働前チェックリスト

  • 正しいイベント日付とフィルターで Axur プラットフォームにフィードを作成済み

  • AxurFeedMapper Script Include を作成済み

  • 使用するタイプで TYPE_LABELS テーブルを更新済み

  • Pull:axur.feed.url と axur.feed.api_key のプロパティを作成済み(読み取り制限あり)

  • Pull:Scheduled Job が有効 · Push:フィードにエンドポイントを登録済み

  • ?dry-run=true でテスト済み、重複なし

  • ?dry-run=true を削除済み

こちらの回答で解決しましたか?