やりたかったのは「Rubular」が提供してくれるマッチした部分をハイライトしてくれる機能。
ちょっとしたツールにこういう機能を入れようと思ったのですが、ニーズを満たしてくれるライブラリが意外と無くて色々試行錯誤してみました。
イメージに近いNPMモジュール
3年前から更新無し。試しに適当なnpm initディレクトリを作ってインストール。
C:\src\js\regexp_viewer>npm install regex-viewer
+ regex-viewer@1.0.2
サンプル通りの内容をindex.jsに書いて保存。
メール文字列から電話番号にマッチさせてハイライトさせるストーリーで。
"use strict";
var viewer = require("regex-viewer");
// ダミーメール文字列
var str = `
お世話になっております。○○です。
詳細は口頭でお話させて頂きたいと存じます。
私の個人携帯:999-999-9999
または
代表番号:99-9999-9999
までご連絡下さい。
`;
var regex = /\d{2,4}-\d{2,4}-\d{4}/gi;
viewer(str, regex);
node indexで実行。
ハイライトされますが、ブラウザが起動してしまいます。
うーん、ブラウザが起動するのではライブラリとしては使えません。
HTMLコードだけ返して欲しいけどソースを見てもそういったモードは無さそうです。
諦めてちょんプロを作って検証
前述のNPMモジュールと同様、正規表現マッチした文字列をスタイリングしたspanタグで囲って返却する方式で実装検証してみます。
let replacement = mail.replace(
regex,
str =>
`<span style="color: white;background-color: red;border-radius: 5px;">` +
str +
`</span>`
);
Angular8 + Angular Materialのプロジェクトで検証。mat-cardのコンテンツとしてハイライトされたメール文言がバインドされるようにします。
<mat-card>
<mat-card-title>
メール1
</mat-card-title>
<mat-card-subtitle>
日付
</mat-card-subtitle>
<mat-card-content [innerHtml]="safeHtml">
</mat-card-content>
</mat-card>
Angularはバインド結果の文字列にHTMLタグが含まれているとCSRF対策でエスケープされ、ブラウザ画面上でそのまま<span>が表示されます。
今回はDomSanitizerを使って検証済みのコードである旨をマーク、Angularのエスケープ処理を回避します。
import { Component } from "@angular/core";
import { DomSanitizer, SafeHtml } from "@angular/platform-browser";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.scss"]
})
export class AppComponent {
// 正規表現処理後文字列、DomSsanitizerによってセーフマーク
safeHtml: SafeHtml;
// サンプル原文
mail: string = `
お世話になっております。○○です。
詳細は口頭でお話させて頂きたいと存じます。
私の個人携帯:999-999-9999
または
代表番号:99-9999-9999
までご連絡下さい。
`;
constructor(private sanitizer: DomSanitizer) {
this.safeHtml = this.extractPhoneNumber(this.mail);
}
public extractPhoneNumber(mail: string): SafeHtml {
let regex = /\d{2,4}-\d{2,4}-\d{4}/gi;
// マッチした文字列をハイライトする為のspanタグを挿入
let replacement = mail.replace(
regex,
str =>
`<span style="color: white;background-color: red;border-radius: 5px;">` +
str +
`</span>`
);
// 中に含まれているタグを安全なものとしてマーク
let safeHtml: SafeHtml = this.sanitizer.bypassSecurityTrustHtml(
replacement
);
return safeHtml;
}
実行結果
ng serveしてhttp://localhost:4200にブラウザアクセス。期待した機能が実現出来ました。
ただ、bypassSecurityTrustHtml()を使ったことによって悪意のあるJavascriptコードがメール本文に入っていた場合はブラウザに埋め込まれる可能性があります。
AngularのDomSanitizer APIリファレンスには以下の記載があります。
Calling any of thebypassSecurityTrust...
APIs disables Angular’s built-in sanitization for the value passed in. Carefully check and audit all values and code paths going into this call. Make sure any user data is appropriately escaped for this security context. For more detail, see the Security Guide.各種DomSanitizer#bypassSecurrityTrust~APIを呼ぶと譲渡されたデータに対するAngularの組み込みサニタイズが無効になります。この処理に入る全ての値とコードパスを慎重にチェック、監査してください。このセキュリティコンテキスト内ではユーザのデータが適切にエスケープされるようにしてください。
https://angular.io/api/platform-browser/DomSanitizer
DomSanitizerを使ってAngularのサニタイズ処理を無効にした場合は、JS埋め込みされないようにする担保をプログラマがちゃんとするように、ということですね。
使うときは入ってくるデータの事前検証をして、出来ればDomSanitizerは使わない方法で実装したいところです。