7 カスタム監査ルールの記述の開始

監査するOracle JETアプリケーションをカスタム監査ルールを作成およびテストするためのプロジェクトとして使用します。Oracle JAFを実行し、ターゲット・アプリケーションのファイルに対してカスタム・ルールをテストするように、アプリケーションを構成できます。他の開発者がJETアプリケーションの監査に使用するために、実装されたカスタム監査ルールが格納されているアプリケーション・フォルダをカスタム・ルール・パックとしてzipできます。

カスタム監査ルール・テスト・プロジェクトの設定

カスタム監査ルールの記述は、反復開発プロセスであり、カスタム監査ルールのテスト対象として使用できる既存のOracle JETアプリケーション・プロジェクトから開始するのが理想的です。

カスタム監査ルールの記述を開始する前に、カスタム・ルールで監査する実際のファイルが含まれるOracle JETアプリケーションを選択します。このアプリケーションは、カスタム・ルールの作成およびテストのための開発環境のようなものになります。その後で、テスト・アプリケーションのルートに追加するフォルダ内に、カスタム・ルールをJavaScriptファイルとして実装できます。Oracle JAFを実行してカスタム・ルール・フォルダ内の監査ルールを呼び出すようにテスト・アプリケーションを構成すると、テスト・アプリケーションのターゲット・ファイル・セットをテスト/デバッグ監査サイクルで簡単に反復できます。

ヒント:

デフォルトでは、Oracle JAFにより、JETアプリケーションのsrcフォルダにあるアプリケーション・ファイルが監査されます。カスタム監査ルールのソース・コードが監査されないようにするには、テスト・アプリケーションのルート・レベルにカスタム・ルール・フォルダを作成します。

テスト・アプリケーションに追加するカスタム・ルール・フォルダには、カスタム監査ルールを実装するJavaScript (.js)ファイルを含め、次のコンテンツがあります。

rule-1.js        }
rule-2.js         }  these are your custom rule files
. . .            }
rules.json           mandatory file describing the rule properties
msgid.json           optional file associating rules with message ID's

rules.jsonファイルは、単一のルール定義ファイルで、カスタム監査ルールのプロパティを記述するためにカスタム・ルール・フォルダ内に定義する必要があります。ルール定義ファイルにはコメントを含めることができ、次のような構造をしています。

/*-----------------------------------------------------------*/
/*  Test 'rulePack' definition                               */
/*-----------------------------------------------------------*/
{
  "title" :  "A descriptive title for the rule pack",
  "prefix" : "ABCD",                     <--  the prefix prepended to message ids
  "version" : "1.1.0",                   <--  the rule pack version
  "rules" : {
               "rule-1" :  {
                  // Standard rule options
                  "severity": "major",
                    // Additional optional user rule options
                      "customOpts": {
                         "maxLevel": 3
                       }
                      },
               "rule-2" :  { ... },
                . . .
            }
}

prefixプロパティは、共通ルール・セットに属するカスタム・ルールを識別します。実行時に、Oracle JAFにより、指定した接頭辞が発行された診断メッセージのメッセージIDの先頭に追加されます。指定した接頭辞は、呼び出された監査ルールを識別するのに役立ちます。

始める前に:

  • カスタム監査ルールのテストに使用できるOracle JETアプリケーションを選択します。このアプリケーションは、カスタム監査ルールを実装するプロジェクトとして使用できます。

  • npmからOracle JAFをインストールします。詳細は、「Oracle JET Audit Frameworkのインストール」を参照してください。

カスタム監査ルール・プロジェクトを設定するには:
  1. 「コマンド・プロンプト」ウィンドウを開き、JAF初期化コマンドをアプリケーション・ルートから実行します。
    ojaf --init

    コマンドを実行すると、ツールによってoraclejafconfig.jsonというデフォルトのJAF構成ファイルがアプリケーション・ルートでスキャフォールドされます。このファイルを編集して、テスト時にカスタム監査ルールを実行するようにJAFを構成します。

  2. カスタム監査ルール・プロジェクトのルートで、フォルダを作成してカスタム・ルールおよびルール定義ファイルを含めます。フォルダ名は任意の名前にできます。
  3. アプリケーションのルートにある生成されたoraclejafconfig.jsonファイルを編集し、カスタム・ルール・フォルダを指すようにrulePacksプロパティ値を構成します。
    "rulePacks" : [
                    {
                      "path" : "path/to/my/customrulepack/folder",
                      "enabled" : true 
                      "status" : "all" 
                    }
                  ]

    enabledおよびstatusプロパティはオプションであり、完全なルール・パックを簡単に無効にしたり、特定のステータスのルールのみをレポートすることができます。省略した場合、デフォルトでは、ルール・パック内のすべてのルールが有効になり、レポートされます。

  4. 必要に応じて、組込みJETルール・セットの監査レポートの作成を無効にします。生成されたoraclejafconfig.jsonファイルを編集し、builtinJetRulesプロパティ値をfalseに設定します。
    "builtinJetRules" : false

    カスタム監査ルールのみをテストする場合に、builtinJetRulesプロパティは便利なプロパティで、テスト/デバッグ監査サイクル時に実行されないように組込みJETルールを個別に無効にする必要がありません。

  5. JETアプリケーションに追加したカスタム・ルール・フォルダに、必須のルール定義ファイルrules.jsonを作成します。
    {
      "title" :  "My Custom Audit Rules",
      "prefix" : "CUSTOM", 
      "version" : "1.0.0",
      ...
    }

    割り当てた接頭辞は、監査診断メッセージに接尾辞として付けられ、カスタム監査ルールによって得られる診断の識別に役立ちます。タイトルおよびバージョンは任意であり、ルール・パック・バージョンの識別に役立ちます。

  6. ルール定義ファイルに、このプロジェクトで実装するカスタム監査ルールのリストと、構成可能な監査ルールの場合に渡す標準プロパティまたはユーザー定義プロパティの値を指定してrulesプロパティを追加します。
    {
      "title" :  "My Custom Audit Rules",
      "prefix" : "CUSTOM", 
      "version" : "1.0.0",
      "rules": {
        "custom-check-heading-levels-1" : {},
        "custom-check-heading-levels-2" : {},
        "custom-check-heading-levels-3": {
             "filetype": "html",
             "customOpts": {
                     "maxLevel": 4
                   }
             }
      }
    }

    rules.jsonファイルでは、ルール・パックを定義し、監査ルールおよび実行時にJAFによって登録済ルール・パック用にロードされる構成可能なプロパティ(オプション)が識別されます。規則上、ルール名にはルール・パックの接頭辞が含まれます。

    このサンプルでは、ルール名の接頭辞customにより、同じルール・パックに属するルールを識別できます。最初の2つのルールは実行時プロパティを宣言しておらず、3番目のルールは、ターゲット・アプリケーションのoraclejafconfig.jsonファイルでユーザーが必要に応じて構成できるデフォルトのプロパティ値を宣言しています。なお、個々の監査ルールに必要に応じて定義できるシステム・プロパティのリストは、「カスタム監査ルールの実行時プロパティの定義」を参照してください。

  7. オプションで、$requiredプロパティをtrueに設定して、実行時に無効にしないルールを指定します。
    {
      "title" :  "My Custom Audit Rules",
      "prefix" : "CUSTOM", 
      "version" : "1.0.0",
      "rules": {
        "custom-check-heading-levels-1" : {},
        "custom-check-heading-levels-2" : {},
        "custom-check-heading-levels-3": {
             "$required": "true",
             "filetype": "html",
             "customOpts": {
                     "maxLevel": 4
                   }
             }
      }
    }

    ruleMods構成プロパティ(「監査ルール・ランタイム・プロパティの構成」を参照)または間接的にruleNameプロパティを使用すると、ルールの実行を無効にできます。通常、$requiredプロパティは、ルールパック設定やその他の非監査関連機能を実行するルール、およびその実行が必須であるルールに使用します。また、これを使用すると、これらのルールが、他のすべてのルールより前に、指定されている順序でロード/登録されます。

  8. 必要に応じて、ルール・メッセージIDファイルmsgsid.jsonを、JETアプリケーションに追加したカスタム・ルール・フォルダに作成します。
    {
        "custom-check-heading-levels-1" : "1234",
        "custom-check-heading-levels-2" : "1235",
        "custom-check-heading-levels-3" : "1236"
    }

    JAFによって問題がレポートされる場合、prefix-nnnn形式の一意のメッセージIDが挿入されます(prefixはルール・パックの接頭辞、nnnnはルールに定義されたメッセージ番号)。あるいは、「カスタム監査ルールのメッセージIDの定義」の説明に従って、メッセージIDをカスタム監査ルールにハードコーディングすることもできます。

  9. これで、「カスタム監査ルールの実装」の説明に従って、カスタム・ルール・フォルダに追加された.jsファイルとして実装するルールの作成を開始する準備ができました。

    ヒント:

    スケルトン監査ルールを短時間でスキャフォールドするには、現在のディレクトリで、--initruleコマンドライン・フラグを指定してojafコマンドを実行します。
    ojaf --initrule myRuleName

    監査ルールJavaScriptの概要は、「カスタム監査ルールの構造の理解」を参照してください。

カスタム監査ルールを実装する際に、プロジェクト内のカスタム監査ルールのテストを開始します:

カスタム監査ルールの実行時プロパティの定義

rules.jsonファイルのrulesプロパティを使用して、個々のカスタム監査ルールのプロパティを含め、ルール・パック内にルールを宣言します。

ルール・パック内のカスタム監査ルールはすべて、rules.jsonファイルのrulesプロパティで宣言する必要があります。JAFで定義されているデフォルト値をオーバーライドする場合、定義できるプロパティには標準システム・プロパティがあります。実行時にプロパティ値をカスタム・ルールに渡す場合にオプションのプロパティを含めることもできますが、これらのプロパティは追加のcustomOptsプロパティで囲む必要があります。

ユーザー定義ルールの定義の基本例を次に示します:

"rules": { 
 "my-rule": { 
  // standard system properties 
  "$required" : "true", 
  "severity": "info", 
  "filetype": "html", 
  // optional properties 
  "customOpts": { 
    "maxLevel": 1 
    } 
 } 
}

この宣言では、"my-rule"と呼ばれるカスタム・ルールが存在し、rules.jsonファイルと同じフォルダにあるmy-rule.jsファイルで実装されることを指定します。これには、多数の標準システム・プロパティ($requiredseverityおよびfiletype)が含まれます。さらに、ルール固有のプロパティmaxLevelが宣言されています。このプロパティはJAFによって検査されず、呼び出されたときにRuleコンテキスト・オブジェクト内のルールに渡されます。カスタム・ルール実装では、渡された値を処理して必要な監査結果を達成します。

一部のプロパティ名はJAFで予約されており、カスタム監査ルールで別の用途に使用することはできません。次のJAFシステム・プロパティは予約されており、すべてのプロパティがルール宣言でオプションです。これらのプロパティをカスタム監査ルール宣言に追加しない場合、JAFによってデフォルト値が割り当てられます。たとえば、severityプロパティを特に定義しなければ、カスタム監査ルールは重大度レベルcriticalに関連付けられます。

ルールのプロパティ 説明
inservice このプロパティがfalseに設定されている場合を除き、ルールは稼働状態であると見なされます。この設定ではenabledプロパティがオーバーライドされ、構成のruleModsプロパティの使用が抑制されて、ルールの有効化が試行されます。稼働中でないルールは監査に参加しません。デフォルト値はtrueです。
enabled カスタム監査ルールを有効または無効にします。デフォルトでは、すべての監査ルールが有効です。
severity カスタム監査ルールの重大度レベルを分類します。デフォルトでは、Oracle JAFにより、割り当てることができる一連のレベルが定義されています: infominormajorcritical (デフォルト)、blocker。このプロパティは、カスタム・ルールの重大度を指定してユーザーがルールの重大度レベルによって監査を制限できるようにする場合に使用します。例については、「監査ルールの重大度レベルの制限」を参照してください。
status 開発ステータスをカスタム監査ルールに関連付けます。production (デフォルト)、alphabetaまたはdeprecatedのいずれかです。
filetype

カスタム監査ルールが呼び出されるファイル・タイプを指定します。デフォルトでは、カスタム・ルールはファイル・タイプに制限されません。htmlまたはcssjsjson、あるいはそれらの組合せを指定できます。たとえば:

"filetype : "html"

または

"filetype" : ["html", "css"]

filetypeプロパティは、startup/closedownフェーズに対して宣言されたカスタム・フック・ルールでは無視されます。これらはファイルに関連しないためです。その他すべての監査ルールについて、このプロパティを指定する必要があります。

group カスタム監査ルールが割り当てられるグループ(1つまたは複数)を指定します。このプロパティは、カスタム・ルールを名前のグループに割り当てて、ユーザーがルール・グループによって監査を制限できるようにする場合に使用します。たとえば:

"group" : "jet-html"

または

"group" : [ "jet-html", "jet-aria"]

例については、「特定のルールによる監査」を参照してください。
jetver カスタム監査ルールの呼出しに必要なOracle JETリリース・バージョン(1つまたは複数)を指定します。このプロパティを省略した場合、カスタム・ルールはすべてのJETバージョンに作用します。形式は、npmなどのプログラムで使用されているセマンティック・バージョニングをサポートしています。たとえば:
"jetver" : ">=9.1.0"
または
"jetver" : "~9.1.0"
このプロパティおよびセマンティック・バージョニングの詳細は、「JETおよびECMAスクリプトの特定バージョンによる監査」を参照してください。
theme ルールがテーマに依存する場合は、JETテーマを指定します。値は構成プロパティtheme(またはそのデフォルト)と比較され、一致がない場合はルールは無効になります。文字列またはテーマ文字列の配列として指定できます。たとえば、
"theme": "Redwood"
または
"theme": ["Redwood", "Alta"]
amd ルールがI/Oを実行する場合、監査ルールをAMDモードで使用できないことを指定します。その他のすべてのケースでは省略できます。AMDモードで実行していない場合、このプロパティは無視されます。ルールでファイルI/Oが実行される場合は、amd : falseを設定して将来のAMDの使用時に障害が発生しないようにすることをお薦めします。
$required このルールを構成プロパティruleModsによって、また間接的にruleNamesプロパティによって無効にできないことを指定します。これは通常、パック設定やその他の非監査関連機能を実行するルールに使用されます。$requiredとマークされたルールは、他のすべてのルールより前に(rules.jsonで見つかった順序で)ロード/登録されます。trueを指定すると、パックの設定時にこのルールの実行が必須になります。デフォルト値はfalseです

カスタム監査ルールのプロパティは、「監査ルールの実行時プロパティの構成」の説明に従って、oraclejafconfig.jsonファイルの構成プロパティruleModsを使用してユーザーが実行時にオーバーライドできます。また、$requiredプロパティをtrueに設定することで、実行時に無効にしないルールとして指定することもできます。

カスタム監査ルールのメッセージIDの定義

Oracle JAFで問題のレポートに使用されるメッセージIDは、デフォルトでJAFによって生成することも、オプションでIDを定義してカスタム監査ルールをより適切にドキュメント化することもできます。

JAFによって問題がレポートされる場合、ppp-nnnn形式の一意のメッセージIDが挿入されます(pppはルール・パックの接頭辞、nnnnはメッセージID)。カスタム監査ルールでは、様々な方法でメッセージ番号を指定できます。

メッセージIDは、ハードコーディングすることも、ユーザー定義カスタム・メカニズムから(たとえば、ルール・パック拡張機能を使用して)取得することも、またIssueコンストラクタに指定することもできます。

あるいは、オプションのmsgid.json定義ファイルを使用してルール名とルール・パック内のメッセージIDを関連付けることができます。msgid.jsonファイルの形式を次に示します:

{
   "rulename1" : "1234",
   "rulename2" : "1235"
   . . .
}

//および/* */コメントを使用して、msgid.jsonファイルに注釈を付けることができます。

実行時に、IDがIssueに指定されていない場合にReporterインスタンスに追加されると、JAFでは、ルールの.jsファイルおよび必須のrules.jsonファイルと同じフォルダ内でmsgid.jsonというファイルを探して、解決しようとします。その場合、JAFではルール名をメッセージ検索キーとして使用してメッセージ番号を取得します

msgid.jsonファイルを使用する場合、柔軟性のために、Issue.setMsgKey()を使用して、デフォルトの検索キーをルール名から監査ルール・ハンドラに指定する一意のキーに変更することもできます。

var issue = new ruleContext.Issue(". . .") ;
. . .
issue.setMsgKey("some key value") ;
メッセージIDをハードコーディングするには、カスタム監査ルールで番号(1234など)を監査ルール・ハンドラ関数によって作成されるIssueオブジェクトに直接指定できます。
var issue = newruleContext.Issue("some rule message", "1234");

ハンドラ関数は、その後にメッセージIDをIssueオブジェクトに設定することもできます

var issue = newruleContxt.Issue("some rule message");
 . . .
issue.setMsgId("1234");

カスタム・メカニズムを使用してメッセージIDを取得する方法の例を次に示します。この例では、カスタム・ルール・パック拡張機能を使用しています。

var RPExtension     = ruleContext.rulePack.getExtension() ;  // get the rulepack's extension object
var myMsgIdAssigner = RPExtension.assignMsgId ;          // assumes the rule pack has created a routine for assigning message ID's
 
var issue = new ruleContext.Issue("some rule message", myMsgIdAssigner(ruleContext)) ;  // (myMsgIdAssigner could use ruleContext.ruleName)

Issueコンストラクタおよび使用可能なメソッドの詳細は、「ルール問題クラスのメソッド」を参照してください。

起動フック・ルールで使用するために作成できるカスタム・ルール・パック拡張機能の例は、「監査ライフサイクルを使用したカスタム・ルールの実装」を参照してください。

カスタム監査ルールの実装

カスタム監査ルールとは、ユーザーが実装するJavaScriptファイルで、特定のパブリック関数をエクスポートします。

カスタム監査ルールをプロジェクトに実装する際、プロジェクトのrules.jsonファイルで宣言したルールと同じ名前の.jsファイルを追加します。監査ルールの実装方法を示すために、HTML見出しのネストの過度なレベルについてHTMLファイルを監査する、複雑さが増していく3つのルールを説明します:

  • custom-check-heading-levels-1.js

  • custom-check-heading-levels-2.js

  • custom-check-heading-levels-3.js

これらのルールのrules.jsonファイルでは、次のようにCUSTOMルール・パックを宣言しています。

{
  "title": "Example Custom Audits",
  "prefix": "CUSTOM",
  "version": "1.0.0",
  "rules": {
    "custom-check-heading-levels-1" : {},
    "custom-check-heading-levels-2" : {},
    "custom-check-heading-levels-3": {
	"filetype": "html",
	"customOpts": {
		"maxLevel": 4
	}
     }
  }
}

各ルールの実装では、同じJavaScript正規表現を使用して、ターゲット監査ファイルから渡されたHTML見出しタグの数字部分を照合して抽出します。実行時に、JAFがJETアプリケーション内のHTMLファイルを処理し、監査ルールに登録されているルール・リスナーが各HTMLタグを監査ルールで実装されるイベント・ハンドラ関数に渡します。ルール・ハンドラ関数の実装を変えて、正規表現の照合結果を使用する様々な方法を示します。また、rules.jsonファイルのサンプルが示すように、3番目のルール宣言は、maxLevelプロパティのデフォルト値を定義するため、異なります。3番目の監査ルールは、JAF監査のエンド・ユーザーが監査ルールを構成できるようにする方法を示します。

バージョン1 - H4より深い見出しレベルのレポート

有効なルールとするには、カスタム監査ルールで次の4つのメソッドをエクスポートする必要があります:

/**
  * Copyright (c) 2018, 2022, Oracle and/or its affiliates.
  * Licensed under The Universal Permissive License (UPL), Version 1.0
  * as shown at https://oss.oracle.com/licenses/upl/
  */

/*--------------------------------------------------------------------------------*/
/*   JAF Rule:  'CustomHeadingLevelsAuditBasic'                                   */
/*   Purpose :                                                                    */
/*--------------------------------------------------------------------------------*/


const RULENAME = "CustomHeadingLevelsAuditBasic";
const DESCRIPTION = "This rule checks that excessive levels of header nesting have 
                     not been used on HTML pages by raising an error whenever a 
                     heading tag greater than H4 is used";
const SHORT_DESCRIPTION = "'Checks HTML files for any use of tags <h5> and above";

class Rule {
  getName() {
    return RULENAME;
  }

  getDescription() {
    return DESCRIPTION;
  }

  getShortDescription() {
    return SHORT_DESCRIPTION;
  }

  register(regContext) {
    return ({
      tag: this._doHeaderLevelAudit
    }
    )
  }
. . .
}

module.exports = Rule;

カスタム監査ルール実装の最初の3つのメソッドは、監査ルールについて指定された使用方法の情報を返します。この情報は、エンドユーザーがojafコマンドライン・インタフェースとやり取りして特定の診断メッセージを発行した監査ルールに関する追加の詳細をリクエストするたびに、Oracle JAFに渡されます。

4番目のメソッドregister()は、すべてのカスタム監査ルールに対して必要なエントリ・ポイントです。このメソッドは、JAFの起動時にコールされ、ファイル・セットの監査時に見つかった特定タイプのデータについてノード・リスナーを宣言するのに使用します。このメソッドは、指定されたノード・リスナー・タイプのイベントが含まれるコンテキスト・オブジェクトを返します。ファイル・データを監査するために作成したルールはノード監査ルールと呼ばれますが、これは、register()メソッドが、ターゲット・ファイルで生成される抽象構文ツリー(AST)からJAFによって作成されたコンテキスト・オブジェクトのノード・データを返すためです。

ノート:

ルールの.jsファイルに実装したregister()メソッドは、JAF監査ライフサイクルの様々なフェーズでJAFによってトリガーされるイベントのリスナーを宣言することもできます。この一連のリスナー・タイプは監査エンジンへのフックを提供し、このようなフックのために作成したルールはファイル・データに依存しません。フック・ルールの作成の詳細は、「フック・ルール呼出しについて」を参照してください。

このバージョンの見出しレベル監査ルールでは、register()メソッドは、監査対象のファイル・セット内のすべてのHTMLタグをチェックするtagリスナー・タイプを指定します。処理済タグによってトリガーされるイベントを処理するには、ルールでruleContextオブジェクト用の監査ハンドラ関数およびハンドラ関数に渡されるその他の引数を実装する必要があります。実装では、リスナー・イベントに応答してdoHeaderLevelAuditハンドラ関数を呼び出します。登録済のtagリスナーの場合、ruleContextオブジェクトおよびtagElementName文字列が関数に引数として渡されます。

/**
  * Copyright (c) 2018, 2022, Oracle and/or its affiliates.
  * Licensed under The Universal Permissive License (UPL), Version 1.0
  * as shown at https://oss.oracle.com/licenses/upl/
  */

. . .
class Rule {

 . . .
  register(regContext) {
    return ({
      tag: this._doHeaderLevelAudit
    }
    )
  }
_doHeaderLevelAudit(ruleContext, tagElementName) {
  . . .    
   }
 }

module.exports = Rule;

このバージョンの見出しレベル監査ルールでは、見出しレベルをハードコーディングし、ルール診断メッセージでH4を超える見出しレベルがルールによってレポートされるようにします。次にJavaScriptで、tagElementNameに渡された文字列からHTML <H*>タグの数字部分を照合して抽出できるようになる正規表現を定義します。一致が見つかった場合は、正規表現によって抽出された数字部分をチェックし、ハードコーディングした制限4より大きいかどうかを確認します。最後に、この実装では、診断メッセージおよび監査ルールのオプションのメッセージIDを使用してIssueオブジェクトのインスタンスを作成し、問題をレポートする必要があります。これで、Reporterインスタンスにより、addIssue()をコールしてJAFが監査結果を出力できるようになります。

ノート:

一意の監査ルールのメッセージIDを監査ルール・ハンドラ関数にハードコーディングすることは、カスタム監査ルールをドキュメント化する1つの方法です。定義したIDは、ppp-nnnnとして監査出力に表示されます(pppはルール・パック接頭辞、nnnnはメッセージID)。監査ルールでメッセージIDが定義されておらず、オプションのmsgid.jsonファイルで見つからない場合、JAFによって実行時にルール・メッセージIDが生成されます。詳細は、「カスタム監査ルールのメッセージIDの定義」を参照してください。


/**
  * Copyright (c) 2018, 2022, Oracle and/or its affiliates.
  * Licensed under The Universal Permissive License (UPL), Version 1.0
  * as shown at https://oss.oracle.com/licenses/upl/
  */

. . .
class Rule {

 . . .
  _doHeaderLevelAudit(ruleContext, tagElementName) {
    //Define a regular expression that will allow us to match extract the numerical part of an HTML <H*> tag 
    const matchHeader = new RegExp(/^[h](\d*)$/, 'i');
    //Check the tag being processed against the Regular Expression 
    const matches = tagElementName.match(matchHeader);

    //A not-null result means it's some kind of header tag, so now we check the number portion extracted by the 
    //regular expression to see if it is greater than the hardcoded limit of 4 in this case
    if (matches !== null) {
      const headerLevel = parseInt(matches[1]);
      if (headerLevel > 4) {
        //Report the issue
        const issue = new ruleContext.Issue("Header level 4 exceeded", "001");
        ruleContext.reporter.addIssue(issue, ruleContext, 'minor');
      }
    }
  }
}

module.exports = Rule;

次に、このサンプル・ルールを変更して、ルールの診断メッセージを改善してみましょう。

バージョン2 - レポートへの見出しタグ情報の挿入

このサンプルでは、修正した見出しレベル監査ルールで、引き続きtagリスナー・タイプを登録し、doHeaderLevelAudit監査ハンドラ関数をトリガーします。ただし、このバージョンでは、見出しテキストおよび見出しタグが含まれるように診断メッセージを拡張します。監査ハンドラ関数のロジックにより、ハンドラに渡されるruleContext.nodeオブジェクトのchildren.lengthおよびchildren.typeプロパティでノード・データがテストされます。コンテンツが単純なヘッダー文字列である場合、ノードruleContext.dataを変数headerTextに割り当て、この変数をproblemHeaderの見出しタグで書式設定し、作成したIssueインスタンスに渡します。最後に、Reporterオブジェクトで監査結果を出力するためのaddIssue()へのコールは変更されません。

