【AeyeScan】Google Apps Scriptを使ったスキャンの自動再開

SaaS型Webアプリ診断ツール「AeyeScan」を運営している株式会社エーアイセキュリティラボによる、セキュリティテストの自動化、脆弱性診断の内製化、AI/機械学習などの技術情報の共有を目的とした記事です。
AeyeScanの情報はこちら https://www.aeyescan.jp
エーアイセキュリティラボの情報はこちら https://www.aeyesec.jp

[![2022-155641.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/dd3f69d5-879a-ed11-d6ac-353a85523c43.png)](https://www.aeyescan.jp)

こんにちは！株式会社エーアイセキュリティラボ　カスタマーサクセス担当　日髙です。
今回は、Google Apps Script(GAS)を使って、AeyeScanの通知メールからスキャン再開のAPIを自動で実行する仕組みを作成していきます。

# 目次
[概要](#概要)
[実現するもの](#実現するもの)
[APIキーの作成と設定](#apiキーの作成と設定)
[ステップ１（事前の時刻取得）](#ステップ１事前の時刻取得)
[ステップ２（メールの受信API）](#ステップ２メールの受信api)
[ステップ３（メールの解析）](#ステップ３メールの解析)
[ステップ４（再スキャンの実行）](#ステップ４再スキャンの実行)
[検証](#検証)
[利用上の注意](#利用上の注意)
[おわりに](#おわりに)
[参考文献](#参考文献)


# 概要
脆弱性診断ツールを利用していると、診断対象によっては、一時的にサイトからの応答が遅くなり、スキャンができない状態となることがあります。そういった場合、AeyeScanでは、メールや各種チャットツール等に通知が行われます。
今回は、このメール通知機能とAeyeScanのAPIを活用して、自動でスキャンの再開を行う仕組みを作成します。

# 実現するもの
今回は、GmailでAeyeScanの通知メール（スキャン停止時に送られる）を取得し、受信から5分以上経過していたら、スキャンを再開するAPIを実行するGASのスクリプトを作成します。

それでは早速作っていきましょう！

# APIキーの作成と設定
事前にAeyeScanのAPIを実行するため、APIキーを発行します。
APIキーはAeyeScanのメニューバーの「アカウント管理」→「APIキー管理」から作成できます。
APIキーを作成する際には書き込み・実行権限を「あり」に設定します。
作成したAPIキーとトークンは後から確認できないため、安全な場所に保管してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/1985f432-31a2-2bd0-0ffe-af0db17bb377.png)

今回作成するスクリプトは以下から閲覧可能です。
(ご自身のgoogleアカウントへのログインが必要です。)
https://script.google.com/d/15O3VKy7sZ5cWOEvRAgs2wjPU04eh7eSP4ZbprrsK9eSpLZw4J1H5ur6m/edit?usp=share_link

APIキーとトークンを以下の箇所に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/18a4394a-45e2-96e4-a156-aa3790c501b5.png)


以降スクリプトの詳細についてステップ１～４に分けて解説していきます。


# ステップ１（事前の時刻取得）
まずは、スクリプトの中で使用する時間を事前に取得しておきます。

今回は、5分おきにメールボックスを確認して、直近**10分**に届いたメールを取得し、メールの受信から**5分**以上経過していたら
スキャンの再開を実行します。
そのため、現在時刻から10分前と5分前の時間（ミリ秒）を取得しておきます。


```javascript:gas
//10分前のUNIXタイム(ミリ秒)を取得
const date = new Date(); // 現在日時を取得
const now = date.getTime();
const before10Time = now - (1000 * 60 * 10); // 10分前
const before5Time = now - (1000 * 60 * 5); // 5分前
```

# ステップ２（メールの受信API）
次にGmailを取得する処理です。
Gmailの検索を行うには、GmailAppのsearchメソッドを利用します。

また、AeyeScanの通知メールは、「no-reply@aeyescan.com」という宛先から届くので、
「10分前」＋「no-reply@aeyescan.comからのメール」という条件でメールを取得します。
※メールの検索条件の「10分前」を指定するためミリ秒から秒に変換する必要があります。

```javascript:gas
//メール取得処理（直近10分）
const before10Sec = Math.floor(before10Time / 1000); // ミリ秒を秒に変換
//取得条件(10分前から現在で届いた、AeyeScanからのメールのスレッドを取得)
const query = 'after:' + before10Sec.toString() + ' ' + 'From:no-reply@aeyescan.com';
var threads = GmailApp.search(query);
```

# ステップ３（メールの解析）
次に、受信したメールの中から、「サイトから応答がなく、スキャン停止した場合に送信されるメール」が存在するかを確認します。

GmailAppのsearchメソッドでは、スレッドという単位で、メールが取得されます。
スレッドの中には、複数のメッセージ（メール）が格納されているので、for文を利用して、
一つひとつメールの内容をチェックしていきます。

以下2つの内容をチェックします。

## その１. メール受信時間
GmailAppでは、メールをスレッド単位で取得しているため、スレッド内すべてのメールが取得されてしまいます。
そのため、スレッドから取得したメールの受信日時を再度チェックして、10分前のメールであるかをチェックします。
また、更にここで、受信から5分以上経過しているかをチェックします。

## その２. メール本文
メールの本文をチェックして、「サイトから応答がなく、スキャン停止した場合に送信されるメール」であるかを確認します。
該当のメールが見つかったら、ステップ４の再スキャンを行います。
また、スキャン再開時に必要となる、スキャンIDも、メール本文から取得します。

```javascript:gas
  //スレッド数分繰り返し 
  threads.forEach(function(thread) {
    // スレッド内のメールを取得
    let messages = thread.getMessages();

    // メールを一つずつ取り出す
    messages.forEach(function(message) {
      var before10Date = new Date(before10Time);
      var before5Date = new Date(before5Time);

      //メールの受信日時が10分前以内 かつ 5分以上前の場合が対象
      if (message.getDate() >= before10Date 
          && message.getDate() <= before5Date) {
        // メール本文を取得
        let plainBody = message.getPlainBody();

        // スキャン失敗の場合
        if (plainBody.includes('サイトからの応答が無いためスキャンを一時停止しました')) {
          var mScanId = plainBody.match(/スキャン名: ID:([0-9]+)\./);
          var scanId = mScanId[1];
          console.log('スキャンIDをメールから取得：' + scanId);

          //スキャン再開
          Logger.log('サイトから応答が無い状態が5分続いたためスキャンID:' + scanId + ' の再スキャンを実行');
          resumeScan(scanId);
        }
      }
    });
  });
```

# ステップ４（再スキャンの実行）
最後にAeyeScanのAPIを実行して、再スキャンを実行します。

呼び出すAPIは、[スキャン再開のAPI](https://aeyescan.com/api-doc.html?_=3161126543561#tag/Exec/operation/resumeScanById)となります。

```javascript:gas
//再スキャンを実行する
function resumeScan(scanId) {
  //再スキャンＡＰＩを実行する
  try{
    const api_scan_url = API_BASE_URL + scanId + API_RESUME_SCAN;
    const api_scan_params = { "method":"PUT",...API_HEADERS_PARAMS,"payload":JSON.stringify({"speed": "normal"}) };
    UrlFetchApp.fetch(api_scan_url, api_scan_params);
    Logger.log("スキャン再開: " + scanId);
  }catch(err){
    Logger.log("スキャン再開エラー" + err );
  }
}
```

# 検証

スクリプトが完成したので、さっそく実行してみましょう。

## トリガーの設定
今回は、5分毎にスクリプトを実行するため、「時間主導型」、「分ベースのタイマー」、「5分おき」で設定をおこないます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/ef6da3b3-5f9d-d268-8cd3-393da4e0c77c.png)

### スキャンを作成します。
スキャン作成の際は「メール通知設定」でご自身のGMailに通知されるように設定しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/60b5c2ec-205d-7245-6a39-93b1ce29b976.png)

用意したサンプルサイトに対してスキャン実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/bc42ad32-a6a3-1b87-724d-003770329f7c.png)

検証のためサンプルサイトを意図的に停止します。
以下のようにスキャンが自動で停止し、通知内容が表示さます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/6f15ed20-7e16-99a3-a9b6-b1780afc1d9e.png)

一定時間経過するとスキャンが再開されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/f69b2226-072b-2433-d4e7-f1b70956cea4.png)


ちなみに、GASの実行ログを確認すると、問題なく、再スキャンが実行されていることを確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/63183e0f-374a-62ce-11ad-3a3b1f2a7ac0.png)



# 利用上の注意
今回利用したコードについては、自由に編集しご利用ください。ただし、無償無保証のため不明点の質問やカスタマイズについてのサポートはできません。コードの改善、不具合については、是非ご連絡ください。

# おわりに
今回は、GASとAeyeScanのAPIを利用して、スキャンを自動再開する仕組みを作成しました。
また、Gmailをご利用でない場合は、今回の仕組みをAeyeScanの[Custom Webhook機能](https://help.aeyescan.com/ja-JP/support/solutions/articles/67000694155)と他自動化ツールを利用して、作成することも可能です。
是非ご活用ください。

# 参考文献
Google Apps ScriptでGmailの情報を取得する際に、受信日時を細かく指定する方法。
https://qiita.com/3mc/items/39b2c8241c6b52811ad2



AeyeScanのトライアル、脆弱性診断の自動化のご相談は[こちら](https://www.aeyescan.jp/)
[![2022-155641.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2422105/dd3f69d5-879a-ed11-d6ac-353a85523c43.png)](https://www.aeyescan.jp)