اعتبارًا من الإصدار 120 من Chrome، يتوفّر خيار unsanitized
جديد في واجهة برمجة التطبيقات Async Clipboard
API. يمكن أن يساعدك هذا الخيار في حالات خاصة تتعلّق بتنسيق HTML، حيث تحتاج إلى
لصق محتويات الحافظة بالطريقة نفسها التي تم بها نسخها.
وهذا يعني أنّه بدون أي خطوة تطهير وسيطة تطبّقها المتصفّحات عادةً
لأسباب وجيهة. يمكنك الاطّلاع على كيفية استخدامها في هذا الدليل.
عند استخدام واجهة برمجة التطبيقات Async Clipboard API، في معظم الحالات، لا يحتاج المطوّرون إلى القلق بشأن سلامة المحتوى في الحافظة ويمكنهم افتراض أنّ ما يكتبونه في الحافظة (النسخ) هو نفسه ما سيحصلون عليه عند قراءة البيانات من الحافظة (لصق).
وينطبق ذلك بالتأكيد على النصوص. جرِّب لصق الرمز البرمجي التالي في DevTools
Console ثم إعادة التركيز على الصفحة على الفور. (يجب استخدام setTimeout()
لكي يكون لديك وقت كافٍ للتركيز على الصفحة، وهو شرط واجهة برمجة التطبيقات Async
Clipboard API.) كما ترى، الإدخال تمامًا مطابق للإخراج.
setTimeout(async () => {
const input = 'Hello';
await navigator.clipboard.writeText(input);
const output = await navigator.clipboard.readText();
console.log(input, output, input === output);
// Logs "Hello Hello true".
}, 3000);
يختلف الأمر قليلاً في ما يتعلّق بالصور. لمنع الهجمات التي يُطلق عليها اسم الضغط المكثّف، تعيد المتصفّحات кодَة الصور، مثل ملفات PNG، ولكن الصور المُدخلة والصور الناتجة بصريًا تتطابق تمامًا بكسلًا بكسل.
setTimeout(async () => {
const dataURL =
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII=';
const input = await fetch(dataURL).then((response) => response.blob());
await navigator.clipboard.write([
new ClipboardItem({
[input.type]: input,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const output = await clipboardItem.getType(input.type);
console.log(input.size, output.size, input.type === output.type);
// Logs "68 161 true".
}, 3000);
ماذا يحدث لنص HTML؟ كما هو متوقّع، يختلف الموقف مع HTML. في هذه الحالة، ينظّف المتصفّح رمز HTML لمنع حدوث
أمور سيئة، على سبيل المثال، من خلال إزالة علامات <script>
من رمز HTML (وغيرها مثل <meta>
و<head>
و<style>
) وعن طريق تضمين CSS.
راجِع المثال التالي وجرِّبه في وحدة تحكّم أدوات المطوّرين. ستلاحظ
أنّ النتيجة تختلف اختلافًا كبيرًا عن الإدخال.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read();
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
إنّ إزالة المحتوى غير المرغوب فيه من صفحات HTML أمر جيد بشكل عام. لا تريد أن تتعرّض لمشاكل أمنية من خلال السماح بمحتوى HTML غير المحوَّل في معظم الحالات. ومع ذلك، هناك سيناريوهات يعرف فيها المطوّر ما يفعله بالضبط ويُعدّ فيه تكامل تنسيق HTML للدخل والخرج أمرًا بالغ الأهمية ليعمل التطبيق بشكلٍ صحيح. وفي هذه الظروف، لديك خياران:
- إذا كنت تتحكّم في كل من عملية النسخ واللصق، على سبيل المثال، إذا كنت تنسخ من داخل تطبيقك لتلصقها بالمثل داخل تطبيقك، عليك استخدام تنسيقات الويب المخصّصة لواجهة برمجة التطبيقات Async Clipboard API. يُرجى التوقف عن القراءة هنا والاطّلاع على المقالة المرتبطة.
- إذا كنت تتحكّم فقط في عملية اللصق في تطبيقك، وليس في عملية النسخ،
ربما لأنّ عملية النسخ تحدث في تطبيق أصلي لا يتيح استخدام
التنسيقات المخصّصة للويب، عليك استخدام الخيار
unsanitized
الذي تتم توضيحه في بقية هذه المقالة.
تشمل عملية التطهير إجراءات مثل إزالة علامات script
والأنماط المضمّنة، و
ضمان صحة تنسيق HTML. هذه القائمة غير شاملة، وقد تتم إضافة المزيد من الخطوات في المستقبل.
نسخ ولصق محتوى HTML غير المحوَّل
عند write()
(نسخ) محتوى HTML إلى الحافظة باستخدام Async Clipboard API،
يتأكّد المتصفّح من أنّه تمّ تشكيله بشكل صحيح من خلال تمريره عبر أحد برامج تحليل بنية المستندات DOM
وتسلسل سلسلة HTML الناتجة، ولكن لا تتمّ عملية تطهير في
هذه الخطوة. ليس عليك اتخاذ أي إجراء. عند read()
محتوى HTML تم وضعه في ملف الحافظة من خلال تطبيق آخر، وتطبيق الويب الخاص بك يوافق على الحصول على محتوى بدرجة دقة كاملة ويحتاج إلى إجراء أي عملية تطهير في الرمز الخاص بك،
يمكنك تمرير عنصر خيارات إلى طريقة read()
باستخدام سمة
unsanitized
وقيمة ['text/html']
. في حال استخدامها بشكل منفصل، ستظهر على النحو التالي:
navigator.clipboard.read({ unsanitized: ['text/html'] })
. نموذج الرمز البرمجي التالي
أدناه هو نفسه النموذج الذي سبق أن عرضناه، ولكن هذه المرة مع الخيار unsanitized
. عند تجربته في "وحدة تحكّم أدوات المطوّرين"، ستلاحظ أنّ الإدخال
والناتج متطابقان.
setTimeout(async () => {
const input = `<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="ProgId" content="Excel.Sheet" />
<meta name="Generator" content="Microsoft Excel 15" />
<style>
body {
font-family: HK Grotesk;
background-color: var(--color-bg);
}
</style>
</head>
<body>
<div>hello</div>
</body>
</html>`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html'],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
console.log(input, output);
}, 3000);
توفّر المتصفّح ورصد الميزات
لا تتوفّر طريقة مباشرة للتحقّق مما إذا كانت الميزة متوافقة، لذا يستند رصد الميزة
إلى مراقبة السلوك. لذلك، يعتمد المثال التالي
على رصد ما إذا كانت علامة <style>
تبقى ظاهرة، ما يشير إلى توفّر التوافق، أو يتم تضمينها، ما يشير إلى عدم توفّر التوافق. يُرجى العلم أنّه لن يعمل هذا الإجراء ما لم تكن الصفحة قد حصلت على إذن استخدام الحافظة.
const supportsUnsanitized = async () => {
const input = `<style>p{color:red}</style><p>a`;
const inputBlob = new Blob([input], { type: 'text/html' });
await navigator.clipboard.write([
new ClipboardItem({
'text/html': inputBlob,
}),
]);
const [clipboardItem] = await navigator.clipboard.read({
unsanitized: ['text/html],
});
const outputBlob = await clipboardItem.getType('text/html');
const output = await outputBlob.text();
return /<style>/.test(output);
};
عرض توضيحي
للاطّلاع على الخيار unsanitized
أثناء استخدامه، يمكنك الاطّلاع على
العرض التقديمي على Glitch والاطّلاع على
رمز المصدر.
الاستنتاجات
كما هو موضّح في المقدمة، لن يحتاج معظم المطوّرين إلى القلق بشأن
تطهير الحافظة ويمكنهم العمل فقط مع خيارات التطهير التلقائية
التي يختارها المتصفّح. في الحالات النادرة التي يحتاج فيها المطوّرون إلى الانتباه، يتوفّر خيار
unsanitized
.
روابط مفيدة
الشكر والتقدير
راجعت أنوبام سنيغدها و راشيل أندرو هذه المقالة. حدّد فريق Microsoft Edge واجهة برمجة التطبيقات و نفّذها.