ヒント:

Oracle JET ojafユーティリティ(VS Codeなど)を起動して実行時コンテキスト・オブジェクト・プロパティとそのデータをさらに簡単に視覚化できる開発ツール内で監査ルールをテストします。

/**
  * Copyright (c) 2018, 2022, Oracle and/or its affiliates.
  * Licensed under The Universal Permissive License (UPL), Version 1.0
  * as shown at https://oss.oracle.com/licenses/upl/
  */

class Rule {

  . . .
  
  _doHeaderLevelAudit(ruleContext, tagElementName) {
    //Define a regular expression that will allow us to match extract the numerical part of an HTML <H*> tag 
    const matchHeader = new RegExp(/^[h](\d*)$/, 'i');
    //Check the tag being processed against the Regular Expression 
    const matches = tagElementName.match(matchHeader);

    //A not-null result means it's some kind of header tag, so now we check the number portion extracted by the 
    //regular expression to see if it is greater than the hardcoded limit of 4 in this case
    if (matches !== null) {
      const headerLevel = parseInt(matches[1]);
      if (headerLevel > 4) {
        //In this enhanced version, before we report the issue let's get the actual 
        //tag information to add to the report
        //Only report the actual content for the simple case though otherwise use ellipsis
        let headerText = '...';
        if (ruleContext.node.children.length === 1 && ruleContext.node.children[0].type === 'text') {
          headerText = ruleContext.node.children[0].data;
        }

        const problemHeader = `<${tagElementName}>${headerText}</${tagElementName}>`;
        const issue = new ruleContext.Issue(`Header level 4 exceeded for element: ${problemHeader}`, "002");
        ruleContext.reporter.addIssue(issue, ruleContext, 'minor');
      }
    }
  }
}

module.exports = Rule;

カスタム監査ルールが重大度レベルを引数としてaddIssue()に渡すことにも注目してください。重大度レベルをハードコーディングしない場合やプロジェクトのrules.jsonファイルのルール宣言にseverityシステム・プロパティを定義しない場合、JAFによってカスタム監査ルールがデフォルトの重大度レベルcriticalに割り当てられます。サンプルでは、3つすべてのカスタム監査ルールに対して重大度レベルminorをハードコーディングしています。severityおよびカスタム監査ルールで定義できるその他のシステム・プロパティの詳細は、「カスタム監査ルールの実行時プロパティの定義」を参照してください。

次に、このサンプル・ルールを変更して、監査の実行前に、エンドユーザーが監査見出しレベルを構成できるようにする構成可能な監査ルールを示してみましょう。

バージョン3 - 見出しレベルの監査ルールの構成

すべてのルール・パックには、監査の起動時にOracle JAFによってロードされる監査ルールのリストが指定されたrules.jsonファイルが含まれる必要があります。ルールが構成可能である場合、構成可能な見出しレベルをチェックするこの最終バージョンの見出しレベル監査ルールで宣言するmaxlevelプロパティのように、rules.jsonファイルでこのプロパティを宣言行に指定します。

{
    "title": "My Custom Audit Rules",
    "prefix": "CUSTOM",
    "version": "1.0.0",
    "rules": {
        "custom-check-heading-levels-3": {
            "customOpts": {
                "maxLevel": 4
            }
        }
    }
}

構成可能なプロパティmaxLevelを使用するには、監査ルール・サンプルではgetRuleOption()をコールして、起動時にルール・パックがロードされる際に渡されるRegisterコンテキスト・オブジェクトに関するルール・パック情報を問い合せます。値をconfiguredLevelに割り当てると、監査ハンドラ関数により、前のバージョンのルール用に記述した同じロジックを使用して値がテストされます。見出しタグのノード・データが構成されたレベルを超える場合、問題がレポートされ、問題となる見出しタグについてメッセージがconfiguredLevel値とともに出力されます。


/**
  * Copyright (c) 2018, 2022, Oracle and/or its affiliates.
  * Licensed under The Universal Permissive License (UPL), Version 1.0
  * as shown at https://oss.oracle.com/licenses/upl/
  */

