下記のようなコードブロックとコードブロック外の混在するマークダウン形式の文章があるとします。
![33178870c736b737203c8b0678552038.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/e52c61a3-18e4-010a-2fed-66b4005de7c9.png)
以下の要件で文字列をサニタイズします。
・以下の記号をエスケープシーケンスに置き換えます。
(ただしコードブロック内の記号については置き換えない。)
(ただし、画像のURL内に含まれる&は置き換えない)
& は & に置き換え
< は < に置き換え
> は > に置き換え
" は " に置き換え
' は ' に置き換え
実装方針
1.文字列を「```」で分割する。
2.分割されたブロックがそれぞれコードブロック部分か、そうでないかを判別する。
3.コードブロック外の文字列の場合、上記の記号を置き換える。
4.画像URLないの&については置き換えない。
5.分割した文字列を再結合する。
サニタイズ後の文字列
ここはコード外です。# 大文字< > & ' "<>&'"```javascriptconst test = () => { console.log('テスト')const one = 1;const two = 2;if(one < tow && one >=0){ console.log("over") }}```コード外です2。*斜体*<a onmouseover=alert(document.cookie)>click me!</a>```javascriptonmouseover=alert(document.cookie)>click me!</a>```![image](https://firebasestorage.googleapis.com/v0/b/searchistory.appspot.com/o/images%2F45d84e1d-5c54-48df-ae98-8ec8c7a6cdd1?alt=media&token=4c436dd6-92f7-41a2-8211-0cd41f769278)
1についての関数(文字列を「```」で分割する。)
const originWord = "```javascript <inCodeWord> ``` <outCodeWord> ```javascrpt <inCodeWord> ```"
const sanitize = (originWord) => {
const splitReg = /(?=```)/
const splitedArray = originWord.split(splitReg);
console.log(splitedArray);
}
//結果
//["```javascript <inCodeWord> ","``` <outCodeWord> ","```javascrpt <inCodeWord> ","```"]
// 正規表現の/(?=```)/でsplitすることにより肯定先読みで、
// ```の直前の位置で文字列を分割し配列化する。
2についての実装追加(分割されたブロックがそれぞれコードブロック部分か、そうでないかを判別する。)
配列の各文字列の先頭の```の直後に文字(言語名)が続く場合はコードブロック内、そうでなければコードブロック外と判定する。
const originWord = "```javascript <inCodeWord> ``` <outCodeWord> ```javascrpt <inCodeWord> ```"
const sanitize = (originWord) => {
const splitReg = /(?=```)/
const splitedArray = originWord.split(splitReg);
for (let i = 0; i < splitedArray.length; i++) {
const splitReg = /^```\w+\s/
if (!splitReg.test(splitedArray[i])) {
console.log("out code");
} else {
console.log("in code");
}
}
}
sanitize(originWord)
//結果
//"in code", "out code", "in code"
3についての実装追加(コードブロック外の文字列の場合、上記の記号を置き換える。)
const originWord = "```javascript <inCodeWord>' ``` <outCodeWord> ```javascrpt <inCodeWord> ```"
//記号置き換え関数を追加
const escapeHTML = (word) => {
return word.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, "'");
}
const sanitize = (originWord) => {
const splitReg = /(?=```)/
const splitedArray = originWord.split(splitReg);
for (let i = 0; i < splitedArray.length; i++) {
const splitReg = /^```\w+\s/
if (!splitReg.test(splitedArray[i])) {
const sanitizedBlockWord = escapeHTML(splitedArray[i])
console.log(sanitizedBlockWord)
} else {
console.log(splitedArray[i]);
}
}
}
sanitize(originWord)
//結果
//"```javascript <inCodeWord>' "
//"``` <outCodeWord> "
//"```javascrpt <inCodeWord> "
4についての実装追加(画像URL内の&については置き換えない。)
//略
const originWord = "```javascript & ``` media&token &```"
const escapeHTML = (word) => {
return word.replace(/(?<!(media))&(?!(token))?/g, '&')//修正
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, "'");
}
//略
//結果
//"```javascript & "
//"``` media&token &"
//"```"
//escapeHTMLの&の置き換え関数を修正
//画像URLに含まれる&は必ず media&tokenという形式なので、正規表現で置き換え対象から除外する。
5の実装追加(分割した文字列を再結合する。)
const originWord = "```javascript <inCodeWord> ``` <outCodeWord>& media&token ```javascrpt <inCodeWord> ```"
const escapeHTML = (word) => {
return word.replace(/(?<!(media))&(?!(token))?/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, "'");
}
const sanitize = (originWord) => {
const splitReg = /(?=```)/
const splitedArray = originWord.split(splitReg);
let sanitizedAllWord = ""
for (let i = 0; i < splitedArray.length; i++) {
const splitReg = /^```\w+\s/
if (!splitReg.test(splitedArray[i])) {
sanitizedAllWord = sanitizedAllWord + escapeHTML(splitedArray[i]);
} else {
sanitizedAllWord = sanitizedAllWord + splitedArray[i];
}
}
return sanitizedAllWord;
}
console.log(sanitize(originWord));
//結果
//"```javascript <inCodeWord> ``` <outCodeWord>< media&token ```javascrpt <inCodeWord> ```"
以上です。