Skip to content

Instantly share code, notes, and snippets.

@W-Yoshida
Last active February 12, 2020 06:09
Show Gist options
  • Save W-Yoshida/6235cc3d4aeb0cba5e580ce4afaac2ee to your computer and use it in GitHub Desktop.
Save W-Yoshida/6235cc3d4aeb0cba5e580ce4afaac2ee to your computer and use it in GitHub Desktop.
MyJVNから脆弱性情報を取得してスプレッドシートに追記するGAS

MyJVNから脆弱性情報を取得してスプレッドシートに追記するGAS

概要

  • MyJVNで前日に更新された脆弱性の一覧を取得し、スプレッドシートに追記する
  • 情報更新による再掲が多いため、既に取得済みのJVN-IDの場合はスキップするように重複チェックを行う

[参考] MyJVN APIリファレンス

https://jvndb.jvn.jp/apis/index.html

補足

  • [getVulnOverviewList]では「発見日」でフィルタできないので、「更新日」で条件を指定。
    ※「発見日」は[getVulnDetailInfo]で個別に取得する必要があるため、
     API実行回数を減らすために[getVulnOverviewList]のみで実装

  • [getVulnOverviewList]は一度に50件しか取得できないので、50件以上フィードがある場合は処理を繰り返す

  • [severity]は複数指定できないので、重要度でのフィルタはスプレッドシート上で行う