. . .
class Rule {

. . .

_doHeaderLevelAudit(ruleContext, tagElementName) {

    //Before we start, in this version, find out what the configured max level is from 
    //the rules.json declaration for our custom rule
    const configuredLevel = ruleContext.rulePack.getRuleCustomOptions().maxLevel;

    //Define a regular expression that will allow us to match extract the numerical part of an HTML <H*> tag 
    const matchHeader = new RegExp(/^[h](\d*)$/, 'i');
    //Check the tag being processed against the Regular Expression 
    const matches = tagElementName.match(matchHeader);

    //A not-null result means it's some kind of header tag, so now we check the number portion extracted by the 
    //regular expression to see if it is greater than the hardcoded limit of 4 in this case
    if (matches !== null) {
      const headerLevel = parseInt(matches[1]);
      //This time we check against the configured level passed in with the options
      if (headerLevel > configuredLevel) {
        //In this enhanced version, before we report the issue let's get the actual tag information to add to the report
        //Only report the actual content for the simple case though otherwise use ellipsis
        let headerText = '...';
        if (ruleContext.node.children.length === 1 && ruleContext.node.children[0].type === 'text') {
          headerText = ruleContext.node.children[0].data;
        }

        const problemHeader = `<${tagElementName}>${headerText}</${tagElementName}>`;
        const issue = new ruleContext.Issue(`Header level ${configuredLevel} exceeded for element: ${problemHeader}`, "003");
        ruleContext.reporter.addIssue(issue, ruleContext, 'minor');
      }
    }
  }
}

module.exports = Rule;

これで、基本ノード・ルールのウォークスルーは終わりです。演習として、3つの見出しレベル監査ルールのサンプル・コードを再利用して、JETアプリケーションのHTMLソース・ファイルを監査するためのカスタム・ルール・パックを作成することができます。作成するルール・パックには、各監査ルールの.js実装ファイルと、ルールを宣言するためのrules.jsonファイルが含まれます。各実装ファイルには、ルール・バージョン1で説明したサンプルで示した(簡潔にするため、ルール・バージョン2および3のサンプルでは省略された)必須メソッドを必ず含めます。Oracle JAF監査で参照できるカスタム・ルール・パックの作成の詳細は、「監査でのカスタム監査ルールの参照」を参照してください。

ノート:

エンド・ユーザーは、JETアプリケーションのoraclejafconfig.jsonファイルを編集してrulePacksプロパティを定義することで、カスタム・ルール・パックを登録します。また、ruleModsプロパティを定義して、カスタム・ルール・パックのルール定義内に宣言されているデフォルト値をオーバーライドできます。エンド・ユーザーがカスタム・ルール・パックを有効にしてJETアプリケーションを監査する方法の詳細は、「カスタム・ルール・パックによる監査」を参照してください。また、エンド・ユーザーが監査の実行で構成可能な監査ルールのプロパティをオーバーライドできる方法の詳細は、「監査ルールの実行時プロパティの構成」を参照してください。

監査でのカスタム監査ルールの参照

oraclejafconfig.jsonファイルのrulePacksプロパティを使用して、監査実行時に、JAFによってロードされるプロジェクトにカスタム監査ルールを登録します。

監査ルールとは、特定のパブリック関数をエクスポートするJavaScriptファイルです。共通の診断目的を持つルール(ユーザー定義Webコンポーネントのグループに固有など)は、フォルダおよび構成ファイルで参照されるそのフォルダの場所に配置できます。関連付けられたルールのグループは、JAFではルール・パックと呼ばれます。ルール・パックは、他のユーザーに配布するために圧縮することもできます。その場合、Oracle JAF構成ファイルでは、zipファイルの場所を参照します。

zipファイルまたはフォルダには、「カスタム監査ルール・テスト・プロジェクトの設定」で説明したコンテンツが含まれている必要があります。

ルール・パックを宣言するには、生成されたoraclejafconfig.jsonファイルのrulePacksプロパティを編集して、カスタム・ルール・パック・フォルダまたはzipファイルへのパスを指定します。

"rulePacks" : [
                {
                  "path" : "path/to/myrulepack.zip",
                  "enabled" : [true (default) | false]
                  "status" : ["all" (default), "production", "deprecated", "beta", "alpha"] 
                },
                {
                  "path" : "path/to/my/rulepack/folder",
                  "enabled" : [true (default) | false]
                  "status" : ["all" (default), "production", "deprecated", "beta", "alpha"] 
                },
                ...
              ]

enabledプロパティはオプションであり、完全なルール・パックを簡単に無効にできます。省略した場合、デフォルトは有効になります。

指定するpathは、相対パスにできます。相対パスの場合、構成ファイルの場所または構成ファイルのbaseプロパティ(定義されている場合)に対して相対と見なされます。