はじめに
Vulnerability Response(VR)を使い込んでくると「自社の脆弱性スキャナーや内製ツールの情報をCVEレコードに直接反映したい」というニーズが出てきます。
そのとき気になるのが 「自前で登録した値は、後からNVDが走ったときに上書きされるのか?」 という点です。
本記事では実インスタンス(PDI)のテーブル定義とCVDUtilのスクリプトを直接確認した結果をもとに、この動作を解説します。
CVDB(Central Vulnerability Database)とは
CVDBはServiceNow Store配布のプラグイン(アプリ名: Central Vulnerability Database)です。
VRが依存するモジュールで、複数のセキュリティソース(NVD / CISA KEV / EPSS / Qualys / Wiz ほか)から投入されたCVEデータを 単一の統合CVEレコード に名寄せ・エンリッチします。
VRでいう「CVEレコード」の実体は sn_vul_nvd_entry テーブルです(CVDBが有効な環境でも変わりません)。
実機で確認したCVDB関連テーブル
sys_db_object を nameLIKEcvd で照会すると以下5テーブルが確認できます。
| テーブル | ラベル | 役割 |
|---|---|---|
sn_sec_cvd_source_config |
Source configuration | ソースレベル優先度の定義 |
sn_sec_cvd_source_field_priority |
Source field priority | フィールド単位の優先度上書き |
sn_sec_cvd_field_update_history |
Field update history | フィールドごとの最終更新ソース記録(provenance) |
sn_sec_cvd_m2m_entry_cvd |
Vulnerability CVD | ソースレコード ↔ 統合CVEレコードのM2M |
sn_vul_manual_ingestion_cvd_attributes |
Manual Ingestion CVD Attributes | 手動投入ソースの生データ保持用 |
sn_sec_cvd_source_config の実レコードは次の2件でした。
| ソース名 | priority | cvd_table |
|---|---|---|
| National Vulnerability Database Integration | 10 | sn_vul_nvd_entry |
| Manual Ingestion | 1000 | sn_vul_nvd_entry |
数値が小さいほど優先度が高い。NVD(10)はManual Ingestion(1000)より強い。
優先度判定の実装 — CVDUtil._shouldUpdateField
CVDBへの書き込みはすべて Script Include sn_sec_cvd.CVDUtil(publicアクセス)を経由します。
フィールド更新可否を決めるコアロジックは _shouldUpdateField です(実機から抜粋)。
_shouldUpdateField: function(cvdGr, fieldName, incomingPriority, getLastPriorityFn, fieldSourcesMap) {
// ① フィールドが空なら無条件で更新
if (gs.nil(cvdGr[fieldName])) return true;
var lastUpdateSource = fieldSourcesMap.hasOwnProperty(fieldName)
? fieldSourcesMap[fieldName]
: this._getSource(cvdGr);
// ② 前回の書き込み元が不明、または自分自身なら更新
if (lastUpdateSource == "" || lastUpdateSource == this.source) return true;
// ③ incoming の priority ≤ 前回書き込み元の priority のときだけ更新
return incomingPriority <= getLastPriorityFn(lastUpdateSource);
}
判定の流れをまとめると次の通りです。
フィールドが空?
→ YES: 無条件で書く
→ NO:
前回の書き込み元(sn_sec_cvd_field_update_history より)を特定
前回書き込み元が不明 or 自分自身?
→ YES: 書く
→ NO: 自分の priority ≤ 前回書き込み元の priority?
→ YES(同値含む): 上書き
→ NO: スキップ(既存値を保持)
前回の書き込み元は
sn_sec_cvd_field_update_history.field_sources(JSON)に記録されています。CVDUtilを通さず直接sn_vul_nvd_entryに書くと、この履歴が残らないため優先度管理が完全に無効化されます。
自前Import SetでCVEを登録したあと、NVDに上書きされるか
シナリオ: 自前ソースを priority 500 で sn_sec_cvd_source_config に定義し、CVE-2026-XXXX を登録。その後、NVD連携(priority 10)が同一CVEを処理。
| フィールド | 状態 | 判定 | 結果 |
|---|---|---|---|
| 空フィールド | — | ルール① | NVDが埋める |
| 自前が書いた値 | 最終更新元: 自前(500) | ルール③: 10 ≤ 500 → true | NVDが上書き |
→ はい、NVDで上書きされます。
なお、自前ImportでCVDUtilを通さなかった場合も全フィールドがNVDに無条件で上書きされます。
自前の値をNVDから守りたい方法
方針A: フィールドレベル優先度で特定フィールドだけ守る(推奨)
sn_sec_cvd_source_field_priority に自ソース × 守りたいフィールドを登録し、priority を10未満にします(例: 5)。そのフィールドだけは自前が勝ち、他はNVDに任せられます。CVSSスコアはNVDに、社内のexploit情報は自前に、という使い分けが可能です。
方針B: ソースレベルでNVDより強い priority を設定する
sn_sec_cvd_source_config の priority を 10 未満(例: 1)にします。全フィールドで自前が勝ちますが、NVDによるエンリッチがほぼ効かなくなります。通常は非推奨。
方針C: ソース固有テーブルに切り出す(OOBのManual Ingestionの設計)
OOBの Manual Ingestion は priority 1000 でCVD本体では常にNVDに負ける設定ですが、modified severity や summary といった独自フィールドをソース専用テーブル sn_vul_manual_ingestion_cvd_attributes に保持します。CVD本体レコードで負けてもソース固有データは失われないため、「NVDには勝てないが独自情報も消えない」状態を維持できます。自前ソースでも同様に source_table を定義することで同じ設計にできます。
Import SetでCVDUtilを呼ぶコード例(変換マップ onComplete)
// onComplete Transform Script
(function runTransformScript(source, map, log, target) {
// ソースとして登録した sn_sec_int_integration の sys_id を指定
var integrationGr = new GlideRecord('sn_sec_int_integration');
integrationGr.get('<自ソースのsys_id>');
var util = new sn_sec_cvd.CVDUtil(integrationGr);
// Import Set の各行を処理
var row = new GlideRecord(import_set.getValue("table_name"));
row.addQuery("sys_import_set", import_set.getUniqueValue());
row.query();
while (row.next()) {
util.createOrUpdateCVDRecord({
id: row.getValue("u_cve_id"), // 必須。CVE ID
summary: row.getValue("u_summary"),
// cvss_score, exploit, cweList, referenceList, softwareList なども設定可
});
}
})(source, map, log, target);
createOrUpdateCVDRecordはid(CVE ID)でコアレス判定を行うため、Transform MapにcoalesceフィールドマップやTarget Field設定は不要です。
まとめ
| 確認事項 | 答え |
|---|---|
| 自前Import SetのCVEはNVDで上書きされるか | される(NVD priority 10 < 自前 priority 500 のため) CVDUtilを通さなかった場合も全フィールドがNVDに無条件で上書きされる |
| タイミング | NVD連携の次回実行時(デルタ取り込み or フル取り込み) |
| 防ぎ方 | フィールドレベル優先度 or ソース固有テーブルへの切り出し |
| CVDUtilを通さず直接書いた場合 | 優先度管理・履歴が無効化される。非推奨 |
CVDBの優先度設計は「どのフィールドをどのソースに任せるか」を明示的に宣言するものです。自前ソースを追加する際は、NVDとの役割分担を sn_sec_cvd_source_config / sn_sec_cvd_source_field_priority で意図的に設計することをお勧めします。