//実行メニューを作成
function onOpen() {
var ui = SpreadsheetApp.getUi();
var menu = ui.createMenu("GAS実行");
menu.addItem("脆弱性一覧取得", "getVulnOverviewListFromAPI");
menu.addToUi();
}
//MyJVN APIからフィードを取得
function getVulnOverviewListFromAPI() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var col_count = 8;
var loop_count = 0;
var isContinued = true;
var values = [];
//当月のスプレッドシート名が存在するか確認する
var today = new Date();
var yesterday = new Date();
yesterday.setDate(today.getDate() - 1);
var sheetName = Utilities.formatDate(yesterday, 'Asia/Tokyo', 'yyyyMM');
var sheet = ss.getSheetByName(sheetName);
//シートが存在しなければ2シート目に作成してヘッダーをセット(1シート目は説明用シートを想定)
if (sheet == null) {
ss.insertSheet(sheetName, 1);
sheet = ss.getActiveSheet();
//ヘッダー追加
values.push([
'更新日',
'ID',
'スコア',
'緊急度',
'タイトル',
'説明',
'影響を受ける製品',
'リンク'
]);
}
//対象シートの最終行を取得
var lastRow = sheet.getLastRow();
try
{
//名前空間を取得
var xmlns = XmlService.getNamespace('http://purl.org/rss/1.0/');
var rss = XmlService.getNamespace('http://purl.org/rss/1.0/');
var dc = XmlService.getNamespace('dc', 'http://purl.org/dc/elements/1.1/');
var dcterms = XmlService.getNamespace('dcterms', 'http://purl.org/dc/terms/');
var sec = XmlService.getNamespace('sec', 'http://jvn.jp/rss/mod_sec/3.0/');
while(isContinued){
// rangeDatePublic=n : 発見日指定なし(デフォルトは1週間以内)
// rangeDateFirstPublished=n : 発行日指定なし(デフォルトは1週間以内)
var API_URL = 'https://jvndb.jvn.jp/myjvn?method=getVulnOverviewList&feed=hnd&rangeDatePublic=n&rangeDateFirstPublished=n';
// 前日に更新された脆弱性のみを取得
var publishedStartY = 'datePublishedStartY=' + Utilities.formatDate(yesterday, 'Asia/Tokyo', 'yyyy');
var publishedStartM = 'datePublishedStartM=' + Utilities.formatDate(yesterday, 'Asia/Tokyo', 'M');
var publishedStartD = 'datePublishedStartD=' + Utilities.formatDate(yesterday, 'Asia/Tokyo', 'd');
var publishedEndY = 'datePublishedEndY=' + Utilities.formatDate(yesterday, 'Asia/Tokyo', 'yyyy');
var publishedEndM = 'datePublishedEndM=' + Utilities.formatDate(yesterday, 'Asia/Tokyo', 'M');
var publishedEndD = 'datePublishedEndD=' + Utilities.formatDate(yesterday, 'Asia/Tokyo', 'd');
var startItem = 'startItem=' + (1 + 50 * loop_count);
// 重要度でフィルタしたい場合はseverityをセット(ただし"h:重要 or c:クリティカル"といった複数指定はできない)
//var severity = 'severity=h' //h=重要(7.0~8.9)
API_URL += '&' + publishedStartY + '&' + publishedStartM + '&' + publishedStartD;
API_URL += '&' + publishedEndY + '&' + publishedEndM + '&' + publishedEndD+ '&' + startItem;
// 脆弱性概要一覧を取得(最大50行)
var xml = UrlFetchApp.fetch(API_URL).getContentText();
var document = XmlService.parse(xml);
var root = document.getRootElement();
var items = root.getChildren('item', rss);
// 該当する脆弱性が存在しない場合は処理を終了
// itemsが0件の場合(startItemが検索結果件数を超えた場合)はgetChildできないのでitemsが1件の場合のみに限定
if(items.length == 1)
{
if(items[0].getChild('title', rss).getText() == 'MyJVN 該当する脆弱性対策情報はありません。')
{
Logger.log('該当する脆弱性対策情報はありません。');
return;
}
}
for(var i = 0; i < items.length; i++) {
var value = [];
// スプレッドシート上で既出のJVNであればスキップする
if(JVN_Exists(items[i].getChild('identifier', sec).getText()))
{
continue;
}
// pubDate:更新日
// ※issueDateは発行日であり、発見日に該当する値は詳細取得APIでなければ取得できない
var pubDate = new Date(items[i].getChild('date', dc).getText());
value.push(Utilities.formatDate(pubDate, 'Asia/Tokyo', 'yyyy/MM/dd'));
// JVN識別子
value.push(items[i].getChild('identifier', sec).getText());
// 稀にCVSSが存在しないケースがあるので、その場合はscoreとseverityはブランクにする
if(items[i].getChildren('cvss', sec).length == 0)
{
value.push('');
value.push('');
}
else
{
// CVSSはv2とv3の2つあるので、v3の方を取得する
var item_cvss = items[i].getChildren('cvss', sec).filter(function(item) {
return item.getAttribute('version').getValue() == '3.0';
});
// 古い脆弱性情報はCVSS v2しかないので、v2を取得する
if(item_cvss.length == 0)
{
item_cvss = items[i].getChildren('cvss', sec).filter(function(item) {
return item.getAttribute('version').getValue() == '2.0';
});
}
value.push(item_cvss[0].getAttribute('score').getValue());
value.push(item_cvss[0].getAttribute('severity').getValue());
}
value.push(items[i].getChild('title', rss).getText());
value.push(items[i].getChild('description',rss).getText());
// 影響を受ける製品(複数存在する場合はセル内改行)
var products = '';
for(var j= 0; j < items[i].getChildren('cpe', sec).length;j++){
products = products + items[i].getChildren('cpe', sec)[j].getAttribute('product').getValue() + String.fromCharCode(10);
}
// 末尾のセル内改行を削除
if(products.slice(-1) == String.fromCharCode(10))
{
products = products.substr(0,products.length - 1);
}
value.push(products);
value.push(items[i].getChild('link', rss).getText());
values.push(value);
}
// 取得件数が50件だった場合は、残りが無なくなるまで処理を繰り返す
if(items.length == 50){
loop_count++;
}else{
isContinued = false;
}
}
// 取得したデータをスプレッドシートにセット
if(values.length > 0){
sheet.getRange(lastRow + 1, 1, values.length, col_count).setValues(values);
sheet.getRange(lastRow + 1, 1, values.length, col_count).setVerticalAlignment('top')
}
}catch(e){
Logger.log(e);
console.log(e);
}
}
//既出のJVN IDの場合はtrueを返す
function JVN_Exists(JVNID){
var ss = SpreadsheetApp.getActiveSpreadsheet();
for(i = 0; i < ss.getNumSheets();i++){
var sheet = ss.getSheets()[i] ;
var textFinder = sheet.createTextFinder(JVNID);
var results = textFinder.findAll();
if(results.length > 0)
{
return true;
}
}
return false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment